Skip to content

feat(agents): MCP server support with per-agent enablement#823

Open
ericvicenti wants to merge 8 commits into
mainfrom
feat/mcp
Open

feat(agents): MCP server support with per-agent enablement#823
ericvicenti wants to merge 8 commits into
mainfrom
feat/mcp

Conversation

@ericvicenti

Copy link
Copy Markdown
Collaborator

Summary

Adds account-scoped remote MCP (Model Context Protocol) servers that extend agents with external tools. Servers are configured per account (like model providers) and enabled per agent. When an agent runs, the service connects to its enabled MCP servers, lists their tools, exposes them to the model under an mcp__<server>__<tool> namespace, proxies tool calls back to the server, and disconnects when the run ends.

You can add and manage MCP servers from the agent Tools tab: add a server (name, URL, transport, optional encrypted auth header), enable it per agent with a checkbox, inspect the tools it advertises, and remove it.

What's included

  • Protocol (agents/protocol/src/index.ts): McpServerConfig, RedactedMcpServer, McpToolInfo, mcpServers on AgentDefinition; ListMcpServers / SetMcpServer / DeleteMcpServer / ListMcpServerTools actions + responses.
  • Persistence: new mcp_servers table (baseline schema + prepend migration, with the migration test updated).
  • Service: new agents/src/mcp.ts (Streamable HTTP/SSE connect, tool listing, namespacing, and tool-call proxying via @modelcontextprotocol/sdk); CRUD handlers and runtime wiring in api-service.ts with failure isolation.
  • Desktop: Tools-tab MCP section (add/enable/inspect/remove) + React Query hooks + client types.
  • Docs: new agents/docs/mcp.md, cross-referenced from the agents knowledgebase (readme/tools/security/persistence/signed-api/desktop-ui).

Behavior fixes from review

  • Immediate enforcement: UpdateAgent aborts in-flight runs when tools/mcpServers change, so changes take effect right away instead of only on the next message. Scoped to capability changes, so prompt/name/model autosaves don't interrupt a running response.
  • Visible failures: MCP connect/listing failures surface as a durable error session event (instead of failing silently), so a missing tool is explained in the chat.
  • Clean deletes: DeleteMcpServer scrubs the server name from all agents' mcpServers, so runs never reference a deleted server.

Scope / limitations

  • Only remote HTTP/SSE transports are supported — the hosted multi-tenant service never spawns local stdio subprocesses.
  • Authentication is via static headers (secret-backed, encrypted at rest), not an interactive OAuth authorization-code flow. OAuth-protected MCP servers can't be authenticated yet; full OAuth is a possible follow-up.

Testing

  • agents/src/mcp.test.ts — namespacing/header helpers plus an end-to-end test that stands up a real Streamable HTTP MCP server and verifies connect → list tools → proxy a tool call (including the auth header).
  • agents/src/api-service.test.ts — MCP CRUD, redaction, header-secret cleanup on delete, URL validation, mcpServers persistence, delete-scrubs-agents, and a visible-error-on-connect-failure test.
  • All 77 agents tests pass; bun check (typecheck + format) and pnpm typecheck clean.

🤖 Generated with Claude Code

ericvicenti and others added 8 commits June 23, 2026 22:25
Add two model-facing web research tools to the Agents runtime, fully
self-hosted with no third-party API keys:

- web_search: queries a self-hosted SearXNG instance (GET /search?format=json)
  with engine-rotation retry on upstream blocking and a degraded flag for
  partial coverage.
- web_read: tiered cheapest-first reader returning the first tier that yields
  substantial content: MediaWiki REST/Parsoid API -> in-process static
  extraction (@mozilla/readability on a linkedom DOM + Turndown) -> optional
  Crawl4AI headless-browser /md escalation (one retry). Output bounded to
  200 KiB.

Implementation in agents/src/web-tools.ts; config (SEED_AGENTS_SEARXNG_URL,
SEED_AGENTS_CRAWLER_URL, SEED_AGENTS_CRAWLER_TOKEN) threaded from Service into
the tool context; registry entries in tool-registry.ts; desktop Tools-tab web
group. Tools are opt-in per agent and degrade gracefully when unconfigured
(web_search errors; web_read uses MediaWiki + static tiers only).

