Repository navigation
Conversation
4 tasks done
Reads mcp.json from the user and project layers, keeps each session's
servers connected across exchanges, and declares their tools to the model.
- mcp.json: project entries replace user ones or override only enabled,
exposure and toolExposure; broken files and entries are reported and
skipped. env and headers resolve ${VAR} and !command values.
- Plugins get a ctx.plugin.mcp bucket in a new mcp domain that runs first;
it is settled before the tool domain, so tool plugins see MCP tools.
- Tool definitions are frozen per exchange; each call uses the server's
current connection. A dropped connection reconnects on the next call, an
expired HTTP session is replaced and the call retried once, tool calls
are never retried otherwise.
- Tools are named mcp__<server>__<tool> with a hash suffix on collisions;
exposure is direct or hidden.
- Tool.fromJsonSchema sends a server's JSON Schema to the provider as
written and validates arguments against its Effect translation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A server's own relative cwd now resolves against the session's base directory, and a leading ~/ is expanded in command, args and cwd. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sanchitrk
force-pushed
the
claude/cod-114-stacked-pr-92a28d
branch
from
October 14, 2026 19:30
f3014f3 to
b02d026
Compare
…tled - Connection callbacks run in the service's scope. A server exiting while idle used to retire its connection from inside the connection's own scope, which waited on itself and left the server locked for good. - Connect attempts run on their own and are shared by callers; closing a server interrupts one in progress instead of waiting behind it. - An exchange waits up to 10 s for every server it starts, not only on the first exchange, so a restarted server keeps its tools declared. - Server configs in ctx.plugin.mcp are copied and deep-frozen, so a plugin cannot change a settled server afterwards. - A hashed tool name grows until it is free, so a server tool named like the hash cannot replace another tool. - Server log messages go to <home>/mcp.log. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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.
Summary
Stacked on #79 (MCP client). Turns the client into something a session uses: servers from
mcp.json, kept connected across a session's exchanges, with their tools declared to the model. Behaviour follows Pi's MCP integration; the runtime is built on Effect scopes.Config:
mcp.jsonsettings.jsonc: the user layer (--user-config-diror--home), then the project's.codework/mcp.jsonfrom the session'shostDir.enabled/exposure/toolExposureof it without restating it, so credentials stay in the user file.envandheadersvalues resolve${VAR}/$VARand!commandwhen the server connects.Plugins: a
ctx.plugin.mcpbucketmcpplugin domain runs first:{ mcp: 1, tool: 2, prompt: 3 }.mcp.json;mcpplugins canlist/get/set/update/removeservers, checked with the same rules as the file.tooldomain runs. MCP tools are then in the tool bucket, so tool plugins can patch, override or hook them, and prompt plugins index them. Later domains can read the bucket but not write it.Runtime:
src/mcp/mcp.tsTools
mcp__<server>__<tool>(non-[A-Za-z0-9_]→_, max 64), with a hash suffix on collisions or overflow. Names stay with their tool for the session.directorhidden(per server, or per tool with patterns).Tool.fromJsonSchema(in@codeworksh/plugin) sends the server's JSON Schema to the provider unchanged and validates arguments against its Effect translation. A schema the translation cannot express validates nothing. The server receives the arguments as the model wrote them.isErrorresults and failed calls reach the model as error results.Other changes
mcp.json(util/jsonc.ts).cwd, resolved against the session's base directory, with a leading~/expanded incommand,argsandcwd.Connectiongainsdropped(), so a call notices a drop before theonClosehandler has run.Not in this PR (follow-ups)
space/appscopes, idle timeouts and leases. Until then, a session's servers live until the process exits.deferred/codemodeexposure.Review fixes (Codex peer review)
Rebased onto #79's latest head, so it includes
onLog/McpLog, condensed validation errors and the cleanup fixes.ctx.plugin.mcpare copied and deep-frozen, so a plugin can't change a settled server.<home>/mcp.log.mcp.session.test.tsgains a lifecycle test, with artifactmcp.session.lifecycle.json. It covers:envchange, which starts a new processTest plan
test/mcp.session.test.ts: the full harness with a scripted model and a real stdio fixture server, two exchanges in one session. It covers:mcp-domain edit, and atool-domain write being refused.isErrorresult, arguments rejected by validation.__artifacts__/mcp.session.json, stable across runs.vp check(the only errors are insites/www, which doesn't generate its Astro types in this checkout) andvp run typecheck🤖 Generated with Claude Code