feat(mcp): support plugin authorization targets - #2831
Conversation
🦋 Changeset detectedLatest commit: c1823eb The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
1538c93 to
e1ef0e3
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e1ef0e339b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
|
Codex Review: Didn't find any major issues. You're on a roll. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c710b10545
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2db35b6ed2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
|
Codex Review: Didn't find any major issues. Bravo. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ac58763eb0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c1823eb431
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return Promise.all( | ||
| targets.map(async (target) => { | ||
| const server = await this.resolveMcpServerTarget(target); | ||
| return { target, authStatus: await this.mcpServerAuthState(server, oauth) }; |
There was a problem hiding this comment.
Serialize probes that share an OAuth identity
When targets contains the same locator twice, or multiple locators resolve to the same runtime name and URL, this Promise.all starts concurrent connections through the same cached OAuth provider. If the credential requires refresh and the authorization server rotates refresh tokens, both probes can submit the old token; one succeeds while the other reports oauth-required, producing inconsistent results and potentially disrupting concurrent live-server authentication. Fresh evidence beyond the earlier cache-coherence comment is that reloadCachedMcpOAuthTokens runs only after each probe finishes, so it cannot prevent this refresh race; deduplicate or serialize probes by OAuth credential identity. The v1 implementation in core-impl.ts mirrors the same parallel behavior.
Useful? React with 👍 / 👎.
Related Issue
No public issue is linked. This PR exposes the MCP authorization surface needed by hosts that manage plugin-provided MCP servers.
Problem
MCP OAuth management is currently keyed only by entries in the user-global
mcp.json. Plugin MCP servers are loaded by the runtime too, but hosts cannot explicitly:Treating plugin MCP servers as global configuration would give hosts an authorization entry point, but it would also mix plugin-owned configuration into the global registry and create conflicting ownership.
The existing status shortcuts can also become stale: the presence of stored tokens or
auth: "oauth"does not prove that the server currently accepts the credential.What Changed
Target-aware authorization API
Introduced a shared locator for both configuration sources:
The Core and Node SDK now expose target-aware operations for:
Plugin targets are resolved through plugin metadata. OAuth credentials continue to use the runtime server name and URL, matching the identity used by the connection manager.
Runtime-backed status
Authorization status is determined with an isolated, non-interactive connection probe instead of treating stored credentials or an auth marker as authoritative.
oauth-required.oauth-authorized.unavailablerather than being mislabeled as authentication failures.SSE startup now preserves a resource-level HTTP 401 even when the MCP SDK subsequently wraps it in an OAuth discovery or registration error. Non-401 startup failures are not reclassified.
Compatibility and ownership boundaries
listGlobalMcpServersand global CRUD still operate only onmcp.json.enabled: falseand do not connect disabled servers. Explicit OAuth management calls retain their existing behavior and do not mutate enabled state.Validation
All GitHub Actions workflows for the current head passed:
Focused local validation also passed:
@moonshot-ai/agent-core,@moonshot-ai/agent-core-v2, and@moonshot-ai/kimi-code-sdkgit diff --checkThe regression coverage includes V1/V2 plugin target parity, runtime credential identity, disabled-server probe behavior, stale credentials, unavailable servers, configuration hints, SSE stale-provider 401 recovery, clearing stale 401 state when an authenticated retry returns HTTP 500, and non-401 classification.
Checklist