返回市场
动态-mcp-服务器

动态-mcp-服务器

作者:mcpc-tech3 星标更新:2025-11-18

项目介绍

Client-Tool-Execution MCP Server 🚀

JSR npm

创建 Client-Tool-Execution MCP 服务器

核心特性 🎯

  • 动态工具注册:客户端连接并自动注册其工具
  • 客户端工具执行:工具在客户端执行,而不是在服务器上 - 完美适用于浏览器 DOM 操作、本地文件访问或特定环境操作
  • 透明代理:服务器充当代理,将工具调用路由到适当的客户端进行执行
  • 木偶传输:将 MCP 方法委托给另一个传输 - 例如,让 Cursor 的 AI 调用在 Chrome 浏览器环境中执行的工具

这使您能够:

  • 🔄 当客户端连接时动态注册自定义工具
  • ⚡ 直接在客户端执行工具 - 不在服务器上
  • 🌐 构建与客户端特定环境交互的工具(如浏览器 DOM、本地文件等)
  • 🔗 创建灵活的、由客户端驱动的 AI 工具生态系统,其中执行发生在数据所在的位置

快速开始 🚀

安装

# 使用 Node(更好的兼容性)
npm i @mcpc-tech/cmcp

# 使用 Deno
deno add jsr:@mcpc/cmcp

完整示例

这里是一个最小的工作示例:

服务器用法 📡

服务器充当一个 代理和注册表 - 它没有预定义的工具,只是将执行路由到客户端:

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { createClientExecServer } from "@mcpc/cmcp";

// 服务器只是一个代理 - 没有工具,没有执行逻辑
const server = createClientExecServer(
  new Server({ name: "dynamic-mcp-server", version: 1.0.0 }),
  "dynamic-server",
);

// 服务器将所有工具调用路由到适当的客户端
// 所有执行都在客户端侧发生

客户端用法 🖥️

客户端注册带有本地实现的工具,这些工具在客户端本地执行:

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import { type ClientToolDefinition, createClientExecClient } from "@mcpc/cmcp";

const client = createClientExecClient(
  new Client({ name: "browser-client", version: 1.0.0 }),
  "browser-client-001",
);

// 定义带有本地实现的工具(在客户端执行)
const tools: ClientToolDefinition[] = [
  {
    name: "querySelector",
    description: "使用 CSS 选择器查询 DOM 元素",
    inputSchema: {
      type: "object",
      properties: {
        selector: { type: "string", description: "要查询的 CSS 选择器" },
        action: {
          type: "string",
          description: "要执行的操作",
          enum: ["getText", "click", "getAttribute"],
        },
        attribute: { type: "string", description: "属性名称" },
      },
      required: ["selector", "action"],
    },
    // 🔥 实现在客户端侧运行 - 有访问 DOM、本地文件等权限
    implementation: async (args: Record<string, unknown>) => {
      const { selector, action, attribute } = args;
      const element = document.querySelector(selector as string);

      if (!element) {
        throw new Error(`未找到元素: ${selector}`);
      }

      switch (action) {
        case "getText":
          return element.textContent || "";
        case "click":
          element.click();
          return `点击了元素: ${selector}`;
        case "getAttribute":
          return element.getAttribute(attribute as string);
        default:
          throw new Error(`未知操作: ${action}`);
      }
    },
  },
];

// 注册工具(存储在本地直到连接)
client.registerTools(tools);

// 连接到服务器并注册工具
await client.connect(
  new SSEClientTransport(new URL("http://localhost:9000/sse")),
);

console.log("客户端已连接并注册工具!");
// 客户端保持连接以处理工具执行请求

示例工具调用 🔧

// 外部 MCP 客户端连接到服务器
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";

const mcpClient = new Client({
  name: "external-client",
  version: 1.0.0,
});

await mcpClient.connect(
  new SSEClientTransport(new URL("http://localhost:9000/sse")),
);

