DEV Community

Akash Das
Akash Das

Posted on

A pre-flight checklist for shipping a Claude connector

Writing an MCP server is the easy part. Shipping one as a Claude connector means passing four gates that have nothing to do with your business logic: Anthropic's network can reach you, Claude can get an OAuth client identity, a human reviewer approves your tool design, and your server does not waste the user's context window.

Here is the checklist I wish I had had, in the order the failures actually happen.

Gate 1 — Can Anthropic's infrastructure reach you?

Claude connects from Anthropic's servers, not from your laptop. So the tests you run at your desk prove almost nothing. Run these from a network that is not yours:

# Every returned address must be globally routable.
# Any 10.x, 172.16–31.x, 192.168.x, 100.64.x, loopback or link-local
# address in the answer kills the connection before an HTTP request is sent.
dig +short your-server.example.com

# Connectors are IPv4-only. Empty first line + populated second line = the bug.
dig +short A    your-server.example.com
dig +short AAAA your-server.example.com

# A 301/302/307/308 to a different host strips the Authorization header
# (RFC 9110 §15.4). The target answers 401 and Claude reports an auth failure.
curl -sSI https://your-server.example.com/mcp | grep -i '^location:'
Enter fullscreen mode Exit fullscreen mode

Checks:

  • [ ] DNS answer contains only public addresses, from outside your network
  • [ ] An A record exists, not just AAAA
  • [ ] The MCP URL does not cross-host redirect

If your access log is empty while Claude says Couldn't reach the MCP server, one of those three is why. Full teardown of all four documented causes: Claude cannot reach your MCP server, but curl can.

For local development, tunnel instead of fighting this: cloudflared tunnel --url http://localhost:3000 or ngrok http 3000.

Gate 2 — Can Claude get an OAuth client ID?

Incompatible auth server: does not support dynamic client registration
Enter fullscreen mode Exit fullscreen mode

The obvious fix is the wrong one for most public connectors. Dynamic Client Registration mints a fresh OAuth client on every new connection — that is a row per connection, not per customer, so a busy connector slowly fills your identity provider with junk clients.

Claude accepts three ways of getting an identity:

Method What you host Good fit for
oauth_dcr (RFC 7591) a POST /register endpoint internal servers, few users
oauth_cimd a static JSON document at an HTTPS URL public connectors, high traffic
oauth_anthropic_creds nothing new — you mail Anthropic a client ID and secret teams who cannot change their IdP

The CIMD trap is worth memorising, because it fails silently. Claude picks CIMD only when your metadata says both of these:

{
  "client_id_metadata_document_supported": true,
  "token_endpoint_auth_methods_supported": ["none"]
}
Enter fullscreen mode Exit fullscreen mode

Miss the second and Claude falls back to dynamic registration, and then fails with the error above even though your CIMD is fine.

Checks:

  • [ ] Metadata advertises both CIMD fields, if you are using CIMD
  • [ ] registration_endpoint is omitted, not set to nullnull fails schema validation rather than being ignored
  • [ ] You have decided DCR vs CIMD on expected connection volume, not on which error message you saw first

Comparison of the three methods and when each one is right: Claude cannot register with your OAuth server. Now what?.

Gate 3 — Will directory review reject your tool design?

This one has an automatic-fail that a lot of servers ship on day one:

// Rejected. One tool, safe and unsafe methods in the same surface.
{
  "name": "api_request",
  "inputSchema": {
    "properties": { "method": { "enum": ["GET", "POST", "DELETE"] } }
  }
}
Enter fullscreen mode Exit fullscreen mode

Read and write must be separate tools, and writes should be split further by action where you can — create, update, delete. No description text saves the combined version.

Checks:

  • [ ] You are on a Team or Enterprise plan (the portal is in Claude.ai org settings; individual plans cannot submit at all)
  • [ ] No tool accepts both safe and unsafe HTTP methods
  • [ ] Freeform query tools name or link the target API in their description — "Queries the Slack Web API" passes, "Makes a request to the API" fails
  • [ ] Every tool has a title plus readOnlyHint: true or destructiveHint: true
  • [ ] Every tool name is 64 characters or fewer
  • [ ] No description tells Claude what to do, calls other software, or embeds hidden or encoded text
  • [ ] Privacy policy is real, not a stub — a thin one is an instant fail for local connectors

Those hints are not paperwork. They drive auto-permissions, so read-only tools can run without prompting each time. Skip them and your connector is both non-compliant and slower to use. The rest of the rejection triggers: What gets a Claude connector rejected from the directory.

Gate 4 — What does your connector cost the context window?

Stop splitting servers to save context. Tool search ships on by default in Claude Code: only tool names and server instructions load at session start, and full schemas arrive on demand. Anthropic's reference says adding more servers has minimal impact on the window, with no fixed per-server tool cap.

What still costs you:

  • Each tool description and each server instructions block is truncated at 2KB
  • Tool output warns above 10,000 tokens and is capped at 25,000 by default (raise with MAX_MCP_OUTPUT_TOKENS)
  • Deferral turns off entirely under several conditions, including ANTHROPIC_BASE_URL on a non-first-party host — so a team gateway silently restores the old up-front cost

You can also opt one server out on purpose:

{
  "mcpServers": {
    "core-tools": {
      "type": "http",
      "url": "https://mcp.example.com/mcp",
      "alwaysLoad": true
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Checks:

  • [ ] Descriptions and instructions fit in 2KB, before truncation picks the cut for you
  • [ ] Large tool results are paginated or filtered server-side, not dumped
  • [ ] You know whether your org's gateway config is disabling tool search

The full table of conditions that keep tools loading up front: Your MCP connector spends context before you type.

Optional gate — should it draw a UI at all?

MCP Apps is the first official MCP extension: your server returns interactive HTML, the client renders it in a sandboxed iframe, and the page talks back over JSON-RPC on postMessage. Claude, ChatGPT, VS Code and Goose all render it, so it is portable rather than a single-vendor bet.

Worth it when the result is genuinely visual — a brushable scatter chart, a map, compiled shader output. Not worth it when your tool returns three fields. Directory submission for an MCP App also wants 3 to 5 PNG screenshots at 1000px or wider, and any link destination missing from your allowed link URIs makes the user confirm every click.

Run an example server locally first; it takes about five minutes:

{
  "mcpServers": {
    "qr": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/qr-server", "--stdio"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The build-vs-skip trade-off in full: Should your Claude connector draw its own UI?.


TL;DR

Four of the five ways a connector fails are environmental, not logical — network reachability, client identity, review criteria, and client-side budget. Your code being correct is exactly what makes them hard to find. Run the dig checks from outside your network, choose CIMD if you expect volume, split read from write before you submit, and stop hand-optimising a context cost the client already handles.

What has bitten you shipping an MCP server? The redirect-strips-the-token one cost me the most time.

Top comments (0)