> ## Documentation Index
> Fetch the complete documentation index at: https://paperplane-justin-winter-s-projects.vercel.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP server

> Give Claude or any MCP client the ability to send real physical mail.

## Before you connect

No account, no API key, no signup. The endpoint is public, streamable HTTP,
and takes anonymous calls by default — `sandbox: true` works on the very
first call from a fresh install. An optional developer API key
(`Authorization: Bearer pp_live_…` or `pp_test_…` for sandbox-only) narrows
what a credential can do (spend caps, scoped tools) if you want that; it is
never required. A key that's sent and doesn't verify is refused outright,
not silently downgraded to anonymous — so a typo fails loudly instead of
quietly widening access.

```
https://sendpaperplane.com/api/mcp
```

## Connect

Every client speaks to the same URL above. Pick yours below for the exact
config — the shapes differ more than you'd expect (a `url` field here, an
`httpUrl` there, a whole different top-level key in VS Code), which is
exactly the kind of thing worth copy-pasting rather than guessing.

<AccordionGroup>
  <Accordion title="Claude Code" icon="terminal">
    ```bash theme={null}
    claude mcp add --transport http paperplane https://sendpaperplane.com/api/mcp
    ```

    Add `--scope user` to make it available across all your projects instead of
    just this one, or `--scope project` to share it with teammates via a
    committed `.mcp.json`. With an optional API key:

    ```bash theme={null}
    claude mcp add --transport http paperplane https://sendpaperplane.com/api/mcp \
      --header "Authorization: Bearer pp_live_..."
    ```
  </Accordion>

  <Accordion title="Claude Desktop" icon="message-circle">
    Remote MCP servers connect through **Settings → Connectors → Add custom
    connector** — not by hand-editing `claude_desktop_config.json`, which only
    recognizes locally-spawned servers.

    1. Settings → Connectors → Add custom connector
    2. Paste `https://sendpaperplane.com/api/mcp`
    3. Add

    For a Team or Enterprise workspace, an org owner adds it once under
    Organization Settings → Connectors, and members connect it from their own
    Connectors panel.
  </Accordion>

  <Accordion title="Cursor" icon="arrow-pointer">
    [One-click install](cursor://anysphere.cursor-deeplink/mcp/install?name=paperplane\&config=eyJ1cmwiOiJodHRwczovL3BhcGVycGxhbmUuZXhhbXBsZS9hcGkvbWNwIn0=) (officially documented at [cursor.com/docs/mcp/install-links](https://cursor.com/docs/mcp/install-links)), or add to `~/.cursor/mcp.json` (or a project-local `.cursor/mcp.json`) by hand:

    ```json theme={null}
    {
      "mcpServers": {
        "paperplane": { "url": "https://sendpaperplane.com/api/mcp" }
      }
    }
    ```
  </Accordion>

  <Accordion title="VS Code / GitHub Copilot" icon="microsoft">
    `.vscode/mcp.json`, or via the **MCP: Open User Configuration** command for a
    user-wide install. Note the top-level key is `servers`, not `mcpServers` —
    VS Code's own file, not the community convention most other clients share:

    ```json theme={null}
    {
      "servers": {
        "paperplane": { "type": "http", "url": "https://sendpaperplane.com/api/mcp" }
      }
    }
    ```

    Requires VS Code 1.101 or newer.
  </Accordion>

  <Accordion title="Windsurf" icon="wind">
    `~/.codeium/windsurf/mcp_config.json` — the field is `serverUrl`, not `url`:

    ```json theme={null}
    {
      "mcpServers": {
        "paperplane": { "serverUrl": "https://sendpaperplane.com/api/mcp" }
      }
    }
    ```
  </Accordion>

  <Accordion title="Cline" icon="code">
    Via Cline's MCP settings UI, or the underlying JSON. `type` must be the exact
    string `streamableHttp` (camelCase) — `http` or a missing `type` falls back
    to legacy SSE and gets a 405 against this endpoint:

    ```json theme={null}
    {
      "mcpServers": {
        "paperplane": {
          "type": "streamableHttp",
          "url": "https://sendpaperplane.com/api/mcp"
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Continue.dev" icon="infinity">
    `.continue/config.yaml` — note the hyphenated `streamable-http`, the opposite
    convention from Cline's camelCase:

    ```yaml theme={null}
    mcpServers:
      - name: paperplane
        type: streamable-http
        url: https://sendpaperplane.com/api/mcp
    ```
  </Accordion>

  <Accordion title="Gemini CLI" icon="sparkles">
    ```bash theme={null}
    gemini mcp add --transport http paperplane https://sendpaperplane.com/api/mcp
    ```

    The resulting `settings.json` entry uses **`httpUrl`**, not `url` — Gemini
    CLI reserves `url` for its older SSE transport, so if you're editing the
    file directly rather than using the CLI, use `httpUrl`:

    ```json theme={null}
    {
      "mcpServers": {
        "paperplane": { "httpUrl": "https://sendpaperplane.com/api/mcp" }
      }
    }
    ```
  </Accordion>

  <Accordion title="OpenAI Codex CLI" icon="terminal">
    `~/.codex/config.toml` (shared with the Codex IDE extension and desktop app):

    ```toml theme={null}
    [mcp_servers.paperplane]
    url = "https://sendpaperplane.com/api/mcp"
    ```

    Remote HTTP support landed recently — if your Codex CLI is older and
    silently ignores a `url`-only entry, either upgrade or add the feature flag
    above the server table:

    ```toml theme={null}
    [features]
    rmcp_client = true
    ```
  </Accordion>

  <Accordion title="Amp" icon="terminal">
    ```bash theme={null}
    amp mcp remote add paperplane https://sendpaperplane.com/api/mcp --auth none
    ```

    Or add to Amp's settings directly — the field is `url`:

    ```json theme={null}
    {
      "amp.mcpServers": {
        "paperplane": { "url": "https://sendpaperplane.com/api/mcp" }
      }
    }
    ```
  </Accordion>

  <Accordion title="GitHub Copilot CLI" icon="terminal">
    Distinct from VS Code's Copilot integration above — this is the standalone
    `copilot` terminal tool.

    ```bash theme={null}
    copilot mcp add --transport http paperplane https://sendpaperplane.com/api/mcp
    ```

    Config lives at `~/.copilot/mcp-config.json`:

    ```json theme={null}
    {
      "mcpServers": {
        "paperplane": { "type": "http", "url": "https://sendpaperplane.com/api/mcp" }
      }
    }
    ```
  </Accordion>

  <Accordion title="Devin CLI" icon="terminal">
    ```bash theme={null}
    devin mcp add paperplane --url https://sendpaperplane.com/api/mcp
    ```
  </Accordion>

  <Accordion title="Factory CLI (droid)" icon="terminal">
    ```bash theme={null}
    droid mcp add paperplane https://sendpaperplane.com/api/mcp --type http
    ```
  </Accordion>

  <Accordion title="Grok Build CLI" icon="terminal">
    xAI's Grok Build CLI reuses Claude Code's config format directly rather than
    its own schema — same `.mcp.json`/`claude_desktop_config.json` shape as the
    Claude Code and Claude Desktop entries above. This is a new (2026) product;
    treat the format as provisional and re-check if it stops working.
  </Accordion>

  <Accordion title="JetBrains AI Assistant" icon="code">
    Settings → Tools → AI Assistant → Model Context Protocol, or add directly:

    ```json theme={null}
    {
      "mcpServers": {
        "paperplane": { "url": "https://sendpaperplane.com/api/mcp" }
      }
    }
    ```
  </Accordion>

  <Accordion title="Junie" icon="code">
    A separate JetBrains product from AI Assistant above, with its own config
    path — easy to conflate, but `.junie/mcp/mcp.json` (project) or
    `~/.junie/mcp.json` (global):

    ```json theme={null}
    {
      "mcpServers": {
        "paperplane": { "url": "https://sendpaperplane.com/api/mcp" }
      }
    }
    ```
  </Accordion>

  <Accordion title="Kiro" icon="code">
    `.kiro/settings/mcp.json` (workspace) or `~/.kiro/settings/mcp.json` (user):

    ```json theme={null}
    {
      "mcpServers": {
        "paperplane": {
          "type": "streamableHttp",
          "url": "https://sendpaperplane.com/api/mcp"
        }
      }
    }
    ```

    Kiro has had open bug reports around streamable-HTTP detection (sometimes
    misread as SSE) — if the connection fails, that's a known rough edge to
    check for, not necessarily a problem with this config.
  </Accordion>

  <Accordion title="Mistral Vibe" icon="terminal">
    `~/.vibe/config.toml` — note this is the one TOML-based config on this list
    besides Codex CLI, and the key names don't match it either:

    ```toml theme={null}
    [[mcp_servers]]
    name = "paperplane"
    transport = "streamable-http"
    url = "https://sendpaperplane.com/api/mcp"
    ```

    OAuth isn't supported yet — only static API-key/header auth, not relevant
    here since no auth is required.
  </Accordion>

  <Accordion title="OpenCode" icon="terminal">
    `opencode.json` — the only client on this list using `"type": "remote"`
    rather than `"http"`/`"streamableHttp"`:

    ```json theme={null}
    {
      "mcp": {
        "paperplane": { "type": "remote", "url": "https://sendpaperplane.com/api/mcp", "enabled": true }
      }
    }
    ```
  </Accordion>

  <Accordion title="Visual Studio" icon="microsoft">
    Distinct from VS Code above — same `"type": "http"` shape, but the
    top-level key is `servers`, matching VS Code rather than the community
    `mcpServers` convention:

    ```json theme={null}
    {
      "servers": {
        "paperplane": { "type": "http", "url": "https://sendpaperplane.com/api/mcp" }
      },
      "inputs": []
    }
    ```

    Files: `%USERPROFILE%\.mcp.json` (global) or `<SolutionDir>\.mcp.json`.
  </Accordion>

  <Accordion title="Warp" icon="terminal">
    ```json theme={null}
    {
      "paperplane": { "url": "https://sendpaperplane.com/api/mcp" }
    }
    ```

    Supports both streamable HTTP and SSE.
  </Accordion>

  <Accordion title="Zed" icon="code">
    Native support as of current Zed docs — no local proxy needed (older
    tutorials describing an `mcp-remote` stdio-bridge workaround are outdated):

    ```json theme={null}
    {
      "context_servers": {
        "paperplane": { "url": "https://sendpaperplane.com/api/mcp" }
      }
    }
    ```
  </Accordion>

  <Accordion title="Other MCP clients" icon="ellipsis">
    Any client that speaks the streamable HTTP MCP transport can point directly
    at the endpoint — most use a `url` key under `mcpServers` (occasionally
    `httpUrl` or `serverUrl` instead, as above; check your client's docs for
    which). If your client only supports locally-spawned (stdio) servers, use
    the [REST fallback](#rest-fallback-no-mcp) further down this page instead —
    it's the same tools and the same guarantees, over plain HTTP calls.
  </Accordion>
</AccordionGroup>

Tell your agent to try it once connected — this exercises the whole
quote → confirm → send loop with nothing charged and nothing mailed:

> Using the paperplane tools, quote a 1-page certified letter to Property
> LLC, 1 Main St, Richmond VA 23220, then send it in sandbox mode.

## The tools

| Tool                | Access | Effect                                                                                                                                                 |
| ------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `quote_letter`      | read   | Prices a piece and mints the `confirmation_token` that `send_letter` requires. Creates nothing, costs nothing.                                         |
| `send_letter`       | write  | Mails a physical piece. Requires a quote's `confirmation_token`. Returns a payment link for the human to approve.                                      |
| `get_letter_status` | read   | Where an order is: `draft`, `pending_payment`, `screening`, `held_for_review`, `submitted`, `mailed`, `delivered`, `refused`, `canceled`, `failed`.    |
| `buy_credits`       | write  | Buys a prepaid credit pack over x402 (Base USDC) from a funded agent wallet — no human checkout. Staged: see [Agent payments](/docs/guides/agent-payments). |

<Note>
  `quote_letter`/`send_letter` mail a **letter by default**, but both take an
  optional `format` argument for every other piece in the catalogue —
  `notecard` ($5.99, folded and enclosed like a greeting card) and the three
      postcard sizes (`postcard_4x6` $1.99, `postcard_6x9` $2.99, `postcard_6x11`
      $3.49 — open-face). All four are message-only: `text` prints on the piece
  itself, and there's no `pdf_url`/`upload_key` path for them. Pass the same
  `format` to both calls — the `confirmation_token` `quote_letter` mints is
  bound to it, so a token quoted for one physical piece is refused if
  `send_letter` is called with a different one, even at an identical price.
  See [Mail formats](/docs/concepts#mail-formats).
</Note>

## The required flow

<Steps>
  <Step title="1. Quote">
    `quote_letter` with the recipient, content, and class. Free. It returns the all-in price plus a single-use `confirmation_token` (30-min TTL) bound to those exact parameters.
  </Step>

  <Step title="2. Confirm with the user">
    Show the human the recipient and total. This is the confirmation step assistant platforms require before a purchase.
  </Step>

  <Step title="3. Send">
    `send_letter` with the SAME parameters and the `confirmation_token`. Any change invalidates it. Returns a `payment_url` for the human to approve (or a completed sandbox order). Pass a prepaid `credit_code` instead and the letter moves immediately, with no human payment step.
  </Step>

  <Step title="4. Track">
    `get_letter_status` until terminal.
  </Step>
</Steps>

```json theme={null}
// send_letter result (live)
{
  "status": "action_required",
  "order_id": "ord_...",
  "total": "$12.99",
  "payment_url": "https://checkout.stripe.com/...",
  "reason": "The sender must authorize payment before we print.",
  "capability": { "cancel_token": "ppc_...", "review_token": "ppc_..." }
}
```

<Note>
  A send is **never** the first call. If `confirmation_token` is missing, `send_letter` refuses — the quote→confirm→send discipline is enforced server-side.
</Note>

## Sandbox

Pass `sandbox: true` to run the whole flow — rendering, screening, simulated fulfillment with a mock tracking number — instantly and free, still requiring a token. An agent's first call can succeed before any human signs up.

## Capability tokens

`send_letter` returns `capability.cancel_token` and `capability.review_token`, required to cancel the order or review it once delivered. Bound to the order id + action, 7-day TTL. See [Core concepts](/docs/concepts).

## When a call fails

A tool failure is the same envelope the REST API returns — `status`, a stable
`code`, a human `reason`, and a `next` array of concrete steps — plus one field
only the tool surface carries:

```json theme={null}
{
  "status": "failed",
  "code": "address_undeliverable",
  "reason": "USPS cannot deliver to the recipient address as entered (not found).",
  "next": ["Check street number, spelling, and ZIP; then resubmit."],
  "retryable": false
}
```

`retryable` is the bit a tool-calling loop needs and cannot get from prose: it
is `true` only for rate limits and our own 5xx, never for anything the caller
has to change first. Both surfaces answer from the same mapping in
`lib/http.ts`, so a failure cannot be permanent over one wire and transient over
the other. Every `code` is listed in the [error contract](/docs/guides/errors).

## Content rules

Every letter is screened (fraud/threats only — lawful demand letters are fine). `refused` releases the payment.

## REST fallback (no MCP)

Every client eventually supports MCP, but if yours doesn't yet — or you're
scripting from somewhere that can't run one — the same three tools are two
plain HTTP calls with the same confirmation-token invariant, same error
envelope, same sandbox mode. Full walkthrough: [Quickstart](/docs/quickstart).
