anthropics / anthropics/claude-code

Plugin MCP OAuth is per-session and not durable; Cowork sessions can never authenticate one at all

オープン
#94,321 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る
area:auth area:cowork area:desktop area:mcp area:plugins bug has repro platform:macos
主要言語
Python
スター
145k
フォーク
23.1k
PR マージ指標
PR 指標を取得中

説明

# Plugin MCP OAuth is per-session and not durable; Cowork sessions can never authenticate one at all

## Summary

A plugin-delivered remote MCP server can be authenticated from a **Code** session, but the connection is bound to that session and does not survive — the same session needed a full browser consent four times in six minutes, and a second session needed its own. **Cowork sessions cannot authenticate one at all**: they load the plugin, they record the server as needing auth, and there is no affordance anywhere to connect it, so the plugin's tools are permanently unavailable in Cowork.

This is distinct from #93671 (see below).

## Environment

- Claude Desktop **1.52386.6**, 3P deployment (gateway inference provider)
- Bundled Claude Code **2.1.270** (earlier observations on 2.1.266)
- macOS (Darwin 25.6)
- Plugin installed from an **organization marketplace, `url` source** served over HTTPS, declaring one remote MCP server:
```json
{ "mcpServers": { "atlassian": { "type": "http", "url": "https://mcp.atlassian.com/v1/mcp/authv2", "oauth": true } } }
```
- SDK server key: `plugin:atlassian:atlassian`

## Part A — Cowork sessions can never authenticate the server

The plugin **is** delivered to Cowork. From a Cowork session's `sdkOptions`:

```
plugins: [
{ type: 'local', path: '…/claude-hostloop-plugins//' },
{ type: 'local', path: '…/claude-hostloop-plugins//atlassian' }
]
```

and its CLI config records `pluginUsage: { "atlassian@inline": { … } }`.

But every Cowork session records the server as unauthenticated, in its own per-session CLI home:

```
local-agent-mode-sessions//00000000/31991e06/.claude/mcp-needs-auth-cache.json
{"plugin:atlassian:atlassian":{"timestamp":1789401264697}} # 2026-09-14 16:54

local-agent-mode-sessions//00000000/203d1886/.claude/mcp-needs-auth-cache.json
{"plugin:atlassian:atlassian":{"timestamp":1789395131601}} # 2026-09-14 15:12
```

The 16:54 entry was created **after** a successful sign-in from a Code session at 15:17, so nothing carries over.

Two structural details that appear to make this unreachable for a user:

1. **`mcpAuthenticate` is per-session.** Every call in the log carries a session id:
`LocalSessions.mcpAuthenticate: sessionId=local_…, server=plugin:atlassian:atlassian`.
No such call has ever been made for a Cowork session — not because it failed, but because nothing initiates one.
2. **Each Cowork session gets a fresh `.claude` home** under
`local-agent-mode-sessions//00000000//.claude/`, with a synthetic identity
(`oauthAccount.emailAddress = "cowork-3p@localhost"`), so per-home state cannot accumulate.

Settings → Plugins → *(plugin)* → **Connect** is the only visible entry point, and it is disabled unless it can borrow a session that advertises the key:

```js
disabled: !borrowedSessionId || busy
// tooltip: "Open a Code session that loads this connector, then try again"
```

## Part B — In Code, the connection is not durable

The same Code session ran the **full** OAuth cycle (`authUrl` → callback → reconnect) four times in six minutes:

```
15:10:51 mcpAuthenticate sessionId=local_6c377534 → authUrl= callbackExpected=true redirectScheme=custom error=none
15:11:20 mcpSubmitOAuthCallbackUrl → error=none
15:11:20 mcpReconnect → connected
15:12:58 mcpAuthenticate (same session, full cycle again)
15:13:32 mcpAuthenticate (again)
15:16:54 mcpAuthenticate (again) → connected at 15:17:24
```

A second Code session used the tools successfully at 16:55, then required its own full cycle at 17:14:

```
17:14:54 mcpAuthenticate sessionId=local_53073f69 → authUrl= …
17:15:04 mcpReconnect → connected
```

Each of those is a browser consent, not a silent token refresh.

## Why this is not #93671

#93671 reports the desktop sending `redirect_uri=claude://claude.ai/mcp-auth-callback/sdk`, which Cloudflare's authorization server **refuses**, and the CLI's connection not being visible to the desktop.

Here the custom scheme is **accepted** — `redirectScheme=custom … error=none`, callback submitted, `mcpReconnect → connected` — so OAuth completes end to end against this authorization server. The failures are different:

- the connection does not persist for the session that authorized it, and
- Cowork sessions have no path to authorize one at all.

## Expected

- A plugin MCP server authorized once is reachable from subsequent sessions without re-consent, or at most a silent reconnect.
- Cowork sessions can authenticate a plugin-delivered MCP server, or the UI states why they cannot.

## Impact

Plugins are the documented way for an organization to distribute an MCP server together with its skills. On this build the skills load in Cowork while the server's tools never do, so an org plugin silently delivers half of itself on that surface. In Code it works but asks for consent repeatedly.

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。