Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

## [Unreleased]

### 新增

- 自定义 MCP 连接器支持 OAuth:从 MCP URL 自动发现授权端点,统一 `POST /connectors/oauth/start` 启动授权;保存后可探测、一键授权,Bearer 注入 harness spec;Custom Connectors UI 支持启用与「对话默认选中」分离、保存后可选探测

### 变更

- Docker / FnOS 镜像对外统一为 `ghcr.io/tencentcloud/octop`(仍同步推送 Docker Hub;移除 vanilla 镜像标签)
Expand Down
71 changes: 57 additions & 14 deletions dashboard/src/api/modules/connectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,23 @@ export interface ConnectorProbeResult {
tool_count?: number;
tools?: { name: string; description: string }[];
error?: string;
error_type?: string;
status_code?: number;
oauth?: {
available: boolean;
issuer?: string;
resource?: string;
};
}

export type CustomMcpTransport = "streamable_http" | "stdio";

export interface CustomMcpOAuthPreview {
configured?: boolean;
required?: boolean;
expires_at?: number;
}

export interface CustomMcpServerSpec {
transport: CustomMcpTransport;
url?: string;
Expand All @@ -99,8 +111,13 @@ export interface CustomMcpServerSpec {
display_name?: string;
/** When true, chat composer pre-selects this MCP server. */
default_open?: boolean;
oauth?: CustomMcpOAuthPreview;
}

export type OAuthStartTarget =
| { type: "catalog"; kind: string }
| { type: "custom_mcp"; server_name: string };

export type CustomMcpServers = Record<string, CustomMcpServerSpec>;

export interface ConnectorCliInstallResult {
Expand Down Expand Up @@ -144,7 +161,9 @@ export const connectorsApi = {
listInstances: () => request<ConnectorInstance[]>("/connector-instances"),

getInstance: (instanceId: string) =>
request<ConnectorInstanceDetail>(`/connector-instances/${instanceId}`),
request<ConnectorInstanceDetail>(
`/connector-instances/${encodeURIComponent(instanceId)}`,
),

createInstance: (body: {
kind: string;
Expand All @@ -158,23 +177,41 @@ export const connectorsApi = {
}),

deleteInstance: (instanceId: string) =>
request<void>(`/connector-instances/${instanceId}`, { method: "DELETE" }),
request<void>(`/connector-instances/${encodeURIComponent(instanceId)}`, {
method: "DELETE",
}),

patchInstance: (
instanceId: string,
body: { status?: "active" | "disabled"; default_open?: boolean },
) =>
request<ConnectorInstance>(`/connector-instances/${instanceId}`, {
method: "PATCH",
body: JSON.stringify(body),
}),
request<ConnectorInstance>(
`/connector-instances/${encodeURIComponent(instanceId)}`,
{
method: "PATCH",
body: JSON.stringify(body),
},
),

testInstance: (instanceId: string) =>
request<ConnectorProbeResult>(`/connector-instances/${instanceId}/test`, {
method: "POST",
}),
request<ConnectorProbeResult>(
`/connector-instances/${encodeURIComponent(instanceId)}/test`,
{
method: "POST",
},
),

oauthStart: (kind: string, redirectAfter?: string) =>
oauthStart: (target: OAuthStartTarget, redirectAfter?: string) =>
request<{ authorize_url: string; state_id: string }>(
"/connectors/oauth/start",
{
method: "POST",
body: JSON.stringify({ target, redirect_after: redirectAfter }),
},
),

/** @deprecated Prefer oauthStart with `{ type: "catalog", kind }`. */
oauthStartCatalog: (kind: string, redirectAfter?: string) =>
request<{ authorize_url: string; state_id: string }>(
`/connectors/oauth/${kind}/start`,
{
Expand All @@ -184,9 +221,12 @@ export const connectorsApi = {
),

oauthPending: (stateId: string) =>
request<{ kind: string; tokens: Record<string, unknown> }>(
`/connectors/oauth/pending/${stateId}`,
),
request<{
kind: string;
tokens: Record<string, unknown>;
server_name?: string;
applied?: boolean;
}>(`/connectors/oauth/pending/${stateId}`),

authorizeUrl: (kind: string) =>
request<{ authorize_url: string | null }>(
Expand Down Expand Up @@ -284,7 +324,10 @@ export const connectorsApi = {
body: JSON.stringify({ servers }),
}),

patchCustomMcpServer: (name: string, body: { enabled: boolean }) =>
patchCustomMcpServer: (
name: string,
body: { enabled?: boolean; default_open?: boolean },
) =>
request<{ servers: CustomMcpServers }>(
`/connectors/custom-mcp/servers/${encodeURIComponent(name)}`,
{
Expand Down
15 changes: 14 additions & 1 deletion dashboard/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -4308,9 +4308,22 @@
"duplicateName": "Server IDs must be unique",
"emptyName": "Server ID is required",
"probeNeedConfig": "Fill in the configuration before probing",
"probeOnSave": "Probe connection after save",
"probeOnSaveHint": "Octop will request your MCP URL to verify connectivity. If sign-in is required, we will guide you through OAuth.",
"probeNeedsOAuth": "This MCP requires OAuth before it can be used",
"probeComplete": "Connection verified",
"oauthConfigured": "OAuth authorization completed",
"oauthAuthorizeHint": "One-click OAuth saves your config first, then opens sign-in; we verify the connection again afterward.",
"oauthBeforeEnable": "Complete OAuth authorization before enabling this server",
"enable": "Enable connector",
"enableHint": "When off, agents will not load tools from this MCP.",
"defaultOpen": "Selected by default in chat",
"defaultOpenHint": "When on, your Dashboard, IM, and Cron (with no manual picks) include this MCP by default.",
"defaultOpenRequiresEnable": "Enable the connector first.",
"authorizing": "Authorizing…",
"probeOk": "Probe ok — found {{count}} tools",
"deleteConfirm": "Delete MCP server \"{{name}}\"?",
"deleteConfirmHint": "Changes take effect after you click Save."
"deleteConfirmHint": "Saved servers are removed immediately; unsaved drafts are dropped from this editor only."
},
"configuredBadge": "Connected",
"clickToConnect": "Click to connect",
Expand Down
15 changes: 14 additions & 1 deletion dashboard/src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -4447,9 +4447,22 @@
"duplicateName": "服务器 ID 不能重复",
"emptyName": "请填写服务器 ID",
"probeNeedConfig": "请先填写完整配置再探测",
"probeOnSave": "保存后自动探测连接",
"probeOnSaveHint": "将向您填写的 MCP 地址发起请求以验证可用性;若需登录,会引导您完成 OAuth。",
"probeNeedsOAuth": "此 MCP 需要 OAuth 授权才能访问",
"probeComplete": "连接正常",
"oauthConfigured": "已完成 OAuth 授权",
"oauthAuthorizeHint": "点击「一键授权」将先保存配置再打开登录页;完成后我们会自动再次验证连接。",
"oauthBeforeEnable": "请先完成 OAuth 授权后再启用",
"enable": "启用连接器",
"enableHint": "关闭后 Agent 不会加载此 MCP 的工具。",
"defaultOpen": "对话默认选中",
"defaultOpenHint": "开启后,你的 Dashboard、IM 与 Cron(未手动选连接器时)会默认带上此 MCP。",
"defaultOpenRequiresEnable": "需先启用连接器。",
"authorizing": "授权中…",
"probeOk": "探测成功,发现 {{count}} 个工具",
"deleteConfirm": "确定删除 MCP 服务器「{{name}}」?",
"deleteConfirmHint": "删除后需点击保存才会生效。"
"deleteConfirmHint": "已保存的服务器将立即删除;尚未保存的配置只会从当前编辑中移除。"
},
"configuredBadge": "已连接",
"clickToConnect": "点击连接",
Expand Down
Loading
Loading