Skip to content

Support Buzz: MCP servers, client system prompts, and agent identity - #28

Closed
sarahwooders wants to merge 4 commits into
mainfrom
sarah/buzz-acp-support
Closed

sarahwooders wants to merge 4 commits into
mainfrom
sarah/buzz-acp-support

Conversation

@sarahwooders

Copy link
Copy Markdown
Contributor

Makes letta-acp usable as a first-class harness for Buzz, whose buzz-acp harness drives ACP agents over stdio. Companion PR adds letta as a preset runtime in Buzz itself.

Buzz passes MCP servers in session/new, may send a per-session system prompt, and reads agentInfo from initialize to identify the adapter. We ignored all three.

Changes

  • MCP servers — session/new and session/load now connect the client's stdio MCP servers and register their tools as Letta external tools, namespaced mcp__<server>__<tool>. Tool calls proxy through to tools/call; servers shut down with the session. A server that fails to start is logged and skipped rather than failing session/new; http/sse are skipped with a warning (which is why initialize still advertises no mcpCapabilities). Zed benefits from this too, not just Buzz.
  • systemPrompt — a client-supplied system prompt on session/new is delivered as a framed preamble on the session's first prompt, since Letta agents carry their own persona and the SDK has no per-session system prompt to set.
  • agentInfo — initialize reports name + package version. buzz-acp models showed Agent: unknown vunknown before; it now shows letta-acp v0.1.4.
  • Cloud caveat — cloud agents run their harness in Letta's sandbox, which has no executor for adapter-side tools: both the editor fs tools and MCP tools fail there with External tool executor not set. The adapter now warns once at session setup, and the README says which backend to use instead.
  • Docs — a "Use from Buzz" section with a backend table: the agent answers Buzz by shelling out to the buzz CLI, so the CLI has to live wherever the chosen backend runs tools.

Testing

  • bun run check (46 tests, typecheck, build). New coverage: the MCP bridge (namespacing, proxying, env pass-through, error results, unreachable server, non-stdio transport) against a fixture stdio MCP server, plus agentInfo and one-shot system-prompt delivery.
  • Live end-to-end through a real ACP client with an MCP server attached — the model called mcp__mock__echo and got its output back — on the local, remote, and cloud-oauth backends. On cloud the call fails as described above, which is where the new warning comes from.
  • buzz-acp models --agent-command bun --agent-args src/index.ts against a locally built buzz-acp, on the local, remote, and cloud-oauth backends.

Buzz drives ACP agents through its buzz-acp harness, which passes MCP
servers in session/new, may send a per-session system prompt, and reads
agentInfo from initialize to identify the adapter. The adapter ignored
all three.

- session/new and session/load now connect the client's stdio MCP
  servers and register their tools as Letta external tools, namespaced
  mcp__<server>__<tool>. A server that cannot start is logged and
  skipped rather than failing the session; http/sse are skipped with a
  warning, matching the absent mcpCapabilities.
- A client-supplied systemPrompt is delivered as a framed preamble on
  the session's first prompt, since Letta agents carry their own
  persona and the SDK has no per-session system prompt.
- initialize reports agentInfo (name + package version).
Covers the buzz-acp harness and Buzz Desktop, and which backend to pick:
the agent answers Buzz through the `buzz` CLI, so the CLI has to live
wherever tools run. local and cloud-oauth inherit the harness env;
remote needs the CLI on the app server; cloud runs tools in Letta's
sandbox, so Buzz access goes through an MCP server, which the adapter
spawns in its own process.
Cloud agents run their harness in Letta's sandbox, which has no executor
for tools that live in this process: the editor fs tools and MCP-backed
tools both fail there with "External tool executor not set". Log that
once at session setup instead of leaving it to a failed tool call
mid-turn, and correct the Buzz backend guidance — cloud-oauth is the
backend for cloud-hosted agents with local tool execution.
@sarahwooders
sarahwooders marked this pull request as draft July 29, 2026 03:02
@sarahwooders
sarahwooders marked this pull request as ready for review July 29, 2026 03:03
Clients with a model picker read the model-category entry of
configOptions from session/new. We published none, so Zed had nothing to
show and Buzz reported "letta-acp reported no models" and fell back to a
free-text custom-model box.

- session/new and session/load now carry a model selector built from
  session.listModels(), and session/set_config_option switches the model
  through session.updateModel().
- The catalog is fetched once per process and shared across sessions.
  session/new waits at most 2s for it: a slow or unsupported catalog must
  not hold up session creation, and a late arrival is pushed as a
  config_option_update instead.
- Entries are keyed by handle (provider/model), the same identifier
  LETTA_ACP_MODEL and /model take. Letta lists one entry per reasoning
  tier and tiers share a handle, so they are collapsed to one option --
  as ACP options they would be indistinguishable duplicates.
- Each entry also carries the pre-rename `configId`/`displayName`
  spellings, since buzz-acp's reader requires configId and would
  otherwise see no options at all.
@sarahwooders
sarahwooders force-pushed the sarah/buzz-acp-support branch from f0e86df to 7213093 Compare July 29, 2026 04:10
@just-cameron

Copy link
Copy Markdown
Contributor

Overlord (agent-c2adbf5c-8419-4211-8cd8-3740db164974)

I ran the full suite on 7213093 (57 tests, typecheck, and build all pass), but I found three blockers before this is ready to merge:

  1. The systemPrompt path is not reachable through current Buzz negotiation. letta-acp caps its initialize response at the ACP SDK’s PROTOCOL_VERSION (1), while current Buzz only includes systemPrompt in session/new for agents reporting protocol version 2+. The integration test injects the extra field directly, so it bypasses that real client gate. We need to settle and test the actual capability/version contract end to end.

  2. MCP startup has no deadline. Both client.connect() and client.listTools() can wait indefinitely if a subprocess starts but never completes initialization or tool listing, which wedges session/new. The existing failure test covers only a nonexistent executable, which rejects immediately. Please add bounded initialization/list deadlines, cleanup of the partially started client/process, and a silent-server regression test.

  3. The model-selector portion now conflicts with the implementation merged in feat: expose ACP model configuration #30 and drops its active-session behavior. This branch builds one process-wide selection from config/catalog defaults rather than the runtime model, so a resumed conversation previously switched to another model—or a custom model outside the catalog—can be reported incorrectly. The rebase should preserve main’s runtime/current-model handling and add only the Buzz compatibility fields that are still needed.

The MCP bridge and agentInfo pieces look useful. I would rebase onto current main, keep the merged model implementation, then address the protocol gate and MCP liveness boundary above.

@just-cameron

Copy link
Copy Markdown
Contributor

Overlord (agent-c2adbf5c-8419-4211-8cd8-3740db164974)

Update after #29 merged: please drop this PR’s MCP implementation when rebasing.

#29 now owns this path by forwarding ACP stdio server configuration into @letta-ai/letta-agent-sdk, which owns MCP startup, namespaced tool registration, execution, and cleanup. We validated that path end to end in Zed, including real tool calls, and added shutdown plus duplicate-session lifecycle coverage. Keeping this PR’s separate mcp-tools.ts bridge and direct @modelcontextprotocol/sdk dependency would duplicate that ownership and discard the lifecycle fixes.

The rebase should preserve current main for the merged model selector (#30), terminal rendering (#34), bookkeeping permissions (#35), session discovery (#33), and MCP/session cleanup (#29).

That leaves agentInfo as the clear independently useful Buzz change. systemPrompt still needs its real negotiation contract resolved separately: current Buzz only sends it to agents reporting ACP v2+, while this adapter reports v1, so the existing direct-injection test does not prove a reachable Buzz path.

@sarahwooders

Copy link
Copy Markdown
Contributor Author

Superseded — every piece of this landed upstream or turned out to be unnecessary.

  • MCP servers → superseded by refactor: delegate MCP transports to Agent SDK #41, which delegates stdio/http/sse transports to the Agent SDK instead of the hand-rolled bridge in this branch.
  • Model catalog as a session config option → superseded by feat: expose ACP model configuration #30/fix: restore permission selection with model options #36 (sessionConfigOptions()).
  • Client system prompts → not needed. Buzz's session_new_system_prompt() returns None for any agent negotiating protocol v1, and it prepends the base prompt into the first user message for legacy agents instead. It never sends systemPrompt to letta-acp.
  • agentInfo in initialize → optional. Every Buzz reader falls back to "unknown"; it only affects session titles and log lines.

Published @letta-ai/letta-acp@0.1.5 works with Buzz as-is, so the integration is now a Buzz-side-only change (block/buzz#3454).

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.

2 participants