// 调用已连接客户端注册的工具
const result = await mcpClient.callTool({
  name: "querySelector",
  arguments: {
    selector: "#my-button",
    action: "click",
  },
});

console.log(result); // "点击了元素: #my-button"
// ✨ 实际的 DOM 操作发生在客户端侧!

想要更多现成的客户端工具?在 AI 工具注册表中找到许多示例工具定义:https://ai-tools-registry.vercel.app/

为什么是客户端执行?🤔

传统 MCP:工具在服务器上执行

  • ❌ 服务器需要访问所有资源(文件、DOM、API)
  • ❌ 服务器端执行的安全问题
  • ❌ 仅限于服务器环境的能力

Client-Tool-Execution MCP:工具在客户端执行

  • ✅ 客户端自然可以访问其自身的环境(DOM、本地文件等)
  • ✅ 更好的安全性 - 不需要将敏感资源暴露给服务器
  • ✅ 可扩展 - 每个客户端处理自己的执行负载
  • ✅ 环境特定 - 浏览器客户端可以操纵 DOM,桌面客户端可以访问文件

架构流程 🔄

  1. 服务器:作为空代理启动,没有预定义的工具
  2. 客户端连接:客户端通过 SSE 连接到服务器
  3. 工具注册:客户端通过 client/register_tools 发送工具定义(仅模式)
  4. 服务器注册表:服务器更新其工具注册表,添加客户端的工具模式
  5. MCP 调用:外部系统通过服务器发现并调用工具
  6. 代理调用:服务器通过通知将调用代理到适当的客户端
  7. 客户端执行:🔥 工具在客户端侧运行,具有完全访问客户端环境的权限
  8. 响应:结果通过服务器返回给调用者
  9. 客户端断开连接:服务器自动移除客户端的工具

关键点:服务器从不执行工具 - 它只将调用路由到实际执行发生的客户端!

高级:木偶传输 🎭

bindPuppet 将两个客户端传输连接起来,使得一个客户端可以使用另一个客户端的工具。

核心理念:绑定 Cursor 的传输到 Chrome 的传输 → Cursor 的请求转发到 Chrome。

示例:Cursor → Chrome

import { bindPuppet, SSEServerTransport } from "@mcpc/cmcp";

// Chrome 的传输(连接到具有 DOM 工具的 Chrome 客户端)
const chromeTransport = new SSEServerTransport("/messages", "chrome");

// Cursor 的传输,绑定到 Chrome 的
const cursorTransport = new SSEServerTransport("/messages", "cursor");
const boundTransport = bindPuppet(
  cursorTransport, // 主传输
  chromeTransport, // 木偶 - 接收转发的调用
  ["tools/list", "tools/call"],
);

// 结果:当 Cursor 调用一个工具 → 转发到 Chrome → Chrome 执行

如何工作:

  1. 🌐 Chrome 连接并注册 DOM 工具
  2. 💻 Cursor 连接,并通过 bindPuppet 指向 Chrome 的传输
  3. 🤖 AI 通过 Cursor 调用工具 → bindPuppet 转发到 Chrome → Chrome 执行
  4. ✨ 结果通过 Chrome → Cursor → AI 返回

实际应用(使用 handleConnecting):

// Chrome: GET /sse?sessionId=chrome
// Cursor: GET /sse?sessionId=cursor&puppetId=chrome
// !!! 现在 Cursor 像木偶一样控制 Chrome

使用场景

  • 🖥️ Cursor + Chrome:编辑器中的 AI 控制浏览器自动化
  • 🤖 AI 代理 + 多个浏览器:一个 AI 协调多个浏览器标签中的工具
  • 📱 桌面应用 + 移动客户端:桌面 AI 访问移动特定功能
  • 🔗 多环境工作流:跨不同运行时环境链工具

可委托的方法

您可以委托的方法(来自 PUPPET_METHODS):

  • tools/list, tools/call - 工具操作
  • resources/list, resources/read - 资源操作
  • prompts/list - 提示操作