fix(filesystem): wait for roots initialization before handling tool calls#3806
Draft
majiayu000 wants to merge 2 commits intomodelcontextprotocol:mainfrom
Draft
Conversation
Add a Promise-based gate that blocks tool handlers until allowedDirectories are initialized. This prevents a race condition where tool calls arrive before oninitialized finishes fetching roots, causing false "Access denied" errors. If CLI args already provide directories the gate opens immediately. Otherwise it opens when oninitialized completes (via try/finally). Fixes modelcontextprotocol#3204 Signed-off-by: majiayu000 <1835304752@qq.com>
Extract the inline Promise gate into roots-gate.ts with a configurable timeout (default 10s) to prevent indefinite hangs when oninitialized never fires. Add unit tests for immediate resolution, delayed resolution, timeout rejection, and concurrent waiters. Signed-off-by: majiayu000 <1835304752@qq.com>
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.
Fixes #3204
Description
Tool calls can arrive before
oninitializedfinishes fetching roots, soallowedDirectoriesis empty and every path check returns "Access denied". This adds a Promise-based gate that blocks tool handlers until roots are loaded.The SDK fires
oninitializedwithout awaiting it (() => this.oninitialized?.()in SDK index.js:53). The filesystem server's handler asynchronously callsserver.server.listRoots(), but tool handlers run immediately against an emptyallowedDirectories.Server Details
Motivation and Context
When CLI args aren't provided and roots come from the client, there's a window where tool calls hit empty
allowedDirectories. This is the race condition described in #3204.How Has This Been Tested?
__tests__/roots-gate.test.tscovering: immediate resolution, delayed resolution, timeout, rejection, and concurrent waiters.lib.test.tshas a pre-existingvi.mockfailure on main, unrelated to this change.)npx tsc --noEmitpasses clean.Breaking Changes
None. If CLI args already provide allowed directories, the gate resolves immediately and there's no behavior change. The 10s timeout only applies when waiting for client-provided roots.
Types of changes
Checklist
Additional context
What changed:
roots-gate.ts(~25 lines) -createRootsGate(timeoutMs)factory returning{ promise, resolve, waitForReady }. Races the internal promise against a configurable timeout.index.ts- imports the gate, resolves it immediately when CLI args provide directories, resolves it inoninitializedafter roots load, and addsawait gate.waitForReady()at the top of all 13 tool handlers.__tests__/roots-gate.test.ts- 5 test cases for the gate in isolation.