MCP 是什么

MCP 是一个开放协议,它为应用程序向 LLM 提供上下文的方式进行了标准化。

使用 MCP,像 Claude 或 ChatGPT 这样的 AI 应用程序可以连接到数据源(例如本地文件、数据库)、工具(例如搜索引擎、计算器)和工作流(例如专用提示)——使它们能够访问信息并执行任务。

你可以将 MCP 想象成 AI 应用程序的 USB-C 接口。就像 USB-C 为设备连接各种外设和配件提供了标准化的方式一样,MCP 为 AI 模型连接各种数据源和工具提供了标准化的接口。

可以用几个词来总结 MCP 的关键点:

开放,协议,标准化,接口,工具,工作流

为什么需要 MCP

为了提高 LLM 的能力边界,需要让它们能够访问外部数据和工具。然而,不同的 AI 应用程序通常使用不同的集成方法,这导致了碎片化和重复工作。

在没有 MCP 之前,每个 AI 应用都需要为不同的数据源和工具开发专门的集成方案。这导致了大量重复开发工作,缺乏互操作性,且维护成本高昂。MCP 的出现解决了这些问题,提供了一个统一的标准,让开发者可以一次开发,到处使用,同时降低项目中组件的耦合度。

怎么使用 MCP

MCP 的实现方式主要分为两种:自建远程服务和使用社区托管服务。

远程 MCP

当你需要在不同服务器之间进行 MCP 通信时,可以部署远程 MCP 服务器。这种方式适合企业级应用或需要自定义工具的场景。

  • 服务端
import asyncio
from mcp.server import Server
from mcp.server.sse import SseServerTransport
from mcp.types import TextContent, Tool
from starlette.applications import Starlette
from starlette.responses import Response
from starlette.routing import Mount, Route

app = Server("remote_tool_server")

@app.list_tools()
async def list_tools() -> list[Tool]:
    return [
        Tool(
            name="add_numbers",
            description="Add two numbers together",
            inputSchema={
                "type": "object",
                "properties": {
                    "a": {
                        "type": "number",
                        "description": "First number"
                    },
                    "b": {
                        "type": "number",
                        "description": "Second number"
                    }
                },
                "required": ["a", "b"]
            }
        )
    ]

@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
    if name == "add_numbers":
        a = arguments.get("a", 0)
        b = arguments.get("b", 0)
        result = a + b
        return [TextContent(type="text", text=f"The sum of {a} and {b} is {result}")]
    return [TextContent(type="text", text="Unknown tool")]

# Create SSE transport
sse = SseServerTransport("/messages/")

async def handle_sse(request):
    async with sse.connect_sse(request.scope, request.receive, request._send) as streams:
        await app.run(streams[0], streams[1], app.create_initialization_options())
    return Response()

# Create Starlette app
starlette_app = Starlette(
    routes=[
        Route("/sse", endpoint=handle_sse, methods=["GET"]),
        Mount("/messages/", app=sse.handle_post_message),
    ]
)

if __name__ == "__main__":
    import uvicorn
    print("Starting HTTP MCP server on port 8000...")
    uvicorn.run(starlette_app, host="0.0.0.0", port=8000)

上面的服务器端代码创建了一个提供加法运算的 MCP 服务器,使用 SSE(Server-Sent Events)作为传输协议。服务器暴露了一个 add_numbers 工具,可以接收两个数字并返回它们的和。

  • 客户端
import asyncio
import sys
from mcp.client.sse import sse_client
from mcp.client.session import ClientSession
import openai

# Custom base_url and model - replace with your actual values
BASE_URL = "api_base_url"
MODEL = "model"
API_KEY = "your_key"  # Replace with your actual API key

client = openai.OpenAI(base_url=BASE_URL, api_key=API_KEY)

async def main():
    user_query = "What is 15 + 27?"

    # Connect to remote SSE MCP server
    server_url = "http://localhost:8000/sse"  # Adjust URL for remote server

    async with sse_client(server_url) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()

            # Get tools from server
            tools_result = await session.list_tools()
            tools = tools_result.tools

            # Convert MCP tools to OpenAI format
            openai_tools = []
            for tool in tools:
                openai_tools.append({
                    "type": "function",
                    "function": {
                        "name": tool.name,
                        "description": tool.description,
                        "parameters": tool.inputSchema
                    }
                })

            # Call LLM with tools
            response = client.chat.completions.create(
                model=MODEL,
                messages=[{"role": "user", "content": user_query}],
                tools=openai_tools,
                tool_choice="auto"
            )

            message = response.choices[0].message

            if message.tool_calls:
                # LLM decided to call a tool
                tool_call = message.tool_calls[0]
                tool_name = tool_call.function.name
                arguments = eval(tool_call.function.arguments)  # In production, use json.loads

                # Call the actual tool via MCP server
                result = await session.call_tool(tool_name, arguments)
                tool_result = result.content[0].text

                # Send result back to LLM for final response
                final_response = client.chat.completions.create(
                    model=MODEL,
                    messages=[
                        {"role": "user", "content": user_query},
                        message,
                        {"role": "tool", "tool_call_id": tool_call.id, "content": tool_result}
                    ]
                )
                print("Final response:", final_response.choices[0].message.content)
            else:
                # No tool call needed
                print("LLM response:", message.content)

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

客户端代码连接到远程 MCP 服务器后,获取可用工具,并与 LLM 集成。当用户提出数学问题时,LLM 会智能地决定是否需要调用加法工具来解决问题。

但是现在的 LLM 普遍可以精准的直接回答两数之和是多少的问题,所以我们需要一个更复杂的例子来展示 MCP 的优势。

社区 MCP

除了自建服务器,现在也有很多社区托管的 MCP 服务,可以直接使用,无需自己搭建服务器。

例如 ModelScope 的 MCP 广场提供了丰富的预构建服务,涵盖了网页抓取、数据处理、API 调用等常见需求。以 fetch 服务为例,你可以直接使用远程 MCP,而无需自己搭建服务器。

以下就是一个使用 ModelScope 提供的 fetch 服务来抓取网页内容的示例:

import asyncio
import json
from mcp.client.sse import sse_client
from mcp.client.session import ClientSession
import openai

# Custom base_url and model - replace with your actual values
BASE_URL = "api_base_url"
MODEL = "model"
API_KEY = "your_key"  # Replace with your actual API key

client = openai.OpenAI(base_url=BASE_URL, api_key=API_KEY)

async def main():
    # 用户查询
    user_query = "请帮我获取 https://www.python.org 的内容摘要"
    
    # 替换为您的远程 MCP 服务器地址
    # 这里使用 ModelScope 提供的 fetch 服务器地址(需要替换为实际地址)
    server_url = "https://mcp.api-inference.modelscope.net/your_id/sse"  # 替换为实际的远程服务器URL
    
    try:
        # 连接到远程 MCP 服务器
        async with sse_client(server_url) as (read, write):
            async with ClientSession(read, write) as session:
                await session.initialize()
                
                print("已连接到远程 MCP 服务器")
                
                # 获取可用工具
                tools_result = await session.list_tools()
                tools = tools_result.tools
                
                print(f"可用工具: {[tool.name for tool in tools]}")
                
                # 转换为 OpenAI 工具格式
                openai_tools = []
                for tool in tools:
                    openai_tools.append({
                        "type": "function", 
                        "function": {
                            "name": tool.name,
                            "description": tool.description,
                            "parameters": tool.inputSchema
                        }
                    })
                
                # 调用 LLM,让它决定是否需要使用工具
                response = client.chat.completions.create(
                    model=MODEL,
                    messages=[{"role": "user", "content": user_query}],
                    tools=openai_tools,
                    tool_choice="auto"
                )
                
                message = response.choices[0].message
                
                if message.tool_calls:
                    # LLM 决定使用工具
                    print("LLM 决定使用网页抓取工具...")
                    
                    for tool_call in message.tool_calls:
                        tool_name = tool_call.function.name
                        arguments = json.loads(tool_call.function.arguments)
                        
                        print(f"调用工具: {tool_name}")
                        print(f"参数: {arguments}")
                        
                        # 通过 MCP 调用远程工具
                        result = await session.call_tool(tool_name, arguments)
                        tool_result = result.content[0].text
                        
                        print(f"工具结果长度: {len(tool_result)} 字符")
                        
                        # 将工具结果发送回 LLM 获取最终响应
                        final_response = client.chat.completions.create(
                            model=MODEL,
                            messages=[
                                {"role": "user", "content": user_query},
                                message,
                                {"role": "tool", "tool_call_id": tool_call.id, "content": tool_result}
                            ]
                        )
                        
                        print("\n=== 最终响应 ===")
                        print(final_response.choices[0].message.content)
                else:
                    # LLM 不需要使用工具
                    print("LLM 响应:", message.content)
                    
    except Exception as e:
        print(f"连接远程 MCP 服务器失败: {e}")
        print("\n作为演示,这里使用本地 fetch 工具:")
        


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

其他

简单的说,这两张图我认为就是 MCP 的精髓

1.jpg

2.jpg