MCP模型上下文协议开发完全教程

教程简介

本教程全面讲解MCP模型上下文协议的核心架构与开发实战,涵盖MCP协议概述、JSON-RPC 2.0通信机制、Resources/Tools/Prompts三大核心能力、Python/TypeScript Server开发、Client集成、常见Server案例、安全最佳实践等核心内容,帮助开发者掌握大模型工具集成的标准协议。

MCP(Model Context Protocol)模型上下文协议开发完全教程

适用读者:有基础编程经验的开发者,希望深入理解并实践 MCP 协议的 AI 应用开发者、工具链构建者。

预计阅读时间:30-40 分钟


目录

  1. MCP 协议概述与发展背景
  2. MCP 核心架构:Host / Client / Server
  3. MCP 协议通信机制:JSON-RPC 2.0
  4. 三大核心能力:Resources、Tools、Prompts
  5. MCP Server 开发实战
  6. MCP Client 集成开发
  7. 常见 MCP Server 案例
  8. MCP 与 Function Calling 的区别和联系
  9. 安全考虑与最佳实践
  10. MCP 生态系统与未来展望

1. MCP 协议概述与发展背景

1.1 什么是 MCP

MCP(Model Context Protocol,模型上下文协议)是由 Anthropic 于 2024 年底发布的一项开放协议标准,旨在为大语言模型(LLM)与外部数据源、工具之间建立统一的连接方式。

你可以将 MCP 理解为 AI 应用的 USB-C 接口——正如 USB-C 让各种外设可以用同一种线缆连接到不同设备,MCP 让各种 AI 模型可以用同一种协议接入不同的工具和数据源。

1.2 为什么需要 MCP

在 MCP 出现之前,AI 应用开发面临一个核心痛点:集成碎片化

假设你正在构建一个 AI 助手,需要让它能够:

  • 读取本地文件系统
  • 查询数据库
  • 调用第三方 API(如 GitHub、Slack)
  • 访问内部知识库

每一种集成都需要编写定制化的代码,处理不同的认证方式、数据格式和错误逻辑。如果你要支持多个 LLM 提供商(OpenAI、Anthropic、Google),每家的 Function Calling 接口还不完全兼容,工作量会成倍增长。

MCP 的核心价值在于:

痛点 MCP 解决方案
每个数据源都需要定制集成 统一协议,一次开发,多处复用
不同 LLM 的工具调用接口不兼容 标准化的工具描述和调用规范
上下文传递缺乏规范 定义了 Resources 和 Prompts 的标准格式
安全模型不一致 内置权限控制和安全边界

1.3 MCP 的发展脉络

  • 2024 年 11 月:Anthropic 开源发布 MCP 规范和 Python SDK
  • 2024 年 12 月:TypeScript SDK 发布,社区开始构建 MCP Server 生态
  • 2025 年初:OpenAI、Google 等厂商开始支持或兼容 MCP
  • 2025 年中:MCP 注册中心(Registry)和标准化工具链逐步完善

2. MCP 核心架构:Host / Client / Server

MCP 采用经典的三层架构,角色清晰,职责分明:

┌─────────────────────────────────────────────┐
│                   Host                       │
│  (Claude Desktop, IDE, 自定义应用)            │
│                                              │
│  ┌──────────────┐    ┌──────────────┐        │
│  │  MCP Client A │    │  MCP Client B │       │
│  └──────┬───────┘    └──────┬───────┘        │
│         │                    │                │
└─────────┼────────────────────┼────────────────┘
          │                    │
    ┌─────▼─────┐        ┌────▼──────┐
    │ MCP Server │        │ MCP Server │
    │  (文件系统)  │        │  (数据库)   │
    └───────────┘        └───────────┘

2.1 Host(宿主层)

Host 是用户的交互入口,负责:

  • 管理一个或多个 MCP Client 实例
  • 处理用户界面和交互逻辑
  • 控制权限和安全策略
  • 协调 LLM 与工具之间的对话流

典型的 Host 包括:Claude Desktop App、Cursor IDE、自建的 AI 应用等。

2.2 Client(客户端层)

Client 是 Host 与 Server 之间的桥梁,每个 Client 与一个 Server 保持一对一的连接。Client 的职责包括:

  • 维护与 Server 的会话状态
  • 处理协议握手和能力协商
  • 将 Server 暴露的能力(Resources、Tools、Prompts)注册到 Host
  • 转发请求和响应

2.3 Server(服务端层)

Server 是能力的实际提供者,专注于:

  • 暴露具体的工具(Tools)、资源(Resources)和提示模板(Prompts)
  • 处理来自 Client 的请求
  • 管理与底层数据源或服务的连接

Server 的设计原则是轻量、专注、可组合——每个 Server 通常只负责一个领域(如文件操作、数据库查询、API 调用)。


3. MCP 协议通信机制:JSON-RPC 2.0

MCP 基于 JSON-RPC 2.0 协议进行通信,这是一个成熟、轻量的远程过程调用规范。

3.1 三种消息类型

① Request(请求)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "read_file",
    "arguments": {
      "path": "/home/user/document.txt"
    }
  }
}

② Response(响应)

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "文件内容..."
      }
    ]
  }
}

③ Notification(通知,无 id,无需响应)

{
  "jsonrpc": "2.0",
  "method": "notifications/resources/updated",
  "params": {
    "uri": "file:///home/user/document.txt"
  }
}

3.2 传输层

MCP 支持两种标准传输方式:

① stdio(标准输入/输出)

适用于本地进程间通信,Client 启动 Server 作为子进程,通过 stdin/stdout 交换 JSON-RPC 消息。

Client ──stdin──► Server (子进程)
Client ◄──stdout── Server

这是最常用的传输方式,适合本地工具集成。

② HTTP + SSE(Server-Sent Events)

适用于远程通信,Client 通过 HTTP POST 发送请求,Server 通过 SSE 推送响应和通知。

Client ──HTTP POST──► Server (远程)
Client ◄──SSE stream── Server

适合需要网络访问的场景,如团队共享的 MCP Server。

3.3 生命周期

MCP 连接的完整生命周期如下:

Client                          Server
  │                               │
  │──── initialize ──────────────►│  能力协商
  │◄─── initialize result ────────│
  │──── initialized (通知) ───────►│  确认连接
  │                               │
  │     ═══ 正常通信阶段 ═══       │
  │                               │
  │──── tools/list ──────────────►│  发现能力
  │──── tools/call ──────────────►│  调用工具
  │◄─── resources/list ───────────│  获取资源
  │                               │
  │──── shutdown ────────────────►│  优雅关闭
  │◄─── exit ─────────────────────│

4. 三大核心能力:Resources、Tools、Prompts

4.1 Resources(资源)

Resources 是 Server 向 Client 暴露的只读数据,类似于 REST API 中的 GET 端点。

用途:为 LLM 提供上下文信息,如文件内容、数据库记录、API 响应等。

资源标识:使用 URI 格式,如:

  • file:///path/to/document.txt
  • postgres://database/customers
  • github://repo/owner/repo/issues

资源定义示例

{
  "uri": "file:///home/user/config.json",
  "name": "应用配置文件",
  "description": "当前应用的 JSON 配置",
  "mimeType": "application/json"
}

资源模板(支持动态参数):

{
  "uriTemplate": "file:///home/user/{path}",
  "name": "文件资源",
  "description": "读取指定路径的文件内容"
}

4.2 Tools(工具)

Tools 是 Server 暴露的可执行操作,LLM 可以决定何时调用它们。这是 MCP 最常用的能力。

与 Resources 的区别

特性 Resources Tools
语义 只读数据 可执行动作
触发方 Client 主动获取 LLM 决定调用
副作用 可能有(写入、发送等)
类比 REST GET REST POST/PUT/DELETE

工具定义示例

{
  "name": "search_files",
  "description": "在指定目录中搜索匹配关键词的文件",
  "inputSchema": {
    "type": "object",
    "properties": {
      "directory": {
        "type": "string",
        "description": "要搜索的目录路径"
      },
      "keyword": {
        "type": "string",
        "description": "搜索关键词"
      },
      "max_results": {
        "type": "integer",
        "default": 10,
        "description": "最大返回结果数"
      }
    },
    "required": ["directory", "keyword"]
  }
}

4.3 Prompts(提示模板)

