当前位置:首页 > 技术 > 正文内容

MCP协议应用实践:构建可扩展的AI服务接口

访客 技术 2026年7月18日 1

一、MCP协议核心概念

MCP(Model Context Protocol)是由Anthropic提出的一种开放标准,旨在打通大模型与外部系统之间的连接壁垒。它通过标准化的工具调用机制,使AI能够访问数据库、执行计算任务、调用外部API或操作本地文件,从而实现真正意义上的"智能代理"。

二、支持MCP的主流模型

当前具备稳定Function Calling能力的模型均能良好兼容MCP。推荐使用以下模型:

  • DeepSeek V3(0324版本)
  • Claude 3.7
  • GPT-4o
  • Gemini 2.5 Pro

可通过OpenRouter平台查询模型参数支持情况,若存在Tools字段则表示支持MCP。

三、开发环境配置

推荐使用UV作为包管理器:

# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows
irm https://astral.sh/uv/install.ps1 | iex

# 或通过pip安装
pip install uv

四、MCP通信模式与实现

MCP采用客户端-服务器架构,支持两种传输方式:

  • stdio:基于进程间通信,适合本地脚本调用
  • sse:基于HTTP的流式响应,适用于远程服务部署
  • 两者均遵循JSON-RPC协议进行数据交互。

    1. 基于stdio的本地服务实现

    以下示例展示如何创建一个简单的加法计算服务:

    from mcp.server.fastmcp import FastMCP
    
    app = FastMCP("数学计算服务")
    
    @app.tool()
    def add_numbers(x: float, y: float) -> float:
        """执行两数相加运算"""
        return x + y
    
    if __name__ == '__main__':
        app.run(transport='stdio')

    2. 客户端调用逻辑

    客户端通过子进程启动服务,并动态解析可用工具:

    from mcp.client.stdio import StdioServerParameters, stdio_client
    from mcp import ClientSession
    import asyncio
    import json
    from openai import OpenAI
    
    class MCPPromptHandler:
        def __init__(self, server_script: str):
            self.client = OpenAI(
                api_key="your_api_key",
                base_url="https://api.deepseek.com"
            )
            self.server_script = server_script
    
        async def execute(self, user_query: str):
            params = StdioServerParameters(
                command="python",
                args=[self.server_script],
                env=None
            )
    
            async with stdio_client(params) as (read_stream, write_stream):
                async with ClientSession(read_stream=read_stream, write_stream=write_stream) as session:
                    await session.initialize()
                    tools_list = await session.list_tools()
    
                    # 构建符合Function Calling规范的工具列表
                    tool_specs = [
                        {
                            'type': 'function',
                            'function': {
                                'name': tool.name,
                                'description': tool.description,
                                'input_schema': tool.inputSchema
                            }
                        } for tool in tools_list.tools
                    ]
    
                    messages = [{'role': 'user', 'content': user_query}]
                    response = self.client.chat.completions.create(
                        model="deepseek-chat",
                        messages=messages,
                        tools=tool_specs
                    )
    
                    choice = response.choices[0]
                    if choice.finish_reason == 'tool_calls':
                        messages.append(choice.message.model_dump())
                        call = choice.message.tool_calls[0]
                        func_name = call.function.name
                        args = json.loads(call.function.arguments)
    
                        result = await session.call_tool(name=func_name, arguments=args)
                        messages.append({
                            'role': 'tool',
                            'content': result.content[0].text,
                            'tool_call_id': call.id
                        })
    
                        final_response = self.client.chat.completions.create(
                            model="deepseek-chat",
                            messages=messages
                        )
                        print(f"结果:{final_response.choices[0].message.content}")
                    else:
                        print(f"直接回答:{choice.message.content}")
    
    # 启动执行
    asyncio.run(MCPPromptHandler("./math_server.py").execute("计算3.5和4.2的和"))

    3. 扩展功能:天气查询服务

    在原有基础上添加天气查询能力:

    WEATHER_DB = {
        "福州": {"temp": 25, "condition": "晴朗", "wind": "微风", "humidity": 45},
        "上海": {"temp": 28, "condition": "多云", "wind": "东风3级", "humidity": 60}
    }
    
    @app.tool()
    def query_weather(city: str) -> str:
        """获取指定城市的实时天气信息"""
        city = city.strip()
        if city not in WEATHER_DB:
            return f"不支持的城市,请选择:{', '.join(WEATHER_DB.keys())}"
        data = WEATHER_DB[city]
        return (f"{city}天气:{data['condition']},"
                f"温度{data['temp']}°C,"
                f"风力{data['wind']},"
                f"湿度{data['humidity']}%")

    五、Java后端集成(基于Spring AI)

    使用Spring Boot搭建MCP服务端:

    application.yml配置

    server:
      port: 8080
    spring:
      ai:
        mcp:
          server:
            name: weather-server
            sse-message-endpoint: /mcp/message
            version: 1.0.0
            type: SYNC
            instructions: "提供城市天气查询功能"
            sse-endpoint: /sse
            capabilities:
              tool: true
              resource: false
              prompt: false
              completion: false

    WeatherService.java

    @Service
    public class WeatherService {
    
        private static final Map<String, WeatherData> DATA = new HashMap<>();
    
        static {
            DATA.put("福州", new WeatherData(25, "晴朗", "微风", 45));
            DATA.put("上海", new WeatherData(28, "多云", "东风3级", 60));
        }
    
        @Tool(name = "getWeather", description = "根据城市名称获取今日天气")
        public String getWeather(@ToolParam("城市名") String city) {
            if (city == null || city.isEmpty()) {
                return "请输入有效城市名称";
            }
            var data = DATA.get(city.trim());
            if (data == null) {
                return "暂无该城市天气数据,支持城市:" + DATA.keySet();
            }
            return String.format("【%s】气温:%d°C,天气:%s,风力:%s,湿度:%d%%",
                    city, data.temp, data.condition, data.wind, data.humidity);
        }
    
        private record WeatherData(int temp, String condition, String wind, int humidity) {}
    }

    工具注册配置

    @Configuration
    public class ToolConfig {
        @Bean
        public ToolCallbackProvider weatherTools(WeatherService service) {
            return MethodToolCallbackProvider.builder().toolObjects(service).build();
        }
    }

    客户端调用更新

    if __name__ == '__main__':
        client = MCPClient(server_url="http://127.0.0.1:8080/sse")
        asyncio.run(client.run('福州今天天气如何'))

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。