第十章:MCP 协议实现

第十章:MCP 协议实现

🔌 本章详细介绍 MiniClaw 如何实现 MCP(Model Context Protocol)协议。


10.1 MCP 协议概述

10.1.1 什么是 MCP?

MCP(Model Context Protocol)是 Anthropic 开发的开放协议,用于连接 AI 模型与外部工具和数据源。

┌─────────────────────────────────────────────────────────────────────┐
│                    MCP 协议概述                                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│    ┌─────────────────────────────────────────────────────────────┐ │
│    │                    MCP 架构                                  │ │
│    │                                                             │ │
│    │    ┌─────────────┐         ┌─────────────┐                  │ │
│    │    │   MCP Host  │         │ MCP Server  │                  │ │
│    │    │ (Claude等)  │ ◄─────► │ (MiniClaw)  │                  │ │
│    │    └─────────────┘         └─────────────┘                  │ │
│    │           │                       │                          │ │
│    │           │                       │                          │ │
│    │           ▼                       ▼                          │ │
│    │    ┌─────────────┐         ┌─────────────┐                  │ │
│    │    │   AI 模型   │         │  工具/资源  │                  │ │
│    │    │ (Claude等)  │         │  (本地数据) │                  │ │
│    │    └─────────────┘         └─────────────┘                  │ │
│    │                                                             │ │
│    └─────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  MCP 核心概念:                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  • Resources  → 可读取的数据源                               │   │
│  │  • Tools      → 可执行的函数                                 │   │
│  │  • Prompts    → 预定义的提示词模板                           │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

10.1.2 MCP 通信方式

┌─────────────────────────────────────────────────────────────────────┐
│                    MCP 通信方式                                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  Stdio 传输(MiniClaw 使用的方式)                                  │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                             │   │
│  │    MCP Host (Claude Code)                                   │   │
│  │           │                                                 │   │
│  │           │ stdin/stdout                                    │   │
│  │           │ (JSON-RPC 消息)                                 │   │
│  │           │                                                 │   │
│  │           ▼                                                 │   │
│  │    MCP Server (MiniClaw)                                    │   │
│  │           │                                                 │   │
│  │           │ 文件系统                                        │   │
│  │           ▼                                                 │   │
│  │    ~/.miniclaw/                                             │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  消息格式:JSON-RPC 2.0                                             │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  {                                                          │   │
│  │    "jsonrpc": "2.0",                                        │   │
│  │    "id": 1,                                                 │   │
│  │    "method": "tools/call",                                  │   │
│  │    "params": {                                              │   │
│  │      "name": "miniclaw_note",                               │   │
│  │      "arguments": { "content": "..." }                      │   │
│  │    }                                                        │   │
│  │  }                                                          │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

10.2 服务器初始化

10.2.1 服务器配置

┌─────────────────────────────────────────────────────────────────────┐
│                    服务器配置                                        │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  import { Server } from '@modelcontextprotocol/sdk/server/index.js';│
│  import { StdioServerTransport } from '@modelcontextprotocol/sdk... │
│                                                                     │
│  // 创建服务器实例                                                  │
│  const server = new Server(                                         │
│    {                                                                │
│      name: "miniclaw",                                              │
│      version: "0.5.0",                                              │
│    },                                                               │
│    {                                                                │
│      capabilities: {                                                │
│        resources: {},    // 支持资源读取                            │
│        tools: {},        // 支持工具调用                            │
│        prompts: {},      // 支持提示词                              │
│      },                                                             │
│    }                                                                │
│  );                                                                 │
│                                                                     │
│  // 创建传输层                                                      │
│  const transport = new StdioServerTransport();                      │
│                                                                     │
│  // 连接服务器                                                      │
│  await server.connect(transport);                                   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

10.2.2 能力声明

