Skip to content
Published at:

第 15 章:MCP 协议与服务器

第 14 章结束时,我们提到一个关键问题:如何在保证安全的前提下,让 Claude Code 连接更广阔的外部世界? 文件系统和 Shell 命令已经覆盖了大量日常场景,但当 Claude 需要查阅最新的 React 文档、查询你的生产数据库、或者调用公司内部的 API 服务时,仅靠 Read/Write/Bash 是不够的。你需要一个标准化的扩展机制——这就是 MCP。

本章是第四篇"生态篇"的第一章,也是整个扩展体系的基石。MCP(Model Context Protocol)是 Claude Code 最核心的扩展机制,Plugins、Skills 甚至部分 Hooks 都建立在它的基础上。理解了 MCP,你就理解了 Claude Code 是如何"长出"新能力的。

本章目标:理解 MCP 协议的架构与工作原理,掌握配置 MCP 服务器的完整流程,能够使用 Context7 等常用 MCP 服务器,并具备开发简单自定义 MCP 服务器的能力。

15.1 什么是 MCP(Model Context Protocol)

MCP 的全称是 Model Context Protocol,由 Anthropic 于 2024 年底提出并开源。它是一个开放协议,定义了 AI 模型(如 Claude)与外部工具、数据源之间交互的标准方式。

一句话定义

MCP 是一套让 AI 安全地连接外部世界的标准接口协议。

如果把 Claude Code 比作一台电脑,那么 MCP 就是它的 USB-C 接口——统一的插拔标准,让任何符合协议的外部设备(服务器)都能即插即用。

为什么需要 MCP?

在没有 MCP 之前,如果你想让 Claude Code 查询某个数据库,你只能:

  1. 自己写一段脚本,把数据库内容导出为文件
  2. 让 Claude Code 读取这个文件
  3. Claude Code 分析后给出结论
  4. 如果数据库更新了,你要重新导出

这本质上是一个"数据搬运"的过程——你充当了 Claude Code 和外部世界之间的"人工适配器"。MCP 消除了这个中间人:Claude Code 通过 MCP 直接与数据库通信,实时获取数据,无需你的手动干预。

没有 MCP:
  Claude Code → 只能操作文件 + Shell → 你手动搬运外部数据进来

有 MCP:
  Claude Code → MCP 协议 → 数据库 / API / 文档 / 搜索 / 定制工具

MCP 的三个核心角色

MCP 协议定义了三个角色,它们的关系类似于客户端-服务器架构:

Claude Code (MCP Client)

    ├── MCP Server 1: Filesystem(安全文件访问)
    ├── MCP Server 2: PostgreSQL(数据库查询)
    ├── MCP Server 3: Context7(实时文档查询)
    └── MCP Server 4: Custom(你自定义的工具)
角色说明示例
MCP Client(客户端)发起请求的一方,通常是 AI 应用Claude Code、Claude Desktop、其他 AI Agent
MCP Server(服务器)接收请求并提供能力的一方,是一个独立进程Context7 服务器、PostgreSQL 服务器、自定义服务器
Resource(资源)服务器暴露的数据或功能数据库表、API 端点、文件目录、工具函数

Claude Code 作为 Client,通过 MCP 协议发现和调用各个 Server 提供的工具和资源。每个 Server 是一个独立的进程,通过标准输入/输出(stdio)或 HTTP 与 Client 通信。

MCP 解决了什么核心问题

回顾第 10 章讲到的工具系统,Claude Code 内置了 Read、Write、Edit、Bash、WebSearch 等十几种工具。这些工具覆盖了"一个 AI 编程助手最常见的需求"。但现实世界的需求远超这十几种:

  • 你的项目数据在 PostgreSQL 里,Claude 需要直接查表来分析数据关系
  • 你在写 React 代码,需要查阅 React 19 的 use() hook 最新文档
  • 你的公司有一个内部的 CI/CD 系统,你希望 Claude 能触发构建并查看结果
  • 你需要 Claude 操作浏览器来验证前端页面的交互行为

这些需求都有一个共同特点:它们需要访问"文件系统和 Shell 之外"的世界。MCP 就是为这个场景设计的。它把"如何安全地让 AI 接触外部世界"这个复杂问题标准化了:

  1. 标准化:所有 MCP Server 遵循统一的协议,Claude Code 无需知道每个 Server 的内部实现
  2. 安全性:Server 是独立进程,权限隔离;你可以精确控制哪些 Server 被启用
  3. 可扩展性:任何人都可以编写 MCP Server,生态可以无限生长
  4. 可组合性:多个 MCP Server 可以同时运行,互不干扰,Claude Code 自动发现和调度

15.2 MCP 架构

MCP 的架构设计遵循经典的客户端-服务器模式,但在细节上有不少值得理解的设计决策。

整体架构

flowchart TB subgraph CLIENT["Claude Code (MCP Client)"] DISCOVER["服务发现"] ROUTE["请求路由"] COORD["工具编排"] end subgraph TRANSPORT["传输层"] direction LR STDIO["stdio<br>(本地进程通信)"] HTTP["HTTP/SSE<br>(远程服务)"] end subgraph SERVER1["MCP Server A"] TOOLS1["Tools"] RESOURCES1["Resources"] PROMPTS1["Prompts"] end subgraph SERVER2["MCP Server B"] TOOLS2["Tools"] RESOURCES2["Resources"] PROMPTS2["Prompts"] end subgraph SERVER3["MCP Server C"] TOOLS3["Tools"] RESOURCES3["Resources"] end CLIENT --> TRANSPORT STDIO --> SERVER1 STDIO --> SERVER2 HTTP --> SERVER3

五个核心概念

MCP 协议定义了五个核心概念,理解它们就理解了整个协议:

1. Client(客户端)

Client 是协议的使用方。在 Claude Code 的场景中,Claude Code 本身就是 Client。它的职责包括:

  • 服务发现:读取配置文件中定义的 MCP Server 列表,启动对应的 Server 进程
  • 能力查询:向每个 Server 询问"你能提供哪些工具/资源/提示?"
  • 请求路由:当 Claude 需要调用某个工具时,将请求路由到正确的 Server
  • 结果聚合:收集各个 Server 的返回结果,统一呈现给 Claude 模型

2. Server(服务器)

Server 是一个独立的进程,实现了 MCP 协议的服务端。每个 Server 向 Client 暴露一组能力(工具、资源、提示)。Server 进程由 Client 启动和管理——当 Claude Code 启动时,它根据配置文件启动所有 MCP Server;当 Claude Code 退出时,这些 Server 进程也随之终止。

json
// 配置文件中定义一个 MCP Server
{
  "mcpServers": {
    "my-server": {
      "command": "node",
      "args": ["./mcp-server.js"]
    }
  }
}

command + args 定义了启动这个 Server 的命令。Claude Code 会执行这个命令,启动一个子进程,然后通过 stdio 与它通信。

3. Transport(传输层)

传输层定义 Client 和 Server 之间如何交换数据。MCP 支持两种传输方式:

传输方式场景特点
stdio本地 Server通过标准输入/输出通信,低延迟,无需网络。Server 以子进程方式运行
HTTP(SSE)远程 Server通过 HTTP + Server-Sent Events 通信,Server 可以部署在远程机器上

绝大多数本地 MCP Server 使用 stdio 传输。HTTP 传输通常用于文档查询服务(如 Context7)或部署在服务器上的团队共享 MCP 服务。

4. Tool(工具)

Tool 是 MCP Server 暴露给 Client 的可执行操作。每个 Tool 有三个要素:

  • name:工具名称,Client 用这个名称来调用它
  • description:工具描述,Claude 模型根据描述来决定何时使用这个工具
  • inputSchema:输入参数的 JSON Schema,定义了调用这个工具时需要传入哪些参数及其类型

举个实际的例子——Context7 MCP Server 暴露的"查询文档"工具:

json
{
  "name": "query-docs",
  "description": "检索并查询来自 Context7 的最新文档和代码示例",
  "inputSchema": {
    "type": "object",
    "properties": {
      "libraryId": {
        "type": "string",
        "description": "Context7 兼容的库 ID,如 '/mongodb/docs'"
      },
      "query": {
        "type": "string",
        "description": "需要帮助的问题或任务"
      }
    },
    "required": ["libraryId", "query"]
  }
}

Claude 模型看到这个工具定义后,就会知道"当我需要查某个库的文档时,我可以调用 query-docs 工具,并传入库 ID 和问题"。

5. Resource(资源)与 Prompt(提示模板)

Resource 是 Server 暴露的数据,而非操作。例如:

  • 一个数据库 Server 可能将每个数据表暴露为一个 Resource
  • 一个文件系统 Server 可能将目录结构暴露为 Resource
  • Claude 可以通过 resources/read 请求来获取 Resource 的内容

Prompt 是 Server 提供的提示模板。Server 可以说"我有一个名为 code-review 的 Prompt 模板,建议用户在审查代码时使用"。当用户选择这个 Prompt 时,Server 返回一段结构化的提示文本,供 Claude 参考。

在 Claude Code 的实际使用中,Tool 是 MCP 最重要、最常用的能力。Resource 和 Prompt 在特定场景下有用,但绝大多数 MCP Server 的核心价值体现在它们提供的 Tool 上。

服务发现与调用流程

Claude Code 是如何"发现"并使用 MCP 工具的?整个流程分为启动和运行时两个阶段:

启动阶段

1. Claude Code 启动
2. 读取 settings.json 中的 mcpServers 配置
3. 为每个配置的 Server 启动子进程
4. 向每个 Server 发送 tools/list 请求
5. 收集所有 Server 返回的工具列表
6. 将所有工具合并到 Claude Code 的工具注册表中

运行时(当 Claude 需要调用某个 MCP 工具时)

1. Claude 模型判断需要调用某个工具(如 Context7 的 query-docs)
2. Claude Code 识别该工具属于哪个 MCP Server
3. Claude Code 向该 Server 发送 tools/call 请求(包含工具名和参数)
4. Server 执行操作,返回结果
5. Claude Code 将结果传递给 Claude 模型
6. Claude 模型基于结果继续推理或执行下一步

这个流程对用户完全透明——你不需要手动指定"请使用 Context7 服务器",你只需要说"帮我查一下 React 19 的 use() hook",Claude 会自动判断这个需求最适合使用哪个工具。

15.3 配置 MCP 服务器

理解了架构之后,来看如何实际配置 MCP 服务器。配置的核心是在 settings.jsonmcpServers 字段中定义服务器列表。

配置位置

MCP 服务器可以在两个层级配置:

配置位置作用域适用场景
~/.claude/settings.json(全局)所有项目通用工具:Context7 文档查询、网络搜索等
.claude/settings.json(项目)当前项目项目专用工具:连接项目数据库、项目特定的 API

全局 MCP Server 对所有项目生效,适合文档查询、网络搜索等通用能力。项目级 MCP Server 仅对当前项目生效,适合数据库连接、内部 API 等与项目强绑定的工具。

基本的 stdio 配置

大多数 MCP Server 以 stdio 方式运行——也就是通过命令行启动一个子进程。配置格式如下:

json
{
  "mcpServers": {
    "my-database": {
      "command": "node",
      "args": ["./mcp-servers/db-server.js"],
      "env": {
        "DATABASE_URL": "postgresql://localhost:5432/mydb"
      }
    }
  }
}

逐字段说明:

字段必填说明
command启动 Server 进程的命令(如 nodenpxpythonuvx
args命令行参数数组。第一个参数通常是入口文件或包名
env传递给 Server 进程的环境变量。适合传递数据库连接字符串、API 密钥等
cwdServer 进程的工作目录。默认为项目根目录

HTTP 配置

对于远程 MCP 服务器,使用 HTTP 传输:

json
{
  "mcpServers": {
    "context7": {
      "type": "http",
      "url": "https://mcp.context7.com/sse"
    }
  }
}

HTTP 配置的关键字段:

字段说明
type固定为 "http"
urlMCP Server 的 SSE 端点 URL
headers可选的 HTTP 请求头(如认证 Token)

通过 npx 启动(推荐方式)

对于 Node.js 生态的 MCP Server,推荐使用 npx 启动——无需预先安装,自动下载和缓存:

json
{
  "mcpServers": {
    "context7": {
      "command": "npx",
      "args": ["-y", "@upstash/context7-mcp"]
    },
    "postgres": {
      "command": "npx",
      "args": ["-y", "@anthropic/mcp-server-postgres", "postgresql://localhost/mydb"]
    },
    "playwright": {
      "command": "npx",
      "args": ["-y", "@playwright/mcp"]
    }
  }
}

-y 参数表示自动确认安装(不需要手动输入 y)。首次启动时 npx 会下载对应的包,后续启动会使用缓存,速度很快。

对于 Python 生态的 MCP Server,推荐使用 uvx(类似 npx 的 Python 工具运行器):

json
{
  "mcpServers": {
    "my-python-server": {
      "command": "uvx",
      "args": ["my-mcp-server"]
    }
  }
}

enableAllProjectMcpServers 设置

enableAllProjectMcpServers 是一个重要的安全设置,控制在进入项目时是否自动启用项目配置中的所有 MCP Server:

json
{
  "enableAllProjectMcpServers": false
}
行为
true进入项目时自动启动所有项目 MCP Server
false(默认)需要手动确认后才启动项目 MCP Server

安全考虑:这个设置默认为 false 是有原因的。MCP Server 本质上是可执行程序——如果克隆了一个恶意项目,项目的 .claude/settings.json 中可能配置了会执行危险操作的 MCP Server。保持此设置为 false,Claude Code 会在启动项目 MCP Server 前提示你确认。

第 14 章花了大量篇幅讲权限和安全。MCP Server 的"自动启用"设置正是权限体系在扩展生态中的体现——deny 规则阻止危险操作,而 enableAllProjectMcpServers 阻止危险的自动连接。两层防线,互不替代。

完整配置示例

一个结合了全局和项目级的完整 MCP 配置:

json
// ~/.claude/settings.json(全局配置)
{
  "mcpServers": {
    "context7": {
      "command": "npx",
      "args": ["-y", "@upstash/context7-mcp"]
    },
    "brave-search": {
      "command": "npx",
      "args": ["-y", "@anthropic/mcp-server-brave-search"],
      "env": {
        "BRAVE_API_KEY": "your-brave-api-key"
      }
    }
  },
  "enableAllProjectMcpServers": false
}
json
// .claude/settings.json(项目配置,提交 Git)
{
  "mcpServers": {
    "project-database": {
      "command": "node",
      "args": [".mcp/postgres-server.js"],
      "env": {
        "DATABASE_URL": "postgresql://localhost:5432/project_db"
      }
    }
  },
  "enableAllProjectMcpServers": true
}

在这个例子中:

  • Context7Brave Search 在全局配置,所有项目都能用——它们是通用工具
  • project-database 在项目配置,只对这个项目生效——它是项目专用工具
  • 全局的 enableAllProjectMcpServersfalse——你控制了"克隆新项目不会自动启用其 MCP Server"
  • 项目的 enableAllProjectMcpServerstrue——但对于你自己信任的项目,进入时自动启用

15.4 Context7 文档查询实战

在所有 MCP 服务器中,Context7 是使用频率最高、体验最直观的一个。它提供了"实时文档查询"能力——让 Claude 能够查阅最新的库、框架、API 文档,而不是依赖训练数据中可能过时的知识。

Context7 是什么

Context7 是一个专门为 AI 编程工具设计的文档查询服务。它维护了一个庞大的文档索引库,覆盖了几乎所有主流编程库和框架的最新版本文档。通过 MCP 协议,Claude Code 可以直接查询这些文档。

核心价值:Claude 模型的训练数据有截止日期。对于训练数据之后发布的 API(如 React 19 的 use() hook)、小版本更新、刚发布的库,模型的知识是缺失的。Context7 弥补了这个 Gap——它提供"实时文档",让 Claude 能够基于最新文档给出准确的代码。

配置 Context7

json
{
  "mcpServers": {
    "context7": {
      "command": "npx",
      "args": ["-y", "@upstash/context7-mcp"]
    }
  }
}

配置完成后,重启 Claude Code。在对话中可以这样确认它是否生效:

你现在能访问 Context7 来查询文档吗?

Claude 会确认它可以通过 Context7 查询最新文档。

实战示例

场景:你在写 React 项目,需要了解 React 19 中 use() hook 的用法。你只知道它叫 use(),不确定它的 API 签名和最佳实践。

你输入

帮我查一下 React 19 的 use() hook 的用法,给出示例代码

背后发生了什么

1. Claude 分析你的需求:"查 React 19 use() hook" — 这需要最新文档
2. Claude 判断:Context7 MCP 可以查询 React 文档
3. Claude 调用 Context7 的 resolve-library-id 工具,传入 "React"
4. Context7 返回 React 的库 ID:/facebook/react
5. Claude 调用 Context7 的 query-docs 工具,传入库 ID 和查询 "use() hook"
6. Context7 返回 use() hook 的文档:API 签名、使用示例、注意事项
7. Claude 基于文档内容组织回答,给出准确的代码示例

Claude 的最终回复会包含基于最新文档的准确代码:

jsx
// React 19 的 use() hook —— 在组件中读取 Promise 和 Context
import { use, Suspense } from 'react';

async function fetchUser(id) {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
}

function UserProfile({ userId }) {
  // use() 可以直接读取 Promise,在 resolve 之前组件会 suspend
  const user = use(fetchUser(userId));
  return <div>{user.name}</div>;
}

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <UserProfile userId={1} />
    </Suspense>
  );
}

如果没有 Context7,Claude 可能基于训练数据(可能截止于 React 18 时代)给出过时的建议,或者无法提供关于 use() API 的准确信息。

Context7 的使用技巧

  1. 描述具体:不要只说"查一下 React",而要说"查 React 19 的 use() hook API 签名和示例"
  2. 信任结果:Context7 返回的内容来自官方文档,可以直接采用
  3. 结合代码:最好的用法是"查文档 → 用查到的知识写当前项目的代码"——一个自然衔接
  4. 不要滥用:对于你很熟悉的库、常见的 API(如 useState),不需要特意查询 Context7。它最适合的场景是:新版本特性、不熟悉的库、需要确认的 API 细节

其他文档查询 MCP 服务器

