From 0192a62f21e7f6cf75008e1e1c39bc004150e81b Mon Sep 17 00:00:00 2001 From: matevip Date: Thu, 14 May 2026 14:09:44 +0800 Subject: [PATCH] feat(ui/mcp): redesign MCP connections page with componentized layout --- README.md | 20 +- README_zh.md | 20 +- assets/architecture-biz-en.svg | 12 +- assets/architecture-biz-zh.svg | 13 +- assets/architecture-tech-en.svg | 24 +- assets/architecture-tech-zh.svg | 22 +- .../src/assets/icons/mcp/atlassian.svg | 1 + .../src/assets/icons/mcp/chrome_devtools.svg | 1 + .../src/assets/icons/mcp/cloudflare.svg | 1 + mateclaw-ui/src/assets/icons/mcp/context7.svg | 7 + mateclaw-ui/src/assets/icons/mcp/figma.svg | 1 + mateclaw-ui/src/assets/icons/mcp/github.svg | 1 + .../src/assets/icons/mcp/hugging_face.svg | 1 + mateclaw-ui/src/assets/icons/mcp/linear.svg | 1 + mateclaw-ui/src/assets/icons/mcp/notion.svg | 1 + .../src/assets/icons/mcp/playwright.svg | 1 + mateclaw-ui/src/assets/icons/mcp/posthog.svg | 1 + mateclaw-ui/src/assets/icons/mcp/sentry.svg | 1 + mateclaw-ui/src/assets/icons/mcp/slack.svg | 1 + mateclaw-ui/src/assets/icons/mcp/stripe.svg | 1 + mateclaw-ui/src/assets/icons/mcp/supabase.svg | 1 + mateclaw-ui/src/assets/icons/mcp/vercel.svg | 1 + mateclaw-ui/src/composables/useMcpServers.ts | 189 ++++ mateclaw-ui/src/i18n/locales/en-US.ts | 42 +- mateclaw-ui/src/i18n/locales/zh-CN.ts | 42 +- mateclaw-ui/src/views/McpServers.vue | 878 ++++++------------ mateclaw-ui/src/views/mcp/McpCard.vue | 397 ++++++++ mateclaw-ui/src/views/mcp/McpEmptyState.vue | 77 ++ mateclaw-ui/src/views/mcp/McpFormModal.vue | 450 +++++++++ mateclaw-ui/src/views/mcp/McpKvEditor.vue | 265 ++++++ mateclaw-ui/src/views/mcp/McpServerIcon.vue | 96 ++ mateclaw-ui/src/views/mcp/catalog.ts | 267 ++++++ mateclaw-ui/src/views/mcp/icons.ts | 48 + mateclaw-ui/src/views/mcp/types.ts | 67 ++ 34 files changed, 2315 insertions(+), 636 deletions(-) create mode 100644 mateclaw-ui/src/assets/icons/mcp/atlassian.svg create mode 100644 mateclaw-ui/src/assets/icons/mcp/chrome_devtools.svg create mode 100644 mateclaw-ui/src/assets/icons/mcp/cloudflare.svg create mode 100644 mateclaw-ui/src/assets/icons/mcp/context7.svg create mode 100644 mateclaw-ui/src/assets/icons/mcp/figma.svg create mode 100644 mateclaw-ui/src/assets/icons/mcp/github.svg create mode 100644 mateclaw-ui/src/assets/icons/mcp/hugging_face.svg create mode 100644 mateclaw-ui/src/assets/icons/mcp/linear.svg create mode 100644 mateclaw-ui/src/assets/icons/mcp/notion.svg create mode 100644 mateclaw-ui/src/assets/icons/mcp/playwright.svg create mode 100644 mateclaw-ui/src/assets/icons/mcp/posthog.svg create mode 100644 mateclaw-ui/src/assets/icons/mcp/sentry.svg create mode 100644 mateclaw-ui/src/assets/icons/mcp/slack.svg create mode 100644 mateclaw-ui/src/assets/icons/mcp/stripe.svg create mode 100644 mateclaw-ui/src/assets/icons/mcp/supabase.svg create mode 100644 mateclaw-ui/src/assets/icons/mcp/vercel.svg create mode 100644 mateclaw-ui/src/composables/useMcpServers.ts create mode 100644 mateclaw-ui/src/views/mcp/McpCard.vue create mode 100644 mateclaw-ui/src/views/mcp/McpEmptyState.vue create mode 100644 mateclaw-ui/src/views/mcp/McpFormModal.vue create mode 100644 mateclaw-ui/src/views/mcp/McpKvEditor.vue create mode 100644 mateclaw-ui/src/views/mcp/McpServerIcon.vue create mode 100644 mateclaw-ui/src/views/mcp/catalog.ts create mode 100644 mateclaw-ui/src/views/mcp/icons.ts create mode 100644 mateclaw-ui/src/views/mcp/types.ts diff --git a/README.md b/README.md index 69a4e5f9..a961cd7f 100644 --- a/README.md +++ b/README.md @@ -78,21 +78,26 @@ Same brain. Same memory. Same tools. Different doors. You hire coworkers, not chat boxes. Each one has a **Role**, a **Goal**, a **Backstory**, a pixel-art avatar, and a color of their own — five career templates ship ready (Product Researcher · Customer Support · Knowledge Curator · Data Analyst · Executive Assistant). **ReAct** drives iterative reasoning, **Plan-and-Execute** decomposes complex multi-step work, employees can delegate to one another in parallel. Dynamic context pruning, smart truncation, stale-stream cleanup — the boring stuff that makes long conversations actually work. ### Knowledge & memory -- **LLM Wiki** — raw materials digest into linked pages with citations; the **hot cache** auto-injects into every employee's system prompt +- **LLM Wiki** — raw materials digest into linked pages with citations; the **hot cache** auto-injects into every employee's system prompt. **Transformations engine** (1.3.0+) turns the Wiki from a search index into a processing pipeline - **Workspace memory** — `AGENTS.md`, `SOUL.md`, `PROFILE.md`, `MEMORY.md`, daily notes -- **Memory lifecycle** — post-conversation extraction, scheduled consolidation, Dreaming workflows +- **Memory lifecycle** — post-conversation extraction, scheduled consolidation, Dreaming workflows. Workflows can also write directly into an employee's `MEMORY.md` via the `write_memory` step ### Skills · MCP · ACP — three ways to extend capability - **SKILL.md packages** — manifest + prompt + tool list + **LESSONS.md (gets smarter the more you use it)**. Eight starter templates plus a five-step creation wizard, with **Pre-flight checks** that tell you what's missing before install -- **MCP** — stdio / SSE / Streamable HTTP, plug into any external tool server +- **MCP** — stdio / SSE / Streamable HTTP, plug into any external tool server. **Per-employee binding** (1.3.0+) means a tool you install for one employee doesn't bleed into another's toolbox - **ACP** — bring top-tier coding agents like Claude Code and Codex in as employees, auto-bridged to skill cards with wrapper tools - **Tool Guard** — RBAC + approval flow + path protection. Capability needs boundaries +### Business orchestration (1.3.0+) +- **Workflow** — compose multiple employees plus system actions (approval / channel dispatch / write-memory) into a publishable, triggerable, replayable linear DSL. Seven step modes (`sequential` / `fan_out` / `collect` / `conditional` / `await_approval` / `dispatch_channel` / `write_memory`). JSON-first authoring with Monaco + schema validation, or natural-language → draft generation +- **Triggers** — wire system events to workflows or to employee conversations. Six pattern types (`cron` / `webhook` / `channel_message` / `agent_lifecycle` / `content_match` / `workflow_completion`). Default-on event governance: dedup, per-trigger rate limit, bot-self filter, recursion guard, fail-closed unknown patterns +- **Wiki Transformations** — Wiki stops being retrieval-only. User-authored templates run against raw materials or existing pages, with cross-material map-reduce aggregation, reverse-citation extraction, JSON output mode, and per-template model picker + ### You see what every employee is doing **Admin Runtime Console** (`Settings → System → Runtime`) — who's running, what step they're on, how many tokens, one-click force-recycle when stuck. Streaming is staged honestly (thinking / tool / answer), per-event SSE IDs make reconnects safe, multi-employee delegation no longer fights itself, long tasks demand evidence-grounded answers. ### Multimodal creation -Text-to-speech · Speech-to-text · Image · Music · Video · 3D. First-class, not add-ons. +Text-to-speech · Speech-to-text · Image · Music · Video · 3D. First-class, not add-ons. **Sidecar routing** (1.3.0+) means a text-only main model + an image attachment no longer dead-ends — a configured vision model describes the image, and the main model answers. **Image edit** lands too: refer to an earlier conversation attachment by `msg::` and ask the model to recolor or restyle it. Four **document-generation tools** (`DocxRenderTool` / `XlsxRenderTool` / `PptxRenderTool` / `PdfRenderTool`) render Markdown straight to Office files inside the JVM — no subprocess, no Office install. ### Enterprise-ready RBAC + JWT. **Personal Access Tokens** for headless scripts and CI. **HMAC-SHA-256 outbound webhook signing**. **Distributed Cron lock** so multi-instance deployments don't double-fire. Full audit trail. Flyway-managed schema that auto-heals on upgrade. One JAR to ship. MySQL in production, H2 for dev — nothing to change in your code. @@ -192,7 +197,8 @@ Desktop binaries ship via [GitHub Releases](https://github.com/matevip/mateclaw/ |---|---| | Backend | Spring Boot 3.5 · Spring AI Alibaba 1.1 · MyBatis Plus · Flyway | | Digital Employee Runtime | StateGraph · ReAct + Plan-Execute · Role / Goal / Backstory · LESSONS self-evolution | -| Capability Extension | SKILL.md packages · MCP (stdio / SSE / HTTP) · ACP bridge (Claude Code / Codex) | +| Orchestration | Workflow (7 step modes · Pebble DSL) · Triggers (6 pattern types · event governance) · Wiki Transformations (1.3.0+) | +| Capability Extension | SKILL.md packages · MCP (stdio / SSE / HTTP · per-agent binding) · ACP bridge (Claude Code / Codex) | | Database | H2 (dev) · MySQL 8.0+ (prod) | | Auth | Spring Security + JWT | | Frontend | Vue 3 · TypeScript · Vite · Element Plus · TailwindCSS 4 | @@ -207,7 +213,9 @@ Full docs at **[claw.mate.vip/docs](https://claw.mate.vip/docs)** — setup, arc ## Roadmap -Sharper multi-employee collaboration · Smarter model routing · Deeper multimodal understanding · Longer-lived memory · A richer ClawHub · More ACP upstream integrations. +**v1.3.0 (shipped 2026-05-13)** — Workflow engine · 6-pattern trigger system · Wiki transformations · per-agent MCP binding · multimodal sidecar routing · four JVM-native document generation tools · image edit. See the [v1.3.0 release notes](https://claw.mate.vip/docs/en/releases/1.3.0) for the full story. + +**Next** — Drag-to-edit workflow canvas · run replay timeline · `loop` and `invoke_skill` step modes · trigger priorities and event replay · industry scenario marketplace · more ACP upstream integrations. ## Contributing diff --git a/README_zh.md b/README_zh.md index a5be3b4e..eb5e6d73 100644 --- a/README_zh.md +++ b/README_zh.md @@ -78,21 +78,26 @@ MateClaw 的 **LLM Wiki** 把它消化成结构化页面,页面之间自己长 你雇佣员工,不是开聊天框。每位有**角色**、**目标**、**背景故事**,像素艺术头像、专属配色——5 个职业模板(产品研究员 · 客户支持 · 知识管理员 · 数据分析师 · 行政助理)开箱可用。**ReAct** 做迭代推理,**Plan-and-Execute** 做复杂多步任务,员工之间可以并行委派。动态上下文裁剪、智能截断、僵死流清理——让长对话真正能用的那些"不起眼"的基础设施。 ### 知识与记忆 -- **LLM Wiki** — 原始材料消化成有链接、带引用的结构化页面;**热点缓存**自动注入到员工的 system prompt +- **LLM Wiki** — 原始材料消化成有链接、带引用的结构化页面;**热点缓存**自动注入到员工的 system prompt。**加工器引擎**(1.3.0+)把 Wiki 从"搜索索引"升级为"处理流水线" - **工作区记忆** — `AGENTS.md` / `SOUL.md` / `PROFILE.md` / `MEMORY.md` / 每日笔记 -- **记忆生命周期** — 对话后自动提取 · 定时整理 · Dreaming 工作流 +- **记忆生命周期** — 对话后自动提取 · 定时整理 · Dreaming 工作流。工作流也可以通过 `write_memory` step 直接写进员工的 `MEMORY.md` ### 技能 · MCP · ACP — 三种"接外部能力"的方式 - **SKILL.md 技能包** — 一份 manifest + prompt + 工具列表 + **LESSONS.md(用得越多越聪明)**。8 个起步模板 + 5 步创作向导,安装前自动跑 **Pre-flight 检查**告诉你缺什么 -- **MCP** — stdio / SSE / Streamable HTTP 三种传输,接入任意外部工具服务器 +- **MCP** — stdio / SSE / Streamable HTTP 三种传输,接入任意外部工具服务器。**每位员工独立绑定**(1.3.0+)——一位员工装的工具不会渗到其他人的工具栏里 - **ACP** — 把 Claude Code、Codex 这种顶级编码 Agent 以"员工"身份接入,桥接成技能卡 + 包装工具 - **Tool Guard** — RBAC + 审批流 + 文件路径保护。能力必须有边界 +### 业务流程编排(1.3.0+) +- **工作流(Workflow)** — 把多位员工 + 系统动作(审批 / 渠道分发 / 写记忆)按线性 step DSL 编排成一条可发布、可触发、可重放的业务流程。7 种 step mode(`sequential` / `fan_out` / `collect` / `conditional` / `await_approval` / `dispatch_channel` / `write_memory`)。JSON-first 编辑(Monaco + JSON schema + Pebble 静态检查),或者用一句话生成草稿 +- **触发器(Trigger)** — 把"系统里发生的事"自动接到工作流或员工对话上。6 种 pattern type(`cron` / `webhook` / `channel_message` / `agent_lifecycle` / `content_match` / `workflow_completion`)。事件治理默认开:去重、per-trigger 限速、bot 自循环过滤、A→B→A 递归保护、未知 pattern fail-closed +- **Wiki 加工器** — Wiki 不再只是被动检索。用户自定义模板对原料或现有页面跑模板,跨原料 map-reduce 聚合,reverse-citation 绑定到源 chunk,JSON 输出 + 可选 JSON Schema,每个模板独立选模型 + ### 你看得见每位员工正在干什么 **Admin 运行时控制台**(`后台 → 系统 → 运行时`)——谁在跑、跑到哪一步、占多少 token、卡住了一键回收。流式分阶段显示(思考 / 工具 / 回答),SSE 每事件 ID 支持安全重连,多员工协作不打架,长任务必须有真实证据才回答。 ### 多模态创作 -语音合成 · 语音识别 · 图片 · 音乐 · 视频 · 3D。一等公民,不是附加插件。 +语音合成 · 语音识别 · 图片 · 音乐 · 视频 · 3D。一等公民,不是附加插件。**多模态旁路**(1.3.0+)让纯文本主模型遇到图片附件时自动调用配置好的视觉模型转描述,主对话保持便宜。**图像编辑**也到位:用 `msg::` 引用会话里更早的某张图,让模型改色、改风格。**4 个文档生成工具**(`DocxRenderTool` / `XlsxRenderTool` / `PptxRenderTool` / `PdfRenderTool`)在 JVM 内把 Markdown 直接渲染成 Office 文件——不 fork 子进程、不依赖 npm、不需要装 Office。 ### 企业就绪 RBAC + JWT。**Personal Access Token** 给无人值守脚本和 CI 用。**Webhook 出站 HMAC-SHA-256 签名**。**Cron 分布式锁**多实例不双发。完整审计事件流。Flyway 管理数据库 schema,升级时自愈。一个 JAR 交付。生产用 MySQL,开发用 H2,代码零改动。 @@ -192,7 +197,8 @@ mateclaw/ |---|---| | 后端 | Spring Boot 3.5 · Spring AI Alibaba 1.1 · MyBatis Plus · Flyway | | 数字员工运行时 | StateGraph · ReAct + Plan-Execute · 角色 / 目标 / 背景故事 · LESSONS 自我进化 | -| 能力扩展 | SKILL.md 包 · MCP 协议(stdio / SSE / HTTP)· ACP 桥接(Claude Code / Codex) | +| 业务编排 | 工作流(7 step mode · Pebble DSL)· 触发器(6 pattern type · 事件治理)· Wiki 加工器(1.3.0+)| +| 能力扩展 | SKILL.md 包 · MCP(stdio / SSE / HTTP · per-agent 绑定)· ACP 桥接(Claude Code / Codex) | | 数据库 | H2(开发)· MySQL 8.0+(生产)| | 认证 | Spring Security + JWT | | 前端 | Vue 3 · TypeScript · Vite · Element Plus · TailwindCSS 4 | @@ -207,7 +213,9 @@ mateclaw/ ## 路线图 -更强的多员工协作 · 更智能的模型路由 · 更深度的多模态理解 · 更长久的记忆 · 更繁荣的 ClawHub · 更多 ACP 上游集成。 +**v1.3.0(2026-05-13 发布)** — 工作流引擎 · 6 种 pattern 触发器 · Wiki 加工器 · 每员工独立 MCP 绑定 · 多模态旁路路由 · 4 个 JVM 原生文档生成工具 · 图像编辑。完整故事见 [v1.3.0 release notes](https://claw.mate.vip/docs/zh/releases/1.3.0)。 + +**下一步** — 工作流画布可拖拉编辑 · 运行回放时间线 · `loop` / `invoke_skill` step mode · 触发器优先级 + 事件回放 · 行业场景应用市场 · 更多 ACP 上游集成。 ## 参与贡献 diff --git a/assets/architecture-biz-en.svg b/assets/architecture-biz-en.svg index d3341aa7..424421a5 100644 --- a/assets/architecture-biz-en.svg +++ b/assets/architecture-biz-en.svg @@ -55,7 +55,7 @@ LLM Wiki KB Structured + Backlinks Citations + Soft Archive - Lazy ingest · On-demand + + Transformations (1.3.0+) A library, not a vector store @@ -98,13 +98,19 @@ + + + Orchestration · Workflow + Trigger + Events → multi-employee → approval / dispatch / memory + Provider Pool · Failover Cloud + Local · 14+ providers Health Tracker · Auto-switch - - + + + Mate is companion. Claw is capability. diff --git a/assets/architecture-biz-zh.svg b/assets/architecture-biz-zh.svg index 0fb490eb..25133463 100644 --- a/assets/architecture-biz-zh.svg +++ b/assets/architecture-biz-zh.svg @@ -57,7 +57,7 @@ LLM Wiki 知识库 结构化消化 + 双向链接 引用溯源 + 软归档 - lazy 入库 · 按需出页 + + 加工器流水线(1.3.0+) 是图书馆,不是向量库 @@ -105,14 +105,19 @@ + + + 业务编排 · 工作流 + 触发器 + 事件触发 → 多员工协作 → 审批 / 分发 / 写记忆 + 模型池 · Failover 云端 + 本地 · 14+ 供应商 健康追踪 · 自动切换 - - - + + + diff --git a/assets/architecture-tech-en.svg b/assets/architecture-tech-en.svg index 71b7f8de..1c5afd41 100644 --- a/assets/architecture-tech-en.svg +++ b/assets/architecture-tech-en.svg @@ -74,15 +74,15 @@ - ReAct Agent - Think → Act → Observe - Iterative Reasoning Loop + Reasoning Engines + ReAct · Think→Act→Observe + Plan-Execute · Decompose - - Plan-Execute - Decompose → Step Execute - Complex Task Orchestration + + Workflow + Trigger + 7 step modes · 6 patterns + Business orchestration (1.3.0+) @@ -99,8 +99,8 @@ Wiki KB - Knowledge - Digestion + Knowledge digest + + Transforms (1.3) @@ -178,9 +178,9 @@ - Cron → Channel - Ambient AI - Proactive Delivery + Cron · Event Bus + ShedLock distributed + Ambient AI · Proactive diff --git a/assets/architecture-tech-zh.svg b/assets/architecture-tech-zh.svg index e5eaf93d..6fbe8be7 100644 --- a/assets/architecture-tech-zh.svg +++ b/assets/architecture-tech-zh.svg @@ -77,15 +77,15 @@ - ReAct Agent - 思考 → 行动 → 观察 - 循环推理引擎 + 推理双引擎 + ReAct · 思考→行动→观察 + Plan-Execute · 计划分解 - - Plan-Execute - 计划分解 → 逐步执行 - 复杂任务编排 + + 工作流 + 触发器 + 7 step mode · 6 pattern + 业务流程编排(1.3.0+) @@ -103,7 +103,7 @@ Wiki KB 知识消化 - 结构化页面 + + 加工器(1.3.0) @@ -181,9 +181,9 @@ - Cron → 渠道 - Ambient AI - 主动交付 + Cron · 事件总线 + ShedLock 分布式锁 + 主动交付 · Ambient AI diff --git a/mateclaw-ui/src/assets/icons/mcp/atlassian.svg b/mateclaw-ui/src/assets/icons/mcp/atlassian.svg new file mode 100644 index 00000000..87521386 --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/atlassian.svg @@ -0,0 +1 @@ +Atlassian \ No newline at end of file diff --git a/mateclaw-ui/src/assets/icons/mcp/chrome_devtools.svg b/mateclaw-ui/src/assets/icons/mcp/chrome_devtools.svg new file mode 100644 index 00000000..919ac2a1 --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/chrome_devtools.svg @@ -0,0 +1 @@ +Google Chrome \ No newline at end of file diff --git a/mateclaw-ui/src/assets/icons/mcp/cloudflare.svg b/mateclaw-ui/src/assets/icons/mcp/cloudflare.svg new file mode 100644 index 00000000..a1cf2d6d --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/cloudflare.svg @@ -0,0 +1 @@ +Cloudflare \ No newline at end of file diff --git a/mateclaw-ui/src/assets/icons/mcp/context7.svg b/mateclaw-ui/src/assets/icons/mcp/context7.svg new file mode 100644 index 00000000..123d62ea --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/context7.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/mateclaw-ui/src/assets/icons/mcp/figma.svg b/mateclaw-ui/src/assets/icons/mcp/figma.svg new file mode 100644 index 00000000..6a06daa6 --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/figma.svg @@ -0,0 +1 @@ +Figma \ No newline at end of file diff --git a/mateclaw-ui/src/assets/icons/mcp/github.svg b/mateclaw-ui/src/assets/icons/mcp/github.svg new file mode 100644 index 00000000..23349765 --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/github.svg @@ -0,0 +1 @@ +GitHub diff --git a/mateclaw-ui/src/assets/icons/mcp/hugging_face.svg b/mateclaw-ui/src/assets/icons/mcp/hugging_face.svg new file mode 100644 index 00000000..dd2db93b --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/hugging_face.svg @@ -0,0 +1 @@ +Hugging Face \ No newline at end of file diff --git a/mateclaw-ui/src/assets/icons/mcp/linear.svg b/mateclaw-ui/src/assets/icons/mcp/linear.svg new file mode 100644 index 00000000..f3770d65 --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/linear.svg @@ -0,0 +1 @@ +Linear \ No newline at end of file diff --git a/mateclaw-ui/src/assets/icons/mcp/notion.svg b/mateclaw-ui/src/assets/icons/mcp/notion.svg new file mode 100644 index 00000000..2917f42e --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/notion.svg @@ -0,0 +1 @@ +Notion \ No newline at end of file diff --git a/mateclaw-ui/src/assets/icons/mcp/playwright.svg b/mateclaw-ui/src/assets/icons/mcp/playwright.svg new file mode 100644 index 00000000..3d45a763 --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/playwright.svg @@ -0,0 +1 @@ +Playwright \ No newline at end of file diff --git a/mateclaw-ui/src/assets/icons/mcp/posthog.svg b/mateclaw-ui/src/assets/icons/mcp/posthog.svg new file mode 100644 index 00000000..70d8cb77 --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/posthog.svg @@ -0,0 +1 @@ +PostHog \ No newline at end of file diff --git a/mateclaw-ui/src/assets/icons/mcp/sentry.svg b/mateclaw-ui/src/assets/icons/mcp/sentry.svg new file mode 100644 index 00000000..11bb3c8a --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/sentry.svg @@ -0,0 +1 @@ +Sentry \ No newline at end of file diff --git a/mateclaw-ui/src/assets/icons/mcp/slack.svg b/mateclaw-ui/src/assets/icons/mcp/slack.svg new file mode 100644 index 00000000..004e2663 --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/slack.svg @@ -0,0 +1 @@ +Slack \ No newline at end of file diff --git a/mateclaw-ui/src/assets/icons/mcp/stripe.svg b/mateclaw-ui/src/assets/icons/mcp/stripe.svg new file mode 100644 index 00000000..8ebadf74 --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/stripe.svg @@ -0,0 +1 @@ +Stripe \ No newline at end of file diff --git a/mateclaw-ui/src/assets/icons/mcp/supabase.svg b/mateclaw-ui/src/assets/icons/mcp/supabase.svg new file mode 100644 index 00000000..b7735570 --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/supabase.svg @@ -0,0 +1 @@ +Supabase \ No newline at end of file diff --git a/mateclaw-ui/src/assets/icons/mcp/vercel.svg b/mateclaw-ui/src/assets/icons/mcp/vercel.svg new file mode 100644 index 00000000..821ecfff --- /dev/null +++ b/mateclaw-ui/src/assets/icons/mcp/vercel.svg @@ -0,0 +1 @@ +Vercel \ No newline at end of file diff --git a/mateclaw-ui/src/composables/useMcpServers.ts b/mateclaw-ui/src/composables/useMcpServers.ts new file mode 100644 index 00000000..4039dbb8 --- /dev/null +++ b/mateclaw-ui/src/composables/useMcpServers.ts @@ -0,0 +1,189 @@ +import { computed, ref, watch } from 'vue' +import { useI18n } from 'vue-i18n' +import { ElMessage } from 'element-plus' +import { mcpApi } from '@/api/index' +import type { McpServer, McpServerForm, McpTestResult } from '@/views/mcp/types' +import { mcpCatalog, type McpCatalogEntry } from '@/views/mcp/catalog' + +/** + * Single source of truth for the MCP connections page. Owns the installed + * server list, search/pagination state, and CRUD helpers — the page and + * components stay thin presentational layers. + */ +export function useMcpServers() { + const { t } = useI18n() + + const installed = ref([]) + const isLoading = ref(false) + const isRefreshing = ref(false) + const testingId = ref(null) + const testResult = ref(null) + + const search = ref('') + const pageSize = ref(12) + const installedPage = ref(1) + const catalogPage = ref(1) + + // Reset both pages whenever the search input changes — prevents pointing + // at a page index that no longer exists after filtering shrinks the list. + watch(search, () => { + installedPage.value = 1 + catalogPage.value = 1 + }) + + const lowerSearch = computed(() => search.value.trim().toLowerCase()) + + const filteredInstalled = computed(() => { + const q = lowerSearch.value + if (!q) return installed.value + return installed.value.filter(s => + s.name.toLowerCase().includes(q) || + (s.description ?? '').toLowerCase().includes(q), + ) + }) + + // Don't recommend a catalog entry whose key collides with an existing + // installed server name — once added it belongs in the Added section. + const installedNames = computed(() => new Set(installed.value.map(s => s.name))) + + const filteredCatalog = computed(() => { + const q = lowerSearch.value + return mcpCatalog.filter(c => { + if (installedNames.value.has(c.key)) return false + if (!q) return true + return ( + c.name.toLowerCase().includes(q) || + c.description.toLowerCase().includes(q) || + c.key.toLowerCase().includes(q) + ) + }) + }) + + function paginate(list: T[], page: number, size: number): T[] { + const start = (page - 1) * size + return list.slice(start, start + size) + } + + const pagedInstalled = computed(() => + paginate(filteredInstalled.value, installedPage.value, pageSize.value), + ) + const pagedCatalog = computed(() => + paginate(filteredCatalog.value, catalogPage.value, pageSize.value), + ) + + let loadGeneration = 0 + async function reload() { + const gen = ++loadGeneration + isLoading.value = true + try { + const res: any = await mcpApi.list() + if (gen === loadGeneration) { + installed.value = (res?.data ?? []) as McpServer[] + } + } catch { + if (gen === loadGeneration) ElMessage.error(t('mcp.messages.loadFailed')) + } finally { + if (gen === loadGeneration) isLoading.value = false + } + } + + async function refreshAll() { + isRefreshing.value = true + try { + await mcpApi.refresh() + ElMessage.success(t('mcp.messages.refreshSuccess')) + await reload() + } catch (e: any) { + ElMessage.error(e?.message || t('mcp.messages.saveFailed')) + } finally { + isRefreshing.value = false + } + } + + async function saveServer(form: McpServerForm, editing: McpServer | null): Promise { + try { + if (editing) { + await mcpApi.update(editing.id, form) + ElMessage.success(t('mcp.messages.updateSuccess')) + } else { + await mcpApi.create(form) + ElMessage.success(t('mcp.messages.createSuccess')) + } + await reload() + return true + } catch (e: any) { + ElMessage.error(e?.message || t('mcp.messages.saveFailed')) + return false + } + } + + async function removeServer(server: McpServer): Promise { + try { + await mcpApi.delete(server.id) + ElMessage.success(t('mcp.messages.deleteSuccess')) + await reload() + return true + } catch (e: any) { + ElMessage.error(e?.message || t('mcp.messages.saveFailed')) + return false + } + } + + async function toggleServer(server: McpServer) { + try { + await mcpApi.toggle(server.id, !server.enabled) + ElMessage.success(t('mcp.messages.toggleSuccess')) + await reload() + } catch (e: any) { + ElMessage.error(e?.message || t('mcp.messages.saveFailed')) + } + } + + async function testServer(server: McpServer) { + testingId.value = server.id + testResult.value = null + try { + const res: any = await mcpApi.test(server.id) + testResult.value = res.data as McpTestResult + } catch (e: any) { + testResult.value = { + success: false, + message: e?.message || 'Unknown error', + toolCount: 0, + latencyMs: 0, + discoveredTools: [], + } + } finally { + testingId.value = null + // Auto-dismiss toast after 4s so it doesn't linger on screen. + setTimeout(() => { + testResult.value = null + }, 4000) + } + } + + return { + // state + installed, + isLoading, + isRefreshing, + testingId, + testResult, + search, + pageSize, + installedPage, + catalogPage, + // derived + filteredInstalled, + filteredCatalog, + pagedInstalled, + pagedCatalog, + // actions + reload, + refreshAll, + saveServer, + removeServer, + toggleServer, + testServer, + } +} diff --git a/mateclaw-ui/src/i18n/locales/en-US.ts b/mateclaw-ui/src/i18n/locales/en-US.ts index eb68d153..44231249 100644 --- a/mateclaw-ui/src/i18n/locales/en-US.ts +++ b/mateclaw-ui/src/i18n/locales/en-US.ts @@ -1529,9 +1529,47 @@ export default { mcp: { kicker: 'Capability Bridge', title: 'MCP Connections', + subtitle: 'Connect agents to external data sources and tools', desc: 'Manage MCP (Model Context Protocol) server connections', addServer: 'Add Connection', + addCustom: 'Custom MCP', refreshAll: 'Refresh All', + searchPlaceholder: 'Search MCP servers…', + emptyMatch: 'No servers match your search', + catalogBadge: 'From catalog · {name}', + credentialHint: 'Replace placeholders {keys} before saving', + credentialHintRequired: 'Required credentials: {keys} (replace placeholders before saving)', + credentialHintOptional: 'Optional credentials: {keys} (fill in if you need those APIs)', + credentialHintBoth: 'Required: {required} · Optional: {optional}', + sections: { + added: 'Added', + recommended: 'Recommended', + countItems: '{n}', + }, + card: { + docs: 'View docs', + add: 'Install', + toolCount: '{n} tools', + }, + toggle: { + enable: 'Enable', + disable: 'Disable', + }, + kv: { + envKey: 'KEY', + envValue: 'value', + headerKey: 'Header', + headerValue: 'value', + addEnv: 'Add env var', + addHeader: 'Add header', + required: 'Required', + optional: 'Optional', + }, + time: { + justNow: 'just now', + minutesAgo: '{n}m ago', + hoursAgo: '{n}h ago', + }, columns: { name: 'Name', transport: 'Transport', @@ -1561,10 +1599,10 @@ export default { description: 'Description', transport: 'Transport', url: 'URL', - headers: 'HTTP Headers (JSON)', + headers: 'HTTP Headers', command: 'Command', args: 'Arguments (JSON array)', - env: 'Environment Variables (JSON)', + env: 'Environment Variables', cwd: 'Working Directory', connectTimeout: 'Connect Timeout (seconds)', readTimeout: 'Read Timeout (seconds)', diff --git a/mateclaw-ui/src/i18n/locales/zh-CN.ts b/mateclaw-ui/src/i18n/locales/zh-CN.ts index 2b03cc8a..b833ebb0 100644 --- a/mateclaw-ui/src/i18n/locales/zh-CN.ts +++ b/mateclaw-ui/src/i18n/locales/zh-CN.ts @@ -1427,9 +1427,47 @@ export default { mcp: { kicker: '能力桥接', title: 'MCP 连接', + subtitle: '把 Agent 连到外部数据源和工具', desc: '管理 MCP (Model Context Protocol) 服务器连接', addServer: '添加连接', + addCustom: '自定义 MCP', refreshAll: '全量刷新', + searchPlaceholder: '搜索 MCP 服务…', + emptyMatch: '没有匹配的 MCP 服务', + catalogBadge: '来自 Catalog · {name}', + credentialHint: '请替换占位符 {keys} 再保存', + credentialHintRequired: '必填凭据:{keys}(请替换占位符再保存)', + credentialHintOptional: '可选凭据:{keys}(如需调用相关 API 请填写)', + credentialHintBoth: '必填:{required} · 可选:{optional}', + sections: { + added: '已添加', + recommended: '推荐', + countItems: '{n} 个', + }, + card: { + docs: '查看文档', + add: '安装', + toolCount: '{n} 个工具', + }, + toggle: { + enable: '启用', + disable: '停用', + }, + kv: { + envKey: 'KEY', + envValue: '值', + headerKey: 'Header', + headerValue: '值', + addEnv: '添加环境变量', + addHeader: '添加 Header', + required: '必填', + optional: '可选', + }, + time: { + justNow: '刚刚', + minutesAgo: '{n} 分钟前', + hoursAgo: '{n} 小时前', + }, columns: { name: '名称', transport: '协议', @@ -1459,10 +1497,10 @@ export default { description: '描述', transport: '传输协议', url: 'URL', - headers: 'HTTP Headers (JSON)', + headers: 'HTTP Headers', command: '命令', args: '参数 (JSON 数组)', - env: '环境变量 (JSON)', + env: '环境变量', cwd: '工作目录', connectTimeout: '连接超时 (秒)', readTimeout: '读取超时 (秒)', diff --git a/mateclaw-ui/src/views/McpServers.vue b/mateclaw-ui/src/views/McpServers.vue index 5b11682b..e656c69b 100644 --- a/mateclaw-ui/src/views/McpServers.vue +++ b/mateclaw-ui/src/views/McpServers.vue @@ -1,653 +1,351 @@ diff --git a/mateclaw-ui/src/views/mcp/McpCard.vue b/mateclaw-ui/src/views/mcp/McpCard.vue new file mode 100644 index 00000000..98c13bf5 --- /dev/null +++ b/mateclaw-ui/src/views/mcp/McpCard.vue @@ -0,0 +1,397 @@ + + + + + diff --git a/mateclaw-ui/src/views/mcp/McpEmptyState.vue b/mateclaw-ui/src/views/mcp/McpEmptyState.vue new file mode 100644 index 00000000..d9524f7e --- /dev/null +++ b/mateclaw-ui/src/views/mcp/McpEmptyState.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/mateclaw-ui/src/views/mcp/McpFormModal.vue b/mateclaw-ui/src/views/mcp/McpFormModal.vue new file mode 100644 index 00000000..476c3b4e --- /dev/null +++ b/mateclaw-ui/src/views/mcp/McpFormModal.vue @@ -0,0 +1,450 @@ + + + + + diff --git a/mateclaw-ui/src/views/mcp/McpKvEditor.vue b/mateclaw-ui/src/views/mcp/McpKvEditor.vue new file mode 100644 index 00000000..b95d9471 --- /dev/null +++ b/mateclaw-ui/src/views/mcp/McpKvEditor.vue @@ -0,0 +1,265 @@ + + + + + diff --git a/mateclaw-ui/src/views/mcp/McpServerIcon.vue b/mateclaw-ui/src/views/mcp/McpServerIcon.vue new file mode 100644 index 00000000..7b1448d5 --- /dev/null +++ b/mateclaw-ui/src/views/mcp/McpServerIcon.vue @@ -0,0 +1,96 @@ + + + + + diff --git a/mateclaw-ui/src/views/mcp/catalog.ts b/mateclaw-ui/src/views/mcp/catalog.ts new file mode 100644 index 00000000..0190aa08 --- /dev/null +++ b/mateclaw-ui/src/views/mcp/catalog.ts @@ -0,0 +1,267 @@ +/** + * Curated, locally-bundled directory of one-click MCP servers shown in the + * "Recommended" section of the MCP connections page. Clicking a card opens + * the create-server modal with these fields pre-filled; credential + * placeholders (e.g. `YOUR_API_KEY`) are left intact so the user knows + * what to swap before saving. + * + * All HTTP-based remote MCPs use transport = 'streamable_http' (the + * modern Spring AI default); legacy SSE endpoints can opt into + * 'sse' explicitly. Stdio entries assume the user has `npx` on PATH. + */ +export interface McpCredentialKey { + /** Env var or HTTP header name (e.g. CONTEXT7_API_KEY, Authorization). */ + key: string + /** When true, the server is expected to fail without this key. */ + required: boolean +} + +export type McpCatalogConfig = + | { + transport: 'streamable_http' | 'sse' + url: string + headersJson?: string + } + | { + transport: 'stdio' + command: string + argsJson?: string + envJson?: string + } + +export interface McpCatalogEntry { + /** Stable slug — used as the default server name. */ + key: string + /** Display name shown on the card. */ + name: string + /** One-line capability description. */ + description: string + /** Official docs URL (opened from the hover external-link icon). */ + docsUrl: string + /** Pre-fill payload for the create-server form. */ + config: McpCatalogConfig + /** Which env vars / headers in the config need user-supplied secrets. */ + credentialKeys?: McpCredentialKey[] + /** Short visual key for the icon-bubble color class. */ + iconKey: string +} + +export const mcpCatalog: McpCatalogEntry[] = [ + { + key: 'context7', + name: 'Context7', + description: 'Fetch up-to-date library docs and code examples', + docsUrl: 'https://github.com/upstash/context7', + iconKey: 'context7', + config: { + transport: 'streamable_http', + url: 'https://mcp.context7.com/mcp', + headersJson: JSON.stringify({ CONTEXT7_API_KEY: 'YOUR_API_KEY' }, null, 2), + }, + credentialKeys: [{ key: 'CONTEXT7_API_KEY', required: false }], + }, + { + key: 'github', + name: 'GitHub', + description: 'GitHub Issues, PRs, code search and repository management', + docsUrl: 'https://github.com/github/github-mcp-server', + iconKey: 'github', + config: { + transport: 'stdio', + command: 'npx', + argsJson: JSON.stringify(['-y', '@modelcontextprotocol/server-github']), + envJson: JSON.stringify( + { GITHUB_PERSONAL_ACCESS_TOKEN: 'YOUR_TOKEN' }, + null, + 2, + ), + }, + credentialKeys: [{ key: 'GITHUB_PERSONAL_ACCESS_TOKEN', required: true }], + }, + { + key: 'figma', + name: 'Figma', + description: 'Generate diagrams and better code from Figma context', + docsUrl: 'https://help.figma.com/hc/en-us/articles/32132100833559', + iconKey: 'figma', + config: { + transport: 'streamable_http', + url: 'https://mcp.figma.com/mcp', + }, + }, + { + key: 'linear', + name: 'Linear', + description: 'Manage issues, projects and team workflows', + docsUrl: 'https://linear.app/docs/mcp', + iconKey: 'linear', + config: { + transport: 'streamable_http', + url: 'https://mcp.linear.app/mcp', + }, + }, + { + key: 'notion', + name: 'Notion', + description: 'Search, update and power workflows across your Notion workspace', + docsUrl: 'https://developers.notion.com/docs/mcp', + iconKey: 'notion', + config: { + transport: 'streamable_http', + url: 'https://mcp.notion.com/mcp', + }, + }, + { + key: 'slack', + name: 'Slack', + description: 'Send messages, create canvases and fetch Slack data', + docsUrl: 'https://docs.slack.dev/ai/mcp-server', + iconKey: 'slack', + config: { + transport: 'streamable_http', + url: 'https://mcp.slack.com/mcp', + }, + }, + { + key: 'supabase', + name: 'Supabase', + description: 'Manage databases, authentication and storage', + docsUrl: 'https://supabase.com/docs/guides/getting-started/mcp', + iconKey: 'supabase', + config: { + transport: 'streamable_http', + url: 'https://mcp.supabase.com/mcp', + }, + }, + { + key: 'vercel', + name: 'Vercel', + description: 'Analyze, debug and manage projects and deployments', + docsUrl: 'https://vercel.com/docs/mcp/vercel-mcp', + iconKey: 'vercel', + config: { + transport: 'streamable_http', + url: 'https://mcp.vercel.com', + }, + }, + { + key: 'sentry', + name: 'Sentry', + description: 'Search, query and debug errors intelligently', + docsUrl: 'https://docs.sentry.io/product/sentry-mcp/', + iconKey: 'sentry', + config: { + transport: 'streamable_http', + url: 'https://mcp.sentry.dev/mcp', + headersJson: JSON.stringify({ SENTRY_ACCESS_TOKEN: 'YOUR_ACCESS_TOKEN' }, null, 2), + }, + credentialKeys: [{ key: 'SENTRY_ACCESS_TOKEN', required: true }], + }, + { + key: 'stripe', + name: 'Stripe', + description: 'Payment processing and financial infrastructure tools', + docsUrl: 'https://docs.stripe.com/mcp', + iconKey: 'stripe', + config: { + transport: 'streamable_http', + url: 'https://mcp.stripe.com', + headersJson: JSON.stringify({ STRIPE_SECRET_KEY: 'YOUR_SECRET_KEY' }, null, 2), + }, + credentialKeys: [{ key: 'STRIPE_SECRET_KEY', required: true }], + }, + { + key: 'atlassian', + name: 'Atlassian', + description: 'Access Jira and Confluence from your agent', + docsUrl: 'https://www.atlassian.com/platform/remote-mcp-server', + iconKey: 'atlassian', + config: { + transport: 'streamable_http', + url: 'https://mcp.atlassian.com/v1/mcp', + }, + }, + { + key: 'cloudflare', + name: 'Cloudflare', + description: 'Build with compute, storage and AI on Cloudflare', + docsUrl: 'https://developers.cloudflare.com/agents/model-context-protocol/', + iconKey: 'cloudflare', + config: { + transport: 'streamable_http', + url: 'https://bindings.mcp.cloudflare.com/mcp', + }, + }, + { + key: 'huggingface', + name: 'Hugging Face', + description: 'Access the Hugging Face Hub and thousands of Gradio apps', + docsUrl: 'https://huggingface.co/settings/mcp', + iconKey: 'hugging_face', + config: { + transport: 'streamable_http', + url: 'https://huggingface.co/mcp', + }, + }, + { + key: 'posthog', + name: 'PostHog', + description: 'Query, analyze and manage your PostHog insights', + docsUrl: 'https://posthog.com/docs/model-context-protocol', + iconKey: 'posthog', + config: { + transport: 'streamable_http', + url: 'https://mcp.posthog.com/mcp', + }, + }, + { + key: 'playwright', + name: 'Playwright', + description: 'Browser automation with Playwright', + docsUrl: 'https://github.com/microsoft/playwright-mcp', + iconKey: 'playwright', + config: { + transport: 'stdio', + command: 'npx', + argsJson: JSON.stringify(['@playwright/mcp@latest']), + }, + }, + { + key: 'chrome-devtools', + name: 'Chrome DevTools', + description: 'Browser debugging and performance analysis with Chrome DevTools', + docsUrl: 'https://github.com/ChromeDevTools/chrome-devtools-mcp', + iconKey: 'chrome_devtools', + config: { + transport: 'stdio', + command: 'npx', + argsJson: JSON.stringify(['chrome-devtools-mcp@latest']), + }, + }, + { + key: 'exa', + name: 'Exa', + description: 'Web search and code context retrieval powered by Exa AI', + docsUrl: 'https://docs.exa.ai/reference/exa-mcp', + iconKey: 'exa', + config: { + transport: 'stdio', + command: 'npx', + argsJson: JSON.stringify(['-y', 'exa-mcp-server', 'tools=web_search_exa,get_code_context_exa']), + envJson: JSON.stringify({ EXA_API_KEY: 'YOUR_API_KEY' }, null, 2), + }, + credentialKeys: [{ key: 'EXA_API_KEY', required: true }], + }, +] + +/** Two-letter abbreviation used in the fallback icon bubble. */ +export function catalogInitial(name: string): string { + const trimmed = name.trim() + if (!trimmed) return '?' + // Take leading non-space chunks, prefer first letter of each. + const parts = trimmed.split(/[\s-_/]+/).filter(Boolean) + if (parts.length >= 2) { + return (parts[0].charAt(0) + parts[1].charAt(0)).toUpperCase() + } + return trimmed.slice(0, 2).toUpperCase() +} diff --git a/mateclaw-ui/src/views/mcp/icons.ts b/mateclaw-ui/src/views/mcp/icons.ts new file mode 100644 index 00000000..845cdcd8 --- /dev/null +++ b/mateclaw-ui/src/views/mcp/icons.ts @@ -0,0 +1,48 @@ +/** + * Static MCP brand-icon registry. SVGs are bundled at build time via Vite's + * glob import + `?raw` query, so each icon ships as an inline string and + * can be color-themed with `currentColor` (no external network requests, + * no per-icon component file). + * + * Sources: + * - Brand glyphs adopted from the Simple Icons collection (CC0 1.0). + * - Three-letter convention for filenames: lowercase + underscore. + * The catalog entry's `iconKey` must match the basename. + */ + +/* eslint-disable @typescript-eslint/no-explicit-any */ +const rawSvgs = import.meta.glob('@/assets/icons/mcp/*.svg', { + query: '?raw', + import: 'default', + eager: true, +}) as Record + +function basename(path: string): string { + return path.split('/').pop()!.replace(/\.\w+$/, '') +} + +const ICON_BY_KEY = new Map( + Object.entries(rawSvgs).map(([path, raw]) => [basename(path), raw]), +) + +/** + * Look up an inline SVG by icon key. Returns the raw SVG markup ready + * for v-html, with width/height stripped and `currentColor` injected so + * the consumer can size it via CSS and tint it with `color`. + */ +export function getMcpIconSvg(iconKey: string | undefined): string | null { + if (!iconKey) return null + const raw = ICON_BY_KEY.get(iconKey) + return raw ? prepareSvg(raw) : null +} + +function prepareSvg(svg: string): string { + return svg + // Strip fixed sizing so the consumer's container governs the icon size. + .replace(/\bwidth="[^"]*"/g, '') + .replace(/\bheight="[^"]*"/g, '') + // Drop any embedded