Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ restored over loopback HTTP by the gateway-tool-mcp project, and extended to Day
in-sandbox-tool-mcp project. It is independent of the user MCP capability below — the two
toggle separately.)

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

## Permissions

Expand Down
24 changes: 14 additions & 10 deletions docs/design/agent-workflows/documentation/agent-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ list of `{pattern, permission}` entries for harness-builtin tools (for example
`Bash(rm:*)` set to `ask`). The runner checks rules before falling back to `default`.

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

There is no `ModelRef` type. `model` is a plain string everywhere. There is no provider field.
The rich model picker is built only for the UI by `_model_catalog_type()`
Expand Down Expand Up @@ -217,7 +217,7 @@ Legend: (a) catalog/schema, (b) SDK neutral config, (c) runtime.
| --- | --- | --- | --- | --- |
| 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. |
| tools | yes, strict list | yes, lenient coercion | wired, resolved to builtin names + tool specs | Entries strict, list lenient. |
| mcp_servers | yes, strict list | yes | wired, resolved to runner mcp servers | Strict per entry. Gated by `AGENTA_AGENT_MCPS_ENABLED` at the service. |
| 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. |
| 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. |
| persona | no | no | wired but forced only | Not a config field. The Agenta harness hardcodes an append-system preamble. See below. |
| agents_md | yes, `agents_md: str` | yes, as `instructions` | wired to `agentsMd` | The schema names it `agents_md`. The neutral config names it `instructions`. |
Expand Down Expand Up @@ -259,13 +259,17 @@ This is what the playground saves and the runtime reads:
],
"mcp_servers": [
{
"name": "github",
"transport": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"]
"name": "memory",
"connection": {
"type": "http",
"url": "https://memory.example.com/mcp",
"headers": {},
"credentials": { "type": "none" }
},
"policy": { "tools": { "mode": "all" }, "permission": "ask" }
}
],
"harness": "pi_core",
"harness": "claude",
"sandbox": "local",
"runner": { "permissions": { "default": "allow_reads" } }
}
Expand All @@ -274,8 +278,8 @@ This is what the playground saves and the runtime reads:
With this config, the runtime reads `agents_md`, `model`, `tools`, `mcp_servers`, and the
run-selection fields `harness`, `sandbox`, and `runner.permissions.default` through the one
neutral `AgentConfig`, resolves the tools and MCP servers server-side, and runs one turn on
the Pi harness in a local sandbox. Under `allow_reads`, `web_search` runs as a read with no
prompt; a write tool would pause for approval on this harness exactly as it would on Claude.
the Claude harness in a local sandbox. Under `allow_reads`, `web_search` runs as a read with no
prompt; a write tool pauses for approval.

## See also

