Skip to content

Commit 0b371ee

Browse files
committed
refactor(mcp): replace public server contract
Replace the public MCP configuration with the nested HTTP-only contract, remove the deployment gate and public stdio fields, preserve trusted internal MCP delivery, and keep the shipped documentation and interface inventories in sync.
1 parent 59cec56 commit 0b371ee

69 files changed

Lines changed: 728 additions & 1070 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/design/agent-workflows/documentation/adapters/claude-code.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ restored over loopback HTTP by the gateway-tool-mcp project, and extended to Day
6464
in-sandbox-tool-mcp project. It is independent of the user MCP capability below — the two
6565
toggle separately.)
6666

67-
User-declared `mcp_servers` are a separate thing and effectively off today. They would reach
68-
Claude through `toAcpMcpServers` as additional ACP stdio servers, but only when
69-
`AGENTA_AGENT_MCPS_ENABLED` is set (off by default), so in practice no user MCP server is
70-
attached. See [tools.md](../tools.md#status-and-known-gaps).
67+
User-declared `mcp_servers` are separate from `agenta-tools`. Claude accepts external HTTP MCP
68+
servers through ACP session initialization. The service resolves named header-secret references
69+
before the runner builds the ACP HTTP entries. Public stdio configuration is not accepted. See
70+
[tools.md](../tools.md#status-and-known-gaps).
7171

7272
## Permissions
7373

docs/design/agent-workflows/documentation/agent-configuration.md

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,8 @@ list of `{pattern, permission}` entries for harness-builtin tools (for example
178178
`Bash(rm:*)` set to `ask`). The runner checks rules before falling back to `default`.
179179

180180
Tool entries are strict even though the list is lenient. Each tool subclass is `extra="forbid"`
181-
(`sdks/python/agenta/sdk/agents/tools/models.py`). `MCPServerConfig` is also `extra="forbid"`
182-
with a transport validator (`sdks/python/agenta/sdk/agents/mcp/models.py`).
181+
(`sdks/python/agenta/sdk/agents/tools/models.py`). `MCPServerConfig` is also `extra="forbid"`. It accepts only the nested HTTP
182+
connection, credential, and policy roles defined in `sdks/python/agenta/sdk/agents/mcp/models.py`.
183183

184184
There is no `ModelRef` type. `model` is a plain string everywhere. There is no provider field.
185185
The rich model picker is built only for the UI by `_model_catalog_type()`
@@ -217,7 +217,7 @@ Legend: (a) catalog/schema, (b) SDK neutral config, (c) runtime.
217217
| --- | --- | --- | --- | --- |
218218
| model / provider | yes, `model: str` | yes, `Optional[str]` | wired to the runner | Loose string. No `ModelRef`, no provider enum. There is no separate provider field. |
219219
| tools | yes, strict list | yes, lenient coercion | wired, resolved to builtin names + tool specs | Entries strict, list lenient. |
220-
| mcp_servers | yes, strict list | yes | wired, resolved to runner mcp servers | Strict per entry. Gated by `AGENTA_AGENT_MCPS_ENABLED` at the service. |
220+
| mcp_servers | yes, strict list | yes | wired, resolved to runner MCP servers | Strict per entry. Claude supports external HTTP servers; Pi refuses them until its bridge exists. |
221221
| skills | yes, embed/inline list | yes | wired | Author-settable (`SkillConfig` inline or `@ag.embed` references). The playground build-kit overlay embeds one skill, the `build-an-agent` playbook; the `pi_agenta` harness additionally force-unions `getting-started`. See below. |
222222
| persona | no | no | wired but forced only | Not a config field. The Agenta harness hardcodes an append-system preamble. See below. |
223223
| agents_md | yes, `agents_md: str` | yes, as `instructions` | wired to `agentsMd` | The schema names it `agents_md`. The neutral config names it `instructions`. |
@@ -259,13 +259,17 @@ This is what the playground saves and the runtime reads:
259259
],
260260
"mcp_servers": [
261261
{
262-
"name": "github",
263-
"transport": "stdio",
264-
"command": "npx",
265-
"args": ["-y", "@modelcontextprotocol/server-github"]
262+
"name": "memory",
263+
"connection": {
264+
"type": "http",
265+
"url": "https://memory.example.com/mcp",
266+
"headers": {},
267+
"credentials": { "type": "none" }
268+
},
269+
"policy": { "tools": { "mode": "all" }, "permission": "ask" }
266270
}
267271
],
268-
"harness": "pi_core",
272+
"harness": "claude",
269273
"sandbox": "local",
270274
"runner": { "permissions": { "default": "allow_reads" } }
271275
}
@@ -274,8 +278,8 @@ This is what the playground saves and the runtime reads:
274278
With this config, the runtime reads `agents_md`, `model`, `tools`, `mcp_servers`, and the
275279
run-selection fields `harness`, `sandbox`, and `runner.permissions.default` through the one
276280
neutral `AgentConfig`, resolves the tools and MCP servers server-side, and runs one turn on
277-
the Pi harness in a local sandbox. Under `allow_reads`, `web_search` runs as a read with no
278-
prompt; a write tool would pause for approval on this harness exactly as it would on Claude.
281+
the Claude harness in a local sandbox. Under `allow_reads`, `web_search` runs as a read with no
282+
prompt; a write tool pauses for approval.
279283

