引言:为什么MCP协议值得关注?

2024年底,Anthropic推出了Model Context Protocol(MCP),这是一个开放标准,旨在解决大语言模型与外部工具、数据源之间的连接问题。如果说Function Calling是"给AI一双巧手",那么MCP就是"给AI一套标准化的工具箱接口"。

在MCP出现之前,每接入一个新工具,都需要单独编写适配代码,维护成本极高。MCP的核心理念很简单:一次开发,到处使用——开发者只需按照MCP规范实现一个工具服务器,任何支持MCP的AI客户端都能直接调用。

本文将带你从零开始,5步打造一个完整的MCP工具服务器,让你真正理解MCP的工作原理。

一、MCP协议架构解析

MCP采用经典的客户端-服务器(Client-Server)架构,核心组件如下:

  • MCP Host(宿主):发起连接的应用程序,如Claude Desktop、Cursor等AI客户端
  • MCP Client(客户端):在Host内部运行,与Server保持1:1连接
  • MCP Server(服务器):提供工具、资源、提示词等能力的轻量级服务
  • 传输层:支持stdio(标准输入输出)和SSE(Server-Sent Events)两种通信方式

MCP定义了四大核心能力原语:

原语 说明 示例
Tools 可被LLM调用的函数 查询天气、执行代码
Resources 可被LLM读取的数据 文件内容、数据库记录
Prompts 预定义的提示模板 代码审查模板
Sampling Server向Host请求LLM推理 多步骤任务中的中间推理

理解了架构,我们开始动手实践!

二、第1步:环境准备与项目初始化

MCP Server的官方SDK支持TypeScript和Python,本文以Python为例(更易上手)。

首先安装MCP Python SDK:

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

# 创建虚拟环境
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

# 安装MCP SDK
pip install mcp

项目结构如下:

mcp-weather-server/
├── venv/
├── weather_server.py    # 主服务器文件
├── requirements.txt     # 依赖列表
└── README.md

requirements.txt中添加:

mcp>=1.0.0
httpx>=0.27.0

就这么简单,不需要复杂的框架和依赖。

三、第2步:编写MCP Server核心代码

接下来编写核心的服务器代码。我们将创建一个天气查询工具服务器,提供两个工具:

  • get_current_weather:查询指定城市的当前天气
  • get_weather_forecast:获取未来3天的天气预报
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import httpx
import json

# 创建Server实例
app = Server("weather-server")

# 免费天气API(使用wttr.in,无需API Key)
WEATHER_API = "https://wttr.in"

@app.list_tools()
async def list_tools() -> list[Tool]:
    """注册可用工具列表"""
    return [
        Tool(
            name="get_current_weather",
            description="获取指定城市的当前天气信息,支持中文城市名",
            inputSchema={
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名称,如'北京'、'Shanghai'"
                    }
                },
                "required": ["city"]
            }
        ),
        Tool(
            name="get_weather_forecast",
            description="获取指定城市未来3天的天气预报",
            inputSchema={
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名称"
                    }
                },
                "required": ["city"]
            }
        )
    ]

关键点解析:

五、第4步:启动服务器与配置客户端

添加入口函数,启动stdio传输:

async def main():
    """启动MCP服务器"""
    async with stdio_server() as (read_stream, write_stream):
        await app.run(
            read_stream,
            write_stream,
            app.create_initialization_options()
        )

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

启动方式很简单:

python weather_server.py

但通常我们不需要手动启动,而是在AI客户端中配置。以Claude Desktop为例,编辑配置文件:

{
  "mcpServers": {
    "weather": {
      "command": "python",
      "args": ["C:/path/to/weather_server.py"],
      "env": {}
    }
  }
}

配置文件位置:

重启Claude Desktop后,在对话中你就可以直接问"北京今天天气怎么样",AI会自动调用你的MCP工具服务器获取实时天气数据。

  • @app.list_tools()装饰器注册工具列表,当客户端连接时自动获取
  • inputSchema使用JSON Schema定义参数,LLM据此生成正确的调用参数
  • description非常重要——LLM靠它判断何时调用这个工具,写得越精准越好

    四、第3步:实现工具执行逻辑

    工具列表注册好了,接下来实现实际的执行逻辑:

    @app.call_tool()
    async def call_tool(name: str, arguments: dict) -> list[TextContent]:
        """处理工具调用请求"""
        city = arguments.get("city", "Beijing")
        
        if name == "get_current_weather":
            result = await fetch_weather(city, forecast=False)
        elif name == "get_weather_forecast":
            result = await fetch_weather(city, forecast=True)
        else:
            result = f"未知工具: {name}"
        
        return [TextContent(type="text", text=result)]
    
    
    async def fetch_weather(city: str, forecast: bool = False) -> str:
        """调用天气API获取数据"""
        params = {"format": "j1"}  # JSON格式
        url = f"{WEATHER_API}/{city}"
        
        try:
            async with httpx.AsyncClient(timeout=10) as client:
                resp = await client.get(url, params=params)
                resp.raise_for_status()
                data = resp.json()
            
            if not forecast:
                # 当前天气
                current = data["current_condition"][0]
                return (
                    f"🏙️ {city} 当前天气\n"
                    f"🌡️ 温度: {current['temp_C']}°C "
                    f"(体感 {current['FeelsLikeC']}°C)\n"
                    f"💧 湿度: {current['humidity']}%\n"
                    f"🌬️ 风速: {current['windspeedKmph']} km/h\n"
                    f"☁️ 天气: {current['weatherDesc'][0]['value']}"
                )
            else:
                # 3天预报
                lines = [f"📅 {city} 未来3天天气预报"]
                for day in data["weather"][:3]:
                    lines.append(
                        f"  {day['date']}: "
                        f"{day['mintempC']}°C ~ {day['maxtempC']}°C, "
                        f"{day['hourly'][4]['weatherDesc'][0]['value']}"
                    )
                return "\n".join(lines)
                
        except httpx.HTTPError as e:
            return f"❌ 获取天气失败: {str(e)}"

    注意事项:

  • @app.call_tool()接收工具名和参数,返回TextContent列表
  • 返回内容尽量结构化且人类可读,因为LLM会基于这些内容回答用户
  • 做好异常处理,网络请求随时可能失败
  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

六、第5步:调试技巧与最佳实践

开发MCP Server时,调试是个痛点——因为它通过stdio通信,不能直接print。以下是几个实用技巧:

技巧1:使用MCP Inspector调试

npx @modelcontextprotocol/inspector python weather_server.py

这会启动一个Web界面,让你可视化地测试每个工具、查看请求响应。

技巧2:日志写入文件

import logging

logging.basicConfig(
    filename="/tmp/mcp-weather.log",
    level=logging.DEBUG,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)

# 在关键位置添加日志
@app.call_tool()
async def call_tool(name, arguments):
    logging.info(f"Tool called: {name}, args: {arguments}")
    # ...

技巧3:错误码规范

MCP定义了标准错误码,建议遵循:

from mcp.types import McpError

# 参数错误
raise McpError(
    code=-32602,  # Invalid params
    message=f"Missing required parameter: city"
)

# 内部错误
raise McpError(
    code=-32603,  # Internal error
    message=f"Weather API unavailable: {str(e)}"
)

最佳实践总结:

description要精确——这是LLM理解工具的关键,模糊的描述会导致误调用 参数校验要严格——在call_tool中验证必填参数,避免LLM幻觉导致的异常 返回要结构化——使用换行、emoji、编号让返回内容清晰可读 超时要合理——外部API调用务必设置timeout,避免Server卡死 单一职责——一个Server专注于一个领域,不要把所有工具塞进一个服务

总结:MCP正在重塑AI工具生态

通过这5步,我们完成了一个完整的MCP工具服务器开发。回顾核心流程:

MCP的意义远不止于技术层面。它正在建立AI工具的"USB标准"——就像USB协议统一了设备连接一样,MCP统一了AI与外部世界的交互方式。截至2026年初,已有超过2000个MCP Server发布在各类平台上,覆盖数据库、文件系统、API集成、开发工具等领域。

未来,每个开发者都可能成为"MCP工具提供者",为AI生态贡献可被任何LLM调用的能力。这是一个值得投入的方向。

本文代码已开源,欢迎Star ⭐

如果你觉得有帮助,欢迎点赞收藏,有问题欢迎评论区交流!

  • 安装SDK,初始化项目
  • 使用@app.list_tools()注册工具元信息
  • 使用@app.call_tool()实现执行逻辑
  • 配置客户端,自动发现和调用
  • 善用Inspector和日志进行调试
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