feat(connector): fold OAuth into the custom MCP (PluginDevModal) form (#15661)

*  feat(connector): support API key / custom header / OAuth auth in custom connector

Make the connector backend a full replacement for the legacy custom-MCP plugin form:

- connector create/update now accept bearer/apikey/header credentials (encrypted at rest);
  oauth2 stays callback-only
- map apikey → bearer auth and header → request headers in both the sync path
  (syncTools + callTool) and the agent-runtime manifest path
- pass custom HTTP headers through to the MCP client
- AddConnectorModal becomes a rich form: MCP type (HTTP/STDIO), auth type
  (None / API Key / Custom Headers / OAuth), reusing the plugin form inputs;
  OAuth keeps the existing popup authorize flow, others create + sync directly

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ♻️ refactor(connector): fold OAuth into the PluginDevModal MCP form

Pivot the custom-MCP entry to reuse the rich PluginDevModal / MCPManifestForm
instead of a bespoke connector modal, and add OAuth as an auth type inside it:

- MCPManifestForm: gated `enableOAuth` adds an "OAuth" auth type with
  Client ID / Secret (optional) + redirect-URI hint. Only the custom-connector
  entry enables it, so plain custom-plugin DevModal callers (editing plugins,
  agent tools, …) are unaffected.
- DevModal: opens the OAuth popup synchronously on the save click (browsers
  block window.open once an async boundary is crossed), validates, then hands
  the popup to onSave which navigates it to the authorize URL.
- New CustomConnectorModal wraps DevModal and persists every auth type onto the
  connector backend (none / bearer / custom headers → create + sync; OAuth →
  create with OIDC config + run the authorize popup).
- settings/skill entry now opens CustomConnectorModal; the standalone
  AddConnectorModal rich rewrite from the previous commit is reverted to the
  canary original (it is only referenced by the unused ConnectorList).
- i18n: dev.mcp.auth.oauth* keys (default + en-US + zh-CN).

Backend stays as in the prior commit (connector create/update accept
bearer/apikey/header credentials; sync + manifest paths apply them).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 🐛 fix(connector): route the OAuth auth type through the authorize flow, not the token-less manifest test

Selecting OAuth and clicking "Test connection" called the plugin manifest test
(getStreamableMcpServerManifest), which connects with no token and 401s on any
OAuth-gated server (e.g. Linear MCP / DCR). For OAuth there is nothing to test
without authorizing first, so the button now becomes "Authorize & Connect" and
runs the connector OAuth flow (discovery + DCR + authorize popup), shared with
the footer save button via DevModal.runOAuthFlow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 🐛 fix(connector): make connector.create idempotent on (user, identifier)

Re-adding or re-authorizing a custom connector with an existing identifier hit
the user_connectors unique constraint and 500'd. Now an existing row is updated
(reset to disconnected, refreshed name/url/oidcConfig/credentials) and its id
reused, instead of inserting a duplicate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ♻️ refactor(skill-store): route Add Custom MCP through the connector modal, drop the Custom tab

- Skill Store "Add → Add Custom MCP Skill" now opens CustomConnectorModal
  (connector backend + OAuth), matching the settings/skill entry, instead of
  the legacy plugin DevModal (installCustomPlugin + togglePlugin).
- Remove the now-redundant "Custom" tab from the Skill Store (custom MCP lives
  in the connector list now): drop SkillStoreTab.Custom, its tab option,
  CustomList render, and the matching search branch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
LiJian
2026-06-11 01:00:38 +08:00
committed by GitHub
parent c65cf8c2a0
commit 6d47c1d07e
15 changed files with 600 additions and 85 deletions
+9
View File
@@ -447,6 +447,15 @@
"dev.mcp.auth.desc": "Select authentication method for MCP server",
"dev.mcp.auth.label": "Auth type",
"dev.mcp.auth.none": "No auth",
"dev.mcp.auth.oauth": "OAuth",
"dev.mcp.auth.oauth.authorize": "Authorize & Connect",
"dev.mcp.auth.oauth.clientId.desc": "Leave empty to register a client automatically (dynamic client registration)",
"dev.mcp.auth.oauth.clientId.label": "OAuth Client ID",
"dev.mcp.auth.oauth.clientId.placeholder": "Optional",
"dev.mcp.auth.oauth.clientSecret.desc": "Only required for confidential OAuth clients",
"dev.mcp.auth.oauth.clientSecret.label": "OAuth Client Secret",
"dev.mcp.auth.oauth.clientSecret.placeholder": "Optional",
"dev.mcp.auth.oauth.redirectHint": "Redirect URI to register with your OAuth app:",
"dev.mcp.auth.placeholder": "Select auth type",
"dev.mcp.auth.token.desc": "Enter your API Key or Bearer Token",
"dev.mcp.auth.token.label": "API Key",
+9
View File
@@ -447,6 +447,15 @@
"dev.mcp.auth.desc": "选择 MCP 服务器的认证方式",
"dev.mcp.auth.label": "认证类型",
"dev.mcp.auth.none": "无需认证",
"dev.mcp.auth.oauth": "OAuth",
"dev.mcp.auth.oauth.authorize": "授权并连接",
"dev.mcp.auth.oauth.clientId.desc": "留空将自动注册客户端(动态客户端注册 DCR)",
"dev.mcp.auth.oauth.clientId.label": "OAuth Client ID",
"dev.mcp.auth.oauth.clientId.placeholder": "可选",
"dev.mcp.auth.oauth.clientSecret.desc": "仅机密类型的 OAuth 客户端需要填写",
"dev.mcp.auth.oauth.clientSecret.label": "OAuth Client Secret",
"dev.mcp.auth.oauth.clientSecret.placeholder": "可选",
"dev.mcp.auth.oauth.redirectHint": "请在你的 OAuth 应用中登记以下 Redirect URI",
"dev.mcp.auth.placeholder": "请选择认证类型",
"dev.mcp.auth.token.desc": "输入你的 API Key 或 Bearer Token",
"dev.mcp.auth.token.label": "API Key",