fix(filesystem): reject non-init requests before MCP handshake completes#4203
Open
nakshatra-nahar wants to merge 1 commit into
Open
Conversation
Currently, the secure-filesystem-server accepts and successfully handles tools/list (and other requests) before the MCP lifecycle handshake completes. Per the MCP spec, the only methods a server should accept pre-handshake are initialize and ping. The MCP TypeScript SDK does not currently expose a middleware or pre-dispatch hook for request gating, so we wrap the protocol's internal request handler map. After all tool registrations have installed SDK-managed tools/list and tools/call handlers, installHandshakeGate() replaces each entry except initialize and ping with a wrapper that throws InvalidRequest until oninitialized has fired. Add an integration test that exercises both directions: - tools/list before initialize -> rejected with code -32600 - full initialize -> notifications/initialized -> tools/list -> accepted Closes modelcontextprotocol#4195
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Currently
secure-filesystem-serveraccepts and successfully handlestools/list(and other requests) before the MCP lifecycle handshake completes. Per the MCP spec, the only methods a server should accept pre-handshake areinitializeandping. This PR adds a pre-dispatch gate that rejects all other methods with JSON-RPCInvalidRequest(-32600) until the handshake finishes.Server Details
@modelcontextprotocol/server-filesystem)src/filesystem/index.ts+ new integration test fileMotivation and Context
Closes #4195.
Returning a normal JSON-RPC
resultfortools/list(or anything else) before handshake:initialize.Implementation note (please flag if a different shape is preferred): the MCP TypeScript SDK does not currently expose a middleware or pre-dispatch hook for request gating, so
installHandshakeGate()wraps the protocol's internal_requestHandlersmap. It is invoked once, after allserver.registerTool(...)calls have run, and replaces each entry exceptinitializeandpingwith a thin wrapper that throwsInvalidRequestuntiloninitializedhas fired. A longer-term fix likely belongs at the SDK layer (modelcontextprotocol/typescript-sdk) so every reference server benefits; happy to file a follow-up issue there if you'd like.How Has This Been Tested?
src/filesystem/__tests__/handshake-gating.test.tsadds two integration tests, both spawningdist/index.jsand exchanging line-delimited JSON-RPC over stdio:tools/listsent withoutinitialize→ asserts response haserror.code === -32600and message matches/handshake|initialize/i, with noresultkey.initialize→notifications/initialized→tools/list) → asserts a populatedresult.toolsarray.index.tschange, rebuilt, re-ran the gating tests — Test 1 fails as expected, confirming the regression test catches the bug.npm test --workspace=src/filesystempasses: 8 test files, 148/148 tests (146 pre-existing + 2 new).npm run build --workspace=src/filesystemis clean (tsc exit 0, no warnings).Breaking Changes
No breaking changes for spec-compliant clients (all official MCP clients perform the
initializehandshake before sending other requests). Clients that incorrectly skip the handshake and previously got a successful response fromtools/listwill now receive a JSON-RPCInvalidRequesterror — which is the correct, spec-mandated behaviour.Types of changes
Checklist
Clientexercises this path via the existingstructured-content.test.tssuite)Additional context
The fix lives entirely inside
src/filesystem/index.ts(singleinstallHandshakeGate()function + one flag + one flag-set in the existingoninitializedcallback). The other reference servers in this repo (everything,fetch,git,memory,sequentialthinking,time) have the same underlying SDK behaviour and would benefit from the same gating; I intentionally kept this PR scoped tofilesystemto match the issue and to keep the diff reviewable. Happy to send follow-up PRs per server, or to wait for an SDK-level fix — whichever maintainers prefer.