MCP 入门指南:让 AI 连接真实世界

MCP 入门指南:让 AI 连接真实世界

本文将带你从零开始理解 MCP(Model Context Protocol),并通过 TypeScript 和 Python 两种语言实现一个完整的 MCP Server。

大语言模型很强大,但它们被困在"沙盒"里------无法读取你的本地文件、无法查询你的数据库、无法调用你的内部 API。每个 AI 应用都在重复造轮子:写 Function Calling 的适配代码、处理工具调用的序列化、管理上下文注入...

MCP(Model Context Protocol) 就是为了解决这个问题而生的。它是 Anthropic 在 2024 年底开源的协议标准,目标是成为 AI 模型与外部世界交互的"USB 接口"------一次实现,处处可用。

什么是 MCP?

MCP 是一个开放协议,定义了 AI 应用(如 Claude Desktop、IDE 插件)与外部工具/数据源之间的通信标准。

核心价值:

传统方式 MCP 方式
每个 AI 应用自己实现工具调用 统一协议,一次实现多处复用
工具代码与应用紧耦合 Server 独立部署,即插即用
安全边界模糊 明确的权限模型和沙盒隔离

简单来说:你写一个 MCP Server,就能让所有支持 MCP 的 AI 应用使用你的工具。

核心概念

MCP 架构包含三个核心角色:

复制代码
┌─────────────────────────────────────────────────────────┐
│                      Host                               │
│  (Claude Desktop / IDE / 自定义 AI 应用)                 │
│                                                         │
│   ┌─────────┐   ┌─────────┐   ┌─────────┐              │
│   │ Client  │   │ Client  │   │ Client  │              │
│   └────┬────┘   └────┬────┘   └────┬────┘              │
└────────┼─────────────┼─────────────┼────────────────────┘
         │             │             │
    ┌────▼────┐   ┌────▼────┐   ┌────▼────┐
    │ Server  │   │ Server  │   │ Server  │
    │ (文件)   │   │ (数据库) │   │ (API)   │
    └─────────┘   └─────────┘   └─────────┘
  • Host:运行 AI 模型的宿主应用,负责管理多个 Client 连接
  • Client:由 Host 创建,与 Server 保持 1:1 连接,处理协议通信
  • Server:独立进程,暴露工具(Tools)、资源(Resources)和提示词(Prompts)

通信基于 JSON-RPC 2.0,支持两种传输方式:

  • stdio:通过标准输入/输出通信,适合本地进程
  • HTTP + SSE:适合远程服务,支持服务端推送

MCP 能做什么

MCP Server 可以向 AI 暴露三种能力:

Tools(工具)

让 AI 执行操作。工具由 AI 模型主动调用,类似 Function Calling。

json 复制代码
{
  "name": "search_files",
  "description": "在指定目录中搜索文件",
  "inputSchema": {
    "type": "object",
    "properties": {
      "path": { "type": "string" },
      "pattern": { "type": "string" }
    }
  }
}

典型场景:执行 shell 命令、查询数据库、调用 API、发送消息

Resources(资源)

让 AI 读取数据。资源是只读的,由应用程序控制何时加载。

json 复制代码
{
  "uri": "file:///project/config.json",
  "name": "项目配置",
  "mimeType": "application/json"
}

典型场景:读取文件内容、获取数据库 schema、加载配置信息

Prompts(提示词模板)

预定义的提示词,可带参数,方便复用。

json 复制代码
{
  "name": "code_review",
  "description": "代码审查模板",
  "arguments": [
    { "name": "language", "required": true }
  ]
}

典型场景:代码审查模板、SQL 生成模板、文档生成模板

快速上手

环境准备

TypeScript 版本:

bash 复制代码
# 需要 Node.js 18+
node --version

# 创建项目
mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node tsx

# 初始化 TypeScript
npx tsc --init

Python 版本:

bash 复制代码
# 需要 Python 3.10+
python --version

# 创建项目
mkdir my-mcp-server && cd my-mcp-server
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

# 安装依赖
pip install mcp

TypeScript 实现

创建 src/index.ts,实现一个简单的天气查询工具:

typescript 复制代码
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// 创建 MCP Server 实例
const server = new McpServer({
  name: "weather-server",
  version: "1.0.0",
});

// 模拟天气数据
const weatherData: Record<string, { temp: number; condition: string }> = {
  beijing: { temp: 22, condition: "晴" },
  shanghai: { temp: 25, condition: "多云" },
  shenzhen: { temp: 28, condition: "阵雨" },
};

// 注册工具
server.tool(
  "get_weather",
  "获取指定城市的天气信息",
  {
    city: z.string().describe("城市名称(拼音)"),
  },
  async ({ city }) => {
    const weather = weatherData[city.toLowerCase()];

    if (!weather) {
      return {
        content: [
          {
            type: "text",
            text: `未找到城市 "${city}" 的天气数据`,
          },
        ],
      };
    }

    return {
      content: [
        {
          type: "text",
          text: `${city} 天气:${weather.condition},温度 ${weather.temp}°C`,
        },
      ],
    };
  }
);

// 启动服务器
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Weather MCP Server 已启动");
}

main().catch(console.error);

配置 package.json

json 复制代码
{
  "name": "weather-mcp-server",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "tsx src/index.ts"
  }
}

Python 实现

创建 server.py,实现相同的天气查询工具:

python 复制代码
from mcp.server.fastmcp import FastMCP

# 创建 MCP Server 实例
mcp = FastMCP("weather-server")

# 模拟天气数据
weather_data = {
    "beijing": {"temp": 22, "condition": "晴"},
    "shanghai": {"temp": 25, "condition": "多云"},
    "shenzhen": {"temp": 28, "condition": "阵雨"},
}


@mcp.tool()
def get_weather(city: str) -> str:
    """获取指定城市的天气信息

    Args:
        city: 城市名称(拼音)
    """
    weather = weather_data.get(city.lower())

    if not weather:
        return f'未找到城市 "{city}" 的天气数据'

    return f"{city} 天气:{weather['condition']},温度 {weather['temp']}°C"


# 也可以添加资源
@mcp.resource("weather://cities")
def list_cities() -> str:
    """返回支持的城市列表"""
    return "\n".join(weather_data.keys())


if __name__ == "__main__":
    mcp.run()

运行方式:

bash 复制代码
# 直接运行(stdio 模式)
python server.py

# 或使用 mcp 命令行工具测试
mcp dev server.py

在 Claude Desktop 中测试

编辑 Claude Desktop 配置文件:

  • macOS : ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows : %APPDATA%\Claude\claude_desktop_config.json

TypeScript 版本配置:

json 复制代码
{
  "mcpServers": {
    "weather": {
      "command": "node",
      "args": ["/absolute/path/to/dist/index.js"]
    }
  }
}

Python 版本配置:

json 复制代码
{
  "mcpServers": {
    "weather": {
      "command": "python",
      "args": ["/absolute/path/to/server.py"]
    }
  }
}

重启 Claude Desktop 后,在对话中输入:

"北京今天天气怎么样?"

Claude 会自动调用 get_weather 工具并返回结果。

实战案例:文件搜索工具

下面实现一个更实用的案例------文件搜索工具,支持在指定目录中按名称或内容搜索文件。

TypeScript 版本

typescript 复制代码
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import * as fs from "fs/promises";
import * as path from "path";

const server = new McpServer({
  name: "file-search-server",
  version: "1.0.0",
});

// 递归搜索文件
async function searchFiles(
  dir: string,
  pattern: string,
  results: string[] = []
): Promise<string[]> {
  const entries = await fs.readdir(dir, { withFileTypes: true });

  for (const entry of entries) {
    const fullPath = path.join(dir, entry.name);

    if (entry.isDirectory()) {
      // 跳过 node_modules 和隐藏目录
      if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
        await searchFiles(fullPath, pattern, results);
      }
    } else if (entry.name.toLowerCase().includes(pattern.toLowerCase())) {
      results.push(fullPath);
    }
  }

  return results;
}