Prompts 是 Server 提供的可复用提示模板,帮助用户或 LLM 构建更有效的交互。

用途:封装复杂的提示工程,让用户一键调用专业的工作流。

提示模板定义示例

{
  "name": "code_review",
  "description": "对代码片段进行全面审查",
  "arguments": [
    {
      "name": "language",
      "description": "编程语言",
      "required": true
    },
    {
      "name": "code",
      "description": "待审查的代码",
      "required": true
    }
  ]
}

当用户选择此 Prompt 时,Server 会返回一组结构化的消息:

{
  "messages": [
    {
      "role": "user",
      "content": {
        "type": "text",
        "text": "请对以下 Python 代码进行全面审查,关注性能、安全性和可维护性:\n\n```python\n...\n```"
      }
    }
  ]
}

5. MCP Server 开发实战

5.1 使用 Python 开发 MCP Server

环境准备

Python MCP SDK 要求 Python 3.10+,推荐使用 uv 作为包管理器。

# 创建项目目录
mkdir my-mcp-server && cd my-mcp-server

# 初始化项目
uv init --python 3.12
uv add "mcp[cli]"

最小化 Server 示例

创建文件 server.py

"""
一个简单的 MCP Server 示例:
提供数学计算工具和系统信息资源。
"""

from mcp.server.fastmcp import FastMCP

# 创建 MCP Server 实例
mcp = FastMCP(
    name="math-and-sysinfo",
    version="1.0.0"
)


# ==================== Tools ====================

@mcp.tool()
def calculate(expression: str) -> str:
    """
    计算数学表达式。
    
    支持基本运算:+, -, *, /, **, %
    示例: "2 + 3 * 4", "2 ** 10"
    
    Args:
        expression: 数学表达式字符串
    Returns:
        计算结果
    """
    # 安全检查:只允许数学运算
    allowed_chars = set("0123456789+-*/().% ")
    if not all(c in allowed_chars for c in expression):
        return f"错误:表达式包含不允许的字符"
    
    try:
        result = eval(expression)  # 仅用于教学,生产环境应使用安全的表达式解析器
        return f"结果:{expression} = {result}"
    except Exception as e:
        return f"计算错误:{str(e)}"


@mcp.tool()
def unit_convert(value: float, from_unit: str, to_unit: str) -> str:
    """
    常用单位转换。
    
    支持:
    - 温度:celsius, fahrenheit, kelvin
    - 长度:km, miles, meters, feet
    - 重量:kg, pounds, grams
    
    Args:
        value: 数值
        from_unit: 源单位
        to_unit: 目标单位
    Returns:
        转换结果
    """
    conversions = {
        # 温度需要特殊处理
        ("celsius", "fahrenheit"): lambda v: v * 9/5 + 32,
        ("fahrenheit", "celsius"): lambda v: (v - 32) * 5/9,
        ("celsius", "kelvin"): lambda v: v + 273.15,
        ("kelvin", "celsius"): lambda v: v - 273.15,
        # 长度
        ("km", "miles"): lambda v: v * 0.621371,
        ("miles", "km"): lambda v: v * 1.60934,
        ("meters", "feet"): lambda v: v * 3.28084,
        ("feet", "meters"): lambda v: v * 0.3048,
        # 重量
        ("kg", "pounds"): lambda v: v * 2.20462,
        ("pounds", "kg"): lambda v: v * 0.453592,
        ("kg", "grams"): lambda v: v * 1000,
        ("grams", "kg"): lambda v: v / 1000,
    }
    
    key = (from_unit.lower(), to_unit.lower())
    if key not in conversions:
        return f"不支持的转换:{from_unit} -> {to_unit}"
    
    result = conversions[key](value)
    return f"{value} {from_unit} = {result:.4f} {to_unit}"


# ==================== Resources ====================

@mcp.resource("info://server/status")
def server_status() -> str:
    """返回当前服务器的运行状态信息。"""
    import platform
    import datetime
    
    return f"""服务器状态:
- 名称:math-and-sysinfo
- 版本:1.0.0
- Python:{platform.python_version()}
- 系统:{platform.system()} {platform.release()}
- 当前时间:{datetime.datetime.now().isoformat()}
"""


# ==================== Prompts ====================