280284
## See also
281285

docs/design/agent-workflows/documentation/architecture.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ Batch `/invoke` follows this path:
111111
2. `_agent` parses one `AgentConfig` from request parameters; it carries the run-selection
112112
fields `harness` and `sandbox`, plus the permission policy at
113113
`runner.permissions.default`.
114-
3. The service resolves three things independently: tools, MCP servers, and provider-key
115-
secrets. MCP resolution is gated by `AGENTA_AGENT_MCPS_ENABLED`
116-
(`services/oss/src/agent/tools/resolver.py:23`, off by default).
114+
3. The service resolves three things independently: tools, external HTTP MCP servers, and
115+
provider-key secrets. There is no deployment feature flag for MCP resolution. Harness
116+
capabilities control authoring, and unsupported Pi runs fail loudly.
117117
4. The service builds `SessionConfig` and constructs a harness over an `Environment` and
118118
`SandboxAgentBackend`.
119119
5. The harness opens a cold session, sends one `/run` request to the sidecar, and tears the

docs/design/agent-workflows/documentation/ground-truth.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ this page and the referenced code as the source of truth.
5656
on Daytona (`tools/tool-mcp-stdio.ts`; the harness spawns it, its `tools/call` writes relay
5757
request files, and the runner-side relay loop executes server-side). The channel's name
5858
`agenta-tools` is reserved; a user-declared MCP server claiming it is refused. User-declared
59-
MCP resolution is feature-gated (`AGENTA_AGENT_MCPS_ENABLED`, off by default).
59+
external HTTP MCP configuration is resolved for every run. Harness capabilities expose the
60+
editor for Claude and hide it for Pi until Pi has a delivery bridge.
6061
- `client` tools (browser-fulfilled, e.g. `request_connection`) are delivered to Claude too on
6162
the local path: advertised over the same internal MCP channel and PAUSED in the `tools/call`
6263
handler (no JSON-RPC result + abort the request), then resumed from the browser result next
@@ -84,10 +85,10 @@ this page and the referenced code as the source of truth.
8485
per option (`HARNESS_IDENTITIES`); the stored/wire harness value stays the bare string.
8586
- Per-request model override is not honored on the Pi ACP path. pi-acp accepts only its
8687
default model and silently falls back (`projects/qa/findings.md`, F-007).
87-
- User-declared MCP transports split: remote (`http`) servers are delivered by the runner
88-
(`toAcpMcpServers`, #4834); stdio servers are disabled (`USER_MCP_UNSUPPORTED_MESSAGE`) because
89-
they launch a process on the runner host. The runner's own internal gateway-tool channel is a
90-
separate thing, delivered over loopback HTTP locally and the in-sandbox stdio shim on Daytona.
88+
- User-declared MCP servers are HTTP-only. The runner delivers them to Claude through
89+
`toAcpMcpServers`; Pi refuses them until its MCP bridge exists. The runner's own internal
90+
gateway-tool channel is separate and uses loopback HTTP locally or a trusted in-sandbox stdio
91+
shim on Daytona.
9192
- Trigger lifecycle, Compose.io trigger integration, and event-to-agent mapping are not
9293
implemented in the agent workflow code.
9394
- A persisted agent template object that separates `AGENTS.md`, skills, tools,

docs/design/agent-workflows/documentation/ports-and-adapters.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ result fields should update both sides and the wire tests in the same PR.
159159
- Session history is not persisted: the runtime is cold and completed `/messages` turns are
160160
not stored.
161161
- `AgentaHarness` policy content is placeholder product copy.
162-
- MCP server resolution is disabled unless `AGENTA_AGENT_MCPS_ENABLED` is truthy.
162+
- External HTTP MCP servers are resolved without a deployment flag. Harness capability metadata
163+
exposes authoring for Claude and hides it for Pi until Pi has a delivery bridge.
163164
- The code still has historical WP labels in some comments. Those labels should not guide new
164165
design decisions.

docs/design/agent-workflows/documentation/running-the-agent.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,7 @@ In the EE dev compose, the relevant services are:
113113
- `services`. Runs uvicorn on port `8080` inside the container
114114
(`hosting/docker-compose/ee/docker-compose.dev.yml:519`). It hosts the Python agent
115115
service. Traefik routes `/services/` to it. It sets `AGENTA_RUNNER_INTERNAL_URL` to
116-
`http://runner:8765` and `AGENTA_AGENT_MCPS_ENABLED` to `false` by default (lines 564 to
117-
565). It depends on `runner` being healthy (lines 573 to 574).
116+
`http://runner:8765`. It depends on `runner` being healthy.
118117

119118
- `runner`. The Node runner (line 588 onward). In dev it runs `tsx src/server.ts` after
120119
rebuilding the Pi extension. It listens on `8765`. Its health check hits
@@ -167,7 +166,6 @@ sections).
167166
`http://runner:8765`. When unset, the Python service spawns the runner CLI locally instead
168167
(see `runner_url` in `services/oss/src/agent/config.py` and `select_backend` in
169168
`services/oss/src/agent/app.py`).
170-
- `AGENTA_AGENT_MCPS_ENABLED`. Gates MCP server resolution. Default `false`.
171169
- `SANDBOX_AGENT_PROVIDER`. `local` or `daytona`. Default `local`.
172170
- `SANDBOX_AGENT_DAYTONA_API_KEY`, `_API_URL`, `_TARGET`, `_SNAPSHOT`, `_IMAGE`,
173171
`_INSTALL_PI`. Daytona credentials the runner reads for the `daytona` sandbox provider.

