Home · Guides

Answer initialize and tools/list without a token, or no directory can list you

If a hosted MCP endpoint returns 401 to an unauthenticated POST, catalogues that check the URL they list will record the server as down. It does not matter that the server is healthy and that a real client with a token works perfectly. The prober never gets a token.

That is what happened here. Four connectors were published with a red dot and the title "Server is not responding", and a second directory's remote-server entry could not be filed at all, because POST https://mcp.zovo.one/mcp/<name> answered:

HTTP 401
www-authenticate: Bearer realm="mcp.zovo.one", error="invalid_token"
{"error":"unauthorized","message":"This endpoint needs a token..."}

The endpoints were up the whole time. They simply refused to say so.

The boundary that works

Split the JSON-RPC methods into the ones that cannot touch a user's data and the ones that can. Answer the first group with no credential. Keep the second group behind auth.

MethodNeeds a token?Why
initializeNoNegotiates protocol version and capabilities. Reads nothing.
notifications/initializedNoAn acknowledgement with no payload.
tools/listNoReturns schemas that are already public in your registry manifest.
prompts/list, resources/list, resources/templates/listNoDeclarations, not content.
pingNoLiveness.
tools/callYesRuns code against a tenant's stored data.
resources/readYesReturns content, which is the thing you are protecting.

Measured against the live endpoint on 2026-09-09, with no Authorization header and no token in the path:

POST /mcp/invoice  {"method":"initialize"}       -> 200  serverInfo mcp-invoice 0.21.0
POST /mcp/invoice  {"method":"tools/list"}        -> 200  13 tools
POST /mcp/invoice  {"method":"resources/list"}    -> 200  resource declarations
POST /mcp/invoice  {"method":"ping"}              -> 200  {}
POST /mcp/invoice  {"method":"tools/call"}        -> 401  unauthorized
Authorization: Bearer <wrong token>, tools/list  -> 401  unknown_token

The last line is the one that keeps this honest. A credential that is present but wrong is still rejected. The unauthenticated path is only reachable when no credential was offered at all, so a bad token is never quietly downgraded to anonymous discovery.

Why this is not a security loosening

Three properties make the open half safe, and each is checkable rather than asserted.

  1. Those methods cannot read or write a tenant document. In this implementation they are served with an empty file map, so there is no tenant to leak from.
  2. The tool schemas they return are already published. They sit in the server.json manifest in the official registry and on the product pages of this site.
  3. The open path gets its own rate-limit bucket, keyed on client IP, set here at 120 calls an hour. That is more than any health check needs and far less than a free tier.

How common the mistake is

An independent measurement filed on the registry's issue tracker as modelcontextprotocol/registry#1626 on 2026-09-07 probed every listed entry that declares a network endpoint. Of 16,305 endpoints:

Outcome of a bare initialize then tools/listCountShare
Answered tools/list unauthenticated9,53258.5%
Required authentication4,13525.4%
Answered HTTP but not MCP1,4018.6%
Unreachable1,0436.4%
Timed out1030.6%
Returned a JSON-RPC error910.6%

So a quarter of listed remote servers refuse the handshake a prober sends, and the majority answer it. If you are in the quarter, a directory has no way to tell you apart from the 6.4 percent that are genuinely gone.

What a directory actually sends

Glama's published methodology, section 2.2, read 2026-09-09, describes its connector introspection as "tools/list, resources/list, prompts/list, and full schema capture", run on a continuous schedule rather than once at ingest. That is the same list as the table above, which is not a coincidence: those are the methods that describe a server rather than use it.

After the change here, the same connector listing carried zero occurrences of "not responding" and every health dot was green, with 25 connectors scored instead of 4.

If you cannot open the handshake

Some servers genuinely cannot. A gateway that resolves a tenant before it can enumerate tools has nothing truthful to say to an anonymous caller. Two options remain. Publish a stdio package or a bundle alongside the remote entry, so a catalogue that cannot reach the URL can still list the installable form. Or serve a fixed, tenant-independent tool list to unauthenticated callers and let the real list arrive after auth, which is honest as long as the anonymous list is a subset.

Sources: the live endpoint at mcp.zovo.one, probed 2026-09-09 with the exact requests shown; the method split and rate limit read from remote/src/index.ts in this repository; registry issue 1626 for the population figures; glama.ai/mcp/methodology section 2.2 for the introspection exchange. This project runs a hosted MCP endpoint for every server it publishes and hit this defect on all of them.

Questions

Does answering tools/list unauthenticated leak anything?

Only the tool schemas, and those are already public if you are listed anywhere: the registry manifest, your documentation and any directory entry all carry them. What must stay closed is tools/call and resources/read, because those touch stored data rather than describing it.

What about OAuth-protected servers?

The specification's authorization flow expects a 401 with a WWW-Authenticate header pointing at the resource metadata, and a client that understands OAuth will follow it. A directory health prober usually does not. If you need OAuth, publish an installable package too, so a catalogue has something it can verify.

Will a rate limit on the open path be enough?

Per-IP is the right key because there is no identity to use. 120 an hour was chosen here as more than any prober needs and less than a usable free tier. The list is served from module scope with no storage read, so the cost of a request on this path is small.

How do I test this the way a directory does?

Send one POST with Content-Type application/json, Accept including both application/json and text/event-stream, and a body of {"jsonrpc":"2.0","id":1,"method":"tools/list"}. No Authorization header. If you get anything other than 200 with a tools array, a directory sees your server as broken.

Is a GET on the endpoint enough?

No, and that was the trap here. GET returned 200 with a description page while POST returned 401, so a shallow check passed and the real handshake failed. Probe with the method a client would actually send.

Related

All MCP servers and prices · All guides · Buy the bundle $39