// 搜索文件名
server.tool(
  "search_by_name",
  "按文件名搜索文件",
  {
    directory: z.string().describe("搜索的根目录"),
    pattern: z.string().describe("文件名匹配模式"),
  },
  async ({ directory, pattern }) => {
    try {
      const results = await searchFiles(directory, pattern);
      return {
        content: [
          {
            type: "text",
            text:
              results.length > 0
                ? `找到 ${results.length} 个文件:\n${results.join("\n")}`
                : "未找到匹配的文件",
          },
        ],
      };
    } catch (error) {
      return {
        content: [{ type: "text", text: `搜索失败: ${error}` }],
        isError: true,
      };
    }
  }
);

// 搜索文件内容
server.tool(
  "search_by_content",
  "按内容搜索文件",
  {
    directory: z.string().describe("搜索的根目录"),
    keyword: z.string().describe("要搜索的关键词"),
    extension: z.string().optional().describe("文件扩展名过滤,如 .ts"),
  },
  async ({ directory, keyword, extension }) => {
    const results: { file: string; line: number; content: string }[] = [];

    async function search(dir: string) {
      const entries = await fs.readdir(dir, { withFileTypes: true });

      for (const entry of entries) {
        const fullPath = path.join(dir, entry.name);

        if (entry.isDirectory()) {
          if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
            await search(fullPath);
          }
        } else if (!extension || entry.name.endsWith(extension)) {
          try {
            const content = await fs.readFile(fullPath, "utf-8");
            const lines = content.split("\n");

            lines.forEach((line, index) => {
              if (line.includes(keyword)) {
                results.push({
                  file: fullPath,
                  line: index + 1,
                  content: line.trim().slice(0, 100),
                });
              }
            });
          } catch {
            // 跳过无法读取的文件
          }
        }
      }
    }

    await search(directory);

    return {
      content: [
        {
          type: "text",
          text:
            results.length > 0
              ? results
                  .slice(0, 20)
                  .map((r) => `${r.file}:${r.line}\n  ${r.content}`)
                  .join("\n\n")
              : "未找到包含该关键词的文件",
        },
      ],
    };
  }
);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main().catch(console.error);

Python 版本

python 复制代码
import os
from pathlib import Path
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("file-search-server")


@mcp.tool()
def search_by_name(directory: str, pattern: str) -> str:
    """按文件名搜索文件

    Args:
        directory: 搜索的根目录
        pattern: 文件名匹配模式
    """
    results = []
    root = Path(directory)

    if not root.exists():
        return f"目录不存在: {directory}"

    for path in root.rglob("*"):
        # 跳过隐藏目录和 node_modules
        if any(part.startswith(".") or part == "node_modules"
               for part in path.parts):
            continue

        if path.is_file() and pattern.lower() in path.name.lower():
            results.append(str(path))

    if results:
        return f"找到 {len(results)} 个文件:\n" + "\n".join(results[:50])
    return "未找到匹配的文件"


@mcp.tool()
def search_by_content(
    directory: str,
    keyword: str,
    extension: str | None = None
) -> str:
    """按内容搜索文件

    Args:
        directory: 搜索的根目录
        keyword: 要搜索的关键词
        extension: 文件扩展名过滤,如 .py
    """
    results = []
    root = Path(directory)

    if not root.exists():
        return f"目录不存在: {directory}"

    pattern = f"*{extension}" if extension else "*"

    for path in root.rglob(pattern):
        if any(part.startswith(".") or part == "node_modules"
               for part in path.parts):
            continue

        if not path.is_file():
            continue

        try:
            content = path.read_text(encoding="utf-8")
            for i, line in enumerate(content.split("\n"), 1):
                if keyword in line:
                    results.append(f"{path}:{i}\n  {line.strip()[:100]}")
        except (UnicodeDecodeError, PermissionError):
            continue

    if results:
        return "\n\n".join(results[:20])
    return "未找到包含该关键词的文件"


if __name__ == "__main__":
    mcp.run()

最佳实践

安全注意事项

typescript 复制代码
// 1. 路径验证:防止路径遍历攻击
import * as path from "path";

function validatePath(userPath: string, allowedRoot: string): string {
  const resolved = path.resolve(allowedRoot, userPath);
  if (!resolved.startsWith(allowedRoot)) {
    throw new Error("路径越界");
  }
  return resolved;
}