┌─────────────────────────────────────────────────────────────────────┐
│                    能力声明                                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                             │   │
│  │  capabilities: {                                            │   │
│  │    resources: {},    // 声明支持资源功能                     │   │
│  │    tools: {},        // 声明支持工具功能                     │   │
│  │    prompts: {},      // 声明支持提示词功能                   │   │
│  │  }                                                          │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  能力说明:                                                         │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                                                             │   │
│  │  resources: {}                                              │   │
│  │  ├── ListResourcesRequestSchema   → 列出可用资源            │   │
│  │  └── ReadResourceRequestSchema    → 读取资源内容            │   │
│  │                                                             │   │
│  │  tools: {}                                                  │   │
│  │  ├── ListToolsRequestSchema       → 列出可用工具            │   │
│  │  └── CallToolRequestSchema        → 调用工具                │   │
│  │                                                             │   │
│  │  prompts: {}                                                │   │
│  │  ├── ListPromptsRequestSchema     → 列出可用提示词          │   │
│  │  └── GetPromptRequestSchema       → 获取提示词内容          │   │
│  │                                                             │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

10.3 资源实现

10.3.1 资源列表

┌─────────────────────────────────────────────────────────────────────┐
│                    资源列表实现                                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  server.setRequestHandler(ListResourcesRequestSchema, async () => { │
│    const skills = await kernel.skillCache.getAll();                 │
│                                                                     │
│    const resources = [                                              │
│      {                                                              │
│        uri: "miniclaw://context",                                   │
│        name: "Agent Context",                                       │
│        description: "Agent 人格核心,身份类问题必须先读取",         │
│        mimeType: "text/markdown",                                   │
│      },                                                             │
│      {                                                              │
│        uri: "miniclaw://skills",                                    │
│        name: "Skills Index",                                        │
│        description: "所有已加载的技能概览",                         │
│        mimeType: "text/markdown",                                   │
│      },                                                             │
│    ];                                                               │
│                                                                     │
│    // 添加技能资源                                                  │
│    for (const [skillName] of skills) {                              │
│      resources.push({                                               │
│        uri: `miniclaw://skill/${skillName}/SKILL.md`,               │
│        name: `Skill: ${skillName}`,                                 │
│        mimeType: "text/markdown",                                   │
│      });                                                            │
│    }                                                                │
│                                                                     │
│    return { resources };                                            │
│  });                                                                │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

10.3.2 资源读取

┌─────────────────────────────────────────────────────────────────────┐
│                    资源读取实现                                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  server.setRequestHandler(ReadResourceRequestSchema, async (req) => {│
│    const uri = req.params.uri;                                      │
│                                                                     │
│    // 核心上下文资源                                                │
│    if (uri === "miniclaw://context") {                              │
│      const content = await kernel.boot({ type: "full" });           │
│      return {                                                       │
│        contents: [{                                                 │
│          uri,                                                       │
│          mimeType: "text/markdown",                                 │
│          text: content,                                             │
│        }]                                                           │
│      };                                                             │
│    }                                                                │
│                                                                     │
│    // 技能索引资源                                                  │
│    if (uri === "miniclaw://skills") {                               │
│      const skills = await kernel.skillCache.getAll();               │
│      const content = generateSkillsIndex(skills);                   │
│      return {                                                       │
│        contents: [{                                                 │
│          uri,                                                       │
│          mimeType: "text/markdown",                                 │
│          text: content,                                             │
│        }]                                                           │
│      };                                                             │
│    }                                                                │
│                                                                     │
│    // 技能文件资源                                                  │
│    const skillMatch = uri.match(/^miniclaw:\/\/skill\/(.+)\/(.+)$/);│
│    if (skillMatch) {                                                │
│      const [, skillName, fileName] = skillMatch;                    │
│      const content = await kernel.getSkillContent(skillName, fileName);│
│      return {                                                       │
│        contents: [{                                                 │
│          uri,                                                       │
│          mimeType: "text/markdown",                                 │
│          text: content,                                             │
│        }]                                                           │
│      };                                                             │
│    }                                                                │
│                                                                     │
│    throw new Error(`Unknown resource: ${uri}`);                     │
│  });                                                                │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

10.4 工具实现

10.4.1 工具列表

