本文档是《OpenCode 进阶使用指南》的第三章专注于 MCP(Model Context Protocol)集成的深入讲解预计阅读时间:30-40 分钟
目录
什么是 MCP
3.1.1 从一个实际问题说起
想象这样一个场景:你在开发一个电商网站,需要测试购物车功能。测试流程是:
- 打开网站首页
- 搜索商品
- 选择商品加入购物车
- 进入购物车查看
- 修改数量
- 结算
传统的测试方式是:
- 手动点击(费时费力)
- 写自动化测试脚本(需要学习 Selenium/Playwright)
- 找 QA 帮忙(沟通成本高)
如果能让 AI 直接操作浏览器帮你测试,是不是就很方便?
这就是 MCP 要解决的核心问题:让 AI 能操作外部工具和系统。
3.1.2 MCP 的定义
MCP(Model Context Protocol) 是一个开放的协议标准,它定义了 AI 模型如何与外部工具、数据源、服务进行交互。
你可以把 MCP 理解为 AI 世界的"USB 接口":
- 就像 USB 统一了各种外设的连接方式
- MCP 统一了 AI 与各种工具的连接方式
核心特点:
- 标准化:统一的接口定义,不同工具遵循相同规范
- 安全性:细粒度权限控制,可审计可追溯
- 扩展性:支持任意类型的工具和服务
- 灵活性:本地和远程服务都能连接
3.1.3 MCP 能解决什么问题
场景一:浏览器自动化
- 自动测试 Web 应用
- 截图生成文档
- 数据抓取
场景二:数据库操作
- 查询数据
- 生成报表
- 数据迁移
场景三:API 集成
- 调用第三方服务
- 内部系统集成
- 自动化工作流
场景四:文件系统
- 批量文件处理
- 文档转换
- 代码生成
场景五:版本控制
- 自动化 Git 操作
- PR 审查
- 发布管理
3.1.4 MCP 的生态系统
官方 MCP:
- Playwright MCP:浏览器自动化
- GitHub MCP:GitHub 操作
- Filesystem MCP:文件系统访问
- SQLite MCP:SQLite 数据库
社区 MCP:
- Figma MCP:设计稿操作
- Notion MCP:笔记管理
- Slack MCP:消息通知
- AWS MCP:云服务操作
企业 MCP:
- 内部 API MCP
- 数据库 MCP
- 业务系统 MCP
MCP 的核心概念
3.2.1 MCP 架构
┌─────────────────────────────────────────┐
│ OpenCode │
│ ┌──────────────────┐ │
│ │ MCP Client │ │
│ └────────┬─────────┘ │
└──────────────────┼──────────────────────┘
│
┌────────┴────────┐
│ MCP Protocol │
│ (stdio / sse) │
└────────┬────────┘
│
┌───────────┼───────────┐
│ │ │
┌──────┴──────┐ ┌─┴────────┐ ┌┴──────────┐
│ MCP Server │ │ MCP │ │ MCP │
│ (Playwright)│ │ Server │ │ Server │
│ │ │ (GitHub) │ │ (Filesystem)
└─────────────┘ └──────────┘ └───────────┘
MCP Client:OpenCode 内置的 MCP 客户端,负责管理 MCP 连接。
MCP Server:独立的进程或服务,实现 MCP 协议,提供具体功能。
传输层:Client 和 Server 之间的通信方式:
- stdio:标准输入输出(本地进程)
- sse:Server-Sent Events(远程服务)
3.2.2 资源(Resources)
什么是资源:资源是 MCP 中的数据对象,可以是文件、数据库记录、API 响应等。
资源标识:每个资源都有唯一的 URI:
file:///home/user/project/README.md
database://localhost:5432/mydb/users/123
github://repos/owner/repo/issues/456
browser://page/current-url
资源操作:
resources/list:列出可用资源resources/read:读取资源内容resources/subscribe:订阅资源变化
3.2.3 工具(Tools)
什么是工具:工具是 MCP 提供的功能接口,可以被 AI 调用执行操作。
工具定义:每个工具都有明确的输入输出定义:
{
"name": "browser_navigate",
"description": "导航到指定 URL",
"inputSchema": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "目标 URL"
}
},
"required": ["url"]
},
"outputSchema": {
"type": "object",
"properties": {
"success": { "type": "boolean" },
"title": { "type": "string" },
"url": { "type": "string" }
}
}
}
工具调用:
{
"tool": "browser_navigate",
"params": {
"url": "https://example.com"
}
}
3.2.4 提示词(Prompts)
什么是提示词: MCP Server 可以预定义一些提示词模板,供 AI 使用。
示例:
{
"name": "analyze_page",
"description": "分析当前网页的性能和可访问性",
"template": "请分析当前页面的以下方面:\n1. 加载性能\n2. SEO 优化\n3. 可访问性\n4. 最佳实践"
}
MCP 与 Skills 的区别
3.3.1 核心区别
| 维度 | MCP | Skills |
|---|---|---|
| 定位 | 外部工具连接 | 内部知识封装 |
| 作用 | 扩展 AI 能力 | 规范 AI 行为 |
| 实现 | 独立进程/服务 | Markdown 文件 |
| 范围 | 跨项目通用 | 项目/团队特定 |
| 权限 | 需要显式授权 | 默认允许 |
3.3.2 协作关系
MCP 和 Skills 不是竞争关系,而是互补关系:
┌─────────────────────────────────┐
│ 用户请求 │
└──────────────┬──────────────────┘
│
┌──────┴──────┐
│ Skills │ ← 决定"做什么"
└──────┬──────┘
│
┌──────┴──────┐
│ MCP │ ← 决定"用什么做"
└──────┬──────┘
│
┌──────┴──────┐
│ 外部工具 │
└─────────────┘
示例流程:
- 用户说"测试登录功能"
- Skills 系统匹配到"e2e-testing" Skill
- Skill 指导 AI 使用 Playwright MCP
- Playwright MCP 操作浏览器执行测试
3.3.3 选择指南
使用 MCP 的场景:
- 需要操作外部系统(浏览器、数据库、API)
- 需要访问外部数据
- 需要执行特定领域的专业工具
使用 Skills 的场景:
- 定义团队规范和流程
- 封装重复性的开发任务
- 沉淀领域知识和最佳实践
组合使用的场景:
- 用 Skills 定义测试流程,用 MCP 执行浏览器测试
- 用 Skills 定义代码规范,用 MCP 操作 Git
- 用 Skills 定义数据迁移流程,用 MCP 操作数据库
配置你的第一个 MCP
3.4.1 查看可用 MCP
# 列出已配置的 MCP
opencode mcp list
# 输出示例:
已配置的 MCP Servers:
1. playwright (enabled)
- 浏览器自动化测试
- 状态: 运行中
2. github (disabled)
- GitHub 操作
- 状态: 未启用
3.4.2 添加 MCP
方式一:交互式添加
opencode mcp add
# 交互提示:
? 选择 MCP 类型:
▸ Playwright (浏览器自动化)
GitHub (GitHub 操作)
Filesystem (文件系统)
SQLite (SQLite 数据库)
Custom (自定义)
? Playwright MCP 配置:
可执行文件路径: /usr/local/bin/npx
启动参数: @anthropic-ai/playwright-mcp-server
? 是否启用: Yes
✓ Playwright MCP 已添加并启动
方式二:配置文件添加
编辑 ~/.config/opencode/mcp.json:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@anthropic-ai/playwright-mcp-server"],
"env": {
"DISPLAY": ":1"
}
},
"github": {
"command": "npx",
"args": ["-y", "@anthropic-ai/github-mcp-server"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
}
}
}
}
3.4.3 配置参数详解
基本参数:
| 参数 | 类型 | 说明 | 示例 |
|---|---|---|---|
| command | string | 启动命令 | "npx", "node", "python" |
| args | array | 命令参数 | ["@anthropic-ai/playwright-mcp-server"] |
| env | object | 环境变量 | {"TOKEN": "xxx"} |
| cwd | string | 工作目录 | "/path/to/project" |
高级参数:
| 参数 | 类型 | 说明 | 示例 |
|---|---|---|---|
| timeout | number | 超时时间(毫秒) | 30000 |
| restartOnError | boolean | 出错时重启 | true |
| allowedTools | array | 允许的工具列表 | ["browser_navigate"] |
| deniedTools | array | 禁止的工具列表 | ["browser_evaluate"] |
3.4.4 测试 MCP
添加 MCP 后,测试是否正常工作:
# 查看 MCP 状态
opencode mcp status playwright
# 输出:
Playwright MCP 状态:
- 状态: 运行中
- PID: 12345
- 运行时间: 5 分钟
- 可用工具: 12 个
- browser_navigate
- browser_click
- browser_screenshot
...
# 测试工具调用
opencode mcp test playwright browser_navigate '{"url": "https://example.com"}'
# 输出:
✓ 工具调用成功
结果: {
"success": true,
"title": "Example Domain",
"url": "https://example.com"
}
3.4.5 在 OpenCode 中使用 MCP
配置好 MCP 后,在对话中直接使用:
> 使用 Playwright 打开百度并搜索"OpenCode"
AI:[调用 playwright MCP]
- browser_navigate: https://www.baidu.com
- browser_click: [搜索框]
- browser_type: OpenCode
- browser_click: [百度一下按钮]
- browser_screenshot: 保存结果
AI:已完成操作,截图保存在 /tmp/baidu_search.png
常用 MCP 详解
3.5.1 Playwright MCP(浏览器自动化)
功能概述:基于 Playwright 框架,提供完整的浏览器自动化能力。
主要工具:
| 工具名 | 功能 | 示例 |
|---|---|---|
| browser_navigate | 导航到 URL | 打开网页 |
| browser_click | 点击元素 | 点击按钮 |
| browser_type | 输入文本 | 填写表单 |
| browser_screenshot | 截图 | 保存页面截图 |
| browser_evaluate | 执行 JS | 获取页面数据 |
| browser_go_back | 后退 | 返回上一页 |
| browser_go_forward | 前进 | 前进一页 |
实战示例:
> 帮我测试登录功能:
> 1. 打开 http://localhost:3000/login
> 2. 填写用户名"admin"、密码"123456"
> 3. 点击登录按钮
> 4. 验证是否跳转到首页
> 5. 截图保存
AI 执行过程:
// 伪代码
async function testLogin() {
// 1. 打开页面
await browser_navigate({ url: 'http://localhost:3000/login' });
// 2. 填写表单
await browser_type({
selector: 'input[name="username"]',
text: 'admin',
});
await browser_type({
selector: 'input[name="password"]',
text: '123456',
});
// 3. 点击登录
await browser_click({ selector: 'button[type="submit"]' });
// 4. 等待跳转并验证
await browser_wait_for_selector({ selector: '.dashboard' });
const url = await browser_evaluate({
script: 'window.location.href',
});
// 5. 截图
await browser_screenshot({ path: '/tmp/login_result.png' });
return { success: url.includes('/dashboard') };
}
3.5.2 GitHub MCP(GitHub 操作)
功能概述:提供 GitHub API 的封装,可以操作仓库、Issue、PR 等。
主要工具:
| 工具名 | 功能 | 示例 |
|---|---|---|
| github_create_issue | 创建 Issue | 提交 bug 报告 |
| github_create_pull_request | 创建 PR | 提交代码变更 |
| github_list_issues | 列出 Issues | 查看待办事项 |
| github_get_file_contents | 获取文件内容 | 读取代码 |
| github_create_or_update_file | 创建/更新文件 | 提交修改 |
实战示例:
> 帮我创建一个 Issue:
> 标题:修复登录页面的响应式布局问题
> 内容:在移动端 375px 宽度下,登录按钮被截断
> 标签:bug, ui
> 分配给张三
AI 执行过程:
await github_create_issue({
owner: 'mycompany',
repo: 'my-project',
title: '修复登录页面的响应式布局问题',
body: `## 问题描述
在移动端 375px 宽度下,登录按钮被截断。
## 复现步骤
1. 打开登录页面
2. 使用 Chrome DevTools 切换到 iPhone SE 视图
3. 可以看到登录按钮右侧被截断
## 期望结果
登录按钮应该完整显示
## 实际结果
登录按钮右侧被截断约 20px
## 环境
- 浏览器:Chrome 120
- 设备:iPhone SE (375x667)`,
labels: ['bug', 'ui'],
assignees: ['zhangsan'],
});
3.5.3 Filesystem MCP(文件系统)
功能概述:提供安全的文件系统访问,可以读取、写入、搜索文件。
主要工具:
| 工具名 | 功能 | 示例 |
|---|---|---|
| read_file | 读取文件 | 查看代码 |
| write_file | 写入文件 | 保存修改 |
| list_directory | 列出目录 | 浏览文件 |
| search_files | 搜索文件 | 查找代码 |
| get_file_info | 获取文件信息 | 查看元数据 |
安全配置:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@anthropic-ai/filesystem-mcp-server"],
"env": {
"ALLOWED_PATHS": "/home/user/projects:/tmp/workspace"
}
}
}
}
权限控制:
- 只能访问
ALLOWED_PATHS指定的目录 - 默认禁止访问
~/.ssh,~/.config等敏感目录 - 可以配置只读或读写权限
3.5.4 SQLite MCP(SQLite 数据库)
功能概述:提供 SQLite 数据库的查询和操作能力。
主要工具:
| 工具名 | 功能 | 示例 |
|---|---|---|
| sqlite_query | 执行查询 | SELECT * FROM users |
| sqlite_execute | 执行语句 | INSERT, UPDATE, DELETE |
| sqlite_list_tables | 列出表 | 查看数据库结构 |
| sqlite_get_schema | 获取表结构 | 查看字段定义 |
实战示例:
> 查询过去 7 天注册的用户数量,按天分组
AI 执行:
SELECT
date(created_at) as date,
COUNT(*) as user_count
FROM users
WHERE created_at >= date('now', '-7 days')
GROUP BY date(created_at)
ORDER BY date;
开发自定义 MCP
3.6.1 MCP 开发基础
技术栈选择:
| 语言 | 适用场景 | 示例 |
|---|---|---|
| TypeScript/Node.js | 大多数场景 | 官方 MCP 多为此 |
| Python | 数据科学、AI | 机器学习相关 |
| Go | 高性能服务 | 企业级部署 |
| Rust | 系统级工具 | 性能敏感场景 |
官方 SDK:
# TypeScript SDK
npm install @modelcontextprotocol/sdk
# Python SDK
pip install mcp
3.6.2 最小 MCP Server 示例
TypeScript 版本:
// server.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';
// 创建服务器
const server = new Server(
{
name: 'my-custom-mcp',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
},
);
// 定义可用工具
const tools = [
{
name: 'hello',
description: 'Say hello to someone',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Name of the person to greet',
},
},
required: ['name'],
},
},
];
// 处理工具列表请求
server.setRequestHandler(ListToolsRequestSchema, async () => {
return { tools };
});
// 处理工具调用请求
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === 'hello') {
const greeting = `Hello, ${args.name}!`;
return {
content: [
{
type: 'text',
text: greeting,
},
],
};
}
throw new Error(`Unknown tool: ${name}`);
});
// 启动服务器
const transport = new StdioServerTransport();
server.connect(transport);
console.error('Custom MCP Server running on stdio');
运行 MCP:
# 编译 TypeScript
npx tsc server.ts
# 添加到 OpenCode 配置
在 mcp.json 中添加:
{
"mcpServers": {
"my-custom": {
"command": "node",
"args": ["/path/to/server.js"]
}
}
}
3.6.3 MCP 协议详解
消息格式:
MCP 使用 JSON-RPC 2.0 协议通信。
请求消息:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "hello",
"arguments": {
"name": "World"
}
}
}
响应消息:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "Hello, World!"
}
]
}
}
错误消息:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "Invalid params",
"data": {
"details": "Missing required parameter: name"
}
}
}
3.6.4 实战:开发公司内部 API MCP
场景:公司有一套内部 API,想让 AI 能调用这些 API。
Step 1:定义 API 接口
// 公司内部 API 定义
interface InternalAPI {
// 获取用户信息
getUser(userId: string): Promise<User>;
// 创建工单
createTicket(data: CreateTicketRequest): Promise<Ticket>;
// 查询订单
listOrders(params: ListOrdersParams): Promise<Order[]>;
// 发送通知
sendNotification(userId: string, message: string): Promise<void>;
}
Step 2:实现 MCP Server
// internal-api-mcp.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';
// 公司内部 API 客户端
class InternalAPIClient {
private baseURL: string;
private token: string;
constructor(baseURL: string, token: string) {
this.baseURL = baseURL;
this.token = token;
}
async getUser(userId: string) {
const response = await fetch(`${this.baseURL}/users/${userId}`, {
headers: { Authorization: `Bearer ${this.token}` },
});
return response.json();
}
async createTicket(data: any) {
const response = await fetch(`${this.baseURL}/tickets`, {
method: 'POST',
headers: {
Authorization: `Bearer ${this.token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
return response.json();
}
// ... 其他方法
}
// 创建 MCP Server
const apiClient = new InternalAPIClient(
process.env.INTERNAL_API_URL!,
process.env.INTERNAL_API_TOKEN!,
);
const server = new Server(
{ name: 'internal-api', version: '1.0.0' },
{ capabilities: { tools: {} } },
);
// 定义工具
const tools = [
{
name: 'get_user',
description: '获取用户信息',
inputSchema: {
type: 'object',
properties: {
userId: { type: 'string', description: '用户 ID' },
},
required: ['userId'],
},
},
{
name: 'create_ticket',
description: '创建工单',
inputSchema: {
type: 'object',
properties: {
title: { type: 'string', description: '工单标题' },
description: { type: 'string', description: '工单描述' },
priority: {
type: 'string',
enum: ['low', 'medium', 'high'],
description: '优先级',
},
},
required: ['title', 'description'],
},
},
];
server.setRequestHandler(ListToolsRequestSchema, async () => {
return { tools };
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'get_user': {
const user = await apiClient.getUser(args.userId);
return {
content: [{ type: 'text', text: JSON.stringify(user, null, 2) }],
};
}
case 'create_ticket': {
const ticket = await apiClient.createTicket(args);
return {
content: [
{
type: 'text',
text: `工单创建成功!\n工单号: ${ticket.id}\n标题: ${ticket.title}`,
},
],
};
}
default:
throw new Error(`未知工具: ${name}`);
}
} catch (error) {
return {
content: [{ type: 'text', text: `错误: ${error.message}` }],
isError: true,
};
}
});
const transport = new StdioServerTransport();
server.connect(transport);
Step 3:配置到 OpenCode
{
"mcpServers": {
"internal-api": {
"command": "node",
"args": ["/path/to/internal-api-mcp.js"],
"env": {
"INTERNAL_API_URL": "https://api.company.com",
"INTERNAL_API_TOKEN": "${INTERNAL_API_TOKEN}"
}
}
}
}
Step 4:使用
> 查询用户 ID 为 12345 的信息
AI:[调用 internal-api MCP]
- get_user: { userId: "12345" }
AI:用户信息:
- 姓名:张三
- 部门:技术部
- 邮箱:zhangsan@company.com
- 入职时间:2023-06-01
> 为他创建一个工单,标题是"申请显示器",高优先级
AI:[调用 internal-api MCP]
- create_ticket: {
title: "申请显示器",
description: "申请一台 27 寸 4K 显示器",
priority: "high"
}
AI:工单创建成功!
- 工单号:TICKET-2026-001234
- 标题:申请显示器
MCP 安全实践
3.7.1 权限控制
工具级权限:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@anthropic-ai/playwright-mcp-server"],
"allowedTools": [
"browser_navigate",
"browser_click",
"browser_screenshot"
],
"deniedTools": [
"browser_evaluate", // 禁止执行任意 JavaScript
"browser_download" // 禁止下载文件
]
}
}
}
资源级权限:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["@anthropic-ai/filesystem-mcp-server"],
"env": {
"ALLOWED_PATHS": "/home/user/projects",
"DENIED_PATHS": "/home/user/projects/secret",
"ALLOW_OPERATIONS": "read,list", // 只允许读取和列出
"DENY_OPERATIONS": "write,delete" // 禁止写入和删除
}
}
}
}
3.7.2 凭证管理
环境变量:
# 不要将凭证硬编码在配置文件中
# 使用环境变量
export GITHUB_TOKEN="ghp_xxxxxxxx"
export DATABASE_PASSWORD="secret"
# 然后在 mcp.json 中引用
{
"mcpServers": {
"github": {
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
}
}
}
}
密钥管理服务:
企业环境建议使用专业的密钥管理:
{
"mcpServers": {
"database": {
"command": "node",
"args": ["db-mcp-server.js"],
"env": {
"DB_PASSWORD_CMD": "aws secretsmanager get-secret-value --secret-id db-password"
}
}
}
}
3.7.3 审计日志
启用审计:
{
"mcp": {
"audit": {
"enabled": true,
"logPath": "~/.opencode/logs/mcp-audit.log",
"logLevel": "info" // debug, info, warn, error
}
}
}
日志格式:
{
"timestamp": "2026-01-15T10:30:00Z",
"server": "playwright",
"tool": "browser_navigate",
"params": {
"url": "https://example.com"
},
"result": "success",
"duration": 1200,
"user": "developer@company.com"
}
3.7.4 网络安全
传输加密:
- 远程 MCP 服务必须使用 HTTPS
- 本地 stdio 传输无需加密(进程间通信)
网络隔离:
{
"mcpServers": {
"internal-api": {
"command": "node",
"args": ["internal-mcp.js"],
"network": {
"allowedHosts": ["api.company.com"],
"blockedHosts": ["*"],
"proxy": "http://proxy.company.com:8080"
}
}
}
}
实战案例集
3.8.1 案例一:自动化端到端测试
场景:电商网站的完整购物流程测试。
涉及 MCP:
- Playwright MCP:浏览器操作
- SQLite MCP:验证数据库状态
测试流程:
> 执行完整的购物流程测试:
> 1. 访问首页,验证页面加载
> 2. 搜索商品"iPhone",选择第一个结果
> 3. 加入购物车
> 4. 进入购物车,修改数量为 2
> 5. 结算,填写收货信息
> 6. 选择支付方式,完成支付
> 7. 验证订单在数据库中
> 8. 截图每个关键步骤
AI 执行过程:
// 步骤 1: 访问首页
await browser_navigate({ url: 'https://shop.example.com' });
await browser_wait_for_selector({ selector: '.product-list' });
await browser_screenshot({ path: '/tmp/test/step1_home.png' });
// 步骤 2: 搜索商品
await browser_type({
selector: '[data-testid="search-input"]',
text: 'iPhone',
});
await browser_click({ selector: '[data-testid="search-button"]' });
await browser_wait_for_selector({ selector: '.search-results' });
await browser_click({ selector: '.product-item:first-child' });
await browser_screenshot({ path: '/tmp/test/step2_product.png' });
// 步骤 3: 加入购物车
await browser_click({ selector: '[data-testid="add-to-cart"]' });
await browser_wait_for_selector({ selector: '.cart-notification' });
// 步骤 4: 进入购物车
await browser_click({ selector: '[data-testid="cart-icon"]' });
await browser_clear({ selector: '[data-testid="quantity-input"]' });
await browser_type({ selector: '[data-testid="quantity-input"]', text: '2' });
await browser_click({ selector: '[data-testid="update-cart"]' });
await browser_screenshot({ path: '/tmp/test/step4_cart.png' });
// 步骤 5: 结算
await browser_click({ selector: '[data-testid="checkout"]' });
await browser_type({ selector: '#name', text: '张三' });
await browser_type({ selector: '#phone', text: '13800138000' });
await browser_type({ selector: '#address', text: '北京市朝阳区xxx' });
await browser_screenshot({ path: '/tmp/test/step5_checkout.png' });
// 步骤 6: 支付
await browser_click({ selector: '[data-testid="payment-alipay"]' });
await browser_click({ selector: '[data-testid="place-order"]' });
await browser_wait_for_selector({ selector: '.order-success' });
await browser_screenshot({ path: '/tmp/test/step6_payment.png' });
// 步骤 7: 验证数据库
const orderResult = await sqlite_query({
query: `
SELECT * FROM orders
WHERE customer_name = '张三'
ORDER BY created_at DESC
LIMIT 1
`,
});
// 验证
assert(orderResult.quantity === 2);
assert(orderResult.status === 'paid');
3.8.2 案例二:自动化文档生成
场景:根据代码自动生成 API 文档。
涉及 MCP:
- Filesystem MCP:读取代码文件
- GitHub MCP:发布到 GitHub Pages
工作流程:
> 帮我生成 API 文档:
> 1. 扫描 src/api 目录下的所有接口文件
> 2. 解析 JSDoc 注释
> 3. 生成 Markdown 格式的 API 文档
> 4. 创建 GitHub Pages 分支
> 5. 部署文档
3.8.3 案例三:智能运维助手
场景:自动排查服务器问题。
涉及 MCP:
- SSH MCP:连接服务器
- Filesystem MCP:读取日志
- Slack MCP:发送通知
工作流程:
> 服务器报警了,帮我排查一下:
> 1. 连接到生产服务器
> 2. 查看最近 1 小时的错误日志
> 3. 检查 CPU、内存、磁盘使用情况
> 4. 如果发现问题,尝试自动修复
> 5. 发送报告到 Slack #ops 频道
底层原理剖析
3.9.1 MCP 协议栈
┌─────────────────────────────────┐
│ 应用层 (AI) │
│ 调用工具、处理结果 │
├─────────────────────────────────┤
│ MCP 协议层 │
│ JSON-RPC、资源定义、工具定义 │
├─────────────────────────────────┤
│ 传输层 │
│ stdio / SSE / WebSocket │
├─────────────────────────────────┤
│ 实现层 (MCP Server) │
│ Playwright / GitHub / Custom │
└─────────────────────────────────┘
3.9.2 连接管理
stdio 传输:
OpenCode (MCP Client) MCP Server
| |
|--- fork() --------------->|
| |
|<-- stdout (JSON-RPC) -----|
|--- stdin (JSON-RPC) ----->|
| |
特点:
- 适用于本地 MCP Server
- 进程间通信,安全可靠
- 自动管理进程生命周期
SSE 传输:
OpenCode (MCP Client) MCP Server (Remote)
| |
|--- HTTP GET /events ----->|
|<-- SSE stream ------------|
| |
|--- HTTP POST /invoke ---->|
|<-- JSON response ---------|
| |
特点:
- 适用于远程 MCP Server
- 支持跨网络通信
- 需要处理连接稳定性
3.9.3 工具发现与调用
发现流程:
// 1. Client 发送 tools/list 请求
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}
// 2. Server 返回可用工具列表
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "browser_navigate",
"description": "Navigate to a URL",
"inputSchema": { ... }
}
]
}
}
// 3. Client 缓存工具列表
// 4. AI 调用时,Client 发送 tools/call
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "browser_navigate",
"arguments": {
"url": "https://example.com"
}
}
}
3.9.4 错误处理机制
错误分类:
- 连接错误:无法连接到 MCP Server
- 协议错误:JSON-RPC 格式错误
- 工具错误:工具不存在或参数错误
- 执行错误:工具执行失败
错误处理策略:
// Client 端错误处理
try {
const result = await callTool(server, toolName, params);
} catch (error) {
if (error.code === -32000) {
// 服务器错误,尝试重启
await restartServer(server);
} else if (error.code === -32602) {
// 参数错误,修正参数重试
const correctedParams = fixParams(params, error.data);
return await callTool(server, toolName, correctedParams);
} else {
// 其他错误,向上抛出
throw error;
}
}
故障排查与优化
3.10.1 常见问题
问题 1:MCP Server 启动失败
症状:
Error: Failed to start MCP server 'playwright'
Error: spawn npx ENOENT
解决:
- 检查 Node.js 是否安装:
node --version - 检查 npx 是否可用:
npx --version - 安装 MCP Server:
npm install -g @anthropic-ai/playwright-mcp-server
问题 2:工具调用超时
症状:
Error: Tool call timeout after 30000ms
解决:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@anthropic-ai/playwright-mcp-server"],
"timeout": 60000 // 增加超时时间
}
}
}
问题 3:权限拒绝
症状:
Error: Access denied to /etc/passwd
解决:
- 检查
ALLOWED_PATHS配置 - 确认操作权限
- 使用 sudo(不推荐)
3.10.2 性能优化
优化 1:连接池
对于频繁使用的 MCP,保持长连接:
{
"mcpServers": {
"database": {
"command": "node",
"args": ["db-mcp.js"],
"keepAlive": true, // 保持连接
"maxIdleTime": 300000 // 5 分钟空闲超时
}
}
}
优化 2:并行调用
// 并行调用多个工具
const results = await Promise.all([
callTool(server1, 'tool1', params1),
callTool(server2, 'tool2', params2),
callTool(server3, 'tool3', params3),
]);
优化 3:缓存结果
// 缓存工具列表
const toolCache = new Map();
async function getTools(server) {
if (toolCache.has(server)) {
return toolCache.get(server);
}
const tools = await listTools(server);
toolCache.set(server, tools);
return tools;
}
3.10.3 调试技巧
启用调试日志:
# 设置环境变量
export MCP_DEBUG=true
export MCP_LOG_LEVEL=debug
# 启动 OpenCode
opencode --agent
使用 MCP Inspector:
# 安装 Inspector
npm install -g @anthropic-ai/mcp-inspector
# 测试 MCP Server
mcp-inspector --server "npx @anthropic-ai/playwright-mcp-server"
查看原始通信:
# 使用 socat 查看 stdio 通信
socat -v EXEC:"npx @anthropic-ai/playwright-mcp-server" STDIO
总结与展望
本章要点
- MCP 是什么:AI 与外部工具连接的标准协议
- 核心概念:Resources、Tools、Prompts
- 配置使用:添加 MCP、测试连接、权限控制
- 开发 MCP:使用 SDK 开发自定义 MCP Server
- 安全实践:权限管理、凭证保护、审计日志
- 实战应用:自动化测试、文档生成、运维助手
MCP 生态系统展望
短期(1-2 年):
- 更多官方 MCP(数据库、云服务、开发工具)
- MCP 市场/商店
- 标准化认证体系
中期(3-5 年):
- 企业级 MCP 管理平台
- 跨平台 MCP 互操作
- 可视化 MCP 编排
长期(5 年+):
- MCP 成为 AI 应用基础设施
- 自主 MCP 发现与组合
- AI 自主开发 MCP
下一步学习
学完 MCP 后,建议继续学习:
- 第五章:最佳实践 - 掌握综合使用技巧
文档信息
- 字数:约 8,500 字
- 适用版本:OpenCode 0.1.40+
相关资源
- MCP 官方文档:modelcontextprotocol.io
(第三章完)