Model Context Protocol (MCP) 是 Anthropic 推出的开放协议,旨在为 LLM 提供标准化的外部工具和数据源接入方式。本文将深入解析 MCP 协议的设计理念、核心架构,并带你从零实现一个完整的 MCP Server。


目录


一、什么是 MCP

MCP (Model Context Protocol) 是一个开放的、基于 JSON-RPC 2.0 的协议,它定义了 LLM 应用(客户端)与外部数据源/工具(服务器)之间的标准化通信方式。

你可以把 MCP 理解为 AI 世界的 USB-C 接口

  • 就像 USB-C 让各种设备通过统一接口连接一样,MCP 让各种 AI 应用通过统一协议接入外部能力
  • 无论是文件系统、数据库、API 服务,还是浏览器控制,都可以通过 MCP 暴露给 LLM

核心设计原则

原则 说明
标准化 统一的协议规范,任何客户端都能连接任何服务端
安全性 服务端主动暴露能力,客户端需授权才能调用
可组合性 一个客户端可连接多个服务端,能力可叠加
语言无关 协议基于 JSON-RPC,任何语言都能实现

在这里插入图片描述


二、为什么需要 MCP

在 MCP 出现之前,每个 AI 应用都需要为每个外部工具编写定制的集成代码。这导致了 M×N 问题

没有 MCP:
  应用 A ──定制代码──> 工具 1
  应用 A ──定制代码──> 工具 2
  应用 B ──定制代码──> 工具 1
  应用 B ──定制代码──> 工具 2
  → 需要 M × N 个集成

有了 MCP:
  应用 A ──MCP──> MCP Server 1 (封装工具 1)
  应用 B ──MCP──> MCP Server 1 (封装工具 1)
  应用 A ──MCP──> MCP Server 2 (封装工具 2)
  → 只需要 M + N 个实现

三、MCP 核心架构

MCP 采用 客户端-服务器 (Client-Server) 架构:

┌─────────────────────────────────────────────────┐
│                  MCP Host                        │
│  ┌───────────────┐    ┌───────────────────────┐  │
│  │  MCP Client   │───>│   MCP Server A        │  │
│  │  (协议层)      │    │   (文件系统工具)       │  │
│  └───────────────┘    └───────────────────────┘  │
│  ┌───────────────┐    ┌───────────────────────┐  │
│  │  MCP Client   │───>│   MCP Server B        │  │
│  │  (协议层)      │    │   (数据库查询)        │  │
│  └───────────────┘    └───────────────────────┘  │
│  ┌───────────────┐    ┌───────────────────────┐  │
│  │  MCP Client   │───>│   MCP Server C        │  │
│  │  (协议层)      │    │   (API 集成)          │  │
│  └───────────────┘    └───────────────────────┘  │
└─────────────────────────────────────────────────┘

角色定义

角色 职责
Host(宿主) 运行 LLM 的应用环境,如 Claude Desktop、IDE 插件
Client(客户端) Host 内部的协议处理层,负责与 Server 建立连接和通信
Server(服务端) 暴露具体能力(工具、资源、提示词)的服务进程

四、协议通信机制

4.1 消息格式:JSON-RPC 2.0

MCP 所有消息都遵循 JSON-RPC 2.0 规范:

请求 (Request):

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "read_file",
    "arguments": { "path": "/tmp/hello.txt" }
  }
}

响应 (Response):

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      { "type": "text", "text": "Hello, MCP!" }
    ]
  }
}

通知 (Notification):(无 id 字段,不需要响应)

{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": { "progress": 50, "total": 100 }
}

4.2 传输方式

MCP 支持两种标准传输方式:

stdio(标准输入/输出)
Client                          Server
  │                                │
  │──stdin: JSON-RPC Request─────>│
  │                                │
  │<──stdout: JSON-RPC Response────│
  │                                │
  • 适用于本地进程间通信
  • Client 启动 Server 作为子进程
  • 通过 stdin/stdout 传递 JSON-RPC 消息
  • 最常用的传输方式
HTTP + SSE(Server-Sent Events)
Client                          Server
  │──GET /sse────────────────────>│  (建立 SSE 连接)
  │<──SSE: endpoint URL───────────│
  │                                │
  │──POST /messages──────────────>│  (发送请求)
  │<──SSE: JSON-RPC Response──────│  (通过 SSE 接收)
  • 适用于远程服务、Web 部署
  • 支持流式响应
  • 2025 年已更新为 Streamable HTTP 传输方式

五、MCP 生命周期

一个完整的 MCP 会话经历以下阶段:

Client                              Server
  │                                    │
  │  1. 初始化 (Initialize)             │
  │──{ method: "initialize" }────────>│
  │<──{ result: { capabilities } }────│
  │                                    │
  │  2. 确认 (Initialized)             │
  │──{ method: "initialized" }───────>│
  │                                    │
  │  3. 正常通信                       │
  │──{ method: "tools/list" }────────>│
  │<──{ result: { tools: [...] } }────│
  │                                    │
  │──{ method: "tools/call" }────────>│
  │<──{ result: { content: [...] } }──│
  │                                    │
  │  4. 关闭                           │
  │──(进程退出 / 连接断开)────────────>│

初始化握手细节

Client 发送自身信息和能力:

{
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-03-26",
    "capabilities": {
      "roots": { "listChanged": true },
      "sampling": {}
    },
    "clientInfo": {
      "name": "MyApp",
      "version": "1.0.0"
    }
  }
}

Server 响应自身能力:

{
  "result": {
    "protocolVersion": "2025-03-26",
    "capabilities": {
      "tools": { "listChanged": true },
      "resources": { "subscribe": true },
      "prompts": { "listChanged": true },
      "logging": {}
    },
    "serverInfo": {
      "name": "MyServer",
      "version": "1.0.0"
    }
  }
}

六、核心能力详解

MCP 定义了三大核心能力(Primitives):

6.1 Tools(工具)

工具是 模型控制 的能力 —— LLM 决定何时调用。

{
  "name": "get_weather",
  "description": "获取指定城市的天气信息",
  "inputSchema": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "城市名称,如 '北京'"
      }
    },
    "required": ["city"]
  }
}

6.2 Resources(资源)

资源是 应用控制 的能力 —— 应用代码决定何时读取。

{
  "uri": "file:///project/README.md",
  "name": "项目 README",
  "description": "项目的说明文档",
  "mimeType": "text/markdown"
}

6.3 Prompts(提示词模板)

提示词是 用户控制 的能力 —— 用户主动选择使用。

{
  "name": "code_review",
  "description": "代码审查提示词",
  "arguments": [
    {
      "name": "language",
      "description": "编程语言",
      "required": true
    },
    {
      "name": "code",
      "description": "待审查的代码",
      "required": true
    }
  ]
}

能力对比

能力 控制方 用途 类比
Tools LLM 模型 执行操作、调用 API 函数调用
Resources 应用代码 提供上下文数据 文件读取
Prompts 用户 预设交互模板 快捷指令

七、手把手实现一个 MCP Server

接下来,我们用 TypeScript + Node.js 当前主流的方式从零实现一个功能完整的 MCP Server。这个 Server 将提供:

  • 一个查询天气的 Tool
  • 一个读取本地文件的 Resource
  • 一个代码审查的 Prompt Template

为什么选 TypeScript? MCP 协议本身由 Anthropic 用 TypeScript 开发,官方 SDK 最为成熟;Node.js 生态与 AI 应用天然契合,async/await 异步模型也与 MCP 的异步通信完美匹配。

7.1 环境准备

# 创建项目目录
mkdir my-mcp-server && cd my-mcp-server

# 初始化项目
npm init -y

# 安装 MCP SDK
npm install @modelcontextprotocol/sdk zod

# 安装开发依赖
npm install -D typescript @types/node tsx

初始化 TypeScript 配置:

npx tsc --init

修改 tsconfig.json 关键配置:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"]
}

package.json 中添加启动脚本:

{
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node build/index.js",
    "dev": "tsx src/index.ts"
  }
}

要求: Node.js >= 18,TypeScript >= 5.0

7.2 最小可运行示例

先从一个最简单的 Server 开始,确保环境正确:

// src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

// 创建 MCP Server 实例
const server = new McpServer({
  name: "my-first-server",
  version: "1.0.0",
});

