Skip to content

MCP

MCP 是一种开放式协议,它规范了应用程序向 LLM 提供上下文的方式。把 MCP 想象成人工智能应用的 USB-C 端口。就像 USB-C 提供了将设备连接到各种外设和配件的标准化方式一样,MCP 也提供了将人工智能模型连接到不同数据源和工具的标准化方式。

MCP 本质上是大模型和工具的一种交互方式,需要开发者遵守 MCP 接口来开发给 LLM 或者 Agent 的工具,这个我们叫做 MCP Server

过程应该是:

MCP Client(本地的IDE或者Claude Desktop) -> MCP 协议 -> MCP Server -> 完成任务

MCP Server 去执行对应的功能,作为后端开发者需要了解如何为客户端提供对应的接口和处理响应

信息格式

参考官网的文档: Transports - Model Context Protocol

MCP 使用 JSON-RPC 2.0 作为传输格式。传输层负责将 MCP 协议信息转换成 JSON-RPC 格式进行传输,并将收到的 JSON-RPC 信息转换回 MCP 协议信息

Requests(Client 到 Server)

json
{
  jsonrpc: "2.0",
  id: number | string,
  method: string,
  params?: object
}

Response(Server 到 Client)

json
{
  jsonrpc: "2.0",
  id: number | string,
  result?: object,
  error?: {
    code: number,
    message: string,
    data?: unknown
  }
}

Notifications(通知,一种单向通信)

json
{
  jsonrpc: "2.0",
  method: string,
  params?: object
}

内置传输类型

标准输入/输出(stdio)

  • 构建命令行工具
  • 实施本地集成
  • 需要简单的流程沟通
  • 使用 shell 脚本

服务器发送事件(SSE)

  • 只需要服务器到客户端的流式传输
  • 使用受限网络
  • 实施简单的更新

服务端代码构建

官网是构建了一个用于可用于 LLM 获取天气能力的 MCP:For Server Developers - Model Context Protocol

首先使用 uv 作为 Python 的包管理器,来配置环境:

bash
# Create a new directory for our project
uv init weather
cd weather

# Create virtual environment and activate it
uv venv
source .venv/bin/activate

# Install dependencies
uv add "mcp[cli]" httpx

# Create our server file
touch weather.py

国内的网大概率下不动,得配置一下镜像源,我这里用清华的:

bash
uv add "mcp[cli]" httpx --index-url https://pypi.tuna.tsinghua.edu.cn/simple

首先初始化一个实例和定义一些常量:

python
from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP

# Initialize FastMCP server
mcp = FastMCP("weather")

# Constants
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"

之后来请求这个 API,即 make_nws_request(),这用了一个异步的写法,等待 client.get() 的请求

下面的一个普通函数就是来解析结果的

python
async def make_nws_request(url: str) -> dict[str, Any] | None:
    """Make a request to the NWS API with proper error handling."""
    headers = {
        "User-Agent": USER_AGENT,
        "Accept": "application/geo+json"
    }
    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(url, headers=headers, timeout=30.0)
            response.raise_for_status()
            return response.json()
        except Exception:
            return None

def format_alert(feature: dict) -> str:
    """Format an alert feature into a readable string."""
    props = feature["properties"]
    return f"""
Event: {props.get('event', 'Unknown')}
Area: {props.get('areaDesc', 'Unknown')}
Severity: {props.get('severity', 'Unknown')}
Description: {props.get('description', 'No description available')}
Instructions: {props.get('instruction', 'No specific instructions provided')}
"""

之后需要执行工具,这个就是 MCP Client 实际可调用的部分

python
@mcp.tool()
async def get_alerts(state: str) -> str:
    """Get weather alerts for a US state.

    Args:
        state: Two-letter US state code (e.g. CA, NY)
    """
    url = f"{NWS_API_BASE}/alerts/active/area/{state}"
    data = await make_nws_request(url)

    if not data or "features" not in data:
        return "Unable to fetch alerts or no alerts found."

    if not data["features"]:
        return "No active alerts for this state."

    alerts = [format_alert(feature) for feature in data["features"]]
    return "\n---\n".join(alerts)

@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get weather forecast for a location.

    Args:
        latitude: Latitude of the location
        longitude: Longitude of the location
    """
    # First get the forecast grid endpoint
    points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
    points_data = await make_nws_request(points_url)

    if not points_data:
        return "Unable to fetch forecast data for this location."

    # Get the forecast URL from the points response
    forecast_url = points_data["properties"]["forecast"]
    forecast_data = await make_nws_request(forecast_url)

    if not forecast_data:
        return "Unable to fetch detailed forecast."

    # Format the periods into a readable forecast
    periods = forecast_data["properties"]["periods"]
    forecasts = []
    for period in periods[:5]:  # Only show next 5 periods
        forecast = f"""
{period['name']}:
Temperature: {period['temperature']}°{period['temperatureUnit']}
Wind: {period['windSpeed']} {period['windDirection']}
Forecast: {period['detailedForecast']}
"""
        forecasts.append(forecast)

    return "\n---\n".join(forecasts)

主要逻辑就是拼接字符串,MCP Client 主要是看修饰器 @mcp.tool() 和注释来知道工具是如何调用的,注释就是客户端的提示信息:

python
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get weather forecast for a location.

    Args:
        latitude: Latitude of the location
        longitude: Longitude of the location
    """

MCP 客户端会将这段 docstring 作为模型的提示信息(prompt),告诉模型:

  • 函数的用途(Get weather forecast for a location)
  • 参数的含义(latitude 和 longitude 是经纬度)

之后传入对应的参数,进行运行,最后响应的结果是这样的:

json
{
  "params": {
    "latitude": 38.5816,
    "longitude": -121.4944
  },
  "response": {
    "content": [
      {
        "type": "text",
        "text": "\nToday:\nTemperature: 92°F\nWind: 2 to 8 mph WNW\nForecast: Sunny, with a high near 92. West northwest wind 2 to 8 mph.\n\n---\n\nTonight:\nTemperature: 57°F\nWind: 5 to 8 mph SSW\nForecast: Clear, with a low around 57. South southwest wind 5 to 8 mph.\n\n---\n\nThursday:\nTemperature: 86°F\nWind: 5 to 13 mph S\nForecast: Sunny. High near 86, with temperatures falling to around 83 in the afternoon. South wind 5 to 13 mph, with gusts as high as 23 mph.\n\n---\n\nThursday Night:\nTemperature: 51°F\nWind: 6 to 13 mph SSW\nForecast: Clear, with a low around 51. South southwest wind 6 to 13 mph, with gusts as high as 23 mph.\n\n---\n\nFriday:\nTemperature: 81°F\nWind: 7 mph SSW\nForecast: Sunny, with a high near 81. South southwest wind around 7 mph.\n"
      }
    ],
    "isError": false
  }
}

然后 LLM 或者 Agent 对其进行总结,整理出其中的结果,并且以自然语言的方式输出给用户

实际上手

可以使用 JSON 文件在客户端对服务端进行定义:

json
{
    "mcpServers": {
        "weather": {
            "command": "uv",
            "args": [
                "--directory",
                "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather",
                "run",
                "weather.py"
            ]
        }
    }
}

也可以在 Cherry Studio 这样的客户端里面,进行设置,但大体都相同:

命令如果没有在 Cherry Studio 里面配置,可能需要使用 which uv 来查看一下,一些和密钥相关的最好使用环境变量传入进去,避免暴露

还根据这个改了一个用于天气查询的版本:

最后更新于:

Released under the MIT License.