docs/design/agent-workflows/documentation/tools.md

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ Resolution is the service's job, but most of it now lives in the SDK. The servic
112112
entrypoints in `services/oss/src/agent/app.py` (`_agent`): `resolve_tools(agent_config.tools)`
113113
and `resolve_mcp_servers(agent_config.mcp_servers)`. Both are thin re-exports. The service
114114
files under `services/oss/src/agent/tools/` are shims:
115-
`resolver.py` re-exports the SDK's `resolve_tools` and adds the MCP gate; `gateway.py` and
115+
`resolver.py` re-exports the SDK's `resolve_tools`; `gateway.py` and
116116
`secrets.py` re-export the SDK platform adapters. The real composition is
117117
`resolve_tools` in `sdks/python/agenta/sdk/agents/platform/resolve.py`, which builds a
118118
`ToolResolver` (`sdks/python/agenta/sdk/agents/tools/resolver.py`) wired with two
@@ -162,14 +162,11 @@ invoke immediately instead of failing the model mid-loop, and the agent only eve
162162
name, a schema, and an opaque slug. The Composio key and the connection's auth never leave the
163163
service.
164164

165-
MCP servers resolve on the same path but only when `AGENTA_AGENT_MCPS_ENABLED` is truthy. The
166-
gate lives in `resolve_mcp_servers` (`services/oss/src/agent/tools/resolver.py`): when the
167-
flag is off it returns an empty list before the SDK `MCPResolver` ever runs. When on, the
168-
`MCPResolver` injects each server's named secrets into its `env`, the same way code tools get
169-
theirs. By default this is off, so `mcp_servers` is dropped at the service and `mcpServers` is
170-
omitted from the wire. See the [status](#status-and-known-gaps) section: even with the flag on,
171-
user MCP reaches Claude only, not the default Pi harness, so the field is a no-op in the common
172-
case.
165+
External MCP servers resolve on their own path for every run. The `MCPResolver` reads the nested
166+
HTTP connection, fetches named header-secret references, and creates a secret-bearing per-run
167+
server. There is no deployment feature flag. The harness catalog exposes user MCP authoring for
168+
Claude and hides it for Pi until Pi has a delivery bridge. A direct Pi request carrying an
169+
external MCP server fails loudly.
173170

174171
The whole resolved bundle then rides the `/run` wire: built-in names in `tools`, resolved
175172
specs in `customTools`, the callback in `toolCallback`, and resolved MCP servers in
@@ -398,24 +395,17 @@ separately, at session start. The extension edits Pi's active tool set at
398395
every non-builtin tool untouched. A builtin outside the grant list is simply absent from the
399396
model's active tools, so no call for it ever fires, and the permission hook never sees it.
400397

401-
### MCP servers: a server process the daemon launches
402-
403-
Execution happens in a separate server process. A declared MCP server is resolved server-side
404-
(secrets injected into its `env`) and, for MCP-capable harnesses, passed to the ACP daemon as a
405-
stdio server (`toAcpMcpServers` in `services/agent/src/engines/sandbox_agent/mcp.ts`). The
406-
daemon launches the server's `command` with the resolved `env`, and the harness talks to it
407-
over the MCP protocol.
408-
409-
In practice user MCP is dead on the default path, and for two reasons that stack. First,
410-
resolution is gated behind `AGENTA_AGENT_MCPS_ENABLED`, which is off by default, so the servers
411-
never reach the wire. Second, even with the flag on, `buildSessionMcpServers` drops user MCP
412-
for Pi (Pi's ACP adapter does not forward them), so it would reach Claude only. Pi and Agenta
413-
are the default harnesses, so the `mcp_servers` field is accepted and then silently ignored in
414-
the common case. This is the silent-drop that the
415-
[harness-capabilities project](../../projects/harness-capabilities/proposal.md) is built to fix
416-
(fail loud, or deliver MCP on Pi through the extension). The
417-
[removal-and-capability notes](../../scratch/notes-tools-mcp-capabilities.md) lay out the two
418-
options.
398+
### External MCP servers: remote HTTP connections
399+
400+
A declared user MCP server contains identity, an HTTP connection, credential references, and
401+
policy. The service resolves credential references into request headers for the current run. The
402+
runner validates the remote URL and passes an ACP HTTP MCP entry to Claude. Claude then connects
403+
directly to the external server and discovers its tools through MCP. Public stdio commands and
404+
process environments are not part of the author or runner interface.
405+
406+
The editor shows this section only when the selected harness publishes `mcp.user_servers`. Claude
407+
publishes it. Pi does not and rejects external MCP servers until its bridge exists. The private
408+
`agenta-tools` server remains a separate trusted delivery mechanism for Agenta tools.
419409

420410
## Approval and rendering
421411

@@ -612,7 +602,7 @@ never drift from the files that exist. The canonical playbook format lives in th
612602
| Platform | `callback` spec + direct `call` | the Agenta service | the exposed endpoint, called directly (no `/tools/call` hop) | caller credential reused; self-targeting ids bound server-side |
613603
| Code | `code` spec + `env` | the runner | a local subprocess | only the tool's own secrets, scoped to the child |
614604
| Client | `client` spec | the browser | the user's browser, next turn | none |
615-
| MCP | resolved server + `env` | a server process | a stdio child the daemon launches | secrets injected into the server env |
605+
| MCP | resolved HTTP server + headers | the external MCP server | remote URL reached by Claude | named secret references become per-run request headers |
616606

617607
## Where this lives
618608

@@ -626,7 +616,7 @@ never drift from the files that exist. The canonical playbook format lives in th
626616
| Platform-op catalog (the `op` table + schema/context-binding resolution) | `sdks/python/agenta/sdk/agents/platform/op_catalog.py` |
627617
| Platform tool resolver (catalog → `CallbackToolSpec` + `call`) | `sdks/python/agenta/sdk/agents/platform/platform_tools.py` |
628618
| `x-ag-type-ref` schema expansion | `sdks/python/agenta/sdk/agents/platform/_schema.py` |
629-
| Service entrypoints (shims + MCP gate) | `services/oss/src/agent/tools/resolver.py`, `__init__.py` |
619+
| Service MCP composition | `sdks/python/agenta/sdk/agents/platform/resolve.py` |
630620
| Gateway resolver (calls `/tools/resolve`) | `sdks/python/agenta/sdk/agents/platform/gateway.py` (shim: `services/oss/src/agent/tools/gateway.py`) |
631621
| Named-secret resolution (`/secrets/resolve`) | `sdks/python/agenta/sdk/agents/platform/secrets.py` (shim: `services/oss/src/agent/tools/secrets.py`) |
632622
| API resolve + execute | `api/oss/src/core/tools/service.py`, `api/oss/src/apis/fastapi/tools/router.py` |
@@ -653,12 +643,10 @@ never drift from the files that exist. The canonical playbook format lives in th
653643

654644
## Status and known gaps
655645

656-
- **User MCP is effectively dead on the default path.** Resolution is off unless
657-
`AGENTA_AGENT_MCPS_ENABLED` is truthy, and even on, the runner drops user MCP for Pi. Pi and
658-
Agenta are the default harnesses, so `mcp_servers` is a silent no-op for most runs. It would
659-
reach Claude only. Do not confuse this with the `agenta-tools` server, which is an internal
660-
tool-delivery vehicle for Claude, not a user MCP server; the name is reserved, and a
661-
user-declared server claiming it is refused.
646+
- **User MCP is harness-capability gated.** Claude receives external HTTP MCP servers through ACP.
647+
Pi hides the editor and refuses external MCP servers until its bridge exists. Do not confuse
648+
user MCP with `agenta-tools`, which is the trusted internal tool-delivery channel; its name is
649+
reserved.
662650
- A tool's `permission` is honored on both harnesses now, including Pi's own builtins. Claude
663651
checks its rendered settings file first, then the ACP responder. Pi has no separate
664652
harness-side settings gate, so the relay decides everything Pi runs: gateway and code tools

0 commit comments

Comments
 (0)