// 注册一个简单的 Tool
server.tool("hello", { name: { type: "string", description: "你的名字" } }, async ({ name }) => ({
  content: [{ type: "text", text: `Hello, ${name}! 欢迎使用 MCP Server.` }],
}));

// 使用 stdio 传输启动 Server
const transport = new StdioServerTransport();
await server.connect(transport);

运行测试:

# 开发模式运行(无需编译)
npx tsx src/index.ts

此时 Server 已在 stdin 上监听 JSON-RPC 消息,等待 Client 连接。

7.3 实现完整功能

下面我们扩展 Server,加入实际有用的功能:

// src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "node:fs/promises";
import path from "node:path";

const server = new McpServer({
  name: "demo-server",
  version: "1.0.0",
});

// ============================================================
// Tools:模型可调用的工具
// ============================================================

server.tool(
  "get_weather",
  "获取指定城市的当前天气信息",
  { city: z.string().describe("城市名称,如 '北京'") },
  async ({ city }) => {
    // 模拟天气数据,实际项目中可调用真实 API
    const mockData: Record<string, { temp: string; weather: string; humidity: string }> = {
      "北京": { temp: "22°C", weather: "晴", humidity: "45%" },
      "上海": { temp: "25°C", weather: "多云", humidity: "60%" },
      "深圳": { temp: "28°C", weather: "阵雨", humidity: "80%" },
    };
    const data = mockData[city];
    if (data) {
      return { content: [{ type: "text", text: JSON.stringify(data) }] };
    }
    return { content: [{ type: "text", text: `未找到城市 '${city}' 的天气数据` }] };
  }
);

server.tool(
  "calculate",
  "安全地计算数学表达式",
  { expression: z.string().describe("数学表达式,如 '2 + 3 * 4'") },
  async ({ expression }) => {
    // 仅允许数字和基本运算符,防止注入
    const allowedChars = /^[0-9+\-*/().% ]+$/;
    if (!allowedChars.test(expression)) {
      return { content: [{ type: "text", text: "错误:表达式包含不允许的字符" }] };
    }
    try {
      // 使用 Function 构造器替代 eval,更安全
      const result = new Function(`"use strict"; return (${expression})`)();
      return { content: [{ type: "text", text: String(result) }] };
    } catch (e) {
      return { content: [{ type: "text", text: `计算错误:${e}` }] };
    }
  }
);

server.tool(
  "get_current_time",
  "获取当前时间",
  { timezone: z.string().optional().describe("时区,默认为 Asia/Shanghai") },
  async ({ timezone = "Asia/Shanghai" }) => {
    const now = new Date();
    const result = {
      datetime: now.toLocaleString("zh-CN", { timeZone: timezone }),
      timezone,
      timestamp: Math.floor(now.getTime() / 1000),
    };
    return { content: [{ type: "text", text: JSON.stringify(result) }] };
  }
);

// ============================================================
// Resources:应用可读取的数据源
// ============================================================

server.resource(
  "app-settings",
  "config://app/settings",
  { description: "获取应用配置信息", mimeType: "application/json" },
  async () => ({
    contents: [
      {
        uri: "config://app/settings",
        text: JSON.stringify(
          {
            app_name: "Demo MCP Server",
            version: "1.0.0",
            features: { weather: true, calculator: true, file_reader: true },
            max_file_size_mb: 10,
          },
          null,
          2
        ),
      },
    ],
  })
);

server.resource(
  "read-file",
  "file://{path}",
  { description: "读取本地文件内容" },
  async (uri) => {
    const filePath = uri.pathname;
    // 安全检查:限制可访问的目录
    const allowedDirs = [process.env.HOME + "/documents", "/tmp"];
    const absPath = path.resolve(filePath);

    if (!allowedDirs.some((dir) => absPath.startsWith(dir))) {
      return { contents: [{ uri: uri.href, text: `错误:无权访问路径 '${filePath}'` }] };
    }

    try {
      const content = await fs.readFile(absPath, "utf-8");
      return { contents: [{ uri: uri.href, text: content }] };
    } catch {
      return { contents: [{ uri: uri.href, text: `错误:文件 '${filePath}' 不存在` }] };
    }
  }
);

// ============================================================
// Prompts:用户可选用的提示词模板
// ============================================================

