Skip to content

Model Context Protocol (MCP)

MCP 是 Anthropic 推出的开放协议,用于连接 AI 应用与数据源

2. Model Context Protocol (MCP)

2.1 核心概念

MCP 定义了 AI 模型与外部系统交互的标准化方式,核心架构包含三个角色:

┌────────────────────────────────────────────────────────────────────────┐
│                          MCP 架构                                       │
├────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌──────────────┐          ┌──────────────┐          ┌──────────────┐  │
│  │   MCP Host   │          │  MCP Client  │          │  MCP Server  │  │
│  │  (如 Cursor) │ ──────── │   (协议层)    │ ──────── │  (工具提供方) │  │
│  └──────────────┘          └──────────────┘          └──────────────┘  │
│        │                          │                          │         │
│        │                          │                          │         │
│        ▼                          ▼                          ▼         │
│  ┌──────────────┐          ┌──────────────┐          ┌──────────────┐  │
│  │ 用户界面      │          │ 消息路由      │          │ 具体实现      │  │
│  │ 权限管理      │          │ 协议转换      │          │ 工具/资源     │  │
│  │ 会话管理      │          │ 连接管理      │          │ 提示模板      │  │
│  └──────────────┘          └──────────────┘          └──────────────┘  │
│                                                                         │
└────────────────────────────────────────────────────────────────────────┘

三个角色

角色职责示例
Host容纳 AI 应用的宿主环境Cursor IDE, Claude Desktop
Client与 Server 建立连接的协议层MCP SDK 客户端实现
Server提供工具、资源和提示的服务端文件系统 Server, GitHub Server

2.2 三大核心能力

MCP Server 可以提供三种类型的能力:

typescript
// 1. Resources - 资源(类似 REST 资源)
// 提供数据读取能力,由 Server 暴露,由 Client 读取

interface Resource {
  uri: string;           // 资源唯一标识,如 "file:///path/to/file.txt"
  name: string;          // 人类可读名称
  description?: string;  // 资源描述
  mimeType?: string;     // MIME 类型
}

// 2. Tools - 工具(模型可调用的函数)
// 允许 AI 执行操作,需要模型主动调用

interface Tool {
  name: string;
  description: string;
  inputSchema: JSONSchema;  // JSON Schema 定义参数
}

// 3. Prompts - 提示模板(可复用的提示)
// 预定义的提示模板,支持参数化

interface Prompt {
  name: string;
  description?: string;
  arguments?: PromptArgument[];
}

2.3 通信协议

MCP 基于 JSON-RPC 2.0,支持多种传输层:

typescript
// 请求格式
interface JSONRPCRequest {
  jsonrpc: "2.0";
  id: string | number;
  method: string;
  params?: object;
}

// 响应格式
interface JSONRPCResponse {
  jsonrpc: "2.0";
  id: string | number;
  result?: any;
  error?: {
    code: number;
    message: string;
    data?: any;
  };
}

// 通知格式(无需响应)
interface JSONRPCNotification {
  jsonrpc: "2.0";
  method: string;
  params?: object;
}

支持的传输层

传输方式适用场景特点
stdio本地进程通信简单可靠,适合本地 Server
HTTP + SSE远程服务支持 Web 部署,适合云服务
WebSocket实时双向通信低延迟,适合高频交互

2.4 实现 MCP Server

基础 Server 实现 (TypeScript)

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

// 创建 Server 实例
const server = new Server(
  {
    name: "my-mcp-server",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {},      // 声明支持工具
      resources: {},  // 声明支持资源
    },
  }
);

// 定义工具列表
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "search_codebase",
      description: "在代码库中语义搜索",
      inputSchema: {
        type: "object",
        properties: {
          query: {
            type: "string",
            description: "搜索查询"
          },
          maxResults: {
            type: "number",
            description: "最大结果数",
            default: 10
          }
        },
        required: ["query"]
      }
    },
    {
      name: "read_file",
      description: "读取文件内容",
      inputSchema: {
        type: "object",
        properties: {
          path: { type: "string", description: "文件路径" }
        },
        required: ["path"]
      }
    }
  ]
}));

// 实现工具调用
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;
  
  switch (name) {
    case "search_codebase": {
      const results = await performSearch(args.query, args.maxResults);
      return {
        content: [
          {
            type: "text",
            text: JSON.stringify(results, null, 2)
          }
        ]
      };
    }
    
    case "read_file": {
      const content = await fs.readFile(args.path, "utf-8");
      return {
        content: [
          {
            type: "text",
            text: content
          }
        ]
      };
    }
    
    default:
      throw new Error(`Unknown tool: ${name}`);
  }
});