@mcp.prompt()
def math_tutor(topic: str) -> str:
    """
    数学辅导提示模板。
    
    Args:
        topic: 数学主题(如"微积分"、"线性代数")
    """
    return f"""你是一位耐心的数学老师。请用通俗易懂的方式讲解"{topic}"这个主题。

要求:
1. 先给出直观的解释
2. 然后展示 1-2 个简单的例子
3. 最后提供一道练习题让用户思考

请开始讲解:"""


# 启动 Server
if __name__ == "__main__":
    mcp.run(transport="stdio")

运行与测试

# 直接运行
uv run server.py

# 使用 MCP Inspector 进行可视化测试
uv run mcp dev server.py

MCP Inspector 会在浏览器中打开一个交互界面,你可以:

  • 查看 Server 暴露的所有 Tools、Resources、Prompts
  • 手动调用工具并查看返回结果
  • 测试资源读取
  • 调试协议消息

5.2 使用 TypeScript 开发 MCP Server

环境准备

mkdir my-mcp-server-ts && cd my-mcp-server-ts
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
npx tsc --init

TypeScript Server 示例

创建 src/index.ts

/**
 * MCP Server 示例(TypeScript):
 * 提供笔记管理工具和配置资源。
 */

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

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

// 内存中的笔记存储
const notes: Map<string, { title: string; content: string; created: string }> =
  new Map();

// ==================== Tools ====================

server.tool(
  "create_note",
  "创建一条新笔记",
  {
    title: z.string().describe("笔记标题"),
    content: z.string().describe("笔记内容"),
  },
  async ({ title, content }) => {
    const id = `note-${Date.now()}`;
    const created = new Date().toISOString();

    notes.set(id, { title, content, created });

    return {
      content: [
        {
          type: "text" as const,
          text: `笔记创建成功!\nID: ${id}\n标题: ${title}\n创建时间: ${created}`,
        },
      ],
    };
  }
);

server.tool(
  "search_notes",
  "根据关键词搜索笔记",
  {
    keyword: z.string().describe("搜索关键词"),
  },
  async ({ keyword }) => {
    const results: string[] = [];

    notes.forEach((note, id) => {
      if (
        note.title.includes(keyword) ||
        note.content.includes(keyword)
      ) {
        results.push(
          `[${id}] ${note.title}\n  ${note.content.substring(0, 100)}...`
        );
      }
    });

    if (results.length === 0) {
      return { content: [{ type: "text" as const, text: "未找到匹配的笔记。" }] };
    }

    return {
      content: [
        {
          type: "text" as const,
          text: `找到 ${results.length} 条匹配笔记:\n\n${results.join("\n\n")}`,
        },
      ],
    };
  }
);

// ==================== Resources ====================

server.resource(
  "notes-stats",
  "notes://stats",
  async (uri) => ({
    contents: [
      {
        uri: uri.href,
        text: JSON.stringify(
          {
            totalNotes: notes.size,
            lastUpdated: new Date().toISOString(),
          },
          null,
          2
        ),
      },
    ],
  })
);

// ==================== 启动 ====================

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Note Manager MCP Server 已启动");
}

main().catch(console.error);

编译并运行:

npx tsc
node dist/index.js

6. MCP Client 集成开发

6.1 Python Client 示例

"""
MCP Client 示例:连接 Server,发现并调用工具。
"""

import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client


async def main():
    # 配置 Server 连接参数
    server_params = StdioServerParameters(
        command="uv",          # 启动命令
        args=["run", "server.py"],  # 启动参数
        env=None               # 环境变量
    )
    
    # 建立连接
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # 初始化连接
            await session.initialize()
            
            # ========== 发现能力 ==========
            
            # 列出所有工具
            tools = await session.list_tools()
            print("可用工具:")
            for tool in tools.tools:
                print(f"  - {tool.name}: {tool.description}")
            
            # 列出所有资源
            resources = await session.list_resources()
            print("\n可用资源:")
            for resource in resources.resources:
                print(f"  - {resource.uri}: {resource.name}")
            
            # ========== 调用工具 ==========
            
            # 调用数学计算工具
            result = await session.call_tool(
                "calculate",
                arguments={"expression": "2 ** 10 + 1"}
            )
            print(f"\n计算结果:{result.content[0].text}")
            
            # 调用单位转换工具
            result = await session.call_tool(
                "unit_convert",
                arguments={
                    "value": 100,
                    "from_unit": "celsius",
                    "to_unit": "fahrenheit"
                }
            )
            print(f"转换结果:{result.content[0].text}")
            
            # ========== 读取资源 ==========
            
            resource_content = await session.read_resource("info://server/status")
            print(f"\n服务器状态:\n{resource_content.contents[0].text}")


if __name__ == "__main__":
    asyncio.run(main())

6.2 在 LLM 应用中集成 MCP Client

将 MCP Client 集成到实际的 LLM 应用中,核心流程是:

"""
将 MCP 工具集成到 LLM 对话中的完整示例。
使用 OpenAI API 格式演示(兼容大多数 LLM 提供商)。
"""

import asyncio
import json
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client


async def run_agent():
    """运行一个简单的 MCP Agent。"""
    
    server_params = StdioServerParameters(
        command="uv",
        args=["run", "server.py"]
    )
    
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            
            # 1. 获取所有可用工具,转换为 LLM 的 function calling 格式
            tools_response = await session.list_tools()
            available_tools = []
            
            for tool in tools_response.tools:
                available_tools.append({
                    "type": "function",
                    "function": {
                        "name": tool.name,
                        "description": tool.description,
                        "parameters": tool.inputSchema
                    }
                })
            
            print(f"已注册 {len(available_tools)} 个工具")
            
            # 2. 模拟 LLM 对话循环
            user_query = "帮我算一下 15 的阶乘是多少"
            print(f"\n用户:{user_query}")
            
            # 3. 调用 LLM(此处为伪代码,需替换为实际 API 调用)
            # llm_response = call_llm(
            #     messages=[{"role": "user", "content": user_query}],
            #     tools=available_tools
            # )
            
            # 4. 如果 LLM 决定调用工具
            # 假设 LLM 返回了工具调用请求
            tool_call = {
                "name": "calculate",
                "arguments": {"expression": "1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15"}
            }
            
            # 5. 通过 MCP 执行工具调用
            result = await session.call_tool(
                tool_call["name"],
                arguments=tool_call["arguments"]
            )
            
            print(f"工具结果:{result.content[0].text}")
            
            # 6. 将结果返回给 LLM 生成最终回答
            # final_response = call_llm(
            #     messages=[
            #         {"role": "user", "content": user_query},
            #         {"role": "assistant", "tool_calls": [...]},
            #         {"role": "tool", "content": result.content[0].text}
            #     ]
            # )


if __name__ == "__main__":
    asyncio.run(run_agent())

7. 常见 MCP Server 案例

7.1 文件系统 Server

文件系统 Server 是最基础也是最常用的 MCP Server,提供文件读写、目录浏览、文件搜索等功能。

"""文件系统 MCP Server 核心实现。"""

import os
import glob
from pathlib import Path
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("filesystem")

# 安全根目录:限制可访问的范围
ALLOWED_ROOT = Path("/home/user/workspace")


def safe_path(requested: str) -> Path:
    """确保路径在允许的范围内。"""
    resolved = (ALLOWED_ROOT / requested).resolve()
    if not str(resolved).startswith(str(ALLOWED_ROOT.resolve())):
        raise ValueError(f"访问被拒绝:路径超出允许范围")
    return resolved


@mcp.tool()
def read_file(path: str) -> str:
    """读取文件内容。"""
    target = safe_path(path)
    if not target.exists():
        return f"错误:文件不存在 - {path}"
    if not target.is_file():
        return f"错误:不是文件 - {path}"
    return target.read_text(encoding="utf-8")


@mcp.tool()
def write_file(path: str, content: str) -> str:
    """创建或覆盖文件。"""
    target = safe_path(path)
    target.parent.mkdir(parents=True, exist_ok=True)
    target.write_text(content, encoding="utf-8")
    return f"文件已写入:{path}({len(content)} 字符)"


@mcp.tool()
def list_directory(path: str = ".") -> str:
    """列出目录内容。"""
    target = safe_path(path)
    if not target.is_dir():
        return f"错误:不是目录 - {path}"
    
    entries = []
    for item in sorted(target.iterdir()):
        rel = item.relative_to(ALLOWED_ROOT)
        if item.is_dir():
            entries.append(f"📁 {rel}/")
        else:
            size = item.stat().st_size
            entries.append(f"📄 {rel} ({size} bytes)")
    
    return "\n".join(entries) if entries else "(空目录)"


@mcp.tool()
def search_files(directory: str, pattern: str) -> str:
    """使用通配符模式搜索文件。"""
    target = safe_path(directory)
    matches = glob.glob(str(target / "**" / pattern), recursive=True)
    
    if not matches:
        return f"未找到匹配 '{pattern}' 的文件"
    
    results = [str(Path(m).relative_to(ALLOWED_ROOT)) for m in matches[:20]]
    return f"找到 {len(matches)} 个文件:\n" + "\n".join(results)

7.2 数据库 Server

"""SQLite 数据库 MCP Server 核心实现。"""

import sqlite3
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("database")

DB_PATH = "data/app.db"


def get_connection():
    """获取数据库连接。"""
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row
    return conn


@mcp.tool()
def query_database(sql: str) -> str:
    """
    执行 SQL 查询(仅 SELECT)。
    
    Args:
        sql: SQL 查询语句
    """
    # 安全检查:只允许 SELECT 查询
    sql_stripped = sql.strip().upper()
    if not sql_stripped.startswith("SELECT"):
        return "错误:只允许 SELECT 查询"
    
    try:
        conn = get_connection()
        cursor = conn.execute(sql)
        rows = cursor.fetchall()
        conn.close()
        
        if not rows:
            return "查询结果为空"
        
        # 格式化输出
        columns = rows[0].keys()
        header = " | ".join(columns)
        separator = "-" * len(header)
        
        lines = [header, separator]
        for row in rows[:50]:  # 限制返回行数
            lines.append(" | ".join(str(row[col]) for col in columns))
        
        if len(rows) > 50:
            lines.append(f"\n... 共 {len(rows)} 条记录,仅显示前 50 条")
        
        return "\n".join(lines)
    
    except Exception as e:
        return f"查询错误:{str(e)}"


@mcp.tool()
def list_tables() -> str:
    """列出数据库中的所有表。"""
    conn = get_connection()
    cursor = conn.execute(
        "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
    )
    tables = [row["name"] for row in cursor.fetchall()]
    conn.close()
    
    if not tables:
        return "数据库中没有表"
    
    return "数据库表:\n" + "\n".join(f"  - {t}" for t in tables)


@mcp.tool()
def describe_table(table_name: str) -> str:
    """查看表结构。"""
    conn = get_connection()
    cursor = conn.execute(f"PRAGMA table_info({table_name})")
    columns = cursor.fetchall()
    conn.close()
    
    if not columns:
        return f"表 '{table_name}' 不存在或没有列"
    
    lines = [f"表 '{table_name}' 结构:"]
    for col in columns:
        nullable = "NOT NULL" if col["notnull"] else "NULL"
        pk = " [PK]" if col["pk"] else ""
        lines.append(f"  {col['name']}: {col['type']} {nullable}{pk}")
    
    return "\n".join(lines)

7.3 外部 API Server

"""天气 API MCP Server 核心实现。"""

import json
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather-api")


@mcp.tool()
async def get_weather(city: str) -> str:
    """
    获取指定城市的当前天气。
    
    Args:
        city: 城市名称(中文或英文)
    """
    # 此处使用 wttr.in 免费 API 作为示例
    # 实际生产中应使用正规天气 API
    import httpx
    
    try:
        async with httpx.AsyncClient() as client:
            response = await client.get(
                f"https://wttr.in/{city}?format=j1",
                timeout=10.0
            )
            data = response.json()
            
            current = data["current_condition"][0]
            result = {
                "城市": city,
                "温度": f"{current['temp_C']}°C",
                "体感温度": f"{current['FeelsLikeC']}°C",
                "天气": current["weatherDesc"][0]["value"],
                "湿度": f"{current['humidity']}%",
                "风速": f"{current['windspeedKmph']} km/h",
                "风向": current["winddir16Point"]
            }
            
            return json.dumps(result, ensure_ascii=False, indent=2)
    
    except Exception as e:
        return f"获取天气信息失败:{str(e)}"


