构建您的第一个 MCP 服务器:开发者实践指南
Engineering Team
什么是 Model Context Protocol (MCP)?
Model Context Protocol (MCP) 是 Anthropic 创建的一项开放标准,定义了 AI 应用程序如何与外部数据源和工具进行通信。将 MCP 视为 AI 集成的 USB-C:就像 USB-C 提供了一个用于充电、数据传输和显示输出的通用连接器一样,MCP 提供了一个将 AI 模型连接到数据库、API、文件系统和任何其他服务的通用协议。MCP 消除了每个 AI 应用程序与每个工具之间自定义集成的需求。
2026 年,MCP 已成为 AI 工具通信的主导标准。Gartner 预测,到 2026 年底,40% 的企业应用程序将包含 AI 代理,而 MCP 是使这在规模上变得可行的协议。在 MCP 出现之前,将 AI 模型连接到 N 个工具需要 N 个自定义集成。对于 M 个 AI 应用程序和 N 个工具,您需要 M x N 个集成适配器。MCP 将其减少为 M + N:每个 AI 应用程序实现一个 MCP client,每个工具实现一个 MCP server。
MCP 架构:Host、Client、Server 和 Transport
在编写任何代码之前,理解 MCP 架构至关重要。该协议定义了四个关键角色:
| 组件 | 角色 | 示例 |
|---|---|---|
| Host | 终端用户交互的 AI 应用程序 | Claude Desktop、Cursor、VS Code Copilot |
| Client | Host 内的 MCP 协议处理器 | 内置于 Host 应用程序中 |
| Server | 通过 MCP 公开 tools、resources 和 prompts | 您的自定义服务器(我们将构建的) |
| Transport | Client 和 Server 之间的通信层 | stdio、HTTP+SSE、Streamable HTTP |
通信流程如下:
- Host 启动并为每个配置的服务器初始化 MCP Client。
- Client 通过 Transport 连接到 Server(本地使用 stdio,远程使用 HTTP)。
- Client 发送
initialize请求,协商协议版本和能力。 - Server 响应其可用的 tools、resources 和 prompts。
- 当 AI 模型需要外部数据或操作时,Client 调用相应的服务器方法。
- Server 执行操作并通过 JSON-RPC 2.0 消息返回结果。
Tools vs Resources vs Prompts
MCP server 可以公开三种类型的能力:
- Tools 是 AI 模型可以调用的函数。它们接受结构化输入并返回结构化输出。示例:
query_database(sql: string)或send_email(to: string, subject: string, body: string)。 - Resources 是 AI 模型可以读取的数据。它们由 URI 标识并返回内容。示例:
file:///path/to/document.md或postgres://localhost/mydb/users。 - Prompts 是服务器提供的可重用提示模板。它们帮助标准化 AI 与服务器领域的交互方式。示例:用于 Jira MCP server 的
summarize_ticket提示模板。
逐步指南:在 TypeScript 中构建 MCP Server
让我们构建一个实用的 MCP server,提供用于查询 SQLite 数据库的工具。这是一个常见用例:给 AI 模型提供对应用程序数据的安全只读访问。
步骤 1:初始化项目
mkdir mcp-sqlite-server && cd mcp-sqlite-server
npm init -y
npm install @modelcontextprotocol/sdk better-sqlite3
npm install -D typescript @types/node @types/better-sqlite3
npx tsc --init
配置 tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"declaration": true
},
"include": ["src/**/*"]
}
步骤 2:实现服务器
创建 src/index.ts:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import Database from "better-sqlite3";
import { z } from "zod";
const db = new Database(process.env.DB_PATH || "./data.db", {
readonly: true,
});
const server = new McpServer({
name: "sqlite-query",
version: "1.0.0",
});
server.tool(
"query",
"对 SQLite 数据库执行只读 SQL 查询",
{
sql: z.string().describe("要执行的 SQL SELECT 查询"),
},
async ({ sql }) => {
const normalized = sql.trim().toUpperCase();
if (!normalized.startsWith("SELECT")) {
return {
content: [{ type: "text", text: "错误:仅允许 SELECT 查询。" }],
isError: true,
};
}
try {
const rows = db.prepare(sql).all();
return {
content: [{ type: "text", text: JSON.stringify(rows, null, 2) }],
};
} catch (error) {
return {
content: [{ type: "text", text: `查询错误:${(error as Error).message}` }],
isError: true,
};
}
}
);
server.tool(
"list_tables",
"列出数据库中所有表及其架构",
{},
async () => {
const tables = db
.prepare(
`SELECT name, sql FROM sqlite_master
WHERE type='table' AND name NOT LIKE 'sqlite_%'
ORDER BY name`
)
.all();
return {
content: [{ type: "text", text: JSON.stringify(tables, null, 2) }],
};
}
);
步骤 3:构建并本地测试
npx tsc
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | node dist/index.js
您将看到包含服务器能力的 JSON-RPC 响应。
步骤 4:用 Python 构建相同的服务器
对于 Python 开发者,以下是使用官方 MCP Python SDK 的等效实现:
import sqlite3
import json
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("sqlite-query")
DB_PATH = "data.db"
@mcp.tool()
def query(sql: str) -> str:
"""对 SQLite 数据库执行只读 SQL 查询。"""
normalized = sql.strip().upper()
if not normalized.startswith("SELECT"):
raise ValueError("仅允许 SELECT 查询。")
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
try:
cursor = conn.execute(sql)
rows = [dict(row) for row in cursor.fetchall()]
return json.dumps(rows, indent=2, default=str)
finally:
conn.close()
@mcp.tool()
def list_tables() -> str:
"""列出数据库中所有表及其架构。"""
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
try:
cursor = conn.execute(
"SELECT name, sql FROM sqlite_master "
"WHERE type='table' AND name NOT LIKE 'sqlite_%'"
)
tables = [dict(row) for row in cursor.fetchall()]
return json.dumps(tables, indent=2)
finally:
conn.close()
if __name__ == "__main__":
mcp.run(transport="stdio")
连接到 Claude Desktop
Claude Desktop 原生支持 MCP server。要连接您的服务器,请编辑 Claude Desktop 配置文件:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"sqlite-query": {
"command": "node",
"args": ["/absolute/path/to/dist/index.js"],
"env": {
"DB_PATH": "/absolute/path/to/your/data.db"
}
}
}
}
重启 Claude Desktop。您将在聊天界面中看到一个锤子图标,表示可用的 MCP 工具。现在您可以向 Claude 提问,例如“数据库中有哪些表?“或“按注册日期显示前 10 个用户”,Claude 将使用您的 MCP server 直接查询数据库。
连接到 Cursor
Cursor 也支持 MCP server。在项目根目录的 .cursor/mcp.json 中添加配置:
{
"mcpServers": {
"sqlite-query": {
"command": "node",
"args": ["./dist/index.js"],
"env": {
"DB_PATH": "./data.db"
}
}
}
}
保存后重启 Cursor。MCP 工具将出现在 AI 助手面板中,可在代码生成和调试会话期间调用。
测试和调试您的 MCP Server
使用 MCP Inspector
MCP Inspector 是官方调试工具。它提供了与服务器交互的 Web UI:
npx @modelcontextprotocol/inspector node dist/index.js
这将在 http://localhost:5173 打开浏览器界面,您可以在其中:
- 查看所有已注册的 tools、resources 和 prompts
- 使用自定义输入调用工具并检查响应
- 实时监控 JSON-RPC 消息流
- 通过发送格式错误的请求测试错误处理
常见调试问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| Server 未出现在 Claude Desktop 中 | 配置路径或 JSON 语法错误 | 验证 JSON,检查绝对路径 |
| “Tool not found” 错误 | Server 在连接前未注册 tools | 在调用 server.connect() 之前注册 tools |
| 工具调用超时 | 长时间运行的操作无进度 | 通过 server.sendProgress() 添加进度通知 |
| stderr 输出破坏协议 | Console.log 写入 stdout(stdio transport) | 使用 stdio 时用 console.error() 进行日志记录 |
| 空闲后连接断开 | Transport 超时 | 实现心跳或使用 HTTP transport |
生产环境最佳实践
-
输入验证:始终验证和清理工具输入。使用 Zod schema(TypeScript)或 Pydantic 模型(Python)进行严格的类型检查。
-
默认只读:从只读访问开始。仅在明确需要时添加写入功能,并始终要求确认破坏性操作。
-
错误处理:返回带有
isError: true的结构化错误消息。永远不要暴露内部堆栈跟踪或数据库连接字符串。 -
日志记录:记录所有工具调用的时间戳、输入和执行持续时间。使用 stdio transport 时使用 stderr 进行日志记录(而非 stdout)。
-
速率限制:实现按工具的速率限制,防止失控的 AI 循环使后端服务不堪重负。
-
超时设置:为所有工具处理程序设置执行超时。AI 模型可能会调用触发昂贵查询的工具——保护您的基础设施。
-
环境隔离:使用环境变量进行所有配置。永远不要硬编码数据库 URL、API 密钥或文件路径。
-
版本管理:遵循 MCP server 的语义版本控制。
initialize握手包含版本协商——破坏性更改需要主版本号升级。
常见问题
问:MCP 和 function calling 有什么区别? 答:Function calling(OpenAI、Anthropic 等使用)在每个 API 请求中内联定义工具。MCP 将工具定义外部化到独立服务器中,任何 MCP 兼容的 host 都可以发现和使用。Function calling 是按请求的;MCP 是具有有状态会话的持久协议。
问:我可以在 Claude 以外的模型中使用 MCP 吗? 答:可以。MCP 是开放协议。截至 2026 年初,OpenAI、Google DeepMind 和 Microsoft 已在其平台中采用 MCP 支持。任何实现 MCP client 的 AI 应用程序都可以连接到任何 MCP server。
问:MCP 仅用于本地工具吗? 答:不是。虽然 stdio transport 设计用于本地服务器,但 HTTP+SSE 和 Streamable HTTP transport 支持远程 MCP server。您可以将 MCP server 部署为可通过网络访问的云服务。
问:MCP 如何处理身份验证? 答:该协议支持远程服务器的 OAuth 2.0。本地 stdio 服务器继承 host 进程的安全上下文。对于企业部署,MCP gateway 可以集中身份验证和授权。
问:我可以用什么语言构建 MCP server? 答:官方 SDK 支持 TypeScript、Python、Java、Kotlin、C# 和 Swift。社区 SDK 涵盖 Rust、Go、Ruby 和 PHP。该协议与语言无关——任何可以通过 stdio 或 HTTP 读写 JSON-RPC 的语言都可以实现 MCP server。
问:如何在不重启 host 的情况下更新 MCP server?
答:MCP 支持能力变更通知。当服务器的工具发生变化时,它可以发送 notifications/tools/list_changed 消息,提示 client 重新获取工具列表。对于 stdio 服务器,host 通常需要重启。HTTP 服务器可以在不重启 host 的情况下更新。