server.prompt(
  "code_review",
  "代码审查提示词",
  {
    code: z.string().describe("待审查的代码"),
    language: z.string().optional().describe("编程语言,默认为 typescript"),
  },
  async ({ code, language = "typescript" }) => ({
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: `请对以下 ${language} 代码进行审查,从这几个方面给出评价:

1. **代码质量**:命名规范、可读性、结构清晰度
2. **潜在 Bug**:是否有逻辑错误、边界条件未处理
3. **性能**:是否有明显的性能问题或可优化之处
4. **安全性**:是否存在安全隐患
5. **最佳实践**:是否符合 ${language} 的最佳实践

\`\`\`${language}
${code}
\`\`\`

请给出具体的改进建议和修改后的代码示例。`,
        },
      },
    ],
  })
);

server.prompt(
  "explain_error",
  "错误分析提示词",
  {
    error_message: z.string().describe("错误信息"),
    context: z.string().optional().describe("相关上下文代码"),
  },
  async ({ error_message, context }) => {
    let text = `我遇到了以下错误,请帮我分析原因并给出解决方案:

**错误信息:**
\`\`\`
${error_message}
\`\`\``;

    if (context) {
      text += `

**相关代码:**
\`\`\`
${context}
\`\`\``;
    }

    text += `

请从以下角度分析:
1. 错误的根本原因
2. 可能的触发条件
3. 推荐的解决方案
4. 如何避免类似问题`;

    return {
      messages: [{ role: "user", content: { type: "text", text } }],
    };
  }
);

// 启动 Server
const transport = new StdioServerTransport();
await server.connect(transport);

7.4 代码逐段解析

Tool 定义
server.tool(
  "get_weather",                                        // 工具名称
  "获取指定城市的当前天气信息",                            // 工具描述
  { city: z.string().describe("城市名称,如 '北京'") },   // 参数 Schema(Zod)
  async ({ city }) => { ... }                           // 处理函数
);
  • server.tool(name, description, schema, handler) 四个参数
  • Zod schema 自动转换为 JSON Schema,同时提供运行时验证
  • handler 接收已验证的参数,返回 { content: [...] } 格式
  • 复杂数据用 JSON.stringify() 序列化为 text 类型
