跳转到主要内容

标签(标签)

资源精选(342) Go开发(108) Go语言(103) Go(99) LLM(84) angular(83) 大语言模型(67) 人工智能(56) 前端开发(50) LangChain(43) golang(43) 机器学习(39) Go工程师(38) Go程序员(38) Go开发者(36) React(34) Go基础(29) Python(24) Vue(23) Web开发(20) 深度学习(20) Web技术(19) 精选资源(19) Java(19) ChatGTP(17) Cookie(16) android(16) 前端框架(13) JavaScript(13) Next.js(12) LLMOps(11) 聊天机器人(11) 安卓(11) ChatGPT(10) typescript(10) 资料精选(10) mlops(10) NLP(10) 第三方Cookie(9) Redwoodjs(9) RAG(9) Go语言中级开发(9) 自然语言处理(9) PostgreSQL(9) 区块链(9) 安全(9) 智能体(8) 全栈开发(8) OpenAI(8) Linux(8) AI(8) GraphQL(8) iOS(8) 数据科学(8) 软件架构(7) Go语言高级开发(7) AWS(7) C++(7) whisper(6) Prisma(6) 隐私保护(6) 提示工程(6) JSON(6) DevOps(6) 数据可视化(6) wasm(6) 计算机视觉(6) 算法(6) Rust(6) 微服务(6) 隐私沙盒(5) FedCM(5) 语音识别(5) Angular开发(5) 快速应用开发(5) 生成式AI(5) Agent(5) LLaMA(5) 低代码开发(5) Go测试(5) gorm(5) REST API(5) kafka(5) 推荐系统(5) WebAssembly(5) GameDev(5) 数据分析(5) CMS(5) CSS(5) machine-learning(5) 机器人(5) 游戏开发(5) Blockchain(5) Web安全(5) nextjs(5) Kotlin(5) 低代码平台(5) 机器学习资源(5) Go资源(5) Nodejs(5) PHP(5) Swift(5) RAG架构(4) devin(4) Blitz(4) javascript框架(4) Redwood(4) GDPR(4) 生成式人工智能(4) Angular16(4) Alpaca(4) 编程语言(4) SAML(4) JWT(4) JSON处理(4) Go并发(4) 移动开发(4) 移动应用(4) security(4) 隐私(4) spring-boot(4) 物联网(4) 网络安全(4) API(4) Ruby(4) 信息安全(4) flutter(4) 专家智能体(3) Chrome(3) CHIPS(3) 3PC(3) SSE(3) 人工智能软件工程师(3) LLM Agent(3) Remix(3) Ubuntu(3) GPT4All(3) 模型评估(3) 软件开发(3) 问答系统(3) 开发工具(3) 最佳实践(3) RxJS(3) SSR(3) Node.js(3) Dolly(3) 移动应用开发(3) 低代码(3) IAM(3) Web框架(3) CORS(3) 基准测试(3) Go语言数据库开发(3) Oauth2(3) 并发(3) 主题(3) Theme(3) earth(3) nginx(3) 软件工程(3) azure(3) keycloak(3) 生产力工具(3) gpt3(3) 工作流(3) C(3) jupyter(3) 认证(3) prometheus(3) GAN(3) Spring(3) 逆向工程(3) 应用安全(3) Docker(3) Django(3) R(3) .NET(3) 大数据(3) Hacking(3) 渗透测试(3) C++资源(3) Mac(3) 微信小程序(3) Python资源(3) JHipster(3) JDK(2) SQL(2) Apache(2) Hashicorp Vault(2) Spring Cloud Vault(2) Go语言Web开发(2) Go测试工程师(2) WebSocket(2) 容器化(2) AES(2) 加密(2) 输入验证(2) ORM(2) Fiber(2) Postgres(2) Gorilla Mux(2) Go数据库开发(2) 模块(2) 泛型(2) 指针(2) HTTP(2) PostgreSQL开发(2) Vault(2) K8s(2) Spring boot(2) R语言(2) 深度学习资源(2) 半监督学习(2) semi-supervised-learning(2) architecture(2) 普罗米修斯(2) 嵌入模型(2) productivity(2) 编码(2) Qt(2) 前端(2) Rust语言(2) NeRF(2) 神经辐射场(2) 元宇宙(2) CPP(2) spark(2) 流处理(2) Ionic(2) 人体姿势估计(2) human-pose-estimation(2) 视频处理(2) deep-learning(2) kotlin语言(2) kotlin开发(2) burp(2) Chatbot(2) npm(2) quantum(2) OCR(2) 游戏(2) game(2) 内容管理系统(2) MySQL(2) python-books(2) pentest(2) opengl(2) IDE(2) 漏洞赏金(2) Web(2) 知识图谱(2) PyTorch(2) 数据库(2) reverse-engineering(2) 数据工程(2) swift开发(2) rest(2) robotics(2) ios-animation(2) 知识蒸馏(2) 安卓开发(2) nestjs(2) solidity(2) 爬虫(2) 面试(2) 容器(2) C++精选(2) 人工智能资源(2) Machine Learning(2) 备忘单(2) 编程书籍(2) angular资源(2) 速查表(2) cheatsheets(2) SecOps(2) mlops资源(2) R资源(2) DDD(2) 架构设计模式(2) 量化(2) Hacking资源(2) 强化学习(2) flask(2) 设计(2) 性能(2) Sysadmin(2) 系统管理员(2) Java资源(2) 机器学习精选(2) android资源(2) android-UI(2) Mac资源(2) iOS资源(2) Vue资源(2) flutter资源(2) JavaScript精选(2) JavaScript资源(2) Rust开发(2) deeplearning(2) RAD(2)

category

模型上下文协议 (MCP):让AI真正连接现实世界的"USB-C接口"

是否曾幻想过你的AI聊天机器人能真正执行操作 —— 比如查询天气、访问数据库或操作本地文件,而无需编写繁琐的自定义集成代码?这正是模型上下文协议 (Model Context Protocol,简称MCP) 要解决的痛点。你可以把MCP理解为"AI领域的USB-C":一种标准化的AI模型与外部数据工具对接方式。

本文将通过真实代码示例,演示如何用最简单的方式构建和使用MCP服务器。

MCP的本质是什么?

MCP本质上是一种通信协议,为AI应用(如聊天机器人或IDE助手)定义了与数据源和服务交互的"通用语言"。开发者无需为每个集成对象(如Google Drive、Slack、内部数据库等)编写定制连接器,只需编写一个符合MCP标准的连接器,任何支持MCP的AI都能调用。

为什么这很重要?

  1. ​告别重复造轮子​​:开发者无需为每个数据源重复编写集成代码
  2. ​互操作性​​:为一个AI平台(如Claude、OpenAI)构建的工具,可在其他支持MCP的AI主机(如基于Python的聊天机器人)中复用
  3. ​简洁与安全​​:明确分离数据访问与操作执行,简化审计与权限管理
  4. ​可发现性​​:任何MCP客户端都能查询服务器提供的功能(工具/数据/提示词),消除调用猜测

MCP架构:客户端、服务器与主机

MCP采用客户端-服务器模式,包含以下角色:

  • ​主机 (Host)​​:AI应用本身(如运行GPT的聊天机器人)
  • ​客户端 (Client)​​:运行在主机内部,管理一个或多个MCP服务器的连接
  • ​服务器 (Server)​​:通过MCP暴露特定能力(工具/数据)的独立程序(如天气API访问器)
  • ​数据源 (Data Sources)​​:MCP服务器可读写的底层资源(本地文件/远程API等)

通信流程演示

  1. 主机启动MCP客户端
  2. 客户端连接MCP服务器(如weather.py)
  3. 客户端询问服务器能力(发现工具/资源)
  4. 用户向AI提问
  5. AI通过客户端调用工具(如get_forecast)
  6. 服务器获取天气数据并返回客户端
  7. 客户端将结果传回AI
  8. AI整合数据生成友好响应

就像通过USB-C接口连接不同外设,AI可以通过MCP接入任意服务器。


构建天气MCP服务器

以下是完整的weather.py服务器代码,演示如何从美国国家气象局(NWS)获取数据并通过MCP暴露。我们定义两个工具:

  • ​get_alerts(state)​​:返回指定美国州的活跃天气警报
  • ​get_forecast(latitude, longitude)​​:返回指定坐标的短期天气预报
python
复制
import httpx  # 用于向气象API发起请求
from typing import Any
from mcp.server.fastmcp import FastMCP  # MCP服务器库

# 1. 初始化服务器
mcp = FastMCP("weather")  # 服务器命名为"weather"

# 外部API常量
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"


