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
38 changes: 38 additions & 0 deletions dashboard/src/api/modules/connectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@ export interface ConnectorProbeResult {
status_code?: number;
}

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

export interface CustomMcpServerSpec {
transport: CustomMcpTransport;
url?: string;
headers?: Record<string, string>;
command?: string;
args?: string[];
env?: Record<string, string>;
enabled?: boolean;
}

export type CustomMcpServers = Record<string, CustomMcpServerSpec>;

export const connectorsApi = {
catalog: () => request<ConnectorCatalogEntry[]>("/connectors/catalog"),

Expand Down Expand Up @@ -145,4 +159,28 @@ export const connectorsApi = {
method: "POST",
body: JSON.stringify(body),
}),

getCustomMcp: () =>
request<{ servers: CustomMcpServers }>("/connectors/custom-mcp"),

putCustomMcp: (servers: CustomMcpServers) =>
request<{ servers: CustomMcpServers }>("/connectors/custom-mcp", {
method: "PUT",
body: JSON.stringify({ servers }),
}),

patchCustomMcpServer: (name: string, body: { enabled: boolean }) =>
request<{ servers: CustomMcpServers }>(
`/connectors/custom-mcp/servers/${encodeURIComponent(name)}`,
{
method: "PATCH",
body: JSON.stringify(body),
},
),

testCustomMcp: (body: { name?: string; server?: CustomMcpServerSpec }) =>
request<ConnectorProbeResult>("/connectors/custom-mcp/test", {
method: "POST",
body: JSON.stringify(body),
}),
};
91 changes: 91 additions & 0 deletions dashboard/src/api/modules/expertMarket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { request } from "../request";

export interface LocalizedText {
zh?: string;
en?: string;
}

export interface ExpertMarketQuickPrompt {
title: LocalizedText;
description: LocalizedText;
prompt: LocalizedText;
color?: string;
icon_name?: string | null;
}

export interface MarketExpert {
id: string;
slug: string;
label: LocalizedText;
description: LocalizedText;
scene?: string;
sub_scene?: string;
icon_url?: string | null;
icon_name?: string | null;
color?: string | null;
skill_slugs?: string[];
skill_count?: number;
source?: string;
content?: LocalizedText;
quick_prompts?: ExpertMarketQuickPrompt[];
}

export interface ExpertHubListResponse {
items: MarketExpert[];
scenes: string[];
}

export interface CreateMarketExpertResponse {
id: number | string;
agent_id: string;
user_id: number;
name: string;
description?: string | null;
default_model?: string | null;
state: string;
expert_id: string;
icon_name?: string | null;
color?: string | null;
market: {
source: string;
kind: string;
slug: string;
welcome_enrichment: "pending" | "skipped" | "succeeded" | "failed" | string;
};
bootstrap_pending: boolean;
}

export interface CreateMarketExpertBody {
name?: string;
description?: string;
providers?: string[];
default_model?: string;
backend?: Record<string, unknown>;
}

function hubListPath(query: string, scene: string): string {
const params = new URLSearchParams();
const q = query.trim();
const s = scene.trim();
if (q) params.set("q", q);
if (s) params.set("scene", s);
const qs = params.toString();
return qs ? `/experts/hub?${qs}` : "/experts/hub";
}