Resource 定义
server.resource(
  "app-settings",                          // 资源唯一 ID
  "config://app/settings",                 // URI 模板
  { description: "...", mimeType: "..." }, // 元数据
  async (uri) => ({ contents: [...] })     // 处理函数
);
  • 静态 URI 直接匹配,动态 URI(如 file://{path})通过模板解析参数
  • handler 接收解析后的 URL 对象,返回 { contents: [...] } 格式
  • 应用代码通过 URI 主动读取,LLM 不会自动调用
Prompt 定义
server.prompt(
  "code_review",                           // Prompt 名称
  "代码审查提示词",                         // 描述
  { code: z.string(), language: z.string().optional() }, // 参数
  async ({ code, language }) => ({ messages: [...] })    // 返回消息
);
  • 返回 { messages: [...] } 格式,支持多轮消息
  • 参数使用 Zod 验证,.optional() 标记可选参数

7.5 添加生命周期管理

对于需要初始化资源的 Server(如数据库连接),可以在连接前后管理生命周期:

// src/lifecycle-server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import Database from "better-sqlite3";

// ---- 生命周期:初始化资源 ----
const db = new Database(":memory:");
db.exec(`
  CREATE TABLE notes (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  )
`);

const server = new McpServer({ name: "lifecycle-demo", version: "1.0.0" });

// 注意:handler 是闭包,直接捕获外部的 db 变量
server.tool(
  "add_note",
  "添加一条笔记",
  { content: z.string().describe("笔记内容") },
  async ({ content }) => {
    db.prepare("INSERT INTO notes (content) VALUES (?)").run(content);
    return { content: [{ type: "text", text: `笔记已添加:${content}` }] };
  }
);

server.tool("list_notes", "列出所有笔记", {}, async () => {
  const rows = db.prepare("SELECT id, content, created_at FROM notes ORDER BY id DESC").all();
  if (rows.length === 0) {
    return { content: [{ type: "text", text: "暂无笔记" }] };
  }
  const text = rows.map((r: any) => `[${r.id}] ${r.content} (${r.created_at})`).join("\n");
  return { content: [{ type: "text", text }] };
});

// ---- 启动 ----
const transport = new StdioServerTransport();
await server.connect(transport);

// ---- 生命周期:进程退出时清理 ----
process.on("SIGINT", () => {
  db.close();
  process.exit(0);
});

7.6 使用 Server 实例进行高级交互

MCP TypeScript SDK 的 McpServer 实例提供了丰富的 API:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

const server = new McpServer({
  name: "advanced-server",
  version: "1.0.0",
});

// 注册 Tool 并在内部使用 server 能力
server.tool(
  "long_running_task",
  "执行一个长时间运行的任务",
  { data: z.string().describe("输入数据") },
  async ({ data }) => {
    const totalSteps = 5;
    for (let i = 0; i < totalSteps; i++) {
      // 模拟耗时操作
      await new Promise((resolve) => setTimeout(resolve, 1000));
      console.error(`正在处理步骤 ${i + 1}/${totalSteps}...`);
    }
    return { content: [{ type: "text", text: `任务完成,处理了 ${data}` }] };
  }
);

SDK 核心 API 速览:

API 说明
server.tool(name, desc, schema, handler) 注册工具
server.resource(id, uri, meta, handler) 注册资源
server.prompt(name, desc, schema, handler) 注册提示词模板
server.connect(transport) 连接传输层启动服务
server.sendResourceListChanged() 通知客户端资源列表变化
server.sendToolListChanged() 通知客户端工具列表变化

八、进阶:Resources 与 Prompts

8.1 动态 Resources

URI 模板中的 {param} 会自动提取为参数:

server.resource(
  "user-profile",
  "users://{user_id}/profile",
  { description: "获取用户资料" },
  async (uri) => {
    // user_id 从 URI 路径中自动解析
    const userId = uri.pathname.split("/")[1];
    return {
      contents: [
        {
          uri: uri.href,
          text: JSON.stringify({
            id: userId,
            name: `用户${userId}`,
            email: `user${userId}@example.com`,
          }),
        },
      ],
    };
  }
);

8.2 Resource 的订阅机制

Client 可以订阅 Resource 的变更通知:

// 当资源内容变化时通知已订阅的客户端
server.sendResourceListChanged();

8.3 复杂 Prompt 模板

Prompt 可以返回多条消息,支持更复杂的交互:

server.prompt(
  "debug_session",
  "启动一个调试会话",
  {
    error: z.string().describe("错误信息"),
    logs: z.string().optional().describe("相关日志"),
  },
  async ({ error, logs }) => {
    const messages: any[] = [
      {
        role: "user",
        content: { type: "text", text: `我遇到了一个错误需要调试:\n\n${error}` },
      },
    ];

    if (logs) {
      messages.push({
        role: "user",
        content: { type: "text", text: `以下是相关日志:\n\`\`\`\n${logs}\n\`\`\`` },
      });
    }

    return { messages };
  }
);

九、调试与测试

9.1 使用 MCP Inspector

MCP 官方提供了一个可视化调试工具 Inspector

# 安装并启动 Inspector
npx @modelcontextprotocol/inspector npx tsx src/index.ts

Inspector 提供 Web UI,可以:

  • 查看 Server 暴露的所有 Tools / Resources / Prompts
  • 手动调用 Tool 并查看返回值
  • 查看 JSON-RPC 消息日志

9.2 使用 Claude Desktop 测试

在 Claude Desktop 配置文件中添加你的 Server:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "demo-server": {
      "command": "node",
      "args": ["/absolute/path/to/build/index.js"],
      "env": {
        "API_KEY": "your-api-key-if-needed"
      }
    }
  }
}

开发阶段可以用 tsx 直接运行 TypeScript:

{
  "mcpServers": {
    "demo-server": {
      "command": "npx",
      "args": ["tsx", "/absolute/path/to/src/index.ts"]
    }
  }
}

重启 Claude Desktop 后,在对话中即可使用你的 Server 提供的工具。

9.3 单元测试

使用 Vitest 进行测试:

npm install -D vitest
// src/__tests__/server.test.ts
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
import { createServer } from "../server.js"; // 提取 server 创建函数

