WheatField
WheatField

AI 应用的通用接口:解析模型上下文协议 (MCP)

3534 words18 min read

大型语言模型 (LLM) 的应用日益广泛,但其与外部动态世界交互的能力仍是关键瓶颈。这主要是因为 LLM 本身缺乏直接与外部动态世界交互的内置机制。因此,它们的知识内容停留在训练数据收集完成的时间点,并且难以直接访问实时更新的信息、查询私有数据源或与外部系统进行动态操作。 这种固有的设计局限性限制了 LLM 在需要即时信息或与特定环境紧密集成的复杂场景中的应用潜力。

针对这一问题,Anthropic 提出并开源了 Model Context Protocol (MCP),该开放协议旨在提供一个通用的、标准化的解决方案。它被喻为 AI 应用领域的“USB-C”接口,致力于简化 LLM 与外部数据源、工具及系统的集成。本文将浅谈一下 MCP 的核心概念、设计目标、关键组件,比较其与现有技术的差异,并提供一个具体的 MCP 服务器实现指南,以帮助开发者理解并应用该协议。

什么是 MCP

MCP 旨在解决 LLM 与外部世界隔离的问题,使模型能够实时获取最新信息并执行外部操作,而不再仅仅依赖于训练数据,其核心目标之一在于将复杂的 M×N 集成问题简化为 M+N 的问题。

M×N 的集成问题指:如果存在 M 个不同的 AI 应用(e.g., chatbot、代码助手)和 N 个不同的外部工具或数据系统(e.g., GitHub API、企业数据库),在没有统一标准的情况下,理论上可能需要开发 M×N 个独立的、定制化的集成方案才能实现它们之间的互通。

MCP 的设计目标正是提供标准化接口,以大幅降低这种集成的复杂性,允许应用和外部工具通过统一接口进行交互,从而实现解耦并方便维护。

MCP 的三个核心组件

  • 宿主应用(Host):用户直接与之交互的应用程序,比如 Claude App、Cursor、WindSurf,或者自定义的 AI 代理。宿主应用负责创建和管理一个或多个 MCP 客户端实例,控制客户端的连接权限和生命周期,并协调 AI/LLM 的集成。

  • MCP 客户端(Client):嵌入在宿主应用内部的连接器。每个客户端与一个特定的 MCP 服务器建立一对一的、有状态的会话连接。它充当模型与服务器之间的桥梁,负责发现可用服务、发送调用请求、获取结果,并处理协议协商和能力交换。

  • MCP 服务端(Server):一个独立的程序或服务,它封装了对某一具体数据源(如文件系统、数据库)或工具(如 GitHub API、天气 API)的访问能力。服务器按照 MCP 规范向外提供标准化的功能接口,暴露其拥有的资源、工具和提示给模型使用。

MCP 架构

Why MCP ?

如上所述,LLM 因其核心设计,在直接与外部动态世界交互方面存在固有的局限性。为克服这些限制,业界已探索并广泛采用如检索增强生成 (RAG)、实时网络搜索、定制化的外部 API 调用等多种技术手段,以期向模型动态注入必要的上下文信息。

例如,当用 LLM 分析特定领域的最新数据,开发者通常会构建定制化的数据管道或 API 连接器。虽然这些方法在特定场景下能够解决问题,但它们往往是“点对点”的解决方案。这种方式不仅可能导致较高的开发和维护成本(趋向于前面提到的 M×N 集成复杂度),更重要的是缺乏统一标准,极大地限制了外部工具、数据源在不同 AI 应用之间的复用性和互操作性。

与传统的 RAG 等技术相比,MCP 的优势在于其标准化和通用性。RAG 通常需要为每个应用场景定制化数据管道和 API 连接器,而 MCP 则提供了一个统一的接口,可以连接各种不同的数据源和工具。

MCP 与 Function Calling:核心差异与价值拓展

LLM 若要与外部世界有效交互,调用外部函数或工具是常见机制。在这方面,Function Calling 和 MCP 虽然都致力于使 LLM 能够调用外部能力,但它们在设计理念、标准化程度和应用范围上存在显著差异。

Function Calling 通常指特定 LLM 供应商(如 OpenAI)提供的一项具体功能。它允许模型在处理用户请求时,识别出需要调用外部函数的需求,并生成一个包含函数名和参数的结构化请求(例如,调用 get_weather 函数并传递城市参数)。宿主应用接收到这个请求后,执行相应的函数,并将结果返回给 LLM 以生成最终回复。

MCP 则是一个更为宏观和开放的协议。它可以被理解为对 Function Calling 核心思想的一种泛化、标准化和扩展。MCP 不仅仅局限于函数调用,它定义了一个完整的框架,用于 LLM 与外部工具、数据资源乃至预设提示(Prompts)之间进行结构化、有状态的通信。

可以认为 MCP 采纳了 Function Calling 的核心理念,即让模型能够请求执行外部操作,但将其置于一个更全面、更强大、更具互操作性的标准化框架之内。

下表阐述了两者之间的主要区别:

特性Function CallingMCP
核心功能LLM 请求执行特定的外部函数LLM 通过标准化协议请求执行外部函数、访问资源或获取预设提示
标准化程度通常与特定 LLM 供应商绑定,API 定义可能各异开放标准 (基于 JSON-RPC 2.0),旨在实现模型无关、跨应用的统一接口
通信方向主要是单向(LLM 请求 -> 工具执行 -> 结果返回 LLM)支持双向通信和持久会话,允许更复杂的交互模式
能力范围侧重于“工具”或“函数”的调用更广泛,明确区分并支持工具 (Tools)、资源 (Resources) 和提示 (Prompts)
交互模式通常是无状态的单次请求-响应交互支持有状态的会话,允许进行多步骤、上下文相关的复杂交互
生态定位特定模型增强其工具使用能力的一种机制构建一个开放、可互操作的 AI 工具与数据生态系统的基础协议
主要关注点LLM 决定调用哪个函数及其参数标准化外部能力的发现、声明、调用、响应处理,以及宿主应用与服务器间的安全通信

M*N 是如何转成 M+N 的?

如上所述,如果一个系统里存在 M 个不同的 AI 应用及 N 个不同的数据系统,理论上可能需要开发 M×N 个独立的、定制化的集成方案才能实现两两之间的互通。MCP 通过引入标准化的客户端-服务器架构来解决这一问题:

  • M 个 MCP 客户端:每个 AI 应用开发者也只需在其应用中集成一个符合 MCP 规范的客户端,使其能够与任何 MCP 服务器通信。

  • N 个 MCP 服务器:每个外部工具或数据系统开发者只需按照 MCP 规范实现一个 MCP 服务器,暴露其提供的资源、工具和提示。

通过这种方式,原本需要 M×N 次的集成工作被简化为 M 次客户端实现和 N 次服务器实现,总计 M+N 个标准化组件。这些组件可以自由组合,任何实现了 MCP 客户端的应用原则上都可以与任何 MCP 服务器进行交互,从而极大地降低了集成复杂性,促进了 AI 系统中工具和数据的共享与重用。


如何实现一个 MCP 服务器

MCP 支持多种通信传输方式,常见的包括:

  • STDIO (标准输入/输出): 主要用于 MCP 服务器和客户端在同一本地环境中运行的场景。
  • HTTP+SSE (服务器发送事件): 用于远程连接,客户端通过 HTTP 发送请求,服务器通过 SSE 发送响应和进行流式传输。VS Code 也提及支持可流式传输的 HTTP。

以一个天气查询的 MCP 服务器为例,介绍一下如何实现一个远程 MCP 服务器。

准备工作

首先,我们需要安装必要的依赖:

pip install "mcp[cli]" httpx

基本结构

创建一个简单的天气查询服务,主要功能包括:

  1. 查询城市当前天气情况
  2. 获取城市未来几天的天气预报

首先创建项目的主文件 weather_server.py

from mcp.server.fastmcp import FastMCP, Context
import httpx
import json
from typing import Dict, Any, List, Optional

# 创建一个 MCP 服务器实例
mcp = FastMCP("WeatherServer")

# 模拟的天气 API 密钥(实际应用中应该从环境变量获取)
WEATHER_API_KEY = "demo_key"

实现工具(Tools)

添加两个工具函数,分别用于查询当前天气和天气预报:

@mcp.tool()
async def get_current_weather(city: str, ctx: Context) -> Dict[str, Any]:
    await ctx.info(f"Getting current weather for {city}")
    try:
        # 模拟 API 调用
        result = {
            "city": city,
            "temperature": 23.5,
            "feels_like": 24.2,
            "humidity": 65,
            "pressure": 1012,
            "weather": "多云",
            "wind_speed": 3.6,
            "wind_direction": 120
        }

        await ctx.info(f"Got current weather for {city}")
        return result

    except Exception as e:
        await ctx.error(f"Error getting weather data: {str(e)}")
        return {"error": str(e)}