async def make_nws_request(url: str) -> dict[str, Any] | None:
    """向NWS API发起GET请求并返回JSON,出错时返回None"""
    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:
    """将天气警报特征转换为可读字符串"""
    props = feature["properties"]
    return (
        f"事件: {props.get('event', '未知')}\n"
        f"区域: {props.get('areaDesc', '未知')}\n"
        f"严重性: {props.get('severity', '未知')}\n"
        f"描述: {props.get('description', '无描述信息')}\n"
        f"应对指南: {props.get('instruction', '无应对指南')}"
    )


@mcp.tool()
async def get_alerts(state: str) -> str:
    """获取美国州级天气警报(使用两字母代码,如CA)"""
    url = f"{NWS_API_BASE}/alerts/active/area/{state}"
    data = await make_nws_request(url)
    if not data or "features" not in data:
        return "无法获取警报或未找到警报"
    if not data["features"]:
        return f"{state}当前无活跃警报"

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


@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """获取坐标位置短期天气预报"""
    points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
    points_data = await make_nws_request(points_url)
    if not points_data or "properties" not in points_data:
        return "无法获取该位置气象数据"

    forecast_url = points_data["properties"].get("forecast")
    if not forecast_url:
        return "未找到该位置预报URL"

    forecast_data = await make_nws_request(forecast_url)
    if not forecast_data or "properties" not in forecast_data:
        return "无法获取详细预报"

    periods = forecast_data["properties"].get("periods", [])
    if not periods:
        return "无可用预报时段"

    lines = []
    for period in periods[:5]:  # 仅取前5个时段
        lines.append(
            f"{period['name']}:\n"
            f"  温度: {period['temperature']}°{period['temperatureUnit']}\n"
            f"  风力: {period['windSpeed']} {period['windDirection']}\n"
            f"  预报: {period['detailedForecast']}"
        )
    return "\n\n".join(lines)


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

​关键点:​

  • mcp.tool()装饰器:将get_alerts和get_forecast暴露给客户端
  • 异步处理:使用async/await实现网络请求,确保服务器响应性
  • mcp.run(transport="stdio"):指定通过标准输入输出与客户端通信

运行python weather.py时可能无输出,因为服务器正在等待客户端请求。


构建OpenAI版MCP客户端

接下来构建client.py,实现以下功能:

  1. 将weather.py作为子进程启动
  2. 通过MCP连接服务器
  3. 使用OpenAI函数调用API让GPT决定调用哪个工具
  4. 在服务器执行选定工具并将结果返回GPT生成最终答案
  5. 提供终端聊天界面方便测试
python
复制
import os
import sys
import json
import asyncio
from typing import Optional
from dotenv import load_dotenv

load_dotenv()  # 从.env加载OPENAI_API_KEY

from openai import AsyncOpenAI
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from contextlib import AsyncExitStack


class MCPClient:
    def __init__(self):
        self.session: Optional[ClientSession] = None
        self.exit_stack = AsyncExitStack()
        # 使用异步OpenAI客户端
        self.openai = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY"))
        self.model = "gpt-4o"  

    async def connect_to_server(self, server_script_path: str):
        """启动MCP服务器子进程并建立连接"""
        if server_script_path.endswith(".py"):
            command = "python"
        elif server_script_path.endswith(".js"):
            command = "node"
        else:
            raise ValueError("服务器脚本必须是.py或.js")

        server_params = StdioServerParameters(command=command, args=[server_script_path], env=None)
        stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
        self.stdio, self.write = stdio_transport
        self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
        await self.session.initialize()

        # 打印可用工具列表
        tool_list = await self.session.list_tools()
        print(f"已连接服务器。可用工具: {[t.name for t in tool_list.tools]}")

    async def _get_openai_functions(self):
        """将MCP工具转换为OpenAI函数调用格式"""
        tool_list = await self.session.list_tools()
        functions = []
        for tool in tool_list.tools:
            fn_schema = {
                "name": tool.name,
                "description": tool.description or tool.name,
                "parameters": {
                    "type": "object",
                    "properties": {}
                },
                "required": []
            }
            if tool.inputSchema and tool.inputSchema.get("type") == "object":
                fn_schema["parameters"] = tool.inputSchema
            functions.append(fn_schema)
        return functions

    async def process_user_message(self, user_message: str) -> str:
        if not self.session:
            raise RuntimeError("未找到MCP会话,请先调用connect_to_server")

        functions = await self._get_openai_functions()
        # 第一次GPT调用:提供用户消息+可用函数
        response = await self.openai.chat.completions.create(
            model=self.model,
            messages=[{"role": "user", "content": user_message}],
            functions=functions,
            function_call="auto"
        )
        assistant_message = response.choices[0].message

        if hasattr(assistant_message, "function_call") and assistant_message.function_call:
            fn_name = assistant_message.function_call.name
            fn_args_json = assistant_message.function_call.arguments
            try:
                fn_args = json.loads(fn_args_json) if fn_args_json else {}
            except json.JSONDecodeError:
                return "[错误] GPT生成非法JSON参数"

            print(f"\n[工具请求] GPT希望调用{fn_name},参数:{fn_args}")
            tool_result = await self.session.call_tool(fn_name, fn_args)

            # 第二次GPT调用:提供工具执行结果
            followup = await self.openai.chat.completions.create(
                model=self.model,
                messages=[
                    {"role": "user", "content": user_message},
                    assistant_message.to_dict(),
                    {"role": "function", "name": fn_name, "content": tool_result.content}
                ]
            )
            return followup.choices[0].message.content
        else:
            # GPT不需要调用函数
            return assistant_message.content

    async def chat_loop(self):
        print("MCP + OpenAI 客户端。输入'quit'或'exit'退出")
        while True:
            user_inp = input("\n你: ")
            if user_inp.strip().lower() in ("quit", "exit"):
                break
            answer = await self.process_user_message(user_inp)
            print(f"\n助手: {answer}")

    async def cleanup(self):
        await self.exit_stack.aclose()


