Repository navigation
Conversation
4 tasks done
sanchitrk
force-pushed
the
claude/cod-114-stacked-pr-92a28d
branch
from
October 14, 2026 19:30
f3014f3 to
b02d026
Compare
4 tasks done
Contributor
Author
Review (2026-10-24)should-fix: a server whose first connect fails never connects again in that session
should-fix: a
should-fix: closing servers blocks the next prompt
PR body
Already handled later in the stack, so no action needed here: per-session server shutdown (#83's idle close), the |
4 tasks done
sanchitrk
force-pushed
the
claude/sleepy-gates-ce83d0
branch
from
October 24, 2026 14:19
84e21d0 to
f64f0c1
Compare
sanchitrk
force-pushed
the
claude/cod-114-stacked-pr-92a28d
branch
2 times, most recently
from
October 24, 2026 15:41
403cdd6 to
94bb0b9
Compare
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>
…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>
- Refuse env and header values with a line break or NUL without quoting them. - Run a !command with stdin closed, and reuse its output for the life of the process. - Report auth/oauth entries as unsupported instead of ignoring the keys. - Drop the nulls strict sampling writes for omitted optional arguments before the call. - Validate JSON Schema patterns instead of skipping the whole schema. - Refresh a changed tool list without holding the server lock. - Retry a server whose connect failed at the next exchange, in the background. - Stop removed or changed servers without making the prompt wait. - Keep hashed tool names within 64 characters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…aces - Start a connect attempt after it is recorded, so one that fails at once (an unset variable, say) no longer blocks every later retry. - Close server connections in parallel on shutdown, after interrupting connect attempts. - Keep only the latest tool listing when list_changed notifications overlap. - Write server log messages from the handshake on. - Kill a !command that ignores SIGTERM, and share one run between concurrent connects. - Allow line breaks in env values (a PEM key); headers still refuse them. - Reject arguments a closed JSON Schema object does not declare, instead of passing them on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Write server log messages on the service's fibers, so a failed handshake closing the connection no longer drops the messages that explain it. - Forget a failed !command as soon as it fails, so the next connect runs it again even when every caller waiting on it gave up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sanchitrk
force-pushed
the
claude/cod-114-stacked-pr-92a28d
branch
from
October 24, 2026 16:55
94bb0b9 to
4cd6ee1
Compare
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. 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 (2026-10-15)
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 processReview fixes (2026-10-24)
envandheadersvalues with a line break or NUL are refused, naming the key but not the value.!command: runs with stdin closed; its output is reused for the life of the process (failures run again next time).auth/oauth: reported as not supported yet instead of being silently ignored.nulls strict sampling writes for omitted optional arguments are dropped before the call; JSON Schemapatterns are validated instead of turning validation off.mcp.session.test.tscovers the null argument, apatternrejection, anoauthentry and a header value with a newline (the secret appears nowhere).list_changedrefreshes keep only the latest listing;!commandthat ignores SIGTERM is killed, and concurrent connects share one run;!commandis forgotten as soon as it fails.Test 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