Repository navigation
feat(harness): add MCP resource tools and keep oversized output on the host - #81
Merged
Merged
Conversation
sanchitrk
force-pushed
the
claude/cod-114-stacked-pr-92a28d
branch
from
October 14, 2026 19:30
f3014f3 to
b02d026
Compare
sanchitrk
force-pushed
the
claude/mcp-resources
branch
2 times, most recently
from
October 15, 2026 03:49
60888b5 to
29d2570
Compare
4 tasks done
4 tasks done
Contributor
Author
Review (2026-10-24)Resource tools, truncation, saved files and the should-fix: a blank
Confirmed, no action:
|
3 tasks done
Connect to one MCP server over stdio or Streamable HTTP through the official SDK. Stdio servers run on the host in their own process group and are stopped with the group; HTTP connects retry transient failures. List pages and tool results are validated by the SDK. The process-group stop sequence moves out of the host shell into a shared Process.terminate helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Connections pass the server's notifications/message entries to an onLog handler, and McpLog appends them to a file with one line per entry, rotating it past 5 MB. A result that fails the SDK's schema is reported on one line as `Invalid result for <method>: <path>: <message>` instead of the validator's multi-line issue list, which is still logged at debug level. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Name the MCP entry points with Effect.fn and give each request its own span. A server without resource templates is now recovered from inside the Effect rather than on the SDK promise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Count stderr as an open pipe, so a descendant holding only stderr is still killed with the server's process group. - Bound the whole connect attempt by the startup timeout; the SDK only times the initialize request, not the initialized notification. - End the HTTP session a failed connect attempt was given, through a fresh transport, since the SDK aborts the failed transport's signal. Tests now cover these, retries of reads but not tool calls, session termination and 400 classification, and register their own teardown for spawned processes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The process spawner kills the group when the connection's scope closes, which hid a missing kill. Assert instead that a grandchild holding only stderr is gone once the connection reports the server's exit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…shutdown - Retry connects only on HTTP 408/429/5xx and fetch failures with a system code; any other TypeError, such as an invalid header value, fails once. - Retry list and read requests once, 250 ms later, after a transient HTTP error. - Bound startup by the request timeout unless set on its own. - Ask for progress only on requests given a progress handler. - SIGTERM a stdio server's process group even after the server exits on its own, and before SIGKILL when the leader is gone but a pipe is still open. 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 14:19
08e23a4 to
403cdd6
Compare
sanchitrk
force-pushed
the
claude/mcp-resources
branch
from
October 24, 2026 14:19
29d2570 to
386dd54
Compare
The log handler is now a connect option instead of a setter called after the first tool listing, so messages a server sends while starting, or before failing to, are kept. 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 15:41
403cdd6 to
94bb0b9
Compare
sanchitrk
force-pushed
the
claude/mcp-resources
branch
from
October 24, 2026 15:41
386dd54 to
dfa8e70
Compare
A tools/list_changed sent while the first listing is answered used to reach no handler, leaving the old list in place; it is now reported once the handler is set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
…e host - list_mcp_resources, list_mcp_resource_templates and read_mcp_resource reach every enabled server with resources, through the same request path as tool calls (reconnect, expired-session recovery, sign-in). Listings page per server or cover every page; MCP App resources are left out. - Results keep the start and end of text over 20 KB and save the full text; binary resources are saved, text-typed blobs shown as text, and resource links name read_mcp_resource. - Saved files go to the host temp directory (mode 0600), since MCP runs on the host whatever sandbox a session uses. - Docs: an MCP page under Customize. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Listing every server's resources or templates uses the SDK's own pagination and its limits, so a server repeating a cursor cannot keep a listing running. - A cursor goes back to the server exactly as it was given; only server names and URIs are trimmed. - Tests remove the files they save, and read saved paths between the message's own delimiters. - Docs: which servers a message waits for, exposure changes keep a server running, and where server logs go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g servers - A blank cursor lists the first page; any other cursor goes back exactly as given. - Resource tools wait for servers still connecting, so none is left out of a listing. - Resource failures carry the server's own message; listings name the server beside it. - Docs: where stdio servers start off a local sandbox, $NAME values, command caching, sign-in, and retries after a failed connect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tool and resource images are converted to a format providers take inline and resized to their limits, as the read tool does; one that cannot be becomes a note instead of an image that would fail the next request. 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
sanchitrk
force-pushed
the
claude/mcp-resources
branch
from
October 24, 2026 16:56
dfa8e70 to
f815324
Compare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each thread now owns its MCP servers: reconcile, status and the new connect and disconnect take the thread, so concurrent threads of one session, or threads at different placements, no longer overwrite each other. A thread's record is dropped once it has no servers left. A failed server is no longer retried by every turn; it recovers through a config change or an explicit connect. Disconnect closes a server and keeps it closed until connect or a config change. A dropped connection still reconnects on the next call. Every tool a server offers is registered with its exposure, so deferred tools are found by toolsearch, codemode and hidden ones are registered but not offered. mcp.json accepts codemode and deferred exposure, with codemode-deferred read as codemode. A loaded deferred tool makes the turn wait for its server like a direct one. State changes are published as the ephemeral mcp.server.status event to the owning thread, and the needs-auth state is now authenticate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Connect and disconnect run one at a time per server and return the status read under that lock; a server a turn replaced meanwhile is reported unknown. Stopping reads and interrupts the connect attempt under the server lock, and an attempt that finishes after its server was closed or disconnected closes its connection instead of installing it. An explicit connect always publishes a fresh connecting status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resource tools reach the thread's servers, skip disconnected ones, and stay declared to the model. Sign-in failures on resource requests set the authenticate state through the status event. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…upted attempts by state Deleting a session now forgets its threads' MCP records and closes their servers. A caller waiting on a connect attempt that was interrupted is told whether the server was shut down, disconnected or is reconnecting, and connect-time close reasons read like the ones after connect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sanchitrk
changed the base branch from
claude/cod-114-stacked-pr-92a28d
to
canary
October 30, 2026 14:09
sanchitrk
marked this pull request as ready for review
October 30, 2026 14:11
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 #80. Adds MCP resources, and handling for results the model isn't shown in full. Everything stays on the host: MCP runs in the harness process and never touches a session's sandbox, local or remote.
Resource tools (
src/mcp/resources.ts)list_mcp_resources,list_mcp_resource_templatesandread_mcp_resource, named and shaped like the tools other agents use, so models trained on them use them unchanged.server, one page is listed andcursorcontinues it. Without one, every page of every server is listed, and servers that fail go intoerrors.ui://,profile=mcp-app) and icons are left out of listings.Result content (
src/mcp/content.ts)text/*, JSON, XML) are shown as text.read_mcp_resourcewhen the server's resources can be read.Saved files (
src/mcp/output.ts)codework-mcp-*, mode 0600, created exclusively, never into a sandbox.One request path (
src/mcp/mcp.ts)Docs
customize/mcp: where servers run (always on your machine),mcp.json, project overrides, exposure, resources, connections.Review fixes (2026-10-15)
tools()does. A server repeating a cursor can't keep a listing running." page-2 "to prove it.Review fixes (2026-10-24)
cursor: ""means no cursor; any other cursor still goes back exactly as given.$NAMEvalues, command caching, sign-in, and retries after a failed connect.Test plan
test/mcp.session.test.tsgains a third exchange, through the full harness against a real stdio server. It covers:cwdcursor, with theui://resource left outtest/mcp.client.test.ts: artifact updated for saved binaries and resource linksvp checkandvp run typecheck🤖 Generated with Claude Code