// 定义资源列表
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
  resources: [
    {
      uri: "config://app-settings",
      name: "应用配置",
      description: "应用程序的配置信息",
      mimeType: "application/json"
    }
  ]
}));

// 实现资源读取
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
  const { uri } = request.params;
  
  if (uri === "config://app-settings") {
    return {
      contents: [
        {
          uri,
          mimeType: "application/json",
          text: JSON.stringify(appConfig, null, 2)
        }
      ]
    };
  }
  
  throw new Error(`Unknown resource: ${uri}`);
});

// 启动 Server
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("MCP Server running on stdio");
}

main().catch(console.error);

Python 实现

python
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent, Resource

# 创建 Server
server = Server("my-python-server")

# 定义工具
@server.list_tools()
async def list_tools() -> list[Tool]:
    return [
        Tool(
            name="execute_python",
            description="执行 Python 代码并返回结果",
            inputSchema={
                "type": "object",
                "properties": {
                    "code": {
                        "type": "string",
                        "description": "要执行的 Python 代码"
                    }
                },
                "required": ["code"]
            }
        )
    ]

# 实现工具调用
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
    if name == "execute_python":
        try:
            # 安全考虑:实际应用中需要沙箱执行
            result = eval(arguments["code"])
            return [TextContent(type="text", text=str(result))]
        except Exception as e:
            return [TextContent(type="text", text=f"Error: {e}")]
    
    raise ValueError(f"Unknown tool: {name}")

# 启动 Server
async def main():
    async with stdio_server() as (read_stream, write_stream):
        await server.run(read_stream, write_stream)

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

2.5 实现 MCP Client

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

async function createClient() {
  // 启动 Server 进程
  const serverProcess = spawn("node", ["./my-server.js"]);
  
  // 创建传输层
  const transport = new StdioClientTransport({
    command: "node",
    args: ["./my-server.js"]
  });
  
  // 创建 Client
  const client = new Client(
    { name: "my-client", version: "1.0.0" },
    { capabilities: {} }
  );
  
  // 连接到 Server
  await client.connect(transport);
  
  return client;
}

async function main() {
  const client = await createClient();
  
  // 列出可用工具
  const { tools } = await client.listTools();
  console.log("Available tools:", tools.map(t => t.name));
  
  // 调用工具
  const result = await client.callTool({
    name: "search_codebase",
    arguments: { query: "authentication", maxResults: 5 }
  });
  
  console.log("Search results:", result.content);
  
  // 列出资源
  const { resources } = await client.listResources();
  console.log("Available resources:", resources.map(r => r.name));
  
  // 读取资源
  const resource = await client.readResource({
    uri: "config://app-settings"
  });
  
  console.log("Config:", resource.contents);
}

2.6 配置与集成

Cursor/Claude Desktop 配置

json
// ~/.cursor/mcp.json 或 claude_desktop_config.json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/dir"],
      "env": {}
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_TOKEN": "your-github-token"
      }
    },
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres"],
      "env": {
        "DATABASE_URL": "postgresql://user:pass@localhost/db"
      }
    },
    "custom-server": {
      "command": "node",
      "args": ["/path/to/my-custom-server.js"],
      "cwd": "/path/to/working/dir"
    }
  }
}

2.7 安全最佳实践

typescript
// 1. 权限控制
const ALLOWED_PATHS = [
  process.env.HOME,
  '/tmp',
  process.cwd()
];

function validatePath(path: string): boolean {
  const resolved = path.resolve(path);
  return ALLOWED_PATHS.some(allowed => 
    resolved.startsWith(allowed)
  );
}

// 2. 输入验证
import { z } from 'zod';

const SearchArgsSchema = z.object({
  query: z.string().min(1).max(1000),
  maxResults: z.number().int().positive().max(100).default(10)
});

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const args = SearchArgsSchema.parse(request.params.arguments);
  // 使用验证后的 args
});

// 3. 速率限制
import { RateLimiter } from 'rate-limiter';

const limiter = new RateLimiter({
  tokensPerInterval: 100,
  interval: "minute"
});

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (!limiter.tryRemoveTokens(1)) {
    throw new Error("Rate limit exceeded");
  }
  // 处理请求
});

// 4. 审计日志
function logToolCall(name: string, args: any, result: any) {
  const entry = {
    timestamp: new Date().toISOString(),
    tool: name,
    arguments: args,
    resultSize: JSON.stringify(result).length,
    // 不记录敏感内容
  };
  
  auditLogger.info(entry);
}

前端面试知识库