[BUG] Headless `-p` suppresses plugin MCP servers in favor of same-URL claude.ai connectors, which cannot authenticate headlessly

Status Open
Reported on v2.1.259
Maintainer reply None cached
Activity 1 comment · opened Sep 3, 2026

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

  1. Install and authenticate the official linear plugin. Have a Linear connector added on your claude.ai account (claude.ai/customize/connectors).
  2. Interactive: claude, then /mcp → plugin:linear:linear connected, no claude.ai Linear.
  3. Headless: claude -p --output-format stream-json --verbose "reply with the single word ok" → init.mcp_servers lists claude.ai Linear: needs-auth and no plugin:linear:linear; no Linear tools.
  4. Repeat with the slack plugin: 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.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