Skip to content

模型上下文协议 (MCP) 入门指南

模型上下文协议 (MCP) 是将 AI 模型连接到外部工具和数据的开放标准。

类比: MCP 就像 AI 应用的 USB-C 接口 —— 正如 USB-C 提供了连接电子设备的标准化方式,MCP 提供了将 AI 应用连接到外部系统的标准化方式。

MCP 能做什么?

场景示例
个人助手AI 可以访问你的 Google Calendar 和 Notion,成为更个性化的助手
开发提效Claude Code 可以根据 Figma 设计稿生成整个 Web 应用
企业数据AI 聊天机器人可以连接企业多个数据库,用户用自然语言分析数据
创意工具AI 模型可以在 Blender 中创建 3D 设计并打印

MCP 生态支持

MCP 是一个开放协议,支持广泛的客户端和服务端:

  • AI 助手: Claude, ChatGPT
  • 开发工具: VS Code, Cursor, JetBrains, Zed
  • 更多客户端: 查看完整列表

核心概念

MCP 服务器可以提供三种类型的能力:

能力类型说明AI 控制权
Tools (工具)AI 可以主动调用的函数模型控制
Resources (资源)只读数据,如文件内容、数据库 schema应用控制
Prompts (提示)预置的指令模板用户控制
┌─────────────────────────────────────────────────────┐
│                    MCP Host (AI 应用)                │
│  ┌──────────────────────────────────────────────┐  │
│  │  MCP Client                                   │  │
│  │  • 管理与 Server 的连接                      │  │
│  │  • 将 Tools/Resources/Prompts 提供给 AI      │  │
│  └──────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────┘

           ┌──────────────┼──────────────┐
           ▼              ▼              ▼
    ┌────────────┐ ┌────────────┐ ┌────────────┐
    │   Tools    │ │ Resources  │ │  Prompts   │
    │ (工具)     │ │ (资源)     │ │ (提示模板) │
    └────────────┘ └────────────┘ └────────────┘
           │              │              │
           ▼              ▼              ▼
    AI 可调用     AI 可读取       AI 可加载
    (需用户确认)  (只读)         预置模板

通信流程

分步实现

1. 服务器设置

typescript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";

// 初始化 MCP Server
const server = new Server(
  {
    name: "my-mcp-server",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {}, // 我们提供工具能力
    },
  }
);

2. 定义工具

你需要定义 Schema(AI 看到什么)和 Handler(运行什么代码)。

typescript
// 定义可用的工具
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "calculate_sum",
        description: "将两个数字相加",
        inputSchema: {
          type: "object",
          properties: {
            a: { type: "number", description: "第一个数字" },
            b: { type: "number", description: "第二个数字" },
          },
          required: ["a", "b"],
        },
      },
    ],
  };
});

3. 处理工具调用

typescript
// 当 AI 调用工具时执行逻辑
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  if (name === "calculate_sum") {
    // 使用 Zod 验证输入
    const schema = z.object({ a: z.number(), b: z.number() });
    const { a, b } = schema.parse(args);

    return {
      content: [
        {
          type: "text",
          text: `结果是 ${a + b}`,
        },
      ],
    };
  }

  throw new Error(`未知工具: ${name}`);
});

4. 连接传输

MCP 通常通过 Stdio(标准输入/输出)用于本地应用,或 SSE(服务器发送事件)用于远程应用。

typescript
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  // 注意: 写入 stderr,不要写入 stdout!
  console.error("MCP Server running on Stdio");
}

main();

⚠️ Stdio 日志最佳实践

绝对不要stdout 写入日志!stdout 用于 JSON-RPC 消息,写入会破坏协议。

typescript
// ❌ 错误 (stdio 模式下)
console.log("Processing request");  // 会破坏 JSON-RPC 消息!

// ✅ 正确 (写入 stderr)
console.error("Processing request");  // 安全,写入错误流

// ✅ 正确 (使用日志库)
import pino from "pino";
const logger = pino({ level: "info" });
logger.info("Processing request");  // 默认写入 stderr

客户端集成模式

选项 A: Claude Desktop(最简单)

将此添加到你的 claude_desktop_config.json

json
{
  "mcpServers": {
    "my-lab-server": {
      "command": "node",
      "args": ["/absolute/path/to/learn-ai/examples/mcp-lab/src/index.js"]
    }
  }
}

选项 B: 自定义客户端(高级)

如果你正在构建自己的 AI 应用(例如使用 LangChain),你可以充当 MCP 客户端。

typescript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

const transport = new StdioClientTransport({
  command: "node",
  args: ["./my-server.js"],
});

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

await client.connect(transport);

// 列出可用工具
const { tools } = await client.listTools();

// 调用工具
const result = await client.callTool({
  name: "calculate_sum",
  arguments: { a: 10, b: 20 },
});

安全最佳实践

  1. 输入验证: 始终使用 Zod 验证参数。AI 可能会产生类型幻觉。
  2. 路径遍历: 如果创建文件系统工具,确保用户无法访问 ../../etc/passwd
  3. 只读默认值: 在允许写入/删除操作之前,先从只读工具开始。

下一步

为前端工程师打造 · 基于 VitePress 构建