Skip to content

feat(acp): allow opting out of inherited cloud MCP connectors via [agent].inherit_cloud_mcp_servers #753

@ShinyChang

Description

@ShinyChang

Description

openab's ACP path forwards session/new and session/load to the agent with mcpServers: [] and no strictMcpConfig flag (src/acp/connection.rs:414 and :549). For claude-agent-acp, this means the spawned agent inherits whatever MCP servers its OAuth login carries — for a Claude session signed in to claude.ai, that's the cloud connectors (Linear, Notion, Slack, Drive, Calendar, Gmail, Heptabase, etc.).

Operators today have no way to opt out of that inheritance. Requesting an [agent].inherit_cloud_mcp_servers boolean in config.toml, default true so existing deployments keep their current behavior. Operators flip to false to suppress inheritance per deployment (which in openab's helm-chart-per-agent model is per-bot).

Use Case

The current behavior is genuinely wanted in some deployments and unwanted in others, which is why this should be a config knob and not a unilateral default change:

Inherit = true (default, current behavior)

  • Personal / single-operator bot signed in to the operator's own claude.ai. The operator wants @bot summarize my next 3 calendar events or @bot find Linear issues assigned to me to reach their own connectors. This is a feature, not a bug.

Inherit = false

  • Multi-user community Slack/Discord bot. The operator OAuth-signed-in their account, but other users in the workspace shouldn't be able to query the operator's personal Notion / Drive / Gmail through the bot.
  • Token-budget-sensitive deployments. Each cloud connector contributes its tool schemas to every prompt — typically ~90K tokens of mcp__claude_ai__* tools the operator never intended to expose. For high-throughput bots that's real cost.

Proposed Solution

[agent]
command = "claude-agent-acp"
working_dir = "/home/node"
# inherit_cloud_mcp_servers = true   # default; omit to keep current behavior

Plumbing (~30 lines):

  1. src/config.rs — add field to AgentConfig:

    #[serde(default = "default_inherit_cloud_mcp_servers")]
    pub inherit_cloud_mcp_servers: bool,

    with fn default_inherit_cloud_mcp_servers() -> bool { true }. Cannot use #[serde(default)] because bool::default() is false, which would invert the intended default.

  2. src/acp/connection.rssession_new and session_load accept the flag and, when false, attach the strict marker to the JSON-RPC payload:

    {
      "cwd": "...",
      "mcpServers": [],
      "_meta": { "claudeCode": { "options": { "strictMcpConfig": true } } }
    }
  3. src/acp/pool.rs — thread self.config.inherit_cloud_mcp_servers into the two callers.

  4. config.toml.example and docs/config-reference.md — document the new field.

  5. Two unit tests in src/config.rs (default-true parse; explicit-false parse).

Why this works without any claude-agent-acp upstream change. The createSession handler in claude-agent-acp reads params._meta.claudeCode.options into userProvidedOptions and spreads it into the SDK options object before the explicit override block (dist/acp-agent.js, around the const options = { systemPrompt, ..., ...userProvidedOptions, ..., cwd: params.cwd, ..., mcpServers: { ... }, ... } construction). strictMcpConfig is not in the override block, so the spread path carries it cleanly into the SDK call. Other agents (kiro / codex / gemini / copilot / cursor) ignore the _meta.claudeCode namespace, so the param is harmless when sent to non-Claude backends.

I've been running this behavior in production for several weeks via a runtime SDK overlay (patching strictMcpConfig: true directly into that same SDK options block) and can confirm cloud connectors are cleanly suppressed without other side effects. The proposed config-driven path eliminates the SDK overlay and routes the choice through the existing ACP _meta channel instead.

Happy to send a PR in this shape if the direction is acceptable. The change is intentionally narrow — no per-user MCP injection, no slash commands, no behavior change for any existing deployment.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions