fix(mcp): keep deferred tools callable by name - #571
Merged
Conversation
mcp-go v0.55.1 began re-running registered tool filters at call time (server.passesToolFilters) with the single requested tool, answering "tool '<name>' not found: tool not found" when a filter drops it. toolSurfaceFilter shapes tools/list visibility, not call authorization — so every tool outside a session's preset became uncallable: 141 of 178 tools under the `core` default. `gortex call <tool>` failed for any non-core name, and an agent session could no longer call a tool that tools_search had just promoted. Promote-on-demand still ran; the promoted tool was then filtered out at dispatch. The dispatcher now marks a tools/call whose name already passed IsToolEnabledForSession, and the surface filter lets that single-tool probe through. This does not widen any surface: the marker is only set for names the session's effective policy permits, and checkToolGate remains the authoritative per-call gate — hide-mode, facade, planning, workflow and host exclusions are unchanged. Tests cover the by-name call under core/defer, hide-mode still refusing a non-preset tool with its structured error, and tools/list staying narrow while the marker is set.
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.
The report
From community chat, on v0.63.0:
while
gortex tools listshowsexport_contextas a registered, readonly tool.Scope
Not CLI-specific and not one tool. Reproduced at HEAD: 141 of 178 tools are uncallable. Live (
core-preset) tools work, every deferred one fails:gortex context, the dedicated verb for the same handler, is equally dead. Agent sessions are affected too: any tooltools_searchpromotes stays uncallable for the rest of the session.Root cause
toolSurfaceFilteris registered viaserver.WithToolFilterto shape tools/list visibility. mcp-go v0.55.1 started re-running every registered filter at call time (server.passesToolFilters) against the single requested tool, and reports a filtered tool astool '<name>' not found: tool not found— the reported string. v0.54.1 has no such call.The v0.54.1 → v0.55.1 bump landed in bd5b44b and first shipped in v0.57.0, so every release since has carried this.
Promote-on-demand was never broken: the dispatcher promoted the tool into the live server and the call-time filter dropped it afterwards.
tool_profilestill reportsstatus: deferred, enabled: true, which the call then contradicts.internal/hooks/daemon_tool.goalready dials withTools:"full"and its comment describes this exact symptom (the Stop briefing's contracts section silently rendering nothing) — a workaround for the symptom, with the cause unfound until now.Why CI stayed green:
daemon_mcp_promote_test.goonly covers the frame-name peek, andlazy_tools_e2e_test.godispatches on a session-lesscontext.Background(), where the filter is permissive. Neither exercises a session with a forwarded preset, which is what every real client sends.The fix
The daemon dispatcher marks a
tools/callwhose name already passedIsToolEnabledForSession(WithAuthorizedToolCall), andtoolSurfaceFilterreturns early when it sees that marker alongside a single-tool slice matching it.No surface is widened. The marker is only set for names the session's effective policy permits, and
checkToolGateremains the authoritative per-call gate — hide-mode, facade-v1, planning, workflow and host exclusions are unchanged. The streamable-HTTP dispatcher delegates to the samemcpDispatcher.Dispatch, so it is covered.Tests
internal/mcp/promote_call_gate_test.go:core/deferreaches the handler (fails before this change with the reported error)tool_blocked_by_modeerror rather than a misleading "not found"tools/liststays narrow while the marker is set, so the carve-out cannot leak a tool into the visible surfaceVerification
go test -race ./internal/mcp/ ./cmd/gortex/cleangolangci-lint run ./internal/mcp/... ./cmd/gortex/...— 0 issuesgortex call export_contextandgortex call find_clonesboth return resultsNote for release
Users on v0.57.0 and later have had roughly two thirds of the tool surface unreachable from the CLI and from any session that forwarded a preset. Worth a patch release rather than waiting for the next minor.