describe("MCP Server", () => {
  let client: Client;

  beforeAll(async () => {
    const server = createServer();
    const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
    await server.connect(serverTransport);
    client = new Client({ name: "test-client", version: "1.0.0" });
    await client.connect(clientTransport);
  });

  it("should call hello tool", async () => {
    const result = await client.callTool({ name: "hello", arguments: { name: "MCP" } });
    expect(result.content[0].text).toContain("Hello, MCP");
  });

  it("should calculate expression", async () => {
    const result = await client.callTool({ name: "calculate", arguments: { expression: "2 + 3" } });
    expect(result.content[0].text).toBe("5");
  });

  it("should reject invalid expression", async () => {
    const result = await client.callTool({
      name: "calculate",
      arguments: { expression: "require('child_process').exec('ls')" },
    });
    expect(result.content[0].text).toContain("不允许");
  });

  it("should list resources", async () => {
    const result = await client.listResources();
    expect(result.resources.length).toBeGreaterThan(0);
  });
});

package.json 中添加测试脚本:

{
  "scripts": {
    "test": "vitest run"
  }
}

十、最佳实践与总结

安全性

  1. 输入验证:利用 Zod schema 在 handler 执行前自动验证参数
  2. 路径限制:Resource 访问限制在安全目录内
  3. 避免 eval:计算类工具使用 new Function() 或专用库,不要用 eval
  4. 权限控制:敏感操作需要用户确认

性能

  1. 异步优先:所有 handler 都是 async,避免阻塞事件循环
  2. 流式处理:大数据集使用分页返回
  3. 缓存:对频繁访问的 Resource 结果进行缓存
  4. 连接复用:同一个 transport 连接可处理多个请求

可维护性

  1. 清晰的描述:Tool 的 description 要详细,这是 LLM 理解工具的唯一依据
  2. Zod schema:完善的类型定义同时生成 JSON Schema 和运行时验证
  3. 错误处理:返回有意义的错误信息,而非抛出异常让 Server 崩溃
  4. 日志记录:使用 console.error() 输出调试信息(stdout 用于协议通信)

协议版本兼容

// SDK 自动处理协议版本协商
const server = new McpServer({
  name: "my-server",
  version: "1.0.0",
  // 可选:指定支持的协议版本
  capabilities: {
    tools: {},
    resources: { subscribe: true },
    prompts: {},
  },
});

附录:快速参考

完整项目结构

my-mcp-server/
├── src/
│   ├── index.ts             # 主服务入口
│   ├── server.ts            # Server 实例创建(便于测试)
│   ├── tools/               # 工具实现
│   │   ├── weather.ts
│   │   └── calculator.ts
│   ├── resources/           # 资源实现
│   │   └── files.ts
│   ├── prompts/             # 提示词模板
│   │   └── templates.ts
│   └── __tests__/           # 测试
│       └── server.test.ts
├── build/                   # 编译输出
├── tsconfig.json            # TypeScript 配置
├── package.json             # 项目配置
└── README.md

package.json 完整示例

{
  "name": "my-mcp-server",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node build/index.js",
    "dev": "tsx src/index.ts",
    "test": "vitest run",
    "inspector": "npx @modelcontextprotocol/inspector npx tsx src/index.ts"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.12.0",
    "zod": "^3.23.0"
  },
  "devDependencies": {
    "@types/node": "^22.0.0",
    "tsx": "^4.19.0",
    "typescript": "^5.6.0",
    "vitest": "^3.0.0"
  }
}

常用 MCP 客户端

客户端 说明
Claude Desktop Anthropic 官方桌面应用
Claude Code Anthropic CLI 工具
Cursor AI 代码编辑器
Windsurf AI 代码编辑器
Zed 高性能代码编辑器

总结

MCP 协议通过标准化的 JSON-RPC 通信,为 LLM 生态建立了统一的工具接入标准。本文从协议设计到实际实现,完整覆盖了 MCP 的核心概念:

  • 架构:Host → Client → Server 三层结构
  • 通信:JSON-RPC 2.0 + stdio/SSE 传输
  • 能力:Tools(模型调用)、Resources(应用读取)、Prompts(用户选择)
  • 实现:TypeScript SDK + Zod schema 验证 + stdio 传输

掌握这些内容,你就可以为任何 LLM 应用构建标准化的扩展能力了。


参考资料:

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