v1.3 — MCP Connection Management
Document ID: RM-MCX-001
File Path: docs/18-roadmap/v1.3-mcp-connections.md
Version: 1.1.0
Status: Done — all three phases shipped
Owner: Product / Founder
Last Updated: 2026-07-15
1. Purpose
Section titled “1. Purpose”Execute PRD-006 (decision: ADR-0012): turn the already-shipped, programmatic-only MCP client (RM-AIM-P3 ECO-301) into a persisted, UI-managed capability — connect an external MCP server once, grant it to specific agents declaratively, see its tools in the existing tool picker. Three phases, sequential exit, same discipline as v1.2’s roadmap.
Requirement IDs (MCX-1xx/2xx/3xx) are defined in PRD-006 §6; tickets here carry
the RM-MCX-P<phase> prefix and reference them.
Relationship to other roadmap items: this is not a re-scope of anything — ECO-301 (the MCP client itself) is done and unmodified by this milestone; this milestone is a management/persistence/UI layer on top of it. It is explicitly not FUT-005’s exploratory outbound MCP-gateway/federation bet, and not a reactivation of PRD-005’s cut EMB-702.
2. Phase 1 — Connection Core
Section titled “2. Phase 1 — Connection Core”Theme: persist, secure, gate. No agent wiring or UI yet; acceptance is against the API and the on-disk store directly.
| Ticket | Requirement(s) | Summary |
|---|---|---|
| RM-MCX-P1-101 | MCX-101 | McpConnection model + tenant-scoped, file-backed McpConnectionStore (atomic_write + FileLock, the DUR-401/403 shape) |
| RM-MCX-P1-102 | MCX-102 | POST/GET/DELETE /api/v1/mcp/connections[/{name}] + POST .../{name}/refresh; RBAC mcp:read/mcp:write/mcp:admin (the last required specifically for Stdio transport) |
| RM-MCX-P1-103 | MCX-103 | Hosted-safety gate: Stdio-transport connections refused unless WOVYR_ENABLE_MCP_STDIO=1 (the WOVYR_ENABLE_SHELL_TOOL precedent) |
| RM-MCX-P1-104 | MCX-104 | Http-transport egress reuses http_get’s SEC-304 resolve_and_guard/DNS-pinned-client logic verbatim (extract to a shared helper) |
| RM-MCX-P1-105 | MCX-105 | Connection credentials are SecretRefs into wovyr-secrets’s Vault; the connection store never holds a raw value |
| RM-MCX-P1-106 | MCX-106 | Per-connection client cache (tenant + name keyed); bounded idle timeout for a Stdio connection’s warm process; per-tenant connection-count quota dimension |
Exit: a connection created via the API survives a server restart; a Stdio
connection is refused end-to-end without the operator opt-in (and accepted with
it); an Http connection pointed at a private/loopback/link-local/metadata
address is refused, proven against the same test vectors http_get’s own SEC-304
suite already uses; a grep of the on-disk connection store file for a raw
credential value finds nothing.
Done. wovyr-tools/src/mcp_store.rs (McpConnection/McpConnectionStore),
mcp_cache.rs (McpClientCache, MCX-106’s idle-timeout warm-process cache),
wovyr-server/src/mcp.rs (the full route set incl. stdio_enabled in the list
envelope, added under RM-MCX-P3-302 below), and wovyr-tenancy’s
max_mcp_connections quota dimension. All exit-criterion claims are proven by
wovyr-tools/src/mcp_store.rs’s and mcp_cache.rs’s own unit tests and
wovyr-server/src/mcp.rs’s integration tests (restart survival, Stdio without
opt-in refused, Http SSRF refused against the SEC-304 vectors, no raw secret
in the on-disk file) — all passing in cargo test --workspace.
3. Phase 2 — Agent Wiring
Section titled “3. Phase 2 — Agent Wiring”Theme: declarative, fail-closed. An agent (or workflow agent activity) can
use an MCP connection’s tools by naming it in its manifest — nothing more.
| Ticket | Requirement(s) | Summary |
|---|---|---|
| RM-MCX-P2-201 | MCX-201 | AgentDefinition.spec.mcp_servers: [<connection-name>, ...] allow-list; a run resolves each named connection (tenant-scoped) and registers only its tools into that run’s ToolRegistry |
| RM-MCX-P2-202 | MCX-202 | GET /api/v1/tools includes mcp__<server>__<tool> ids for the caller’s tenant’s configured connections |
| RM-MCX-P2-203 | MCX-203 | Discovered-tool resolution is per-run, bounded by MCX-106’s cache; /refresh (MCX-102) forces immediate re-discovery |
| RM-MCX-P2-204 | MCX-204 | Workflow agent/tool activities inherit MCX-201’s wiring for free via the shared PlatformActivityExecutor — no workflow-specific implementation |
Exit: an agent manifest naming a connection picks up its tools with zero Rust
code, verified against a real external MCP server (not a mock); an agent that
does not name a configured connection cannot reach its tools (a deliberate
negative test, not just the positive path); a workflow agent activity gets the
same wiring with no additional code.
Done. MCX-201/204 (spec.mcp_servers resolution in wovyr-agent’s
AgentDefinition, the CLI’s agents run --local/workflows run --local, and
the server’s resolve_run_registry/StoredAgentResolver, all funneling
through McpClientCache::resolve_agent_mcp_tools) and MCX-202/203
(GET /api/v1/tools merges in the caller’s tenant’s configured connections’
live-discovered mcp__<server>__<tool> ids alongside built-ins, gated on
mcp:read; /refresh (MCX-102) already forced immediate re-discovery, so
MCX-203’s “never silently stale” requirement was satisfied by MCX-106’s cache
design + this listing) all shipped. Proven end to end against a real spawned
stdio MCP server (not a mock) through both the Rust integration-test suite and
the TypeScript SDK’s live mcp: test suite (sdks/typescript/test/ client.test.ts, run against a real wovyr dev), plus the negative case (an
agent naming no connection cannot reach its tools).
4. Phase 3 — Dashboard & DX
Section titled “4. Phase 3 — Dashboard & DX”Theme: no-code, discoverable. Real dogfooding: an admin and a non-admin teammate both complete PRD-006’s acceptance narrative (§9) from the dashboard alone.
| Ticket | Requirement(s) | Summary |
|---|---|---|
| RM-MCX-P3-301 | MCX-301 | TypeScript SDK mcp resource (list/create/delete/refresh), mirroring UiResource’s shape |
| RM-MCX-P3-302 | MCX-302 | Dashboard “MCP Servers” panel (mirrors the Surfaces panel’s compose→call→render pattern): add/list/refresh/delete a connection; Stdio option only offered when WOVYR_ENABLE_MCP_STDIO is reported on |
| RM-MCX-P3-303 | MCX-303 | Agent Studio’s existing tool picker surfaces MCP-sourced tools alongside built-ins |
Exit: PRD-006 §9’s acceptance narrative runs live, in a real browser, against a real external MCP server and a real server — an admin connects it, a separate Editor-role principal picks its tool in Agent Studio and runs the agent, and deleting the connection fails the agent’s next run closed. Verified live, not just typechecked (the standing bar every prior phase in this codebase’s roadmap has held itself to).
Done. MCX-301 (sdks/typescript/src/client.ts’s McpResource:
list/create/get/delete/refresh, mirroring UiResource’s shape,
plus the stdio_enabled capability flag on list()’s envelope), MCX-302
(the dashboard’s features/mcp-servers/ panel — compose → call → render,
mirroring the Surfaces panel; the Stdio transport radio is hidden with an
explanatory note rather than offered-then-rejected when the operator hasn’t
set WOVYR_ENABLE_MCP_STDIO=1), and MCX-303 (Agent Studio’s tool picker lists
mcp__<server>__<tool> ids returned by GET /api/v1/tools alongside
built-ins; picking one tags the tool chip “MCP” and auto-adds the underlying
connection name to spec.mcp_servers — the field a run actually resolves —
rather than only adding the literal tool id, which alone would be a dead
reference at run time). Along the way, fixed a real pre-existing bug in
agent.service.ts’s tools() (it parsed a bare {tools: [...]} shape while
GET /api/v1/tools actually returns the standard cursor-pagination envelope,
RM-GA-P4 API-701 — so the live tool catalog, MCP-sourced or not, silently
never replaced the picker’s hardcoded built-in fallback prior to this fix).
Verified live end to end in a real browser against a real wovyr dev server
and a real spawned stdio MCP connection: an admin connects it from the
dashboard, sees its live tool count, picks its tool in Agent Studio (which
grants the connection via spec.mcp_servers in the emitted manifest), and
deleting the connection immediately empties the picker/list again — the exact
PRD-006 §9 narrative, run for real rather than typechecked.
5. Cut Lines
Section titled “5. Cut Lines”If a phase overruns, cut in this order — never the other:
- P1: the client-cache idle-timeout tuning (MCX-106) → a conservative fixed timeout, no adaptive policy, if time is short.
- P2: workflow-activity inheritance (MCX-204) → agent-only wiring first if the shared executor path needs more work than expected (unlikely, since it’s already shared code, but named here rather than assumed).
- P3: the Agent Studio tool-picker integration (MCX-303) → an MCP tool
remains usable by typing its id directly into the manifest’s
tools:/mcp_servers:fields even if the picker UI isn’t done; the dashboard connection-management panel itself (MCX-302) is not cuttable — it is the entire point of this milestone.
Never cut: the Stdio gating (MCX-103, mcp:admin) and the Http SSRF
reuse (MCX-104) — per ADR-0012, these are the product’s safety floor, not
scope that trades off against schedule.
6. Revision History
Section titled “6. Revision History”| Version | Date | Description |
|---|---|---|
| 1.1.0 | 2026-07-15 | All three phases done: connection core (MCX-1xx) + agent wiring (MCX-2xx) shipped in a prior session; this pass closed MCX-202/203 (GET /api/v1/tools MCP-tool listing), then MCX-301/302/303 (TS SDK, dashboard panel, Agent Studio picker wiring, incl. the stdio_enabled capability flag and fixing a real pre-existing tools() parsing bug along the way). Verified live end to end in a real browser against a real server and a real spawned stdio MCP connection, plus cargo fmt/clippy -D warnings/cargo test --workspace and the dashboard’s ng build/ng test all green. |
| 1.0.0 | 2026-07-15 | Initial v1.3 milestone: three phases executing PRD-006/ADR-0012 — connection core, agent wiring, dashboard & DX. Not started |