fix(mcp): gate mcp serve through the tool dispatcher - #257
Merged
Conversation
Owner
Author
Review — approve, no changesThe severity here is understated by the diff size. Checked:
The banner naming the mode, and naming the clamp when one happens, is the right amount of noise: a silent clamp surprises people the same way a silent grant does. The |
oratis
force-pushed
the
feat/contract-result-filter
branch
from
August 9, 2026 15:39
3cae1f2 to
4aa9a23
Compare
`deepcode mcp serve` exposes Read/Write/Edit/Bash/Grep/Glob to whatever MCP client connects — usually another agent. It called `tool.execute` directly: no mode, no permission rules, no file contract, no PreToolUse hooks. The CLI did not pass a sandbox config either, so Bash ran unsandboxed as well. This is the same shape as the `runAgent` bypass fixed in #181, in an entry point that fix did not reach. DEVELOPMENT_PLAN §"风险" listed it — "`deepcode mcp serve` 反向暴露的线程/权限模型缺失", with the mitigation "M3 出独立 design doc". The design doc was never written and the feature shipped anyway. Every call now goes through `dispatchToolCall`. `gate` is a required field on `BuildMcpServerOpts` rather than an optional one, because AGENTS.md's rule is that safety must not depend on a host remembering an argument — optional is how this happened. Nobody is attached to that pipe, so `ask` is refused rather than granted; otherwise "whoever connected" becomes the authority on what may run. A permissive `permissions.defaultMode` is clamped to `default` through the same `resolveTriggerMode` a scheduled job uses, since `bypassPermissions` is a decision about sitting at a REPL. `--mode` is the explicit opt-in back out, and `--sandbox` now applies too. Directory trust gates project settings, so an untrusted checkout cannot widen the posture of the server serving it. This is breaking: a peer can now do what `permissions.allow` says and nothing else. That is the point, and the startup banner says which mode is in effect. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
oratis
force-pushed
the
fix/mcp-serve-gate
branch
from
August 9, 2026 15:44
11481bf to
eca27d1
Compare
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.
Found while auditing where the file contract reaches. It is a bigger hole than the one I was looking for.
What
mcp servediddeepcode mcp serveexposes Read / Write / Edit / Bash / Grep / Glob to whatever MCP client connects — typically another agent, configured once in Claude Desktop and forgotten.packages/core/src/mcp/serve.tscalledtool.executedirectly:PreToolUsehooksand
apps/cli/src/mcp-cmd.tspassed nosandboxConfigeither, soBashran unsandboxed on top.Same shape as the
runAgentbypass fixed in #181 — a host that assembles tools without the central gate — in an entry point that fix did not reach.It was known.
docs/DEVELOPMENT_PLAN.md§risks:The design doc was never written. The feature shipped.
Change
Every call goes through
dispatchToolCall.gateis a required field onBuildMcpServerOpts, not an optional one. AGENTS.md: "Never make safety depend on a host remembering to pass an optional argument." Optional is exactly how this happened.Posture, for a surface with nobody attached
askdefaultModedefault, via the sameresolveTriggerModea scheduled job uses.bypassPermissionsis a decision about sitting at a REPL.--mode--sandboxThe startup banner names the mode in effect and explains the clamp when one happens — a silent clamp is as surprising as a silent grant.
Breaking
A peer can now do what
permissions.allowsays it can, and nothing else. Anyone relying on the old behaviour has to write rules down, or start the server with--mode. That is a real capability reduction and it is the point.Verification
typecheck, lint, format, docs clean; full suite green through the pre-commit hook. 11 new tests:
ask→ refused, with a reason that names the fix;permissions.allowworks with no mode change;denyholds even underbypassPermissions;--modeopts back out.🤖 Generated with Claude Code