async def main():
    if len(sys.argv) < 2:
        print("用法: python client.py <服务器脚本路径>")
        sys.exit(1)
    server_path = sys.argv[1]

    client = MCPClient()
    try:
        await client.connect_to_server(server_path)
        await client.chat_loop()
    finally:
        await client.cleanup()


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

​关键点:​

  1. ​connect_to_server​​:启动服务器子进程并通过标准输入输出建立MCP会话
  2. ​函数模式转换​​:_get_openai_functions将MCP工具模式转换为OpenAI函数调用需要的JSON格式
  3. ​process_user_message​​:
    • 首次GPT调用:GPT根据用户消息和函数定义决定是否调用工具
    • 执行工具:若需要调用工具,客户端通过await self.session.call_tool(fn_name, fn_args)与MCP服务器通信
    • 二次GPT调用:将工具结果发送给GPT生成最终响应

运行示例

  1. ​安装依赖​​:
bash
复制
pip install "mcp[cli]" openai httpx python-dotenv
  1. ​创建.env文件存放OpenAI API密钥​​:
env
复制
OPENAI_API_KEY=sk-12345...
  1. ​运行客户端(自动启动服务器)​​:
bash
复制
python client.py weather.py
  1. ​开始对话​​:
已连接服务器。可用工具: ['get_alerts', 'get_forecast']
MCP + OpenAI 客户端。输入'quit'或'exit'退出

你: 怀俄明州有哪些警报?
[工具请求] GPT希望调用get_alerts,参数:{'state': 'WY'}

助手: 怀俄明州当前有以下活跃天气警报:
(此处显示格式化后的详细警报信息)

你: 北纬38.58西经121.49的天气预报是?
[工具请求] GPT希望调用get_forecast,参数:{'latitude': 38.58, 'longitude': -121.49}

助手: 该坐标位置未来天气预测如下:
(显示格式化后的天气预报)

GPT4o根据用户输入自动选择MCP工具,服务器从NWS获取数据后,OpenAI API生成简洁的天气摘要。


核心价值

  1. ​标准化AI集成​​:定义服务器(如weather.py)后,任何MCP兼容客户端都能使用 —— 无需为不同AI平台重写代码
  2. ​函数调用模式​​:结合OpenAI函数调用API,实现AI"智能体"的精准工具调用
  3. ​安全的客户端-服务器分离​​:敏感数据和API操作由MCP服务器在受控环境中处理
  4. ​无限扩展性​​:添加邮件发送、数据库查询、文件操作等工具,快速增强AI能力

通过本文示例,您可以快速搭建自己的MCP服务器和客户端,让GPT或其他AI安全高效地访问真实数据。快去构建一些令人惊叹的东西吧!

​延伸思考​​:MCP正在重新定义AI的能力边界 —— 不仅让AI更"聪明",更能安全可靠地与现实世界交互。这种标准化接口模式,或将开启下一代AI应用开发的新范式。

文章链接

标签