Skip to content

feat(harness): add MCP resource tools and keep oversized output on the host - #81

Draft
sanchitrk wants to merge 4 commits into
claude/cod-114-stacked-pr-92a28dfrom
claude/mcp-resources
Draft

sanchitrk wants to merge 4 commits into
claude/cod-114-stacked-pr-92a28dfrom
claude/mcp-resources

Conversation

@sanchitrk

@sanchitrk sanchitrk commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

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_templates and read_mcp_resource, named and shaped like the tools other agents use, so models trained on them use them unchanged.
  • They're registered when any enabled, non-hidden server offers resources, and reach every such server when they run.
  • With a server, one page is listed and cursor continues it. Without one, every page of every server is listed, and servers that fail go into errors.
  • MCP App resources (ui://, profile=mcp-app) and icons are left out of listings.
  • Reads become text and images; several contents are labelled with their URIs.

Result content (src/mcp/content.ts)

  • Text over 20 KB keeps its start and end, cut in the middle, and the full text is saved to a file whose path the result names.
  • Binary resources are saved; text-typed blobs (text/*, JSON, XML) are shown as text.
  • Resource links name read_mcp_resource when the server's resources can be read.

Saved files (src/mcp/output.ts)

  • They go to the host temp directory as codework-mcp-*, mode 0600, created exclusively, never into a sandbox.

One request path (src/mcp/mcp.ts)

  • Tool calls and resource requests share it: the current connection is looked up when the request runs, a dropped one reconnects, an expired HTTP session gets one more attempt on a new session, and a sign-in failure is reported. Tool calls are still never retried otherwise.

Docs

  • A new page, customize/mcp: where servers run (always on your machine), mcp.json, project overrides, exposure, resources, connections.

Review fixes (2026-10-15)

  • Strict paging. Listing every server's resources or templates uses the SDK's own pagination and limits, as tools() does. A server repeating a cursor can't keep a listing running.
  • Opaque cursors. A cursor goes back exactly as the server gave it; only server names and URIs are trimmed. The fixture's cursor is now " page-2 " to prove it.
  • Test cleanup. Tests remove the files they save, and read saved paths between the message's own delimiters, so a temp directory with spaces still parses.
  • Docs. Which servers a message waits for, that exposure changes keep a server running, and where server logs go.

Review fixes (2026-10-24)

  • Blank cursor: cursor: "" means no cursor; any other cursor still goes back exactly as given.
  • Connecting servers: resource tools wait for servers still connecting, so none is silently left out of a listing.
  • Errors: resource failures carry the server's own message; listings already 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.
  • Tests: the third exchange lists with a blank cursor.
  • Images: tool and resource images go through the shared inline pipeline (format conversion and resizing, as the read tool does); one that can't be converted becomes a note.

Test plan

  • test/mcp.session.test.ts gains a third exchange, through the full harness against a real stdio server. It covers:
    • the server's configured cwd
    • a resource link
    • a 30 KB result cut below 21 KB, with the saved file holding all of it
    • listing every page and paging one server by cursor, with the ui:// resource left out
    • templates
    • a text read and a binary read, with the binary saved as a 0600 host file holding the right bytes
    • an unknown server
  • test/mcp.client.test.ts: artifact updated for saved binaries and resource links
  • Full harness suite: 514 passed, 34 skipped (live/remote)
  • vp check and vp run typecheck

🤖 Generated with Claude Code

@sanchitrk
sanchitrk force-pushed the claude/cod-114-stacked-pr-92a28d branch from f3014f3 to b02d026 Compare October 14, 2026 19:30
@sanchitrk
sanchitrk force-pushed the claude/mcp-resources branch 2 times, most recently from 60888b5 to 29d2570 Compare October 15, 2026 03:49
@sanchitrk sanchitrk mentioned this pull request Oct 24, 2026
4 tasks done
@sanchitrk

Copy link
Copy Markdown
Contributor Author

Review (2026-10-24)

Resource tools, truncation, saved files and the ui:// filter all check out.

should-fix: a blank cursor should mean no cursor

  • Where: resources.ts (L121 in this head) keeps cursor: "".
  • Why it matters: models often fill optional strings with "".
    • With no server, list_mcp_resources({ cursor: "" }) fails with "cursor can only be used when a server is specified".
    • With a server, "" is sent to it as a cursor, and many servers reject it.
  • Fix: keep non-blank cursors byte-exact, but treat blank ones as absent: value.trim() === "" ? undefined : value.

Confirmed, no action:

  • The saved-output path isn't readable from a remote sandbox. That was decided (host-only, no notice to the model), so it isn't raised again here.
  • Not listing resources at connect, only checking the resources capability, matches current reference behaviour.

@sanchitrk
sanchitrk force-pushed the claude/cod-114-stacked-pr-92a28d branch from 08e23a4 to 403cdd6 Compare October 24, 2026 14:19
@sanchitrk
sanchitrk force-pushed the claude/mcp-resources branch from 29d2570 to 386dd54 Compare October 24, 2026 14:19
@sanchitrk
sanchitrk force-pushed the claude/cod-114-stacked-pr-92a28d branch from 403cdd6 to 94bb0b9 Compare October 24, 2026 15:41
@sanchitrk
sanchitrk force-pushed the claude/mcp-resources branch from 386dd54 to dfa8e70 Compare October 24, 2026 15:41
sanchitrk and others added 4 commits October 24, 2026 22:23
…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
sanchitrk force-pushed the claude/cod-114-stacked-pr-92a28d branch from 94bb0b9 to 4cd6ee1 Compare October 24, 2026 16:55
@sanchitrk
sanchitrk force-pushed the claude/mcp-resources branch from dfa8e70 to f815324 Compare October 24, 2026 16:56

This branch has not been deployed

No deployments
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.

1 participant