除了 Context7,还有其他文档相关的 MCP Server:

服务器说明适用场景
Context7通用文档查询,覆盖数千个库日常工作首选
MCP DocsAnthropic 官方文档查询查询 Anthropic SDK、Claude API
DevDocs基于 devdocs.io 的离线文档需要离线查询时使用

15.5 常用 MCP 服务器推荐

MCP 生态正在快速成长。以下是根据社区使用频率和实用性整理的推荐清单。

推荐列表

服务器用途配置方式推荐度
Context7实时文档查询,覆盖数千个库的最新文档npx -y @upstash/context7-mcp⭐⭐⭐⭐⭐
Filesystem安全的文件系统访问,限制在指定目录内npx -y @anthropic/mcp-server-filesystem /path⭐⭐⭐⭐
PostgreSQL数据库查询:执行 SQL、查看表结构、分析数据npx -y @anthropic/mcp-server-postgres⭐⭐⭐⭐
GitHubGitHub API 集成:读 Issue、查 PR、搜索代码npx -y @anthropic/mcp-server-github⭐⭐⭐⭐
Brave Search网络搜索(通过 Brave Search API)npx -y @anthropic/mcp-server-brave-search⭐⭐⭐
Puppeteer浏览器自动化:打开网页、截图、点击、表单填写npx -y @anthropic/mcp-server-puppeteer⭐⭐⭐
Sequential Thinking复杂推理:引导模型进行多步骤的深度思考npx -y @anthropic/mcp-server-sequential-thinking⭐⭐⭐

逐个详解

1. Context7(⭐⭐⭐⭐⭐)

第 15.4 节已经详细介绍了 Context7。它是日常使用频率最高的 MCP 服务器,强烈推荐每个人都配置

2. Filesystem(⭐⭐⭐⭐)

Filesystem MCP Server 让 Claude 安全地访问项目外的文件系统路径。在 Claude Code 中,沙箱默认限制了对项目目录外文件的访问(见第 14.5 节)。如果你需要 Claude 操作全局配置文件或共享目录中的资源,可以通过 Filesystem MCP Server 精确授权:

json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@anthropic/mcp-server-filesystem", "/Users/me/shared-configs"]
    }
  }
}

使用场景:让 Claude 帮你管理跨项目的共享配置文件、操作存储在项目外的媒体资源、编辑全局的 Dotfiles。

3. PostgreSQL(⭐⭐⭐⭐)

PostgreSQL MCP Server 让 Claude 直接查询和分析数据库。它暴露了表结构查询、SQL 执行、数据浏览等工具:

json
{
  "mcpServers": {
    "postgres": {
      "command": "npx",
      "args": ["-y", "@anthropic/mcp-server-postgres"],
      "env": {
        "DATABASE_URL": "postgresql://user:password@localhost:5432/mydb"
      }
    }
  }
}

使用场景

  • 让 Claude 分析表关系,帮你写 JOIN 查询
  • 排查数据质量问题
  • 根据表结构自动生成 TypeScript 类型定义
  • 分析生产数据库的慢查询(连接只读副本)

⚠️ 安全提醒:生产数据库的连接字符串应放在 settings.local.json(不提交 Git),并且建议连接只读副本,避免误操作。

4. GitHub(⭐⭐⭐⭐)

GitHub MCP Server 提供 GitHub API 的完整访问能力:

json
{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@anthropic/mcp-server-github"],
      "env": {
        "GITHUB_TOKEN": "ghp_xxxxxxxxxxxx"
      }
    }
  }
}

使用场景

  • 让 Claude 帮你分析 Issue 列表,总结用户反馈
  • 审查 PR 时获取更多上下文(关联 Issue、Blame 历史)
  • 搜索当前仓库的相似代码或历史修改

5. Brave Search(⭐⭐⭐)

Brave Search MCP Server 提供网络搜索能力。注意 Claude Code 本身已有 WebSearch 工具,但 Brave Search 可能返回不同来源的结果:

json
{
  "mcpServers": {
    "brave-search": {
      "command": "npx",
      "args": ["-y", "@anthropic/mcp-server-brave-search"],
      "env": {
        "BRAVE_API_KEY": "your-api-key"
      }
    }
  }
}

适用场景:需要 Claude 搜索最新技术动态、查阅 Stack Overflow 上的解决方案、获取 Claude 训练数据之后发布的文档。

6. Puppeteer(⭐⭐⭐)

Puppeteer MCP Server 提供浏览器自动化,让 Claude 能够操作真实的浏览器:

json
{
  "mcpServers": {
    "puppeteer": {
      "command": "npx",
      "args": ["-y", "@anthropic/mcp-server-puppeteer"]
    }
  }
}

使用场景

  • 让 Claude 帮你验证前端页面的交互逻辑
  • 自动抓取需要 JS 渲染的页面内容
  • 生成页面的截图进行视觉对比

7. Sequential Thinking(⭐⭐⭐)

Sequential Thinking MCP Server 不同于其他"获取外部数据"的服务。它提供的是一个思维框架——引导模型进行结构化的多步骤推理:

json
{
  "mcpServers": {
    "sequential-thinking": {
      "command": "npx",
      "args": ["-y", "@anthropic/mcp-server-sequential-thinking"]
    }
  }
}

适用场景:复杂算法设计、系统架构决策、多因素权衡——任何需要"一步一步想"而不是"一步到位"的深度问题。

如何选择

对于大多数开发者,建议的配置优先级:

第一层(必装):Context7 — 实时文档,每天都会用到
第二层(常用):根据你的技术栈选择 — PostgreSQL(数据库项目)、GitHub(协作项目)
第三层(按需):Puppeteer、Brave Search、Sequential Thinking — 特定场景下的利器

不需要一次性配齐所有 MCP Server。从 Context7 开始,当你遇到"Claude 需要访问 X 但我不知道怎么让它访问"的场景时,再去寻找对应的 MCP Server。

15.6 自定义 MCP 服务器开发入门

除了使用社区提供的 MCP Server,你也可以开发自己的 MCP Server 来接入公司内部 API、自定义工作流、或者任何你希望 Claude 能够操作的特定工具。本节从零开始,带你构建一个可用的自定义 MCP 服务器。

开发环境准备

MCP Server 的开发只需要 Node.js(≥18)。使用官方的 @modelcontextprotocol/sdk 包:

bash
mkdir my-first-mcp && cd my-first-mcp
pnpm init
pnpm add @modelcontextprotocol/sdk

一个最简 MCP 服务器

以下是一个完整的、可运行的 MCP 服务器,它只提供一个工具 hello——接收一个名字,返回问候语:

javascript
// server.js —— 你的第一个 MCP 服务器
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

// 1. 创建 Server 实例
const server = new Server({
  name: "my-first-mcp",
  version: "1.0.0"
}, {
  capabilities: { tools: {} }
});

// 2. 注册 tools/list 处理器 —— 告诉 Client 这个 Server 有哪些工具
server.setRequestHandler("tools/list", async () => ({
  tools: [{
    name: "hello",
    description: "向指定的人打招呼。当你需要问候某人时使用这个工具。",
    inputSchema: {
      type: "object",
      properties: {
        name: {
          type: "string",
          description: "要问候的人的名字"
        }
      },
      required: ["name"]
    }
  }]
}));

// 3. 注册 tools/call 处理器 —— 当 Client 调用工具时执行实际逻辑
server.setRequestHandler("tools/call", async (request) => {
  const { name, arguments: args } = request.params;

  if (name === "hello") {
    const greeting = `Hello, ${args.name}! 这是来自你的第一个 MCP 服务器的问候。`;
    return {
      content: [{ type: "text", text: greeting }]
    };
  }

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

// 4. 启动 stdio 传输,开始监听来自 Client 的请求
const transport = new StdioServerTransport();
await server.connect(transport);

运行你的 MCP 服务器

  1. 将上述代码保存为 server.js
  2. 确保 package.json 中有 "type": "module"(因为用了 ES Module)
  3. 在 Claude Code 的配置中添加这个服务器:
json
{
  "mcpServers": {
    "my-first-mcp": {
      "command": "node",
      "args": ["/absolute/path/to/my-first-mcp/server.js"]
    }
  }
}
  1. 重启 Claude Code,然后在对话中输入:
用 hello 工具向 Claude Code 打个招呼

Claude 会调用你的 hello 工具,传入 name: "Claude Code",然后收到 "Hello, Claude Code! 这是来自你的第一个 MCP 服务器的问候。"

MCP 协议的核心请求/响应

MCP 协议定义了四个核心的请求类型。理解它们就理解了如何开发任何 MCP Server:

请求方向说明Server 需要实现的
tools/listClient → Server"你有哪些工具?"✅ 必须
tools/callClient → Server"请执行工具 X,参数是 Y"✅ 必须
resources/listClient → Server"你有哪些资源?"可选
resources/readClient → Server"请读取资源 X 的内容"可选(配合 resources/list)

进阶:带网络请求的 MCP 服务器

一个更实际的例子——创建一个天气查询 MCP Server,让 Claude 能够查询某个城市的天气:

javascript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new Server({
  name: "weather-mcp",
  version: "1.0.0"
}, {
  capabilities: { tools: {} }
});

server.setRequestHandler("tools/list", async () => ({
  tools: [{
    name: "get_weather",
    description: "获取指定城市的天气信息。当用户询问某个城市的天气时使用。",
    inputSchema: {
      type: "object",
      properties: {
        city: {
          type: "string",
          description: "城市名称(英文),如 'Beijing'、'Tokyo'"
        }
      },
      required: ["city"]
    }
  }]
}));

server.setRequestHandler("tools/call", async (request) => {
  const { name, arguments: args } = request.params;

  if (name === "get_weather") {
    // 使用 wttr.in 的免费天气 API(不需要 API Key)
    const url = `https://wttr.in/${encodeURIComponent(args.city)}?format=3`;
    const response = await fetch(url);
    const weather = await response.text();

    return {
      content: [{ type: "text", text: `${args.city} 的天气:${weather}` }]
    };
  }

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

const transport = new StdioServerTransport();
await server.connect(transport);

为工具写好 description

工具描述(description)是 MCP Server 开发中最重要也最容易被忽视的环节。Claude 模型根据 description 来决定"何时使用这个工具"。一个好的 description 应该:

  1. 说清楚这个工具做什么
  2. 给出使用场景:什么时候应该用这个工具
  3. 描述参数含义:每个 input 参数都应有自己的 description

对比两个 description:

javascript
// ❌ 糟糕的 description —— Claude 看不懂
{
  name: "query",
  description: "查询数据",
  inputSchema: {
    properties: {
      q: { type: "string", description: "" }
    }
  }
}

// ✅ 好的 description —— Claude 知道何时调用
{
  name: "query_database",
  description: "在项目 PostgreSQL 数据库中执行只读 SQL 查询。" +
    "当用户询问关于数据库中的数据、表结构、或需要统计分析时使用。" +
    "注意:此工具只支持 SELECT 查询,不支持 INSERT/UPDATE/DELETE。",
  inputSchema: {
    properties: {
      sql: {
        type: "string",
        description: "要执行的只读 SQL 查询语句(仅 SELECT)"
      }
    },
    required: ["sql"]
  }
}

关键认知:你不是在写给人看的注释,你是在写给 Claude 模型看的"工具说明书"。Claude 根据这段描述来判断"当前用户的问题是否需要调用这个工具"。

调试 MCP 服务器

开发 MCP Server 最常见的调试技巧:

1. 用 stderr 打日志

MCP 的 stdio 传输中,stdout 用于协议通信,所以你不能向 stdout 打印日志。使用 stderr:

javascript
console.error("[DEBUG] Tool called:", name, "with args:", args);

2. 逐步测试

先在 Claude Code 中问"你有哪些 MCP 工具可用?"来确认 Server 的 tools/list 返回了正确的工具定义。确认工具被注册后,再测试实际调用。

3. 使用 JSON Schema 验证工具

确保 inputSchema 是合法 JSON Schema。VSCode 的 JSON 支持能帮你检查结构,但语义正确性需要手动验证。

发布与分享

当你的 MCP Server 完善后,可以分享给团队或社区:

  1. npm 发布:将 Server 发布为 npm 包,团队成员通过 npx your-package-name 使用
  2. MCP Marketplace:提交到 MCP 官方市场(https://github.com/modelcontextprotocol/servers),让更多人发现
  3. 团队内部:放在私有 npm registry 或 Git 仓库中,通过 npx 或本地路径引用

15.7 MCP 服务器最佳实践

开发和使用 MCP 服务器的实践中,社区已经总结出了以下的最佳实践。这些原则帮助你在享受 MCP 扩展能力的同时,保持系统的安全和可靠。

1. 最小权限原则

MCP Server 应该只暴露它确实需要的工具和数据。不要因为"反正能实现"就把整个数据库的所有表、一个文件系统的所有路径都暴露出去。

javascript
// ✅ 好的做法:精确控制
{
  name: "read_user_orders",
  description: "查询指定用户的订单列表(只读)",
  inputSchema: {
    properties: {
      userId: { type: "string", description: "用户 ID" }
    }
  }
}

// ❌ 坏的做法:什么都能做
{
  name: "execute_sql",
  description: "执行任意 SQL",
  inputSchema: {
    properties: {
      sql: { type: "string" }
    }
  }
}

2. 输入验证

永远不要信任从 Claude(Client)传来的参数。在 Server 端做完整的输入验证:

javascript
server.setRequestHandler("tools/call", async (request) => {
  const { name, arguments: args } = request.params;

  if (name === "get_user") {
    const userId = args.userId;

    // ✅ 验证输入
    if (!userId || typeof userId !== "string" || userId.length > 100) {
      return {
        content: [{ type: "text", text: "错误:userId 无效" }],
        isError: true
      };
    }

    // 使用参数化查询,防止 SQL 注入
    const user = await db.query("SELECT * FROM users WHERE id = $1", [userId]);
    return { content: [{ type: "text", text: JSON.stringify(user) }] };
  }
});

3. 错误处理

返回清晰的错误信息,让 Claude 能够理解问题并自我纠正:

javascript
// ✅ 好的错误返回
return {
  content: [{
    type: "text",
    text: "错误:数据库连接失败。请检查 DATABASE_URL 环境变量是否正确设置。"
  }],
  isError: true  // 标记为错误,Claude 会尝试用不同方式重新调用
};

// ❌ 坏的错误返回
throw new Error("Connection refused");

4. 性能:保持工具调用快速

MCP 工具的响应时间直接影响 Claude Code 的使用体验。建议:

  • 目标响应时间:< 5 秒(越短越好)
  • 如果操作需要更长时间,考虑返回"操作已提交,请稍后检查结果"的方式
  • 在工具 description 中提示"此操作可能需要较长时间",让 Claude 做好预期

5. 日志记录

在 Server 端记录所有工具调用,方便调试和安全审计:

javascript
function logToolCall(toolName, args, result) {
  const entry = {
    timestamp: new Date().toISOString(),
    tool: toolName,
    arguments: args,
    result: result.content?.[0]?.text?.substring(0, 200) // 只记录前 200 字符
  };
  console.error("[MCP]", JSON.stringify(entry));
}

6. 安全性

  • 永远不要在 Server 代码中硬编码密钥。使用环境变量传递
  • 限制 Server 的网络访问范围。如果 Server 需要访问外部 API,明确限定允许的域名
  • 定期审查依赖。MCP Server 的 Node.js 依赖也可能有安全漏洞
  • 为生产环境使用只读连接。数据库 MCP Server 应该连接只读副本,而非主库

7. 文档清晰

Server 的使用文档应包含:

  • 是什么:Server 的用途和提供的工具列表
  • 怎么配:完整的配置示例(commandargsenv
  • 需要什么权限:涉及哪些文件路径、网络请求、数据库表
  • 有什么限制:并发限制、速率限制、已知的兼容性问题

15.8 本章小结

MCP 是 Claude Code 扩展体系的基石。理解了它,后续的 Plugins、Skills、Hooks 都会更容易理解——因为它们都建立在 MCP 的"标准化扩展"理念之上。

核心知识回顾

  1. MCP 是什么:一个开放协议,定义了 AI 模型与外部工具/数据源交互的标准方式。类比成"AI 工具的 USB-C 接口"。
  2. 三大角色:Client(Claude Code)→ Transport(stdio/HTTP)→ Server(独立进程)。Server 通过 Tools、Resources、Prompts 向 Client 暴露能力。
  3. 配置位置:全局配置放通用工具(Context7、Brave Search),项目配置放项目专用工具(数据库连接、内部 API)。
  4. Context7 是必装的 MCP Server:它让 Claude 能够查询最新文档,弥补模型训练数据的时效性。
  5. 自定义 MCP Server 开发不难:核心就是实现 tools/listtools/call 两个处理器。@modelcontextprotocol/sdk 封装了协议细节。
  6. 安全原则:最小权限、输入验证、清晰错误、日志记录——这些通用软件开发原则在 MCP 领域同样适用。

MCP 在生态篇中的位置

MCP 是 Claude Code 扩展体系的最底层

Skills(技能/工作流)
    ↓ 依赖
Plugins(MCP 插件)
    ↓ 依赖
MCP(Model Context Protocol)  ← 本章
    ↓ 依赖
Hooks(事件钩子)
    ↓ 依赖
斜杠命令 / 快捷键

理解了这个层次关系,你就理解了为什么 MCP 是第四篇的第一章——它是所有上层扩展的共同基础。

下一步

配置好 MCP 服务器(特别是 Context7)之后,你已经让 Claude Code 的能力扩展到了"文件系统 + Shell + 实时文档"之外。第 16 章将探索 MCP 的上一层抽象——插件系统:Plugins 如何将 MCP Server 封装成更方便安装和管理的形态,以及如何利用官方和社区插件市场来快速增强 Claude Code 的能力。


章节小结:本章覆盖了 MCP 协议从概念到实践的完整路径。你学习了 MCP 的架构原理、学会了配置和使用 MCP 服务器(以 Context7 为核心实战)、了解了社区中最有价值的几个 MCP 服务器、掌握了自定义 MCP 服务器的开发方法、并建立了 MCP 安全使用的最佳实践框架。从下一章开始,我们将探索构建在 MCP 之上的更高层扩展形态——让"添加新能力"这件事变得更简单、更安全、更标准化。