// 2. 输入验证:使用 zod 定义严格的 schema
const schema = {
  command: z.enum(["read", "list"]),  // 限制允许的命令
  path: z.string().max(500),           // 限制长度
};

// 3. 权限最小化:只暴露必要的工具

错误处理

typescript 复制代码
server.tool("safe_operation", "安全操作示例", {}, async () => {
  try {
    const result = await riskyOperation();
    return {
      content: [{ type: "text", text: result }],
    };
  } catch (error) {
    // 返回友好的错误信息,避免泄露内部细节
    return {
      content: [
        {
          type: "text",
          text: `操作失败: ${error instanceof Error ? error.message : "未知错误"}`,
        },
      ],
      isError: true,  // 标记为错误响应
    };
  }
});

调试技巧

bash 复制代码
# 1. 使用 MCP Inspector 调试(推荐)
npx @modelcontextprotocol/inspector node dist/index.js

# 2. 日志输出到 stderr(stdout 用于协议通信)
console.error("Debug:", someValue);

# 3. Python 使用 mcp dev 命令
mcp dev server.py

性能优化

  • 对于耗时操作,返回进度信息
  • 大量数据分页返回,避免一次性返回过多内容
  • 缓存频繁访问的资源

总结与资源

本文要点

  1. MCP 是什么:AI 与外部工具交互的标准化协议
  2. 核心架构:Host → Client → Server 的分层设计
  3. 三大能力:Tools(执行)、Resources(读取)、Prompts(模板)
  4. 快速开发 :TypeScript 用 @modelcontextprotocol/sdk,Python 用 FastMCP

下一步

  • 尝试编写自己的 MCP Server
  • 探索社区已有的 Server 实现
  • 在实际项目中集成 MCP 能力

资源链接

资源 链接
MCP 官方文档 https://modelcontextprotocol.io
TypeScript SDK https://github.com/modelcontextprotocol/typescript-sdk
Python SDK https://github.com/modelcontextprotocol/python-sdk
官方 Server 示例 https://github.com/modelcontextprotocol/servers
MCP Inspector https://github.com/modelcontextprotocol/inspector

社区优秀 Server 推荐

  • filesystem: 文件系统操作
  • sqlite: SQLite 数据库查询
  • github: GitHub API 集成
  • slack: Slack 消息发送
  • puppeteer: 浏览器自动化

如有问题或建议,欢迎在评论区交流!

相关推荐
大猪宝宝学AI3 分钟前
【AI Infra】SonicMoE论文笔记
论文阅读·人工智能·性能优化
sww_102610 分钟前
Spring-AI和LangChain4j区别
java·人工智能·spring
Coovally AI模型快速验证18 分钟前
超越Sora的开源思路:如何用预训练组件高效训练你的视频扩散模型?(附训练代码)
人工智能·算法·yolo·计算机视觉·音视频·无人机
GitCode官方30 分钟前
参会预告 | AtomGit 邀您共赴 TritonNext 2026 技术大会,解锁 AI 系统与编译生态新机遇
人工智能·开源·atomgit
MobiusStack38 分钟前
Cursor团队最新文章解读丨动态上下文发现,重新定义AI记忆
人工智能
Rui_Freely1 小时前
Vins-Fusion之 相机—IMU在线标定(十一)
人工智能·算法·计算机视觉
沛沛老爹1 小时前
Web开发者5分钟上手:Agent Skills环境搭建与基础使用实战
java·人工智能·llm·llama·rag·agent skills
DeepFlow 零侵扰全栈可观测1 小时前
3分钟定位OA系统GC瓶颈:DeepFlow全栈可观测平台实战解析
大数据·运维·人工智能·云原生·性能优化
想用offer打牌1 小时前
一站式讲清Spring AI Alibaba的OverAllState和RunnableConfig
人工智能·架构·github
生成论实验室1 小时前
生成论之基:“阴阳”作为元规则的重构与证成——基于《易经》与《道德经》的古典重诠与现代显象
人工智能·科技·神经网络·算法·架构