Skip to content
Draft
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
37 changes: 37 additions & 0 deletions docs/design/agent-workflows/projects/pi-mcp-parity/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Pi MCP parity

Status: design ready for review
Date: 2026-07-14

This project makes the existing remote MCP configuration work with Pi. The service-side path stays
identical to Claude. Only the harness-specific last mile changes:

```text
saved agent.mcps
|
v
service resolves secret references
|
v
ResolvedMCPServer[]
|
+--> Claude adapter --> ACP mcpServers
|
+--> Pi adapter ------> pi-mcp-adapter config
```

The Pi path reuses [`pi-mcp-adapter`](https://github.com/nicobailon/pi-mcp-adapter). Agenta does
not implement another MCP client, introduce a gateway, or change the saved object.

## Decision

- Keep `MCPServerConfig` and `ResolvedMCPServer` unchanged.
- Resolve secrets in the agent service exactly as for Claude.
- Generate private `pi-mcp-adapter` configuration from the resolved run object.
- Start with its default `mcp` proxy tool, which works without cache warm-up or restart.
- Support remote HTTP, no auth, secret-backed headers, and all tools.
- Use the same implementation locally and on Daytona.
- Do not add OAuth, public stdio, MCP UI, probing, flags, compatibility, or a gateway.

See [context](context.md), [research](research.md), [interface](interface.md),
[implementation plan](plan.md), [QA](qa.md), and [status](status.md).
44 changes: 44 additions & 0 deletions docs/design/agent-workflows/projects/pi-mcp-parity/context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Context

## Current path

The agent service reads `parameters.agent.mcps`, validates it, resolves project secret references,
and sends `ResolvedMCPServer[]` to the runner. This path is harness-independent.

Claude receives the resolved HTTP servers as ACP `sessionInit.mcpServers`. The runner validates
URLs and applies its SSRF policy before delivery.

The session fingerprint already includes `mcpServers`. Changing an endpoint, secret reference, or
policy makes the next normal turn cold. Approval resume intentionally continues the parked process.

## Why Pi does not work

The runner rejects Pi requests containing MCP servers with
`PI_USER_MCP_UNSUPPORTED_MESSAGE`. Removing the gate alone does not help because
`buildSessionMcpServers()` returns an empty list for Pi.

Installed `pi-acp@0.0.29` accepts ACP MCP entries but does not forward them into Pi. Latest
`pi-acp@0.0.31` documents the same limitation. This is not a UI or warm-session bug.

Pi supports extensions, and the runner already installs an Agenta extension in local and Daytona
runs. `pi-mcp-adapter` supplies the missing HTTP MCP client.

## Boundary

| Concern | Shared | Claude last mile | Pi last mile |
| --- | --- | --- | --- |
| Saved config | Yes | None | None |
| Secret resolution | Agent service | Reused | Reused |
| URL and SSRF validation | Runner | Reused | Reused |
| MCP client | No | Claude Code | `pi-mcp-adapter` |
| Harness config | No | ACP entries | Temporary adapter config |

This remains direct MCP delivery, not the future gateway.

## Scope

In scope: remote HTTP, none and secret-header auth, `tools.mode = "all"`, local and Daytona,
cold configuration replacement, and redacted errors.

Out of scope: OAuth, public stdio, host-config imports, setup panels, MCP UI, sampling, elicitation,
direct-tool cache reload, gateway connections, and public contract changes.
75 changes: 75 additions & 0 deletions docs/design/agent-workflows/projects/pi-mcp-parity/interface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Interface

## Public interfaces do not change

```json
{
"name": "exa",
"connection": {
"type": "http",
"url": "https://mcp.exa.ai/mcp",
"headers": {},
"credentials": {
"type": "header_secret_refs",
"headers": {"x-api-key": "exa_api_key"}
}
},
"policy": {
"tools": {"mode": "all"},
"permission": "ask"
}
}
```

The service resolves this into the existing per-run object. Pi adds no API, ACP field, transport
selector, config version, feature flag, or compatibility decoder.

## Private rendering

```ts
interface PiMcpAdapterConfig {
settings: {
toolPrefix: "server";
directTools: false;
sampling: false;
elicitation: false;
};
mcpServers: Record<string, {
url: string;
headers: Record<string, string>;
lifecycle: "lazy";
exposeResources: false;
directTools: false;
}>;
}
```

This is a private renderer, not another source of truth. Unsupported connection and policy modes
fail closed.

## Runtime artifact

The generated adapter file is secret-bearing. It must:

- live outside the durable conversation workspace;
- use a unique per-process path and restrictive permissions;
- remain for the process lifetime because the adapter may reload it;
- stay out of logs, traces, errors, snapshots, and inspect responses;
- be deleted during teardown;
- follow the same lifecycle locally and on Daytona.

A narrow `pi-acp` launch patch may pass only the config path to Pi. Header values never enter the
environment.

This does not claim secrets are hidden from the MCP client. Like Claude, the selected sandboxed
harness receives the resolved header so it can authenticate, while Agenta avoids persistence.

After local and Daytona acceptance, Pi publishes the existing `mcp.user_servers` capability. The
frontend then shows the editor without Pi-specific UI logic.

Pi sees the adapter's `mcp` proxy tool and can list servers, discover tools, and call them. This
presentation differs from Claude's direct list, but public and resolved interfaces are identical.

Remote tool execution must reuse Pi's existing extension `tool_call` permission hook and
`pi-acp` approval request path. Discovery-only proxy actions may proceed without approval.
Do not add another approval protocol.
55 changes: 55 additions & 0 deletions docs/design/agent-workflows/projects/pi-mcp-parity/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Implementation plan

## Slice 0: Prove the adapter

1. Pin `pi-mcp-adapter@2.11.0`.
2. Package and load it beside the Agenta Pi extension.
3. Use an HTTPS fixture to list a server and call one tool.
4. Confirm no `pi-acp` upgrade is required.

Exit: the pinned upstream adapter works in headless Pi. If bundling fails, ship its package tree.
Do not implement an MCP client.

## Slice 1: Render shared resolved servers

1. Add a pure `ResolvedMCPServer[]` to `PiMcpAdapterConfig` renderer.
2. Reuse URL, reserved-name, and SSRF validation.
3. Accept only HTTP, `tools.mode = all`, and current credential outcomes.
4. Set proxy mode, lazy lifecycle, no resources, sampling, or elicitation.
5. Keep headers out of errors and logs.

Exit: the same resolved input used by Claude produces deterministic Pi config.

## Slice 2: Deliver locally

1. Isolate Pi assets when MCP servers exist.
2. Install both extensions and write temporary config with restrictive permissions.
3. Pass the path through the narrow launch option.
4. Remove the unsupported gate only after delivery is wired.
5. Route proxy tool execution through the existing Pi extension permission hook.
6. Preserve fingerprint and teardown behavior.

Exit: local Pi calls no-secret and secret-header fixtures.

## Slice 3: Deliver identically on Daytona

1. Upload the same pinned adapter asset.
2. Write the same config outside durable cwd.
3. Start Pi with the same option and clean up after exit.
4. Add no Daytona-specific protocol or gateway route.

Exit: the same fixtures pass with the same revision.

## Slice 4: Advertise and accept

1. Publish existing `mcp.user_servers` capability for Pi.
2. Verify the existing UI appears without Pi-specific code.
3. Run the local and Daytona matrix.
4. Capture a replay test from a successful no-secret run.

Exit: users configure the same MCP in the same UI and Pi can use it.

## Non-goals

No gateway, Agenta MCP client, OAuth, public stdio, flags, compatibility, migration, direct-tool
cache warm-up, or new saved and `/run` fields.
46 changes: 46 additions & 0 deletions docs/design/agent-workflows/projects/pi-mcp-parity/qa.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# QA plan

## Unit and packaging tests

- Render one and multiple resolved HTTP servers.
- Preserve empty headers and deliver secrets only through the runtime artifact.
- Reject unsupported connection and policy modes.
- Validate URLs and reserved names before materialization.
- Keep headers out of errors and logs.
- Preserve the existing MCP fingerprint behavior.
- Route Pi MCP requests to the adapter and leave non-Pi requests unchanged.
- Reuse the existing Pi approval path for remote tool execution and honor denial.
- Start the packaged extension with pinned Pi, list a fixture server, and call one tool.

## Acceptance matrix

| Environment | Credentials | Expected result |
| --- | --- | --- |
| Local | None | List server, discover tool, call tool |
| Local | Secret header | Call succeeds; secret absent from output and logs |
| Daytona | None | Same behavior as local |
| Daytona | Secret header | Same behavior; no durable secret file |

Use the same revision and fixtures across environments.

## Configuration change

1. Start Pi with endpoint A and confirm A.
2. Edit the endpoint to B and send a normal turn.
3. Assert the session goes cold and reports B only.
4. Repeat with a header-secret reference change.

Approval resume continues the exact parked process and must not rebuild configuration.

## Failure cases

- DNS failure identifies the server without headers.
- SSRF-blocked URLs fail before Pi starts.
- Missing project secrets fail in the service, as for Claude.
- Invalid adapter config fails the run instead of omitting MCP.
- Adapter startup failure does not fall back to an MCP-less run.

## Completion evidence

Include focused unit results, runner typecheck, package smoke output, and four live traces. Capture
one replay test after the first stable live run.
48 changes: 48 additions & 0 deletions docs/design/agent-workflows/projects/pi-mcp-parity/research.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Research

## Versions

The runner pins `@earendil-works/pi-coding-agent@0.80.6` and `pi-acp@0.0.29`, with a small Agenta
patch for skill and session paths.

The latest published `pi-acp` checked on 2026-07-14 is `0.0.31`. Its README still says MCP
servers are stored from ACP parameters but not wired into Pi. Upgrading it alone does not provide
MCP and is not a prerequisite.

The same README points to
[`pi-mcp-adapter`](https://github.com/nicobailon/pi-mcp-adapter). Latest checked release:
`2.11.0`.

## Reused behavior

The adapter already implements:

- Streamable HTTP with SSE fallback;
- arbitrary HTTP headers;
- initialization, discovery, calls, and cleanup;
- argument validation and result conversion;
- one `mcp` proxy tool for status, search, describe, and call;
- optional cache-backed direct tools.

It accepts standard configuration:

```json
{
"mcpServers": {
"exa": {
"url": "https://mcp.exa.ai/mcp",
"headers": {"x-api-key": "resolved-value"}
}
}
}
```

## Why proxy mode is first

The proxy works on first start. Direct tools depend on disk metadata and become available after a
restart for a new server. Adding cache warm-up only to imitate Claude's presentation adds
complexity without adding MCP functionality.

First prove the pinned adapter loads beside `agenta.js` under Pi `0.80.6`. Prefer the existing
bundled-asset path. If auxiliary files prevent one bundle, ship the pinned upstream package
directory. Do not replace it with a custom client.
36 changes: 36 additions & 0 deletions docs/design/agent-workflows/projects/pi-mcp-parity/status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Status

Last updated: 2026-07-14

## Stage

Design ready for review. No implementation has started.

## Confirmed

- PRs #5296 and #5297 established secret resolution and the MCP UI.
- The runner fingerprint includes MCP servers.
- `pi-acp@0.0.29` does not forward ACP MCP entries.
- Latest checked `pi-acp@0.0.31` still documents that limitation.
- `pi-mcp-adapter@2.11.0` supports remote HTTP and headers.
- The runner has local and Daytona Pi extension-asset machinery.

## Decisions

- Reuse `pi-mcp-adapter`; do not build an MCP client.
- Keep service resolution and public config identical to Claude.
- Add only a Pi-private rendering at the harness boundary.
- Use the adapter proxy first.
- Keep secret-bearing config ephemeral.
- Add no gateway, OAuth, stdio, probing, flag, compatibility, or migration.
- Publish Pi capability only after local and Daytona acceptance.

## Review focus

1. Is the proxy acceptable for the first slice, with direct tools deferred?
2. Does the temporary config lifecycle match the secret boundary?
3. Is anything beyond HTTP plus current secret headers required?

## Next action

Approve the design, then implement slices 0 and 1 together.
Loading