Skip to content

roots/list from the server can race the client's handler registration → -32601 Method not found #1797

Description

@cliffhall

Symptom

Connecting to npx -y @modelcontextprotocol/server-filesystem /tmp, the server's roots/list request is answered with:

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

Connecting to server-everything, the same roots/list succeeds and returns the configured roots. Same client, same settings — only the timing of the server's request differs.

Cause

InspectorClient advertises the roots capability at Client construction time (core/mcp/inspectorClient.ts:587-589), so the server sees roots support in the initialize result and may issue roots/list as soon as it receives notifications/initialized.

The roots/list request handler, however, is not registered until core/mcp/inspectorClient.ts:1369-1374, which runs after:

  • await this.client.connect(this.transport) (:1173 / :1200) — connect() is what sends notifications/initialized
  • await this.fetchServerInfo() (:1215)
  • dispatchTypedEvent("connect") and the optional await client.setLoggingLevel(...) (:1220)

That leaves a window in which the server has been told we support roots but the SDK Client has no handler registered, and it replies -32601.

The same window applies to the other peer-request handlers registered in that block: sampling/createMessage (:1292), elicitation/create (:1360), and the receiver-task handlers tasks/list / tasks/get / tasks/result / tasks/cancel (:1385-1413).

server-filesystem requests roots immediately on initialized (that is how it learns its allowed directories) and loses the race; server-everything requests them later and wins. The user-visible consequence is not just a red entry in the Protocol view — server-filesystem silently falls back to its CLI-argument directories instead of the roots configured in the Inspector.

Fix

Register the peer-request handlers before client.connect(...). None of them depend on server capabilities — they read only constructor-set state (this.roots, this.sample, this.elicit, this.receiverTasks).

The notification handlers further down that block (:1430+) that gate on this.capabilities?.tools?.listChanged etc. genuinely need post-initialize data and must stay where they are.

Tests

Integration coverage that a server issuing roots/list immediately on notifications/initialized gets the configured roots rather than -32601.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingv2Issues and PRs for v2

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions