Skip to content

fix(mcp): stop the stdio proxy gating tools/call in defer mode#247

Merged
zzet merged 1 commit into
zzet:mainfrom
vitaliyslion:fix/proxy-defer-mode-call-gating
Jul 4, 2026
Merged

fix(mcp): stop the stdio proxy gating tools/call in defer mode#247
zzet merged 1 commit into
zzet:mainfrom
vitaliyslion:fix/proxy-defer-mode-call-gating

Conversation

@vitaliyslion

Copy link
Copy Markdown
Contributor

Summary

The stdio proxy (gortex mcp --tools / GORTEX_TOOLS) hard-blocked tools/call to any tool outside the configured surface regardless of --tools-mode, contradicting the flag's own help text ("defer (keep reachable via tools_search)"). In defer mode a client that calls tools_search gets its matches promoted server-side, but the proxy then synthesized a -32601 for every call to them — promotion worked, calling the promoted tool didn't.

Reproduced live: with GORTEX_TOOLS=core GORTEX_TOOLS_MODE=defer on the proxy, tools_search query:"select:taint_paths" promote:true succeeded, but the follow-up tools/call taint_paths came back tool "taint_paths" is not available in this client's tool set (gortex mcp --tools).

The fix mirrors the server-side defer semantics on the proxy: defer trims tools/list but never gates calls; hide (the default) keeps blocking both, unchanged.

Changes

  • internal/mcp/tool_presets.go: add ToolSurface.GateCalls() — true only for an active surface in hide mode; nil/inactive/defer surfaces gate nothing.
  • cmd/gortex/proxy_filter.go: gateToolCallFrame now checks surface.GateCalls() instead of surface.Active(), so defer mode forwards every tools/call to the daemon while filterToolsListFrame still trims tools/list. Hide-mode behavior is byte-for-byte unchanged.
  • cmd/gortex/proxy_filter_test.go: new defer-mode case in TestGateToolCallFrame — asserts a disallowed tool is not gated in defer mode and that tools/list is still trimmed.

Testing

  • All tests pass (go test -race ./...)
  • New tests added for new functionality
  • Benchmarks run if performance-relevant

Checklist

  • Code follows existing patterns in the codebase
  • No unnecessary abstractions added
  • Language extractor includes Meta["methods"] for interfaces (if applicable)
  • Methods have EdgeMemberOf edges to their containing type (if applicable)

@vitaliyslion

Copy link
Copy Markdown
Contributor Author

How to reproduce

  1. Make sure the daemon is up and ready.
  2. Run a preset-scoped proxy in defer mode, then promote taint_paths via tools_search, then try to call it:
(
  printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"repro","version":"0"}}}'
  sleep 1
  printf '%s\n' '{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}'
  printf '%s\n' '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"tools_search","arguments":{"query":"select:taint_paths","promote":true}}}'
  sleep 3
  printf '%s\n' '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"taint_paths","arguments":{"source_pattern":"getenv","sink_pattern":"exec","limit":1}}}'
  sleep 5
) | GORTEX_TOOLS=core GORTEX_TOOLS_MODE=defer gortex mcp

(The sleeps just keep stdin open long enough for the async responses to come back.)

  1. What you'll see on main (the bug):
  • The id:2 response succeeds — its payload includes "promoted":["taint_paths"], i.e. the server-side promotion worked.
  • The id:3 response is the contradiction:
{"jsonrpc":"2.0","id":3,"error":{"code":-32601,"message":"tool \"taint_paths\" is not available in this client's tool set (gortex mcp --tools)"}}

Note it comes back instantly — the proxy synthesizes the error locally in gateToolCallFrame (cmd/gortex/proxy_filter.go) without ever forwarding the frame to the daemon, because it checks surface.Allows(name) but never consults the mode. That's the bug

@zzet zzet merged commit 4e756cd into zzet:main Jul 4, 2026
9 of 10 checks passed
@zzet

zzet commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Good catch @vitaliyslion! Merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants