Skip to content

buzz-acp: one MCP server per agent forces a choice between the shell and any domain server #2899

Open

Description

@ArgussLabs

Summary

buzz-acp accepts a single MCP binary (--mcp-command / BUZZ_ACP_MCP_COMMAND), so an agent gets exactly one MCP server. Because a Buzz agent acts in Buzz by shelling out to the buzz CLI — and that shell tool comes from buzz-dev-mcp — the single slot is an either/or rather than a preference:

  • slot = buzz-dev-mcp → the agent can post, but has no domain tools
  • slot = a domain MCP server → the agent has domain tools, but no way to post

Reproduction

Run buzz-acp with BUZZ_ACP_MCP_COMMAND pointing at any external stdio MCP server (--agent-command buzz-agent), add the agent to a channel, and ask it something its domain server can answer.

Actual behavior

The agent answers internally and never posts. In my run it was offered 61 tools — 60 from the domain server plus load_skill — and zero shell or buzz tools. default_heartbeat_prompt (and the reply guidance around crates/buzz-acp/src/lib.rs:3621) instructs the agent to run buzz messages send --reply-to <event-id>, which it has no tool to do.

Expected behavior

An operator can give an agent buzz-dev-mcp and their own MCP server(s) at once.

Notes

PersonaConfig.mcp_servers is already a Vec<McpServerConfig>, and buzz-persona parses, merges (merge_mcp_servers), and validates it — buzz pack describe even prints the count. It is simply never plumbed to the runtime, which uses one mcp_command per agent record. So the multi-server intent seems to already be there in the persona layer.

The narrowest fix is at the harness: make --mcp-command repeatable and the env var comma-separated, matching the agent_args field directly above it in Args. Defaults unchanged; a single value behaves exactly as today. Happy to be redirected if you would rather solve this at the persona/runtime layer instead.

I have that change working with tests and will open a PR referencing this issue.

Searched open issues/PRs first: nothing found for multiple MCP servers. Closest adjacent ones are #2883 (keeping BUZZ_PRIVATE_KEY out of agent-controlled processes) and #2368 (adding an ACP runtime), neither overlapping.

Environment: block/buzz @ c0d7b52, macOS, buzz-agent runtime.

Activity

  1. wolfyy970 commented on Jul 30, 2026

    @wolfyy970

    I found two related paths:

    PR #1785 implemented structured configuration and layering, but was limited to buzz-agent and closed for inactivity. My analytics MCP also needs arguments and environment variables, so repeatable command strings alone would not cover it.

    Would maintainers prefer a small harness PR that accepts a structured server list and passes it through ACP, followed by a separate Desktop configuration PR based on #1785?

  2. MohsinHashmi-DataInn commented on Jul 31, 2026

    @MohsinHashmi-DataInn

    Confirming this from the Buzz Desktop side, and adding a case that I think is narrower than the one described — in case it's useful when shaping the fix.

    Your diagnosis matches exactly what I hit building a pack for a remote MCP server: buzz pack inspect cheerfully reports MCP servers: 1 while the agent receives none. The persona layer parses, merges and validates; nothing downstream reads it.

    Through Desktop with the claude runtime there is no slot at all — not one.

    Against 63496cc1:

    • desktop/src-tauri/src/managed_agents/runtime.rs:527-529 derives the value from the runtime registry constant, never from the agent record:

      let effective_mcp_command = known_acp_runtime(effective_command)
          .and_then(|r| r.mcp_command)
          .unwrap_or("");
    • desktop/src-tauri/src/managed_agents/discovery.rs:113 — the claude entry is hard-coded mcp_command: None, where codex gets Some("buzz-dev-mcp").

    • desktop/src-tauri/src/managed_agents/env_vars.rs:73 — BUZZ_ACP_MCP_COMMAND is in RESERVED_ENV_KEYS, so the Edit-agent Environment variables field can't supply it either. That looks deliberate (code-execution surface) and I'm not suggesting it change.

    Empirically: setting mcp_command on the agent record in managed-agents.json and restarting leaves the spawned harness with BUZZ_ACP_MCP_COMMAND= empty, and build_mcp_servers() returns vec![].

    So making --mcp-command repeatable would fix the either/or for runtimes that already have a slot — buzz-agent, codex — but a Desktop user on the claude runtime would go from zero to zero. Worth knowing before the harness-level fix is called done.

    That's an argument for the persona/runtime option you offered as the alternative: plumbing PersonaConfig.mcp_servers through would cover both cases at once, since the parsing and merging already exist. The harness change would still be worth having on its own — it just wouldn't close this particular gap.

    Happy to file the Claude-runtime piece as its own issue if you'd rather keep this one focused on the harness. Context: I was attaching a remote Streamable HTTP MCP server to a Desktop agent, which also needs an stdio bridge since McpServerConfig carries no url/transport — but that's a separate matter and I don't want to widen this thread.

  3. xerg616 commented on Aug 5, 2026

    @xerg616

    Ran into this same limitation today, plus found a workaround worth sharing.

    Setup: Buzz 0.5.3 (alpha, Windows), agent harness "Codex" (the built-in one, softland-buzz-codex-acp.exe), agent already using buzz-dev-mcp to respond in-channel. Tried adding a second local MCP server (a Python/stdio server, mcp[cli]==1.28.1) via SOFTLAND_BUZZ_MCP_COMMAND pointed straight at the .exe (no .cmd wrapper, args: [], env: {}) — the pattern this thread's append_server mechanism expects.

    What happened: the agent started fine and the injected server showed up in the session, but every call into it (tools/call, tools/list, even resources/subscribe) failed with a pydantic validation error on protocolVersion, something like:
    protocolVersion
    Input should be a valid literal ... input_value=2, input_type=int
    For further information visit https://errors.pydantic.dev/2.13/v/literal_error

    repeated across every request-type variant in the union (CallToolRequest, ListToolsRequest, SubscribeRequest, UnsubscribeRequest, even newer GetTaskRequest/GetTaskPayloadRequest members that aren't part of MCP 1.x at all). Looks like the handshake between the bridge and the second injected server is sending/expecting a protocolVersion shape that doesn't match what a standard MCP 1.28.1 stdio server declares — separate from the "one --mcp-command" limit itself, but it surfaces from the same code path (append_server), so possibly related.

    Workaround that actually worked: skip the bridge's injection mechanism entirely and add the second server directly to the underlying Codex CLI's own config.toml:

    [mcp_servers.softland]
    command = 'C:\path\to\your\server.exe'
    args = []

    right alongside the existing [mcp_servers.node_repl]/[mcp_servers.codegraph] entries Buzz already writes there. Restarted the agent and it picked up all three MCP servers correctly (confirmed both in the Runtime panel — codegraph, node_repl, softland all mcp enabled — and by actually invoking a tool from the new server successfully in chat).

    So it seems Codex itself has no problem with >1 mcp_servers entry — the constraint is specifically in how the Buzz↔Codex bridge injects servers via env var (single command, and whatever it does for the handshake trips the newer request-type validation). Wondering if the fix here could be: instead of (or in addition to) SOFTLAND_BUZZ_MCP_COMMAND/--mcp-command, have the bridge just write additional entries into Codex's config.toml the same way it already does for its own built-in servers — that path clearly works today.

    Happy to share the full harness log if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions