Chapter 7: MCP — 让 Claude 连接万物
附录链接:A06 MCP 协议深入解析 涵盖了线协议、能力协商、传输层以及构建生产级 MCP 服务器的方法。
你将构建什么
三个真实的 MCP 集成,让 Claude 突破文件系统的限制:
- GitHub MCP — 分析你自己的仓库、分流 issue、审查 PR,全程不离开 Claude
- Context7 MCP — 查询任何库的实时文档,始终最新,永不过时
- Playwright MCP — 自动化浏览器测试、截图、验证已部署的应用是否正常
这些是 Everything Claude Code 的 .mcp.json 配置中实际使用的 MCP 服务器。不是理论性的 -- 这就是生产团队将 Claude 接入工具链的方式。
MCP 工作原理
MCP(Model Context Protocol,模型上下文协议)是 Claude 与外部工具之间的标准化接口。把它想象成 AI 的 USB 接口 -- 任何实现 MCP 协议的工具都能插入 Claude 成为原生工具。
Claude Code
|
|-- 内置: Read, Write, Edit, Bash, Glob, Grep
|
+-- MCP 层
|-- GitHub Server --> PR、issue、分支、代码搜索
|-- Context7 Server --> 任何库的实时文档
|-- Playwright Server --> 浏览器自动化、截图
|-- Exa Server --> 带引用的网络搜索
|-- Memory Server --> 跨会话知识图谱
|-- 你的自定义服务器 --> 任何你需要的能力为什么用 MCP 而不是 Bash?
没有 MCP,Claude 仍然可以通过 Bash 调用 gh issue list。MCP 更好因为:
- 类型安全:MCP 工具有 JSON Schema 定义。Claude 精确知道哪些参数有效。
- 进程隔离:每个 MCP 服务器运行在独立进程中。有问题的工具不会让 Claude 崩溃。
- 语义理解:Claude 对
mcp__github__list_issues的理解深度远超解析ghCLI 输出。 - 权限控制:你可以允许 GitHub MCP 工具,同时拦截原始的
gh命令。
工具命名
所有 MCP 工具遵循 mcp__<server>__<tool> 模式:
mcp__github__list_issues -- GitHub 服务器的 list_issues 工具
mcp__context7__query-docs -- Context7 服务器的 query-docs 工具
mcp__playwright__navigate -- Playwright 服务器的 navigate 工具这种命名使它们很容易在 hook 中作为目标:
{
"hooks": {
"PreToolUse": [{
"matcher": "mcp__github__.*",
"hooks": [{ "type": "command", "command": "echo 'GitHub API call'" }]
}]
}
}配置方式
两种添加 MCP 服务器的方式:
CLI(推荐):
claude mcp add github -e GITHUB_PERSONAL_ACCESS_TOKEN -- npx -y @modelcontextprotocol/server-github手动 .mcp.json:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx"
}
}
}
}作用域
| 作用域 | 文件 | 适合 |
|---|---|---|
| user | ~/.claude/.mcp.json | 到处使用的工具(GitHub、Memory) |
| project | 项目根目录 .mcp.json | 项目特定工具(提交到 Git) |
安全规则:不要硬编码 token。使用环境变量:
# 在 .bashrc 或 .zshrc 中
export GITHUB_PERSONAL_ACCESS_TOKEN=ghp_xxx
# 然后引用它
claude mcp add github -e GITHUB_PERSONAL_ACCESS_TOKEN -- npx -y @modelcontextprotocol/server-githubECC 的 .mcp.json 参考
这是生产环境的 .mcp.json 配置(基于 ECC):
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "" }
},
"context7": {
"command": "npx",
"args": ["-y", "@anthropic-ai/context7-mcp"]
},
"playwright": {
"command": "npx",
"args": ["-y", "@anthropic-ai/claude-code-playwright"]
},
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"]
},
"exa": {
"command": "npx",
"args": ["-y", "exa-mcp-server"],
"env": { "EXA_API_KEY": "" }
}
}
}不需要全部配置。从 GitHub 和 Context7 开始 -- 它们覆盖 80% 的真实使用场景。
Demo 18: GitHub MCP — 在 Claude 中管理你的仓库
问题
你在一个项目上工作,需要查看 open issue、找过期的 PR、或审查多个仓库的最近变更。现在意味着切到浏览器、在 GitHub 上点来点去、丢失上下文。有了 GitHub MCP,Claude 内联完成所有操作。
配置
1. 创建 GitHub Token
前往 github.com/settings/tokens?type=beta 创建 fine-grained token:
- Repository access: 选择你的仓库(或全部)
- Permissions: Issues (R/W), Pull Requests (R/W), Contents (R/W)
保存:
# 添加到 shell 配置文件
echo 'export GITHUB_PERSONAL_ACCESS_TOKEN=ghp_your_token_here' >> ~/.bashrc
source ~/.bashrc2. 添加服务器
claude mcp add github -e GITHUB_PERSONAL_ACCESS_TOKEN -- npx -y @modelcontextprotocol/server-github验证:
claude mcp list
# 预期: github: npx -y @modelcontextprotocol/server-github (stdio)3. 在你的真实仓库上使用
mkdir -p ~/claude-demos/demo-18 && cd ~/claude-demos/demo-18
git init
claude用你 真实的 仓库来体验真正的价值:
分流 open issue:
List all open issues in <your-username>/<your-repo>, sorted by most recent.
For each one, summarize the problem in one sentence.> List all open issues in myuser/web-app, sorted by most recent.
For each one, summarize the problem in one sentence.
● mcp__github__list_issues
owner: "myuser"
repo: "web-app"
state: "open"
sort: "created"
direction: "desc"
Allow? [y/n/a]: y
✔ 4 open issues found:
#23 (May 25) — Dashboard chart fails to render when date range
spans a DST transition
#21 (May 22) — Login page shows blank screen on Safari 17.4
#18 (May 15) — CSV export truncates rows after 10,000 entries
#12 (Apr 30) — Dark mode toggle does not persist across sessions找过期 PR:
Find all open PRs in <your-username>/<your-repo> that haven't been updated
in the last 14 days. List them with the author and last activity date.审查特定 PR:
Get the files changed in PR #42 of <your-username>/<your-repo>.
Summarize what the PR does and flag any concerns.从发现创建 issue:
I found a SQL injection vulnerability in src/api/users.js:10. Create a
GitHub issue for it with appropriate labels, referencing the exact code location.刚才发生了什么?
要点:
- MCP 服务器使用你的环境变量自动处理认证
- Claude 收到的是结构化 JSON,不是原始 CLI 输出,解析更可靠
- 你像审批任何内置工具(Read、Bash 等)一样审批工具调用
- MCP 服务器作为子进程运行 -- Claude 启动时启动,退出时停止
底层运行机制
你说:"列出 myuser/myrepo 的 open issue"
|
v
Claude 选择: mcp__github__list_issues
|
v
Claude Code ---(stdio)---> GitHub MCP Server ---(HTTPS)---> GitHub API
|
v
GitHub 返回 issue 的 JSON 数组
|
v
Claude 格式化并呈现结果MCP 服务器作为 Claude Code 的子进程运行。会话开始时自动启动,退出时停止。
真实使用模式
晨会上下文:配合 SessionStart hook 自动展示分配给你的 issue 和待审查的 PR。
Issue 驱动开发:让 Claude 读取一个 issue,实现修复,然后创建关联 issue 的 PR -- 全程不离开终端。
跨仓库搜索:使用 mcp__github__search_code 在多个仓库中搜索代码模式。
Demo 19: Context7 MCP — 始终最新的文档
问题
你在使用一个库,需要查看某个函数的 API。Claude 的训练数据可能已经过时几个月。文档可能已经变了。你可以开浏览器搜索,或者让 Claude 实时查询。
Context7 是一个文档服务器,为数千个库提供实时文档。它列在 ECC 推荐的 MCP 配置中。
配置
claude mcp add context7 -- npx -y @anthropic-ai/context7-mcp验证:
claude mcp list
# 应显示: context7: npx -y @anthropic-ai/context7-mcp (stdio)使用
cd ~/claude-demos/demo-18
claude查询当前 API 文档:
I'm using Drizzle ORM with PostgreSQL. Look up the current documentation for
defining a schema with relations. Show me the exact API for one-to-many relationships.> Look up the current Drizzle ORM docs for one-to-many relationships.
● mcp__context7__resolve-library-id
libraryName: "drizzle-orm"
→ Resolved: /drizzle-team/drizzle-orm (confidence: 0.97)
● mcp__context7__query-docs
libraryId: "/drizzle-team/drizzle-orm"
query: "one-to-many relations schema definition"
✔ Documentation retrieved (2,847 tokens):
## Relations in Drizzle ORM
To define a one-to-many relation, use the `relations()` helper:
```typescript
import { relations } from 'drizzle-orm';
import { pgTable, serial, text, integer } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name'),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
authorId: integer('author_id').references(() => users.id),
title: text('title'),
});
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
...Claude 调用 mcp__context7__resolve-library-id 找到 Drizzle 文档,然后调用 mcp__context7__query-docs 获取相关章节。
检查破坏性变更:
Look up the Tailwind CSS v4 docs. What changed in the configuration format
compared to v3? I need to know if my tailwind.config.js needs updating.获取正在使用的函数的最新 API:
Look up the current Next.js docs for the App Router's generateMetadata function.
I need the TypeScript types and all available fields.刚才发生了什么?
要点:
- 两步解析流程:先找到库,再查询文档
- 返回的文档是实时且最新的,不是来自 Claude 的训练数据
- Context7 返回聚焦的章节,不是整个文档站,节省上下文 token
为什么这比训练数据好
Claude 的训练数据有截止日期。库持续发新版本。Context7 获取 当前 文档,所以你得到:
- 准确的 API 签名(不是过时的)
- 当前的配置格式
- 最新的迁移指南
- 最近新增的功能
进阶技巧:配合 /review 使用
在你的 /review skill 指令中加入:
When reviewing code that uses third-party libraries, use Context7 to verify
that the API usage is current and follows recommended patterns.这样你的代码审查就能自动发现过时的 API 用法。
Demo 20: Playwright MCP — 浏览器自动化
问题
你部署了应用。它真的在工作吗?登录流程能走通吗?有没有控制台错误?你可以手动开浏览器点一遍,或者让 Claude 做并带截图回来。
配置
# 安装 MCP 服务器
claude mcp add playwright -- npx -y @anthropic-ai/claude-code-playwright
# 安装浏览器引擎(仅首次需要)
npx playwright install chromium构建测试页面
mkdir -p ~/claude-demos/demo-20 && cd ~/claude-demos/demo-20
cat > index.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<title>Task Manager</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: system-ui, sans-serif; max-width: 600px; margin: 40px auto; padding: 0 20px; }
h1 { margin-bottom: 20px; }
.add-form { display: flex; gap: 8px; margin-bottom: 20px; }
.add-form input { flex: 1; padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; }
.add-form button { padding: 8px 16px; background: #2563eb; color: white; border: none; border-radius: 6px; cursor: pointer; }
.task { display: flex; align-items: center; gap: 8px; padding: 12px; border-bottom: 1px solid #eee; }
.task.done span { text-decoration: line-through; color: #999; }
.task input[type=checkbox] { width: 18px; height: 18px; }
.task span { flex: 1; }
.task button { background: none; border: none; color: #ef4444; cursor: pointer; font-size: 18px; }
#count { color: #666; margin-top: 12px; }
#error { color: #ef4444; margin-top: 8px; display: none; }
</style>
</head>
<body>
<h1>Task Manager</h1>
<div class="add-form">
<input type="text" id="taskInput" placeholder="Add a task..." />
<button onclick="addTask()">Add</button>
</div>
<div id="tasks"></div>
<div id="count"></div>
<div id="error"></div>
<script>
let tasks = [];
function addTask() {
const input = document.getElementById('taskInput');
const text = input.value.trim();
if (!text) {
document.getElementById('error').style.display = 'block';
document.getElementById('error').textContent = 'Task cannot be empty';
return;
}
document.getElementById('error').style.display = 'none';
tasks.push({ text, done: false });
input.value = '';
render();
}
function toggleTask(i) { tasks[i].done = !tasks[i].done; render(); }
function deleteTask(i) { tasks.splice(i, 1); render(); }
function render() {
const container = document.getElementById('tasks');
container.innerHTML = tasks.map((t, i) =>
`<div class="task ${t.done ? 'done' : ''}">
<input type="checkbox" ${t.done ? 'checked' : ''} onchange="toggleTask(${i})" />
<span>${t.text}</span>
<button onclick="deleteTask(${i})">x</button>
</div>`
).join('');
const remaining = tasks.filter(t => !t.done).length;
document.getElementById('count').textContent = `${remaining} task${remaining !== 1 ? 's' : ''} remaining`;
}
document.getElementById('taskInput').addEventListener('keypress', e => { if (e.key === 'Enter') addTask(); });
render();
</script>
</body>
</html>
EOF启动服务器:
python -m http.server 8765 &使用
claude视觉冒烟测试:
Navigate to http://localhost:8765/ and take a screenshot. Tell me if the page
renders correctly and what UI elements you see.> Navigate to http://localhost:8765/ and take a screenshot.
● mcp__playwright__navigate
url: "http://localhost:8765/"
→ Page loaded (200 OK, 1.2s)
● mcp__playwright__screenshot
→ Screenshot captured (1280x720)
The page renders correctly. I can see:
- A heading: "Task Manager"
- An input field with placeholder "Add a task..."
- An "Add" button (blue, rounded)
- A counter showing "0 tasks remaining"
- No console errors detected功能测试:
On http://localhost:8765/, test the full task workflow:
1. Add a task called "Deploy to staging"
2. Add a task called "Write unit tests"
3. Check off "Deploy to staging"
4. Take a screenshot showing the current state
5. Tell me if the remaining count is correct> Test the full task workflow...
● mcp__playwright__fill
selector: "#taskInput"
value: "Deploy to staging"
● mcp__playwright__click
selector: ".add-form button"
→ Task added
● mcp__playwright__fill
selector: "#taskInput"
value: "Write unit tests"
● mcp__playwright__click
selector: ".add-form button"
→ Task added
● mcp__playwright__click
selector: ".task:first-child input[type=checkbox]"
→ Checkbox toggled
● mcp__playwright__screenshot
→ Screenshot captured (1280x720)
✔ Results:
- "Deploy to staging" shows with strikethrough (checked off)
- "Write unit tests" is unchecked
- Counter reads "1 task remaining" — correct
- All UI interactions worked as expected错误处理测试:
Try to add an empty task (click Add without typing anything).
Take a screenshot — does it show an error message?完整 QA 报告:
Give me a QA report for http://localhost:8765/ covering:
- Does the page load correctly?
- Can you add tasks?
- Does checking/unchecking work?
- Does the counter update?
- Does error handling work for empty input?
Include screenshots for each test.刚才发生了什么?
要点:
- Claude 控制的是真实浏览器,不是模拟 -- 这些是实际的 DOM 交互
- 截图给 Claude 提供视觉确认,不只是 DOM 状态
- 每个 Playwright 操作在交互前等待元素就绪
- 完整的工作流(导航、填充、点击、截图)和 QA 工程师手动操作如出一辙
清理
kill %1 2>/dev/null # 停止 HTTP 服务器真实应用场景
- 部署后验证:
git push后让 Claude 检查 staging URL 是否正常 - 视觉回归:CSS 修改前后截图对比,让 Claude 比较差异
- E2E 冒烟测试:在已部署的应用上跑完整用户流程(登录、创建、验证)
- 竞品分析:访问竞品页面并记录 UX 模式
ECC 的 /e2e-testing skill 使用 Playwright MCP 配合 Page Object Model 模式进行结构化、可重复的浏览器测试。
常见问题排查
MCP 服务器崩溃
服务器进程在会话中意外退出:
> List open issues in myuser/web-app
● mcp__github__list_issues
⚠ Error: MCP server 'github' is not connected.
The server process exited with code 1.诊断和修复:
# 1. 检查服务器是否能正常启动
npx -y @modelcontextprotocol/server-github 2>&1
# 如果看到 "Error: GITHUB_PERSONAL_ACCESS_TOKEN is not set",说明环境变量缺失
# 2. 验证环境变量已导出
echo $GITHUB_PERSONAL_ACCESS_TOKEN
# 应该打印你的 token。如果为空,重新导出:
export GITHUB_PERSONAL_ACCESS_TOKEN=ghp_your_token_here
# 3. 重启 Claude 以重新连接所有 MCP 服务器
# 用 Ctrl+C 退出,然后:
claudeMCP 服务器在你启动新的 Claude 会话时自动重启。如果在会话中崩溃,退出并重新进入即可。
Token 过期或已撤销
GitHub 通过 MCP 服务器返回 401 错误:
> Create an issue in myuser/web-app
● mcp__github__create_issue
⚠ Error: GitHub API returned 401 Unauthorized
{"message":"Bad credentials","documentation_url":"https://docs.github.com/rest"}修复:
- 前往 github.com/settings/tokens
- 检查 token 是否过期或已撤销
- 生成新 token 并赋予所需权限
- 更新 shell 配置并重新导出:bash
export GITHUB_PERSONAL_ACCESS_TOKEN=ghp_new_token_here - 重启 Claude(MCP 服务器在启动时读取环境变量)
服务器超时
慢网络或过载的 API 导致 MCP 调用挂起:
> Look up the Drizzle ORM docs for relations
● mcp__context7__resolve-library-id
libraryName: "drizzle-orm"
... (waiting)
⚠ Error: MCP call timed out after 30s常见原因:
- 企业代理或 VPN 阻止了
npx子进程的出站连接 - 上游 API(GitHub、Context7)正在经历停机
- 受限网络环境中的 DNS 解析失败
修复:
# 直接测试连通性
curl -s https://api.github.com/rate_limit | head -5
# 如果在代理后面,确保 npx 继承代理设置
export HTTPS_PROXY=http://your-proxy:8080Context7 找不到某个库的文档
不是所有库都被索引了。当 Context7 找不到文档时:
> Look up the docs for my-obscure-library
● mcp__context7__resolve-library-id
libraryName: "my-obscure-library"
→ No matching library found (confidence: 0.0)
I could not find documentation for "my-obscure-library" in Context7.
This library may not be indexed yet. I can try alternative approaches:
- Check the npm/PyPI registry for a README
- Read the source code directly if it is installed locally
- Search GitHub for the repository变通方案: 当 Context7 没有文档时,Claude 会回退到训练数据或使用 Bash 查询包注册表。你也可以提交请求将该库添加到 Context7 的索引中。
构建自定义 MCP 服务器
现成的服务器不够用时,自己构建。
TypeScript(使用官方 SDK)
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "internal-api", version: "1.0.0" });
server.tool(
"query_deployments",
"Get deployment status for a service",
{
service: z.string().describe("Service name, e.g. auth-api, web-app"),
environment: z.enum(["staging", "production"]).default("staging"),
},
async ({ service, environment }) => {
const res = await fetch(
`https://deploy.internal.co/api/status/${service}?env=${environment}`,
{ headers: { Authorization: `Bearer ${process.env.DEPLOY_TOKEN}` } }
);
const data = await res.json();
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
const transport = new StdioServerTransport();
await server.connect(transport);注册:
claude mcp add internal-api -e DEPLOY_TOKEN -- npx tsx internal-mcp.tsPython(使用 FastMCP)
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("internal-api")
@mcp.tool()
def search_logs(service: str, query: str, hours: int = 1) -> str:
"""Search application logs by service and query string."""
# Your log search logic here
return f"Found 42 matches for '{query}' in {service} (last {hours}h)"
if __name__ == "__main__":
mcp.run(transport="stdio")claude mcp add internal-api -- python internal_mcp.py调试 MCP
遇到问题时:
# 1. 检查服务器列表
claude mcp list
# 2. 手动启动服务器查看错误
npx -y @modelcontextprotocol/server-github 2>&1
# 3. 在 Claude 会话中检查可用工具
# 输入: /tools
# 4. 强制调用测试
# 问: "Call mcp__github__list_issues for owner/repo"常见问题:
| 问题 | 原因 | 解决 |
|---|---|---|
| 工具没出现 | 服务器未启动 | 检查 npx 能否运行该包 |
| "Permission denied" | 缺少 token | 检查环境变量已设置 |
| 超时 | 网络慢/服务器慢 | 检查连接,增加 timeout |
| "Server disconnected" | 进程崩溃 | 检查 ~/.claude/logs/ 中的日志 |
练习:多 MCP 工作流
配置 GitHub + Context7 MCP 服务器,然后运行以下工作流:
- 用 GitHub MCP 列出你仓库的 open issue
- 选一个涉及你使用的库的 issue
- 用 Context7 查询该库的当前文档
- 让 Claude 基于当前文档提出修复方案
- 用 GitHub MCP 创建分支和 PR
这是 ECC 团队每天使用的工作流 -- Claude 作为全链路开发伙伴,从 issue 分流到 PR 创建。
成功标准
- [ ] 两个 MCP 服务器已配置且响应正常
- [ ] GitHub MCP 能列出 issue 并创建 PR
- [ ] Context7 返回当前文档(非过时的训练数据)
- [ ] Claude 结合两个来源产出真实修复方案
- [ ] Token 存储在环境变量中,未硬编码
知识检测
总结
MCP 把 Claude 从代码编辑器变成了连接的开发平台。本章的三个服务器 -- GitHub、Context7、Playwright -- 覆盖了最常见的集成需求。
要点:
- 从 GitHub 和 Context7 开始 -- 用最少的配置提供最大价值
- 始终使用环境变量存储 token,不要硬编码
- user 作用域给通用工具,project 作用域给团队共享配置
- 自定义 MCP 服务器只需约 30 行 TypeScript 或 Python
- MCP 工具可以被 hook -- 用
mcp__github__.*matcher 审计 API 调用
深入了解:参见 A06 MCP 协议深入解析 了解线协议细节、能力协商、传输选项(stdio vs SSE)以及构建带错误处理和重试逻辑的生产级 MCP 服务器的模式。
下一章:Chapter 8: Subagent 架构 -- 构建专门的 agent 用于架构分析、安全扫描和测试生成。