Tested against live SearXNG + Crawl4AI 0.9.0 containers; unit tests in
web-tools.test.ts and an end-to-end tool-call test in api-service.test.ts.
A ready-to-run local backend compose lives in agents/dev/web-backends/.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- Add web_read `raw: true` option: returns the verbatim response body
  (HTML/JSON/source code/text) with no extraction or markdown conversion,
  bounded to the size limit and rejecting binary content types. For reading
  source files (raw.githubusercontent.com), JSON APIs, and config files.
  source is 'raw' and the result includes contentType.
- Replace the internal tier name in the user-facing summary with plain
  language: "via the wiki API" / "via direct fetch" / "with a browser"
  (the machine-readable `source` field is unchanged).

Adds unit tests for raw mode (verbatim body + binary guard) and the summary
wording; live-checked raw mode against a GitHub raw file and the GitHub JSON API.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Server now advertises optional web-tool backends in its health response
(webTools: {search, readBrowser}, derived from SEED_AGENTS_SEARXNG_URL /
SEED_AGENTS_CRAWLER_URL via Service.webToolCapabilities()).

Desktop Tools tab:
- Reads the server health capabilities and greys out tools the server cannot
  run, with an explanation (e.g. web_search greyed when SearXNG isn't
  configured; web_read notes when browser rendering is unavailable). A group's
  toggle is disabled only when none of its tools can run. Unknown/older servers
  (no capability flags) are treated as available.
- Lists each group's individual member tools, each with an info (i) button
  opening a dialog (ToolInfoDialog) showing the exact model-facing description
  and the input/output JSON schemas from the shared tool registry, so an agent
  owner can see precisely what prompting/context each tool passes to the model.

Adds a server test asserting health reflects configured web backends.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Populate the previously-empty outputSchema slot for both web tools so the
desktop tool-info dialog shows their output contract (results/degraded/markdown
for web_search; finalUrl/source/contentType/truncated/markdown for web_read),
not just the input schema.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- tools.md: order web_read as tiered chain first, then raw mode; add a
  "Server capability and desktop visibility" subsection covering output schemas,
  the health webTools capability, and the desktop greying + per-tool info dialog.
- operations.md: note production sidecar deployment lives in the infrastructure
  repo; reflow.
- desktop-ui.md: reflow of the Tools tab capability/info-dialog notes.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Integrate the evaluation/rationale from the original planning doc into
implementation-history.md: why SearXNG + Crawl4AI + in-process MediaWiki/static
extraction were kept and why ReaderLM-v2, ArchiveBox, Firecrawl, Wayback, and
Trafilatura were dropped for a reliable, easy-to-host, fully self-hostable suite.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
`bun run dev` now defaults SEED_AGENTS_SEARXNG_URL (:8899), SEED_AGENTS_CRAWLER_URL
(:11235), and SEED_AGENTS_CRAWLER_TOKEN (dev-crawl-token) to the
agents/dev/web-backends compose values, so web_search/web_read work in dev with
no manual env. The defaults are overridable (export the env vars). Documented the
simplified dev flow in operations.md and the compose header.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Add account-scoped remote MCP (Model Context Protocol) servers that extend
agents with external tools. Servers are configured per account (like model
providers) and enabled per agent; their tools are discovered at run start,
exposed to the model under an `mcp__<server>__<tool>` namespace, proxied back
to the server, and disconnected when the run ends.

- protocol: McpServerConfig/RedactedMcpServer/McpToolInfo, mcpServers on
  AgentDefinition, and ListMcpServers/SetMcpServer/DeleteMcpServer/
  ListMcpServerTools actions + responses.
- persistence: mcp_servers table (baseline schema + migration).
- service: agents/src/mcp.ts (Streamable HTTP/SSE connect, list, proxy via
  @modelcontextprotocol/sdk) plus CRUD handlers and runtime wiring with
  failure isolation.
- desktop: Tools-tab MCP section (add/enable/inspect/remove) + hooks + types.

Behavior fixes from review:
- UpdateAgent aborts in-flight runs when tools/mcpServers change, so changes
  take effect immediately instead of only on the next message.
- MCP connect/listing failures surface as a visible session error event
  instead of failing silently.
- DeleteMcpServer scrubs the server name from all agents' mcpServers so runs
  never reference a deleted server.

Only remote HTTP/SSE transports are supported (no stdio); auth is via static
headers (secret-backed), not interactive OAuth. Docs added in agents/docs/mcp.md
and cross-referenced from the agents knowledgebase.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
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