Expand Down
6 changes: 3 additions & 3 deletions docs/design/agent-workflows/documentation/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ Batch `/invoke` follows this path:
2. `_agent` parses one `AgentConfig` from request parameters; it carries the run-selection
fields `harness` and `sandbox`, plus the permission policy at
`runner.permissions.default`.
3. The service resolves three things independently: tools, MCP servers, and provider-key
secrets. MCP resolution is gated by `AGENTA_AGENT_MCPS_ENABLED`
(`services/oss/src/agent/tools/resolver.py:23`, off by default).
3. The service resolves three things independently: tools, external HTTP MCP servers, and
provider-key secrets. There is no deployment feature flag for MCP resolution. Harness
capabilities control authoring, and unsupported Pi runs fail loudly.
4. The service builds `SessionConfig` and constructs a harness over an `Environment` and
`SandboxAgentBackend`.
5. The harness opens a cold session, sends one `/run` request to the sidecar, and tears the
Expand Down
11 changes: 6 additions & 5 deletions docs/design/agent-workflows/documentation/ground-truth.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ this page and the referenced code as the source of truth.
on Daytona (`tools/tool-mcp-stdio.ts`; the harness spawns it, its `tools/call` writes relay
request files, and the runner-side relay loop executes server-side). The channel's name
`agenta-tools` is reserved; a user-declared MCP server claiming it is refused. User-declared
MCP resolution is feature-gated (`AGENTA_AGENT_MCPS_ENABLED`, off by default).
external HTTP MCP configuration is resolved for every run. Harness capabilities expose the
editor for Claude and hide it for Pi until Pi has a delivery bridge.
- `client` tools (browser-fulfilled, e.g. `request_connection`) are delivered to Claude too on
the local path: advertised over the same internal MCP channel and PAUSED in the `tools/call`
handler (no JSON-RPC result + abort the request), then resumed from the browser result next
Expand Down Expand Up @@ -84,10 +85,10 @@ this page and the referenced code as the source of truth.
per option (`HARNESS_IDENTITIES`); the stored/wire harness value stays the bare string.
- Per-request model override is not honored on the Pi ACP path. pi-acp accepts only its
default model and silently falls back (`projects/qa/findings.md`, F-007).
- User-declared MCP transports split: remote (`http`) servers are delivered by the runner
(`toAcpMcpServers`, #4834); stdio servers are disabled (`USER_MCP_UNSUPPORTED_MESSAGE`) because
they launch a process on the runner host. The runner's own internal gateway-tool channel is a
separate thing, delivered over loopback HTTP locally and the in-sandbox stdio shim on Daytona.
- User-declared MCP servers are HTTP-only. The runner delivers them to Claude through
`toAcpMcpServers`; Pi refuses them until its MCP bridge exists. The runner's own internal
gateway-tool channel is separate and uses loopback HTTP locally or a trusted in-sandbox stdio
shim on Daytona.
- Trigger lifecycle, Compose.io trigger integration, and event-to-agent mapping are not
implemented in the agent workflow code.
- A persisted agent template object that separates `AGENTS.md`, skills, tools,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ result fields should update both sides and the wire tests in the same PR.
- Session history is not persisted: the runtime is cold and completed `/messages` turns are
not stored.
- `AgentaHarness` policy content is placeholder product copy.
- MCP server resolution is disabled unless `AGENTA_AGENT_MCPS_ENABLED` is truthy.
- External HTTP MCP servers are resolved without a deployment flag. Harness capability metadata
exposes authoring for Claude and hides it for Pi until Pi has a delivery bridge.
- The code still has historical WP labels in some comments. Those labels should not guide new
design decisions.
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,7 @@ In the EE dev compose, the relevant services are:
- `services`. Runs uvicorn on port `8080` inside the container
(`hosting/docker-compose/ee/docker-compose.dev.yml:519`). It hosts the Python agent
service. Traefik routes `/services/` to it. It sets `AGENTA_RUNNER_INTERNAL_URL` to
`http://runner:8765` and `AGENTA_AGENT_MCPS_ENABLED` to `false` by default (lines 564 to
565). It depends on `runner` being healthy (lines 573 to 574).
`http://runner:8765`. It depends on `runner` being healthy.

- `runner`. The Node runner (line 588 onward). In dev it runs `tsx src/server.ts` after
rebuilding the Pi extension. It listens on `8765`. Its health check hits
Expand Down Expand Up @@ -167,7 +166,6 @@ sections).
`http://runner:8765`. When unset, the Python service spawns the runner CLI locally instead
(see `runner_url` in `services/oss/src/agent/config.py` and `select_backend` in
`services/oss/src/agent/app.py`).
- `AGENTA_AGENT_MCPS_ENABLED`. Gates MCP server resolution. Default `false`.
- `SANDBOX_AGENT_PROVIDER`. `local` or `daytona`. Default `local`.
- `SANDBOX_AGENT_DAYTONA_API_KEY`, `_API_URL`, `_TARGET`, `_SNAPSHOT`, `_IMAGE`,
`_INSTALL_PI`. Daytona credentials the runner reads for the `daytona` sandbox provider.
Expand Down
58 changes: 23 additions & 35 deletions docs/design/agent-workflows/documentation/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ Resolution is the service's job, but most of it now lives in the SDK. The servic
entrypoints in `services/oss/src/agent/app.py` (`_agent`): `resolve_tools(agent_config.tools)`
and `resolve_mcp_servers(agent_config.mcp_servers)`. Both are thin re-exports. The service
files under `services/oss/src/agent/tools/` are shims:
`resolver.py` re-exports the SDK's `resolve_tools` and adds the MCP gate; `gateway.py` and
`resolver.py` re-exports the SDK's `resolve_tools`; `gateway.py` and
`secrets.py` re-export the SDK platform adapters. The real composition is
`resolve_tools` in `sdks/python/agenta/sdk/agents/platform/resolve.py`, which builds a
`ToolResolver` (`sdks/python/agenta/sdk/agents/tools/resolver.py`) wired with two
Expand Down Expand Up @@ -162,14 +162,11 @@ invoke immediately instead of failing the model mid-loop, and the agent only eve
name, a schema, and an opaque slug. The Composio key and the connection's auth never leave the
service.

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

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

### MCP servers: a server process the daemon launches

Execution happens in a separate server process. A declared MCP server is resolved server-side
(secrets injected into its `env`) and, for MCP-capable harnesses, passed to the ACP daemon as a
stdio server (`toAcpMcpServers` in `services/agent/src/engines/sandbox_agent/mcp.ts`). The
daemon launches the server's `command` with the resolved `env`, and the harness talks to it
over the MCP protocol.

In practice user MCP is dead on the default path, and for two reasons that stack. First,
resolution is gated behind `AGENTA_AGENT_MCPS_ENABLED`, which is off by default, so the servers
never reach the wire. Second, even with the flag on, `buildSessionMcpServers` drops user MCP
for Pi (Pi's ACP adapter does not forward them), so it would reach Claude only. Pi and Agenta
are the default harnesses, so the `mcp_servers` field is accepted and then silently ignored in
the common case. This is the silent-drop that the
[harness-capabilities project](../../projects/harness-capabilities/proposal.md) is built to fix
(fail loud, or deliver MCP on Pi through the extension). The
[removal-and-capability notes](../../scratch/notes-tools-mcp-capabilities.md) lay out the two
options.
### External MCP servers: remote HTTP connections

A declared user MCP server contains identity, an HTTP connection, credential references, and
policy. The service resolves credential references into request headers for the current run. The
runner validates the remote URL and passes an ACP HTTP MCP entry to Claude. Claude then connects
directly to the external server and discovers its tools through MCP. Public stdio commands and
process environments are not part of the author or runner interface.

The editor shows this section only when the selected harness publishes `mcp.user_servers`. Claude
publishes it. Pi does not and rejects external MCP servers until its bridge exists. The private
`agenta-tools` server remains a separate trusted delivery mechanism for Agenta tools.

## Approval and rendering

Expand Down Expand Up @@ -612,7 +602,7 @@ never drift from the files that exist. The canonical playbook format lives in th
| 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 |
| Code | `code` spec + `env` | the runner | a local subprocess | only the tool's own secrets, scoped to the child |
| Client | `client` spec | the browser | the user's browser, next turn | none |
| MCP | resolved server + `env` | a server process | a stdio child the daemon launches | secrets injected into the server env |
| MCP | resolved HTTP server + headers | the external MCP server | remote URL reached by Claude | named secret references become per-run request headers |

## Where this lives

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

## Status and known gaps

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