跳到主要内容
工程Mar 28, 2026

构建您的第一个 MCP 服务器:开发者实践指南

OS
Open Soft Team

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
ClientHost 内的 MCP 协议处理器内置于 Host 应用程序中
Server通过 MCP 公开 tools、resources 和 prompts您的自定义服务器(我们将构建的)
TransportClient 和 Server 之间的通信层stdio、HTTP+SSE、Streamable HTTP

通信流程如下:

  1. Host 启动并为每个配置的服务器初始化 MCP Client
  2. Client 通过 Transport 连接到 Server(本地使用 stdio,远程使用 HTTP)。
  3. Client 发送 initialize 请求,协商协议版本和能力。
  4. Server 响应其可用的 toolsresourcesprompts
  5. 当 AI 模型需要外部数据或操作时,Client 调用相应的服务器方法。
  6. 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.mdpostgres://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

生产环境最佳实践

  1. 输入验证:始终验证和清理工具输入。使用 Zod schema(TypeScript)或 Pydantic 模型(Python)进行严格的类型检查。

  2. 默认只读:从只读访问开始。仅在明确需要时添加写入功能,并始终要求确认破坏性操作。

  3. 错误处理:返回带有 isError: true 的结构化错误消息。永远不要暴露内部堆栈跟踪或数据库连接字符串。

  4. 日志记录:记录所有工具调用的时间戳、输入和执行持续时间。使用 stdio transport 时使用 stderr 进行日志记录(而非 stdout)。

  5. 速率限制:实现按工具的速率限制,防止失控的 AI 循环使后端服务不堪重负。

  6. 超时设置:为所有工具处理程序设置执行超时。AI 模型可能会调用触发昂贵查询的工具——保护您的基础设施。

  7. 环境隔离:使用环境变量进行所有配置。永远不要硬编码数据库 URL、API 密钥或文件路径。

  8. 版本管理:遵循 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 的情况下更新。