[BUG] Headless `-p` suppresses plugin MCP servers in favor of same-URL claude.ai connectors, which cannot authenticate headlessly
Preflight Checklist
- [x] Searched existing issues (no match for the plugin/connector dedup)
- [x] Reproduced on the latest version (2.1.259)
What's Wrong?
When a plugin's MCP server and a claude.ai connector point at the same URL, Claude Code dedupes them by URL. The two startup paths resolve the duplicate in opposite directions:
| Startup path | Winner | Result |
|---|---|---|
| Interactive REPL (useManageMcpConnections) | plugin server | connector suppressed (Suppressing claude.ai connector "claude.ai Linear": duplicates manually-configured "plugin:linear:linear") |
| Headless -p ([MCP] Lazy dedup) | claude.ai connector | plugin server suppressed (Lazy dedup: suppressing 1 plugin server(s) that duplicate claude.ai connectors: plugin:linear:linear) |
In -p mode the winning connector cannot complete the claude.ai bridge login, so it sits at needs-auth and exposes zero tools. Net effect: a plugin that works in every interactive session has no tools in any -p session, with no error surfaced to the caller. Only the system/init frame's mcp_servers list hints at it.
Observed with the official linear and slack plugins (both have same-URL connectors on my claude.ai account). The github and playwright plugins, which have no matching connector, load fine in -p. User-scope mcpServers entries (atlassian, figma) also load fine, because manual config wins the dedup in both paths.
This affects anything driving claude -p: the Agent SDK, CI, and local wrappers.
What Should Happen?
Both paths should resolve the duplicate the same way. Preferably headless prefers the side that can actually authenticate in that mode (the plugin server, whose OAuth token is already in the keychain), or at minimum matches the interactive behavior.
Error Messages/Logs
-p init frame, cwd with the linear plugin enabled and authenticated:
mcp_servers includes {"name":"claude.ai Linear","status":"needs-auth"}
mcp_servers does NOT include plugin:linear:linear
tools: no mcp__plugin_linear_linear__* entries
Same invocation, same cwd, interactive: mcp__plugin_linear_linear__* tools present, no claude.ai Linear server.
Adding a throwaway user-scope entry (claude mcp add --scope user --transport http lineartest https://mcp.linear.app/mcp) makes lineartest appear in the -p init frame immediately, while plugin:linear:linear still does not — confirming the plugin is being suppressed rather than failing to connect.
Steps to Reproduce
- Install and authenticate the official
linearplugin. Have a Linear connector added on your claude.ai account (claude.ai/customize/connectors). - Interactive:
claude, then/mcp→plugin:linear:linearconnected, noclaude.ai Linear. - Headless:
claude -p --output-format stream-json --verbose "reply with the single word ok"→init.mcp_serverslistsclaude.ai Linear: needs-authand noplugin:linear:linear; no Linear tools. - Repeat with the
slackplugin: identical.
Claude Model
Fable 5.1 (any model; MCP wiring is model-independent)
Is this a regression?
Unknown — headless sessions on this machine had the plugin tools through 2026-08-31 and lost them the same day; every locally installed version (2.1.252, 2.1.257, 2.1.258, 2.1.259) contains the lazy-dedup path.
Claude Code Version
2.1.259 (native arm64 binary)
Platform
Native binary via ~/.local/bin/claude
Operating System
macOS 26 (Darwin 25.6.0), arm64
Terminal/Shell
zsh in Warp for the interactive case; -p spawned from a launchd LaunchAgent (bare env) and also directly from the terminal — both stripped.
Workaround
Register the server as a user-scope mcpServers entry (claude mcp add --scope user --transport http linear https://mcp.linear.app/mcp), which wins the dedup in both paths and auto-suppresses the plugin copy; or remove the claude.ai connector.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