┌─────────────────────────────────────────────────────────────────────┐
│                    工具列表实现                                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  server.setRequestHandler(ListToolsRequestSchema, async () => {     │
│    return {                                                         │
│      tools: [                                                       │
│        {                                                            │
│          name: "miniclaw_update",                                   │
│          description: "更新核心文件,实现自我进化",                 │
│          inputSchema: {                                             │
│            type: "object",                                          │
│            properties: {                                            │
│              file: {                                                │
│                type: "string",                                      │
│                enum: ["AGENTS.md", "SOUL.md", ...],                 │
│              },                                                     │
│              content: { type: "string" },                           │
│              mode: { type: "string", enum: ["append", "replace"] }, │
│            },                                                       │
│            required: ["file", "content"],                           │
│          },                                                         │
│        },                                                           │
│        {                                                            │
│          name: "miniclaw_note",                                     │
│          description: "日志速记,追加今日日志",                     │
│          inputSchema: {                                             │
│            type: "object",                                          │
│            properties: {                                            │
│              content: { type: "string" },                           │
│            },                                                       │
│            required: ["content"],                                   │
│          },                                                         │
│        },                                                           │
│        // ... 更多工具                                              │
│      ],                                                             │
│    };                                                               │
│  });                                                                │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

10.4.2 工具调用

┌─────────────────────────────────────────────────────────────────────┐
│                    工具调用实现                                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  server.setRequestHandler(CallToolRequestSchema, async (req) => {   │
│    const { name, arguments: args } = req.params;                    │
│                                                                     │
│    // 记录工具调用统计                                              │
│    kernel.trackTool(name);                                          │
│                                                                     │
│    try {                                                            │
│      switch (name) {                                                │
│        case "miniclaw_update": {                                    │
│          const { file, content, mode } = UpdateSchema.parse(args);  │
│          const result = await kernel.updateFile(file, content, mode);│
│          return {                                                   │
│        content: [{                                                  │
│          type: "text",                                              │
│          text: result,                                              │
│        }]                                                           │
│          };                                                         │
│        }                                                            │
│                                                                     │
│        case "miniclaw_note": {                                      │
│          const { content } = NoteSchema.parse(args);                │
│          const result = await kernel.appendDailyLog(content);       │
│          return {                                                   │
│        content: [{                                                  │
│          type: "text",                                              │
│          text: result,                                              │
│        }]                                                           │
│          };                                                         │
│        }                                                            │
│                                                                     │
│        case "miniclaw_exec": {                                      │
│          const { command } = ExecSchema.parse(args);                │
│          const result = await kernel.executeCommand(command);       │
│          return {                                                   │
│        content: [{                                                  │
│          type: "text",                                              │
│          text: result,                                              │
│        }]                                                           │
│          };                                                         │
│        }                                                            │
│                                                                     │
│        // ... 更多工具处理                                          │
│                                                                     │
│        default:                                                     │
│          throw new Error(`Unknown tool: ${name}`);                  │
│      }                                                              │
│    } catch (error) {                                                │
│      return {                                                       │
│        content: [{                                                  │
│          type: "text",                                              │
│          text: `Error: ${error.message}`,                           │
│        }],                                                          │
│        isError: true,                                               │
│      };                                                             │
│    }                                                                │
│  });                                                                │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

10.5 提示词实现

10.5.1 提示词列表

┌─────────────────────────────────────────────────────────────────────┐
│                    提示词列表实现                                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  server.setRequestHandler(ListPromptsRequestSchema, async () => {   │
│    const skills = await kernel.skillCache.getAll();                 │
│                                                                     │
│    const prompts = [                                                │
│      {                                                              │
│        name: "miniclaw_wakeup",                                     │
│        description: "创世协议,新会话的默认入口",                   │
│      },                                                             │
│      {                                                              │
│        name: "miniclaw_think",                                      │
│        description: "思考检查,脉搏检测",                           │
│      },                                                             │
│      {                                                              │
│        name: "miniclaw_growup",                                     │
│        description: "成长协议,记忆蒸馏",                           │
│      },                                                             │
│      {                                                              │
│        name: "miniclaw_recall",                                     │
│        description: "回忆协议,查看已记住的内容",                   │
│      },                                                             │
│      {                                                              │
│        name: "miniclaw_briefing",                                   │
│        description: "每日简报,早间工作概览",                       │
│      },                                                             │
│    ];                                                               │
│                                                                     │
│    // 添加技能提示词                                                │
│    for (const [skillName, skill] of skills) {                       │
│      if (skill.prompts) {                                           │
│        for (const prompt of skill.prompts) {                        │
│          prompts.push({                                             │
│            name: `skill_${skillName}_${prompt.name}`,               │
│            description: prompt.description,                         │
│          });                                                        │
│        }                                                            │
│      }                                                              │
│    }                                                                │
│                                                                     │
│    return { prompts };                                              │
│  });                                                                │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

10.5.2 提示词获取

┌─────────────────────────────────────────────────────────────────────┐
│                    提示词获取实现                                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  server.setRequestHandler(GetPromptRequestSchema, async (req) => {  │
│    const { name } = req.params;                                     │
│                                                                     │
│    // 记录提示词使用统计                                            │
│    kernel.trackPrompt(name);                                        │
│                                                                     │
│    switch (name) {                                                  │
│      case "miniclaw_wakeup":                                        │
│        return {                                                     │
│          messages: [{                                               │
│            role: "user",                                            │
│            content: {                                               │
│              type: "text",                                          │
│              text: generateWakeupPrompt(),                          │
│            },                                                       │
│          }]                                                         │
│        };                                                           │
│                                                                     │
│      case "miniclaw_growup":                                        │
│        return {                                                     │
│          messages: [{                                               │
│            role: "user",                                            │
│            content: {                                               │
│              type: "text",                                          │
│              text: generateGrowupPrompt(),                          │
│            },                                                       │
│          }]                                                         │
│        };                                                           │
│                                                                     │
│      // ... 更多提示词处理                                          │
│                                                                     │
│      default:                                                       │
│        // 检查是否为技能提示词                                      │
│        const skillMatch = name.match(/^skill_(.+)_(.+)$/);          │
│        if (skillMatch) {                                            │
│          const [, skillName, promptName] = skillMatch;              │
│          const content = await kernel.getSkillPrompt(skillName, promptName);│
│          return {                                                   │
│            messages: [{                                             │
│              role: "user",                                          │
│              content: {                                             │
│                type: "text",                                        │
│                text: content,                                       │
│              },                                                     │
│            }]                                                       │
│          };                                                         │
│        }                                                            │
│                                                                     │
│        throw new Error(`Unknown prompt: ${name}`);                  │
│    }                                                                │
│  });                                                                │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

10.6 MCP 配置示例

10.6.1 Claude Code 配置

┌─────────────────────────────────────────────────────────────────────┐
│                    Claude Code 配置                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  // claude_desktop_config.json 或 MCP 客户端配置                    │
│                                                                     │
│  {                                                                  │
│    "mcpServers": {                                                  │
│      "miniclaw": {                                                  │
│        "command": "npx",                                            │
│        "args": ["-y", "github:8421bit/miniclaw"],                   │
│        "env": {                                                     │
│          "MINICLAW_TOKEN_BUDGET": "12000"                           │
│        }                                                            │
│      }                                                              │
│    }                                                                │
│  }                                                                  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

10.6.2 配置参数说明

┌─────────────────────────────────────────────────────────────────────┐
│                    配置参数说明                                      │
├─────────────────┬───────────────────────────────────────────────────┤
│      参数       │                    说明                            │
├─────────────────┼───────────────────────────────────────────────────┤
│  command        │  执行命令,使用 npx 运行                           │
├─────────────────┼───────────────────────────────────────────────────┤
│  args           │  命令参数,-y 自动确认,github:... 指定仓库       │
├─────────────────┼───────────────────────────────────────────────────┤
│  env            │  环境变量配置                                     │
├─────────────────┼───────────────────────────────────────────────────┤
│  MINICLAW_      │  Token 预算配置,默认 8000                        │
│  TOKEN_BUDGET   │                                                   │
└─────────────────┴───────────────────────────────────────────────────┘

10.7 MCP 消息流程

10.7.1 初始化流程

┌─────────────────────────────────────────────────────────────────────┐
│                    初始化流程                                        │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│    MCP Host                              MCP Server                 │
│    (Claude Code)                         (MiniClaw)                 │
│         │                                     │                      │
│         │ ────── initialize ────────────────► │                      │
│         │       {protocolVersion: "2024..."} │                      │
│         │                                     │                      │
│         │ ◄───── InitializeResult ─────────── │                      │
│         │       {capabilities: {...}}         │                      │
│         │                                     │                      │
│         │ ────── initialized ────────────────► │                      │
│         │       (notification)                │                      │
│         │                                     │                      │
│         │                                     │                      │
│         │         初始化完成                   │                      │
│         │                                     │                      │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

10.7.2 工具调用流程

┌─────────────────────────────────────────────────────────────────────┐
│                    工具调用流程                                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│    MCP Host                              MCP Server                 │
│    (Claude Code)                         (MiniClaw)                 │
│         │                                     │                      │
│         │ ────── tools/list ────────────────► │                      │
│         │                                     │                      │
│         │ ◄───── ListToolsResult ──────────── │                      │
│         │       {tools: [...]}                │                      │
│         │                                     │                      │
│         │ ────── tools/call ────────────────► │                      │
│         │       {name: "miniclaw_note",       │                      │
│         │        arguments: {...}}            │                      │
│         │                                     │                      │
│         │                           [执行工具] │                      │
│         │                                     │                      │
│         │ ◄───── CallToolResult ───────────── │                      │
│         │       {content: [...]}              │                      │
│         │                                     │                      │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

10.7.3 资源读取流程

┌─────────────────────────────────────────────────────────────────────┐
│                    资源读取流程                                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│    MCP Host                              MCP Server                 │
│    (Claude Code)                         (MiniClaw)                 │
│         │                                     │                      │
│         │ ────── resources/list ────────────► │                      │
│         │                                     │                      │
│         │ ◄───── ListResourcesResult ─────── │                      │
│         │       {resources: [...]}           │                      │
│         │                                     │                      │
│         │ ────── resources/read ────────────► │                      │
│         │       {uri: "miniclaw://context"}  │                      │
│         │                                     │                      │
│         │                           [编译上下文]│                     │
│         │                                     │                      │
│         │ ◄───── ReadResourceResult ──────── │                      │
│         │       {contents: [...]}            │                      │
│         │                                     │                      │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

本章小结

┌─────────────────────────────────────────────────────────────────────┐
│                     第十章 核心要点                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  🔌 MCP 协议                                                        │
│     • Model Context Protocol,连接 AI 模型与外部工具               │
│     • 使用 Stdio 传输,JSON-RPC 2.0 消息格式                       │
│                                                                     │
│  📋 三大核心能力                                                    │
│     • Resources → 可读取的数据源                                    │
│     • Tools     → 可执行的函数                                      │
│     • Prompts   → 预定义的提示词模板                                │
│                                                                     │
│  🛠️ 服务器实现                                                      │
│     • @modelcontextprotocol/sdk                                    │
│     • Server 实例 + StdioServerTransport                           │
│                                                                     │
│  📝 请求处理器                                                      │
│     • ListResourcesRequestSchema / ReadResourceRequestSchema       │
│     • ListToolsRequestSchema / CallToolRequestSchema               │
│     • ListPromptsRequestSchema / GetPromptRequestSchema            │
│                                                                     │
│  ⚙️ 配置示例                                                        │
│     • npx -y github:8421bit/miniclaw                               │
│     • MINICLAW_TOKEN_BUDGET 环境变量                                │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

项目分析报告完成!

← 返回目录