@mcp.tool()
async def get_forecast(city: str, days: int = 3) -> str:
    """
    获取未来几天的天气预报。
    
    Args:
        city: 城市名称
        days: 预报天数(1-3)
    """
    import httpx
    
    try:
        async with httpx.AsyncClient() as client:
            response = await client.get(
                f"https://wttr.in/{city}?format=j1",
                timeout=10.0
            )
            data = response.json()
            
            forecasts = []
            for day in data["weather"][:days]:
                forecasts.append({
                    "日期": day["date"],
                    "最高温度": f"{day['maxtempC']}°C",
                    "最低温度": f"{day['mintempC']}°C",
                    "平均温度": f"{day['avgtempC']}°C",
                    "日出": day["astronomy"][0]["sunrise"],
                    "日落": day["astronomy"][0]["sunset"]
                })
            
            return json.dumps(forecasts, ensure_ascii=False, indent=2)
    
    except Exception as e:
        return f"获取天气预报失败:{str(e)}"

8. MCP 与 Function Calling 的区别和联系

8.1 理解 Function Calling

Function Calling 是 LLM 提供商(如 OpenAI、Anthropic)提供的能力,让模型能够在对话中决定调用预定义的函数。

// OpenAI Function Calling 示例
{
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "获取天气信息",
        "parameters": {
          "type": "object",
          "properties": {
            "city": { "type": "string" }
          }
        }
      }
    }
  ]
}

8.2 核心区别

维度 Function Calling MCP
定位 LLM 的能力特性 应用层协议标准
范围 仅定义工具调用 定义完整的上下文交互(工具+资源+提示)
绑定 与特定 LLM 提供商绑定 与 LLM 无关,跨模型通用
连接 无持久连接概念 支持持久会话和实时通知
发现 工具列表在请求中硬编码 支持动态发现(tools/list)
传输 HTTP 请求/响应 stdio / HTTP+SSE
生态 各家私有格式 统一的开放标准

8.3 它们如何协作

MCP 不是 Function Calling 的替代品,而是补充和扩展。在实际应用中,它们通常协同工作:

用户请求
    │
    ▼
┌─────────┐     ┌──────────────┐     ┌──────────────┐
│  LLM    │────►│ MCP Client   │────►│ MCP Server   │
│         │     │              │     │              │
│ Function│◄────│ 将 MCP 工具  │◄────│ 返回执行结果 │
│ Calling │     │ 转换为 FC    │     │              │
└─────────┘     └──────────────┘     └──────────────┘

