Skip to content

预计时间

1 周

学习目标

  • 理解 MCP 协议架构
  • 能写一个 MCP Server
  • 能接入 MCP Client
  • 知道常用 MCP Server

一、MCP 是什么?

MCP = Model Context Protocol。解决的核心问题:LLM 和外部工具的标准化连接

text
之前(没有 MCP):
  每个 LLM 平台都有自己的 function calling 格式
  每个工具都要为不同平台各写一套集成代码

  ChatGPT 用 OpenAI tools 格式
  Claude 用 Anthropic tool_use 格式
  换平台 = 重写所有工具集成代码

之后(有 MCP):
  MCP 是 LLM 和工具之间的"USB 协议"
  MCP Server 一次编写 → 任何 MCP Client 都能用

  MCP Client (Claude / Qoder / Cursor)
      ↕  标准 JSON-RPC
  MCP Server (GitHub / Jira / Database / ...)

二、协议架构

text
┌────────────────────────────────┐
│         MCP Host               │  ← Claude Desktop / Qoder / Cursor
│  ┌──────────────────────────┐  │
│  │     MCP Client           │  │  ← 协议实现
│  │  - 列出可用工具           │  │
│  │  - 调用工具              │  │
│  │  - 管理连接              │  │
│  └──────────┬───────────────┘  │
└─────────────┼──────────────────┘
              │ JSON-RPC over stdio / HTTP SSE
┌─────────────┼──────────────────┐
│  ┌──────────┴───────────────┐  │
│  │     MCP Server           │  │  ← 你写的服务
│  │  - 注册工具 (Tools)      │  │
│  │  - 处理请求              │  │
│  │  - 返回结果              │  │
│  └──────────────────────────┘  │
│         MCP Server             │
└────────────────────────────────┘

通信方式:
  stdio:    本地进程间通信(命令行执行 Server)
  HTTP SSE: 远程通信(Server 可以部署在另一台机器)

三、写一个 MCP Server

下面用 @modelcontextprotocol/sdk 写一个 GitHub MCP Server:

bash
mkdir github-mcp-server && cd github-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
typescript
// src/index.ts
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';

// 创建 Server
const server = new Server(
  { name: 'github-mcp', version: '1.0.0' },
  { capabilities: { tools: {} } },
);

// ===== 注册工具列表 =====
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: 'search_repos',
      description: '在 GitHub 上搜索仓库',
      inputSchema: {
        type: 'object',
        properties: {
          query: { type: 'string', description: '搜索关键词' },
          language: { type: 'string', description: '编程语言,如 typescript' },
          limit: { type: 'number', description: '返回数量,默认 5' },
        },
        required: ['query'],
      },
    },
    {
      name: 'get_issue',
      description: '获取指定仓库的 Issue',
      inputSchema: {
        type: 'object',
        properties: {
          owner: { type: 'string', description: '仓库所有者' },
          repo: { type: 'string', description: '仓库名' },
          issueNumber: { type: 'number', description: 'Issue 编号' },
        },
        required: ['owner', 'repo', 'issueNumber'],
      },
    },
    {
      name: 'create_issue',
      description: '在指定仓库创建 Issue',
      inputSchema: {
        type: 'object',
        properties: {
          owner: { type: 'string' },
          repo: { type: 'string' },
          title: { type: 'string' },
          body: { type: 'string' },
        },
        required: ['owner', 'repo', 'title'],
      },
    },
  ],
}));

// ===== 处理工具调用 =====
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  switch (name) {
    case 'search_repos': {
      const { query, language, limit = 5 } = args as any;
      const langQuery = language ? `+language:${language}` : '';
      const url = `https://api.github.com/search/repositories?q=${encodeURIComponent(query)}${langQuery}&per_page=${limit}`;

      const response = await fetch(url, {
        headers: {
          'Accept': 'application/vnd.github.v3+json',
          'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`,
        },
      });
      const data = await response.json();

      return {
        content: [{
          type: 'text',
          text: JSON.stringify(data.items?.map((r: any) => ({
            name: r.full_name,
            stars: r.stargazers_count,
            description: r.description,
            url: r.html_url,
          })), null, 2),
        }],
      };
    }

    case 'get_issue': {
      const { owner, repo, issueNumber } = args as any;
      const url = `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}`;

      const response = await fetch(url, {
        headers: {
          'Accept': 'application/vnd.github.v3+json',
          'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`,
        },
      });
      const data = await response.json();

      return {
        content: [{
          type: 'text',
          text: JSON.stringify({ title: data.title, body: data.body, state: data.state, url: data.html_url }, null, 2),
        }],
      };
    }

    case 'create_issue': {
      const { owner, repo, title, body } = args as any;
      const url = `https://api.github.com/repos/${owner}/${repo}/issues`;

      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Accept': 'application/vnd.github.v3+json',
          'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ title, body }),
      });
      const data = await response.json();

      return {
        content: [{
          type: 'text',
          text: `Issue 创建成功:${data.html_url}`,
        }],
      };
    }

    default:
      throw new Error(`Unknown tool: ${name}`);
  }
});

// ===== 启动 Server =====
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error('GitHub MCP Server 已启动');
}

main().catch(console.error);

在 Qoder CLI 中接入

json
// ~/.qoder/settings.json 或通过 /mcp 命令添加
{
  "mcpServers": {
    "github": {
      "command": "node",
      "args": ["/path/to/github-mcp-server/dist/index.js"],
      "env": {
        "GITHUB_TOKEN": "ghp_xxxxxxxxxxxx"
      }
    }
  }
}

配置好后,LLM 就能直接搜索 GitHub 仓库、查看 Issue 了。


四、MCP 的三种角色

text
1. Tool(工具)
   最常用。LLM 可以调用的外部功能。
   如:搜索、查数据库、发邮件、操作文件

2. Resource(资源)
   暴露数据给 LLM。
   如:文件内容、数据库表结构、API 响应

3. Prompt(提示模板)
   预定义的 Prompt 模板。
   如:代码审查模板、翻译模板

五、常用 MCP Server

Server功能配置难度
@anthropic/mcp-server-githubGitHub API简单
@anthropic/mcp-server-filesystem本地文件操作简单
@anthropic/mcp-server-postgresPostgreSQL 查询中等
mcp-server-jiraJira API中等
mcp-server-notionNotion API中等
mcp-server-playwright浏览器自动化中等
mcp-server-fetchHTTP 请求简单

实践

  1. 克隆一个现成的 MCP Server(如 mcp-server-filesystem),跑起来
  2. 写一个自己的 MCP Server ——哪怕只实现一个工具"获取当前时间"
  3. 接入 Qoder CLI,试试 Agent 调用你的工具