export const expertMarketApi = {
list: (query: string, scene = "") =>
request<ExpertHubListResponse>(hubListPath(query, scene)),

get: (slug: string) =>
request<MarketExpert>(`/experts/hub/${encodeURIComponent(slug)}`),

install: (slug: string, body: CreateMarketExpertBody = {}) =>
request<CreateMarketExpertResponse>(
`/experts/hub/${encodeURIComponent(slug)}/install`,
{
method: "POST",
body: JSON.stringify(body),
},
),
};
83 changes: 76 additions & 7 deletions dashboard/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"CONNECTOR_KIND_UNSUPPORTED": "Unsupported connector type.",
"CONNECTOR_NOT_BOUND": "Connector is not bound to this agent.",
"CONNECTOR_ALREADY_BOUND": "Connector is already bound.",
"CONNECTOR_MCP_LOAD_FAILED": "Failed to load tools for MCP server(s): {servers}",
"CRON_TRIGGER_INVALID": "Invalid cron trigger.",
"SLASH_UNKNOWN": "Unknown slash command.",
"SLASH_BAD_ARGS": "Invalid arguments.",
Expand All @@ -92,6 +93,7 @@
"SKILL_IMPORT_UNSUPPORTED_URL": "This skill URL source is not supported.",
"SKILL_IMPORT_FAILED": "Skill import failed: {reason}",
"SKILL_ALREADY_EXISTS": "Skill {name} already exists. Enable overwrite to replace it.",
"EXPERT_MARKET_FAILED": "Expert market is temporarily unavailable. Please try again later.",
"DESKTOP_SESSION_LIMIT": "Too many concurrent remote desktop streams (max {limit}). Close other connections and try again.",
"DESKTOP_CAPTURE_FAILED": "Screen capture failed repeatedly. On Linux servers restart the virtual desktop; on macOS grant Screen Recording and Accessibility, then reconnect."
},
Expand Down Expand Up @@ -333,7 +335,8 @@
"proactive": "Proactive Push"
},
"myExperts": "My Experts",
"expertLibrary": "Expert List",
"expertLibrary": "Built-in Experts",
"expertMarket": "Expert Market",
"viewCard": "Cards",
"viewTable": "Table",
"mbtiDefault": "Default",
Expand All @@ -350,13 +353,52 @@
"actions": "Actions"
},
"totalAgents": "{{count}} expert(s)",
"totalLibrary": "{{count}} template(s)",
"addFromLibrary": "+ Add from Expert List",
"totalLibrary": "{{count}} built-in expert(s)",
"totalMarket": "{{count}} market expert(s)",
"addFromLibrary": "+ Add from Built-in",
"emptyMyExperts": "No experts yet",
"emptyMyExpertsHint": "Pick a template from the Expert List to create your first AI expert.",
"goToLibrary": "Go to Expert List",
"emptyLibrary": "No expert templates available",
"emptyLibraryHint": "Please ask your administrator to configure the expert list.",
"emptyMyExpertsHint": "Pick a template from Built-in Experts or the Expert Market to create your first AI expert.",
"goToLibrary": "Go to Built-in Experts",
"emptyLibrary": "No built-in experts available",
"emptyLibraryHint": "Please ask your administrator to configure built-in experts.",
"marketLoadFailed": "Failed to load the expert market",
"marketBackendMissing": "The backend has not loaded the Expert Market API yet. Restart Octop or confirm it is running this updated code.",
"marketSearchPlaceholder": "Search expert market",
"sceneAll": "All",
"scenes": {
"academic": "Academic",
"content-creation": "Content Creation",
"design": "Design",
"ecommerce": "E-commerce",
"education": "Education",
"finance": "Finance",
"healthcare": "Healthcare",
"hr": "HR & Admin",
"legal": "Legal",
"lifestyle": "Lifestyle",
"marketing": "Marketing",
"media": "Media",
"mysticism": "Mysticism",
"tech": "Tech"
},
"emptyMarket": "No market experts available",
"emptyMarketHint": "Try another keyword or check again later.",
"noMarketDescription": "This market expert has no description yet.",
"marketSkillCount": "{{count}} skill(s)",
"createFromMarket": "Create from Market",
"createAgainFromMarket": "Create another",
"marketCreateSuccess": "Created from Expert Market: {{name}}",
"marketCreateSuccessEnriching": "Created {{name}}. Welcome cards are being refined in the background and will update when you open the chat.",
"marketDetailLoadFailed": "Failed to load expert details",
"marketSource": "SkillHub",
"marketIncludedSkills": "Included Skills",
"marketQuickPrompts": "Quick-start cards",
"marketQuickPromptsEmpty": "No quick-start card preview yet",
"marketWorkflowPrompt": "Workflow overview",
"marketWorkflowMeta": "Package info",
"marketWorkflowVersion": "v{{version}}",
"marketWorkflowChildren": "Orchestrated skills",
"marketWorkflowEmpty": "No workflow overview available",
"installedBadge": "Installed",
"installAgain": "Install Again",
"createFromTemplate": "+ Create from this template",
Expand Down Expand Up @@ -2848,6 +2890,33 @@
"tabMine": "My connections",
"loadFailed": "Failed to load connectors",
"tabCatalog": "Catalog",
"tabBuiltin": "Built-in connectors",
"tabCustom": "Custom connectors",
"customMcp": {
"introTitle": "MCP server configuration (JSON). Refer to the following format:",
"modeVisual": "Visual",
"modeJson": "</> JSON",
"addHttp": "Add HTTP Server",
"addStdio": "Add Stdio Server",
"listTitle": "Configured servers",
"emptyList": "No custom MCP yet — use the buttons above to add one",
"transportHttp": "HTTP",
"transportStdio": "Stdio",
"headers": "Headers (one Key: Value per line; optional Bearer)",
"args": "Args (one argument per line)",
"env": "Env (one KEY=VALUE per line)",
"loadFailed": "Failed to load custom MCP",
"saveSuccess": "Custom MCP saved",
"saveFailed": "Failed to save",
"jsonInvalid": "Invalid JSON",
"visualInvalid": "Fix names and required fields before switching",
"duplicateName": "Server names must be unique",
"emptyName": "Server name is required",
"probeNeedConfig": "Fill in the configuration before probing",
"probeOk": "Probe ok — found {{count}} tools",
"deleteConfirm": "Delete MCP server \"{{name}}\"?",
"deleteConfirmHint": "Changes take effect after you click Save."
},
"configuredBadge": "Connected",
"clickToConnect": "Click to connect",
"noInstancesHint": "Pick a service in Catalog and complete authorization",
Expand Down
83 changes: 76 additions & 7 deletions dashboard/src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"CONNECTOR_KIND_UNSUPPORTED": "不支持的连接器类型。",
"CONNECTOR_NOT_BOUND": "连接器未绑定到此 Agent。",
"CONNECTOR_ALREADY_BOUND": "连接器已绑定。",
"CONNECTOR_MCP_LOAD_FAILED": "无法加载 MCP 工具:{servers}",
"CRON_TRIGGER_INVALID": "无效的定时触发器。",
"SLASH_UNKNOWN": "未知的斜杠指令。",
"SLASH_BAD_ARGS": "参数无效。",
Expand All @@ -92,6 +93,7 @@
"SKILL_IMPORT_UNSUPPORTED_URL": "不支持该技能 URL 来源。",
"SKILL_IMPORT_FAILED": "技能导入失败:{reason}",
"SKILL_ALREADY_EXISTS": "技能 {name} 已存在,如需覆盖请启用 overwrite。",
"EXPERT_MARKET_FAILED": "专家市场暂时不可用,请稍后重试。",
"DESKTOP_SESSION_LIMIT": "远程桌面并发连接已达上限(最多 {limit} 路),请关闭其他连接后重试。",
"DESKTOP_CAPTURE_FAILED": "连续抓屏失败。Linux 服务器请重启虚拟桌面;macOS 请在系统设置中授予屏幕录制与辅助功能权限后重连。"
},
Expand Down Expand Up @@ -333,7 +335,8 @@
"proactive": "主动推送"
},
"myExperts": "我的专家",
"expertLibrary": "专家列表",
"expertLibrary": "内置专家",
"expertMarket": "专家市场",
"viewCard": "卡片",
"viewTable": "表格",
"mbtiDefault": "默认",
Expand All @@ -350,13 +353,52 @@
"actions": "操作"
},
"totalAgents": "共 {{count}} 个专家",
"totalLibrary": "共 {{count}} 个模板",
"addFromLibrary": "+ 从专家列表新建",
"totalLibrary": "共 {{count}} 个内置专家",
"totalMarket": "共 {{count}} 个市场专家",
"addFromLibrary": "+ 从内置专家新建",
"emptyMyExperts": "你还没有专家",
"emptyMyExpertsHint": "从专家列表选择模板,快速创建你的第一个 AI 专家。",
"goToLibrary": "去专家列表",
"emptyLibrary": "暂无可用的专家模板",
"emptyLibraryHint": "请联系管理员配置专家列表。",
"emptyMyExpertsHint": "从内置专家或专家市场选择模板,快速创建你的第一个 AI 专家。",
"goToLibrary": "去内置专家",
"emptyLibrary": "暂无可用的内置专家",
"emptyLibraryHint": "请联系管理员配置内置专家。",
"marketLoadFailed": "加载专家市场失败",
"marketBackendMissing": "后端还没有加载专家市场接口,请重启 Octop 后端或确认当前运行的是这份新代码。",
"marketSearchPlaceholder": "搜索专家市场",
"sceneAll": "全部",
"scenes": {
"academic": "学术",
"content-creation": "内容创作",
"design": "设计",
"ecommerce": "电商",
"education": "教育",
"finance": "金融",
"healthcare": "医疗健康",
"hr": "人力行政",
"legal": "法律",
"lifestyle": "生活健康",
"marketing": "营销",
"media": "影视媒体",
"mysticism": "玄学命理",
"tech": "技术"
},
"emptyMarket": "暂无可用的市场专家",
"emptyMarketHint": "可以换个关键词,或稍后再试。",
"noMarketDescription": "该市场专家暂未提供简介。",
"marketSkillCount": "{{count}} 个技能",
"createFromMarket": "从市场创建",
"createAgainFromMarket": "再创建一个",
"marketCreateSuccess": "已从专家市场创建:{{name}}",
"marketCreateSuccessEnriching": "已创建 {{name}}。欢迎卡片正在后台优化,稍后进入对话即可看到更新。",
"marketDetailLoadFailed": "加载专家详情失败",
"marketSource": "SkillHub",
"marketIncludedSkills": "包含技能",
"marketQuickPrompts": "快速启动卡片",
"marketQuickPromptsEmpty": "暂无快速启动卡片预览",
"marketWorkflowPrompt": "工作流说明",
"marketWorkflowMeta": "技能包信息",
"marketWorkflowVersion": "v{{version}}",
"marketWorkflowChildren": "编排技能",
"marketWorkflowEmpty": "暂无工作流说明",
"installedBadge": "已安装",
"installAgain": "再次安装",
"createFromTemplate": "+ 从此模板新建",
Expand Down Expand Up @@ -3012,6 +3054,33 @@
"tabMine": "我的连接",
"loadFailed": "加载连接器失败",
"tabCatalog": "服务目录",
"tabBuiltin": "内置连接器",
"tabCustom": "自定义连接器",
"customMcp": {
"introTitle": "MCP 服务器配置(JSON 格式)。参考以下格式:",
"modeVisual": "可视化",
"modeJson": "</> JSON",
"addHttp": "添加 HTTP Server",
"addStdio": "添加 Stdio Server",
"listTitle": "已添加的服务器",
"emptyList": "尚未添加自定义 MCP,点击上方按钮开始配置",
"transportHttp": "HTTP",
"transportStdio": "Stdio",
"headers": "Headers(每行 Key: Value,可选 Bearer)",
"args": "Args(一行一个参数)",
"env": "Env(每行 KEY=VALUE)",
"loadFailed": "加载自定义 MCP 失败",
"saveSuccess": "自定义 MCP 已保存",
"saveFailed": "保存失败",
"jsonInvalid": "JSON 格式无效",
"visualInvalid": "可视化配置不完整,请先修正名称与必填项",
"duplicateName": "服务器名称不能重复",
"emptyName": "请填写服务器名称",
"probeNeedConfig": "请先填写完整配置再探测",
"probeOk": "探测成功,发现 {{count}} 个工具",
"deleteConfirm": "确定删除 MCP 服务器「{{name}}」?",
"deleteConfirmHint": "删除后需点击保存才会生效。"
},
"configuredBadge": "已连接",
"clickToConnect": "点击连接",
"noInstancesHint": "在「服务目录」中选择服务并完成授权",
Expand Down
Loading
Loading