@mcp.tool()
async def get_weather_forecast(city: str, days: int = 3, ctx: Context) -> List[Dict[str, Any]]:
    await ctx.info(f"Getting weather forecast for {city} for {days} days")
    try:
        # 模拟 API 调用
        forecast = []
        import datetime
        import random
        today = datetime.datetime.now()

        weather_types = ["晴", "多云", "阴", "小雨", "中雨"]

        for i in range(days):
            day = today + datetime.timedelta(days=i)
            forecast.append({
                "date": day.strftime("%Y-%m-%d"),
                "temperature_high": round(20 + random.uniform(-5, 5), 1),
                "temperature_low": round(15 + random.uniform(-5, 5), 1),
                "weather": random.choice(weather_types),
                "humidity": random.randint(40, 90),
                "wind_speed": round(random.uniform(1.0, 5.0), 1)
            })

        await ctx.info(f"Got weather forecast for {city} for {days} days")
        return forecast

    except Exception as e:
        await ctx.error(f"Error getting weather forecast: {str(e)}")
        return [{"error": str(e)}]

实现资源(Resources)

添加一个资源,允许客户端获取支持的城市列表:

@mcp.resource("cities://list")
def get_supported_cities() -> List[str]:
    """获取支持的城市列表"""
    return [
        "北京", "上海", "广州", "深圳", "成都",
        "杭州", "武汉", "西安", "南京", "重庆"
    ]


@mcp.resource("cities://{city}/info")
def get_city_info(city: str) -> Dict[str, Any]:
    """获取城市基本信息"""
    # 这里使用模拟数据
    city_info = {
        "北京": {"country": "中国", "province": "北京", "population": "2100万", "timezone": "UTC+8"},
        "上海": {"country": "中国", "province": "上海", "population": "2400万", "timezone": "UTC+8"},
        "广州": {"country": "中国", "province": "广东", "population": "1500万", "timezone": "UTC+8"}
        # 其他城市...
    }

    if city in city_info:
        return city_info[city]
    else:
        return {"error": "未找到该城市信息"}

实现提示模板(Prompts)

提供一个提示模板,用于帮助用户获取天气信息:

@mcp.prompt()
def get_weather_help() -> str:
    """创建一个天气查询的帮助提示"""
    return """
    # 天气查询帮助

    我可以帮你查询城市的天气情况,你可以尝试:

    1. 查询当前天气:例如"请告诉我北京现在的天气"
    2. 查询天气预报:例如"请告诉我未来3天上海的天气预报"

    目前支持的城市包括:北京、上海、广州、深圳、成都、杭州、武汉、西安、南京、重庆。
    """

启动服务器

最后,添加启动代码以运行服务器:

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

运行服务器

要运行此服务器,您可以在终端中执行:

python weather_server.py

或者使用 MCP CLI 工具:

mcp run weather_server.py

通过 MCP Inspector 调试

我们也可以使用 MCP CLI 工具中的 Inspector 功能来调试服务器:

mcp dev weather_server.py

客户端调用

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

async def simple_client():
    """Client for weather server"""
    server_params = StdioServerParameters(
        command="python",
        args=["weather_server.py"]
    )

    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            print("✅ Connected to weather server")

            result = await session.call_tool(
                "get_current_weather",
                arguments={"city": "Beijing"}
            )
            weather_data = json.loads(result.content[0].text)
            print(f"🌤️ Beijing weather: {weather_data['weather']} {weather_data['temperature']}°C")

            forecast = await session.call_tool(
                "get_weather_forecast",
                arguments={"city": "Shanghai", "days": 3}
            )
            forecast_data = json.loads(forecast.content[0].text)
            print(f"📅 Shanghai 3-day forecast: {len(forecast_data)} days data")

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

通过这种方式,我们实现了一个简单但功能完整的天气查询 MCP 服务器。它演示了 MCP 的核心概念:工具(Tools)、资源(Resources)和提示(Prompts),以及如何使用上下文(Context)来处理用户请求并返回结果。

总结

MCP 是一种创新的协议,旨在解决大语言模型与外部世界的交互问题。它提供了一种标准化的方式,使 LLM 应用能够访问最新数据、调用外部工具,并执行各种操作,而不再仅仅依赖于其训练数据中的静态知识。

MCP 的核心优势在于:

  • 标准化接口:提供了统一的接口(基于 JSON-RPC 2.0 ),使不同的 AI 应用能够与各种工具和数据源进行交互。
  • 降低集成复杂性:将 M×N 的集成问题(M 个 AI 应用与 N 个工具/系统)简化为 M+N 的问题。
  • 增强 LLM 能力:使 LLM 能够访问实时数据、执行操作 (Tools),并与用户进行更有效的交互(通过 Prompts 和 Resources 获取上下文)。
  • 安全边界:MCP 规范强调用户同意和控制,并提供了处理数据隐私和工具安全的指导原则,尽管具体实现依赖于宿主应用和服务器

作为 AI 应用与外部世界交互的标准化桥梁,MCP 在 AI 应用生态大有可为。MCP 的持续发展和完善,有望为 AI 应用生态带来更深远的影响。

参考资料

Comments

理想拖

理想拖

@SLIPPERTOPIA