Skip to content
all posts

Claude.ai said no MCP server was found at my URL. It was a 404 on a method I hadn't implemented.

4 min readSubscribe via RSS

I built a remote MCP server into this site so Claude could draft blog posts for me. OAuth went through, and then claude.ai said: "Your account was authorized, but no MCP server was found at the provided URL, or your account doesn't have access to it."

When I searched that message the only direct hit was an unanswered GitHub issue, so here's what it was in my case. The server only implemented tools. The client called prompts/list, the server answered HTTP 404, and claude.ai took the 404 to mean there was no server there at all. If you've written a tools-only server on the official TypeScript SDK and you're seeing this error, check that first.

The site is Next.js with Payload CMS, and the server lives at /api/mcp using mcp-handler (2.2) on top of @modelcontextprotocol/server (2.3). Four tools: list_posts, get_post, create_draft_post and update_draft. The site is its own OAuth 2.1 authorisation server, with PKCE and Client ID Metadata Documents and no client registration endpoint. There was an earlier, unrelated failure where the discovery metadata advertised http://localhost:3000, because I'd built the URLs from NEXT_PUBLIC_SITE_URL and hadn't set it in production. I build them from the request host now.

With that fixed, OAuth completed and claude.ai showed the error above. What I checked, in order:

  1. The live discovery metadata and the 401 challenge on /api/mcp, with curl. Both fine.
  2. Response times on the token endpoint and the MCP endpoint. Under a second.
  3. Whether requests identifying as a Claude user agent were being blocked. No.
  4. DNS. Fine.
  5. Whether I'd given claude.ai the www host. The apex redirects to www, and clients drop the Authorization header on a cross-host redirect, so that was a real suspect. I had used www. Ruled out.

None of that moved anything, so I pointed Claude Code (the CLI) at the same URL. It connected, listed the tools, and a tool call worked. That split the problem in two: either the server had a bug that only one client tripped, or Anthropic's side couldn't reach it. Internally the connector was reporting not_found_error: No MCP endpoint was found at the URL provided (mcp_endpoint_not_found).

I found the cause by reading the SDK source. It supports two families of protocol revision, and they answer an unimplemented method differently:

Protocol revision Request for a method with no handler
2025 revisions (2025-03-26, 2025-06-18, 2025-11-25) HTTP 200, with a JSON-RPC error in the body: code -32601, "Method not found"
2026-07-28, which the SDK calls the modern protocol HTTP 404, same body

The 404 comes from a table in the server package called LADDER_ERROR_HTTP_STATUS. It maps errors raised by the SDK's own pre-handler routing (it calls this the ladder) to HTTP statuses, while errors raised inside your request handlers stay in-band on 200:

Ladder error HTTP status
Parse error 400
Invalid request 400
Method not found 404
Unsupported protocol version 400

A method with no handler never reaches a handler, so it goes through the table.

That's the whole difference between the two clients. Claude Code negotiated the older protocol, got the in-band errors, and carried on. claude.ai negotiated 2026-07-28, called prompts/list and resources/list on connect as clients do, got a 404, and reported no endpoint.

I reproduced it locally with curl. prompts/list, resources/list, resources/templates/list and even ping all came back as 404 with the MCP-Protocol-Version: 2026-07-28 header (plus the _meta envelope that revision requires), and as 200 with MCP-Protocol-Version: 2025-11-25. Same body both times:

{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"Method not found"}}

The fix is to declare the prompts and resources capabilities and answer the listings with empty results.

const mcpHandler = createMcpHandler(
  (server) => {
    registerBlogTools(server)

    server.server.setRequestHandler('prompts/list', () => ({ prompts: [] }))
    server.server.setRequestHandler('resources/list', () => ({ resources: [] }))
    server.server.setRequestHandler('resources/templates/list', () => ({ resourceTemplates: [] }))
  },
  {
    serverInfo: { name: 'ralton-dev-blog', version: '1.0.0' },
    capabilities: { prompts: {}, resources: {} },
  }
)

Deployed that and claude.ai connected.

I haven't done anything about ping. It still answers 404 under the newer protocol, and so far nothing has minded. I haven't reported it anywhere either. I don't know whose behaviour is wrong here, the SDK's for turning a method-not-found into a 404 or the client's for reading a 404 as a missing server.