工作流程:

  1. MCP Client 向 Server 发现可用工具(tools/list
  2. Client 将 MCP 工具定义转换为 LLM 的 Function Calling 格式
  3. LLM 根据用户请求决定调用哪个函数
  4. Client 将函数调用转发给 MCP Server 执行
  5. Server 执行后返回结果
  6. Client 将结果反馈给 LLM 生成最终回答

9. 安全考虑与最佳实践

9.1 权限控制

原则:最小权限

# ❌ 不好的做法:暴露危险操作
@mcp.tool()
def run_command(cmd: str) -> str:
    """执行任意系统命令。"""
    import subprocess
    return subprocess.run(cmd, shell=True, capture_output=True, text=True).stdout

# ✅ 好的做法:限制操作范围
@mcp.tool()
def list_processes() -> str:
    """列出当前用户的进程。"""
    import subprocess
    return subprocess.run(
        ["ps", "-u", os.getenv("USER")],
        capture_output=True, text=True
    ).stdout

9.2 输入验证

@mcp.tool()
def query_database(sql: str) -> str:
    """安全的数据库查询。"""
    # 1. 类型检查
    if not isinstance(sql, str):
        return "错误:SQL 必须是字符串"
    
    # 2. 长度限制
    if len(sql) > 10000:
        return "错误:SQL 查询过长"
    
    # 3. 语句类型限制
    sql_upper = sql.strip().upper()
    if not sql_upper.startswith("SELECT"):
        return "错误:仅允许 SELECT 查询"
    
    # 4. 危险关键词过滤
    dangerous = ["DROP", "DELETE", "UPDATE", "INSERT", "ALTER", "EXEC"]
    for keyword in dangerous:
        if keyword in sql_upper:
            return f"错误:包含不允许的关键字 {keyword}"
    
    # 5. 执行查询...

9.3 路径遍历防护

def safe_resolve(base_dir: str, user_path: str) -> str:
    """
    安全地解析用户提供的路径,防止路径遍历攻击。
    """
    import os
    
    # 移除多余的分隔符和 .. 
    normalized = os.path.normpath(user_path)
    
    # 拼接基础目录
    full_path = os.path.join(base_dir, normalized)
    
    # 解析为绝对路径
    resolved = os.path.realpath(full_path)
    
    # 验证是否在允许范围内
    if not resolved.startswith(os.path.realpath(base_dir)):
        raise ValueError(f"路径越界:{user_path}")
    
    return resolved

9.4 最佳实践清单

类别 实践 说明
认证 实施 API Key 或 OAuth 防止未授权访问
授权 基于角色的权限控制 不同用户不同权限
输入 验证所有输入参数 类型、长度、格式检查
输出 过滤敏感信息 不暴露内部路径、密钥等
网络 限制可访问的网络范围 白名单优于黑名单
日志 记录所有工具调用 便于审计和问题排查
超时 设置合理的超时时间 防止资源耗尽
速率 实施速率限制 防止滥用
错误 不暴露内部错误细节 返回用户友好的错误信息

10. MCP 生态系统与未来展望

10.1 当前生态

MCP 生态正在快速发展,主要组成部分包括:

官方资源

  • MCP 规范:https://spec.modelcontextprotocol.io
  • Python SDK:mcp
  • TypeScript SDK:@modelcontextprotocol/sdk
  • MCP Inspector:调试和测试工具

社区 MCP Server(部分列举):

  • 文件系统(filesystem)
  • GitHub 集成
  • PostgreSQL / SQLite 数据库
  • Slack 消息
  • Google Drive
  • Brave Search
  • Memory(知识图谱记忆)
  • Puppeteer(浏览器自动化)

10.2 MCP 注册中心

MCP 正在建立标准化的注册中心(Registry),让开发者可以:

  • 发现和搜索可用的 MCP Server
  • 查看 Server 的能力描述和版本信息
  • 一键安装和配置 Server

10.3 未来发展方向

  1. 标准化推进:更多 LLM 原生支持 MCP,减少适配层
  2. 能力扩展:支持双向通信、流式响应、长时任务
  3. 安全增强:标准化的认证框架(OAuth 2.0 集成)
  4. 性能优化:批处理请求、压缩传输、连接池
  5. 开发工具:更好的调试工具、测试框架、文档生成器
  6. 企业级特性:审计日志、合规控制、多租户支持

10.4 学习资源

  • 官方文档:https://modelcontextprotocol.io
  • GitHub 仓库:https://github.com/modelcontextprotocol
  • MCP Server 仓库:https://github.com/modelcontextprotocol/servers
  • 社区 Discord:MCP 开发者社区

总结

MCP 协议为 AI 应用开发提供了一个标准化的工具集成框架。通过本文的学习,你应该能够:

  1. ✅ 理解 MCP 的架构设计和核心概念
  2. ✅ 使用 Python 或 TypeScript 开发自定义 MCP Server
  3. ✅ 集成 MCP Client 到你的 AI 应用中
  4. ✅ 构建文件系统、数据库、API 等常见 Server
  5. ✅ 理解 MCP 与 Function Calling 的关系
  6. ✅ 遵循安全最佳实践

MCP 仍处于快速发展阶段,建议持续关注官方规范更新和社区动态。掌握 MCP 开发能力,将帮助你构建更强大、更灵活的 AI 应用。


下一步行动

  1. 安装 MCP SDK,运行本文的示例代码
  2. 使用 MCP Inspector 调试你的第一个 Server
  3. 尝试为你常用的工具(如笔记、任务管理)编写 MCP Server
  4. 将 MCP 集成到你的 AI 应用或 Agent 框架中

内容声明

本文内容为AI技术学习教程,仅供学习参考。如涉及技术问题,欢迎通过 xurj005@163.com 与我们交流。

目录