putty
MCP

Authenticating

How an agent authenticates to the MCP server with an sk- key, and which calls need more.

Every Putty surface takes one bearer: either a Privy access token from a signed-in dashboard session, or an sk- API key. This page is about the key, because that is the one an agent holds.

The key

POST /account/api-keys mints one and returns the plaintext exactly once. The store keeps only the SHA-256 hash, a twelve-character display prefix, and the last four characters — there is no way to recover the plaintext afterwards, so copy it when you see it.

curl -X POST http://localhost:3001/account/api-keys \
  -H "Authorization: Bearer $PRIVY_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "label": "ci-deploy" }'

The key looks like sk-AbCdEfGh1jkLmnOpQrStUvWxYz0123456789aBcDeFg — the sk- prefix, then 43 base62 characters of CSPRNG entropy, 46 in all. The prefix is there so that GitHub secret scanning and tools like TruffleHog recognise an accidentally committed key rather than it sitting in a public repo unnoticed.

Treat it like any other secret: a CI secret manager, never source.

Sending it

Over HTTP

curl https://api.putty.wtf/agents \
  -H "Authorization: Bearer $PUTTY_API_KEY"

Over MCP

The Streamable HTTP transport requires the Accept header to list both content types. Send a POST with a JSON-RPC body:

curl -N https://mcp.putty.wtf/mcp \
  -H "Authorization: Bearer $PUTTY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

In an MCP client config, the same three values:

{
  "mcpServers": {
    "putty": {
      "type": "http",
      "url": "https://mcp.putty.wtf/mcp",
      "headers": {
        "Authorization": "Bearer sk-…",
        "Accept": "application/json, text/event-stream"
      }
    }
  }
}

The server is stateless

There is no session to establish. A client that only needs to discover or call tools can skip the initialize round-trip entirely, and a fresh server and transport are built per request — so two concurrent callers cannot cross responses, even if both start at JSON-RPC id 1.

What a key is not enough for

The tool set does not change between the two caller flavours, but some tools need more than a key. Anything scoped to a wallet resolves a Privy DID first, because a wallet belongs to a person and an sk- key names only an account. A caller holding just a key gets a clear refusal rather than a call against nobody's wallet.

That is the whole of Privy — its users, wallets, actions, and transactions — and the wallet half of the Blockchain category's LP tools.

Tools that do not need a wallet: the whole Catalog category, Brand where it is not applied to a wallet-scoped resource, Media, Project, Dexscreener, and the read-only RPC half of Blockchain.

What needs no authentication at all

These need no bearer, and a client can read them with no account:

  • GET /mcp/tools — the full tool list, 75 tools with every input and output schema, plus the categories and subcategories arrays.
  • GET /health — liveness. It performs no dependency checks, so a 200 means the process answered, not that Postgres is reachable.
  • POST /verify and GET /verify/methods — the free Verify. Paste a contract, get the permission diff and the plain-English readback. It never touches the credit ledger.

Beyond the MCP surface, thirteen HTTP operations across the API are free and public, including the token Scan, the Receipts page, the showcase listing and its badge, the leaderboard, the hook catalogue, and the brand preview. That list is on the free layer page, and it is the OpenAPI document's own — each of those operations is marked security: [].

Revoking

DELETE /account/api-keys/{id} revokes a key instantly; any new request presenting it gets a 401. GET /account/api-keys lists every key you have minted, including revoked ones, with its label, prefix, and last four characters — never the plaintext.

What an agent cannot do with a key

A key authenticates; it does not grant. Registering an agent with POST /agents grants nothing either — it names a workload so its spend is separately reportable. The agent can only make metered calls with a bearer the caller already holds, and it draws on the caller's credit balance.

The tools that move value or publish are request-shaped for the same reason. Onchain simulates and reports rather than sending; Social drafts and returns a link for a person to click; media_generate_video prices the job and submits nothing until a second call confirms it.

On this page