跳至主要內容

MCP 技术解读

Liz大约 13 分钟LLMMCP

MCP 技术解读

  • MCP概念、演进与意义
  • MCP架构、核心组件与功能类型
  • MCP Client 与 MCP Server
  • 不同角色使用 MCP 的方式和逻辑

1. MCP是什么

MCP(Model Context Protocol) 是由 Anthropic(就是训练 Claude 的那个公司) 推出的一种新兴的标准开放协议,用于解决 Agent 平台的AI 模型与外部工具交互的痛点,帮助你在 LLM 之上构建智能体和复杂工作流。MCP 将应用程序向 AI 模型提供上下文的方式标准化,旨在通过安全的双向连接来增强 AI 模型与工具和数据源的交互能力。可以将 MCP 视为 AI 应用的 USB-C 接口,正如 USB-C 为设备连接各种外设和配件提供了标准化方式,MCP 也为 AI 模型连接不同数据源和工具提供了标准化方法。

MCP Server 就是为了实现 AI Agent 的自动化而存在的,它是一个中间层,告诉 AI Agent 目前存在哪些服务,哪些 API,哪些数据源,AI Agent 可以根据 Server 提供的信息来决定是否调用某个服务,然后通过 Function Calling 来执行函数。以前每个工具都要单独写代码才能连AI,但现在MCP就像"通用接口",工具和AI各装一个MCP插件,就能直接对话了。

比如要用Github服务,最终实现还是通过调用Github的API(https://api.github.com)来实现和Github交互,在调用 Github 官方的 API 之前,MCP 的主要工作是描述 Server 提供了哪些能力(给 LLM 提供),需要哪些参数(参数具体的功能是什么),最后返回的结果是什么。所以 MCP Server 并不是一个新颖的、高深的东西,它只是一个具有共识的协议。

官网:https://modelcontextprotocol.io/introduction

Github: https://github.com/modelcontextprotocol

2. AI 工具调用演进

从复杂提示词 → Function Calling → GPTs 插件 → MCP 协议的技术演进路径,非常类似于几个经典技术标准化的发展历程,特别是 Web 技术、互联网协议和 API 标准化的过程。以 Web API 的标准化进程类比,

Web API 演进AI 工具调用演进
早期:RPC(远程过程调用)各自实现早期:提示词工程中的工具调用
发展期:SOAP 和 XML-RPC 等框架发展期:Function Calling 的 JSON 结构化调用
成熟期:REST API 成为主流成熟期:GPTs 等平台专属实现
统一期:GraphQL 等新一代标准统一期:MCP 作为统一协议

3. MCP 出来前后的区别是啥?意义是啥?

MCP 出来前后的区别

  • MCP 出来前,Agent 平台需要自己维护可用工具列表以及进行工具调用
  • Agent 平台集成 MCP 后,维护可用工具列表以及进行工具调用都由MCP来做

MCP意义

  • MCP 解决了 Agent 平台工具调用标准不统一的问题,MCP Server 直接提供功能 API 作为 MCP 工具,供任意 Agent 平台直接使用,避免 Agent 平台重复造轮子,大幅降低开发成本
  • MCP 通过标准化协议和客户端-服务器架构,提升了 AI 的上下文感知、自动化能力和安全性
  • MCP 的客户端-服务器架构支持模块化扩展,一个客户端可连接多个服务器,Agent 平台可以轻松解锁多样化功能

4. MCP vs Function Calling

功能深度不同

  • MCP: 连接层
  • Function Calling: 执行层
  • AI Agent: 决策层

应用场景不同

  • MCP: 需要连接大量工具,但不想写代码
  • Function Calling: 简单任务自动化
  • AI Agent: 自主决策

5. MCP 架构

MCP 采用客户端-服务器架构

6. MCP 核心组件

​核心组件

  • Host 主机
    • 是应用程序(如 Claude 桌面或 IDEs),它们发起连接
  • Client 客户端
    • 在主机应用程序内部与服务器保持 1:1 的连接
  • Server 服务器
    • 为客户端提供上下文、工具和提示
  • Protocol layer 协议层
    • 协议层处理消息封装、请求/响应链接和高级通信模式
  • Transport layer 传输层
    • 传输层负责处理 Client 和 Server 之间的实际通信

MCP 支持多种传输机制,所有传输都使用 JSON-RPC 2.0 来交换消息

  • Stdio transport (标准输入输出传输)
    • 使用标准输入/输出进行通信
    • 适用于本地进程
  • HTTP with SSE transport (使用 SSE 传输的 HTTP)
    • 服务器到客户端的消息传输: 服务器发送事件方式(Server-Sent Events,SSE)
    • 客户端到服务器的消息传输: HTTP POST方式

消息类型

  • Requests
  • Results
  • Errors
  • Notifications

7. MCP 连接生命周期

7.1. Initialization 初始化

7.2. Message exchange 消息交换

  • 客户端或服务器发送请求,另一方响应
  • 任一方发送单向消息(Notifications)

7.3. Termination 终止

任何一方均可终止连接

8. MCP 功能类型

MCP 服务器提供三种主要功能类型:

  • Tools 工具
    • 可以由 LLM 集成的功能
    • 常见工具类型
      • System operations 系统操作
        • 与本地系统交互的工具
      • API integrations API 集成
        • 封装外部 API 的工具
      • Data processing 数据处理
        • 转换或分析数据的工具
    • https://modelcontextprotocol.io/docs/concepts/tools#python
  • Resources 资源
    • 可以由 LLM 读取的任何类型的数据
    • 每个资源都由一个唯一的 URI 标识,可以包含文本或二进制数据
    • Resource types 资源类型
      • Text resources 文本资源
        • 文本资源包含 UTF-8 编码的文本数据。这些适用于:
          • Source code 源代码
          • Configuration files 配置文件
          • Log files 日志文件
          • JSON/XML data JSON/XML 数据
          • Plain text 纯文本
      • Binary resources 二进制资源
        • 二进制资源包含原始的二进制数据,以 base64 编码。这些资源适用于:
          • Images 图片
          • PDFs PDF 文件
          • Audio files 音频文件
          • Video files 视频文件
          • Other non-text formats 其他非文本格式
    • https://modelcontextprotocol.io/docs/concepts/resources
  • Prompts 提示
    • 创建可重用的提示模板和工作流程,帮助用户完成特定任务的预写提示模板
    • https://modelcontextprotocol.io/docs/concepts/prompts

两种额外功能:

  • Sampling
    • MCPServer 调用 MCPClient 的 LLM
    • https://modelcontextprotocol.io/docs/concepts/sampling
  • Roots
    • Roots定义了服务器可以操作的范围
    • 比如客户端在服务器上开了个人工作空间,客户端在工作空间中进行操作,告诉服务器它在用这个工作空间
    • Roots通常用于定义:
      • Project directories 项目目录
      • Repository locations 仓库位置
      • API endpoints API 端点
      • Configuration locations 配置位置
      • Resource boundaries 资源边界
    • https://modelcontextprotocol.io/docs/concepts/roots

9. 不同功能类型的标准协议

9.1. Resources

Resources URIs 统一资源标识符

  • 每个Resource都按以下标准定义
[protocol]://[host]/[path]

9.2. Prompts

Prompt Structure 提示结构

  • 每个Prompt都按以下标准定义
{
  name: string;              // Unique identifier for the prompt
  description?: string;      // Human-readable description
  arguments?: [              // Optional list of arguments
    {
      name: string;          // Argument identifier
      description?: string;  // Argument description
      required?: boolean;    // Whether argument is required
    }
  ]
}

9.3. Tools

Tool definition structure 工具定义结构

  • 每个 Tool 都按以下标准定义
{
  name: string;          // Unique identifier for the tool
  description?: string;  // Human-readable description
  inputSchema: {         // JSON Schema for the tool's parameters
    type: "object",
    properties: { ... }  // Tool-specific parameters
  },
  annotations?: {        // Optional hints about tool behavior
    title?: string;      // Human-readable title for the tool
    readOnlyHint?: boolean;    // If true, the tool does not modify its environment
    destructiveHint?: boolean; // If true, the tool may perform destructive updates
    idempotentHint?: boolean;  // If true, repeated calls with same args have no additional effect
    openWorldHint?: boolean;   // If true, tool interacts with external entities
  }
}

9.4. Sampling

Message format 消息格式

  • 服务器发起 Sampling 请求都按以下标准定义
{
  messages: [
    {
      role: "user" | "assistant",
      content: {
        type: "text" | "image",

        // For text:
        text?: string,

        // For images:
        data?: string,             // base64 encoded
        mimeType?: string
      }
    }
  ],
  modelPreferences?: {
    hints?: [{
      name?: string                // Suggested model name/family
    }],
    costPriority?: number,         // 0-1, importance of minimizing cost
    speedPriority?: number,        // 0-1, importance of low latency
    intelligencePriority?: number  // 0-1, importance of capabilities
  },
  systemPrompt?: string,
  includeContext?: "none" | "thisServer" | "allServers",
  temperature?: number,
  maxTokens: number,
  stopSequences?: string[],
  metadata?: Record<string, unknown>
}

Response format 响应格式

  • 客户端返回 Sampling 结果都按以下标准定义
{
  model: string,  // Name of the model used
  stopReason?: "endTurn" | "stopSequence" | "maxTokens" | string,
  role: "user" | "assistant",
  content: {
    type: "text" | "image",
    text?: string,
    data?: string,
    mimeType?: string
  }
}

10. MCP Client (MCP 客户端)

比较火的MCP客户端

  • 适合非工程师
    • Claude Desktop App
      • Anthropic官方产品,支持MCP的本地工具和数据源集成,适合工程师和非工程师
      • https://claude.ai/download
    • LibreChat
      • 开源聊天应用,支持MCP工具集成
      • https://github.com/danny-avila/LibreChat
  • 适合工程师
    • Cursor
      • AI驱动的代码编辑器,快速采用MCP,适合开发者
      • https://www.cursor.com/
    • Cline
      • 开源VSCode扩展,支持MCP市场和自定义服务器
      • https://github.com/cline/cline
    • Continue
      • VSCode和JetBrains扩展,支持完整的MCP功能
      • https://github.com/continuedev/continue
    • Zed
      • 高性能代码编辑器,支持MCP扩展
      • https://zed.dev/
    • Windsurf Editor
      • AI驱动的代码编辑器,支持MCP协议
      • https://windsurf.com/editor
    • Sourcegraph Cody
      • AI编码助手,支持MCP配置
      • https://sourcegraph.com/cody

应用程序集成对 MCP 支持后(也就是作为 MCP 客户端),可为用户提供强大的 AI 上下文能力,方便用户通过 AI 模型与 MCP 服务器进行交互

不同客户端可能支持不同的 MCP 功能,因此与 MCP 服务器的集成程度也有所差异。

ClientResourcesPromptsToolsSamplingRootsNotes
Claude Desktop App✅✅✅❌❌Supports tools, prompts, and resources.
LibreChat❌❌✅❌❌Supports tools for Agents
Cursor❌❌✅❌❌Supports tools.
Cline✅❌✅❌❌Supports tools and resources.
Continue✅✅✅❌❌Supports tools, prompts, and resources.
Zed❌✅❌❌❌Prompts appear as slash commands
Windsurf Editor❌❌✅❌❌Supports tools with AI Flow for collaborative development.
Sourcegraph Cody✅❌❌❌❌Supports resources through OpenCTX

More:

  • 官网提及的客户端列表:https://modelcontextprotocol.io/clients
  • PulseMCP提及的客户端列表:https://www.pulsemcp.com/clients

10.1. Cursor 作为 MCP 客户端

Cursor 配置 MCP 的介绍:https://docs.cursor.com/context/model-context-protocol

将 Cursor 连接到外部工具或数据源,代替了用户通过 Prompt 告诉 Cursor 外部的一些情况

示例:

传输机制:

11. MCP Server (MCP 服务器)

  • 官方 MCP Servers
    • https://github.com/modelcontextprotocol/servers
    • https://modelcontextprotocol.io/examples
  • Cursor MCP Servers
    • https://cursor.directory/mcp
  • punkpeye 精选 MCP Servers: https://github.com/punkpeye/awesome-mcp-servers/blob/main/README-zh.md
  • Cline的MCP Marketplace
  • https://smithery.ai/
  • https://www.pulsemcp.com/
  • http://glama.ai/mcp/servers

好用的MCP工具

  • 数据与文件系统
    • Filesystem
      • 提供安全的文件操作功能,并且可以配置访问控制权限,确保文件访问的安全性和规范性
    • PostgreSQL
      • 提供只读数据库访问,具备架构检查功能
  • 开发工具
    • GitHub
      • 实现仓库管理、文件操作,还集成了 GitHub API,方便与 GitHub 平台进行交互
    • Git
      • 提供读取、搜索和操作 Git 仓库的工具,帮助开发人员管理代码版本
  • 爬虫
    • Firecrawl:复制网站
  • 网络与浏览器自动化
    • Brave Search:利用Brave的搜索API进行网络和本地搜索。
    • Fetch:为LLM优化的网络内容获取和转换
    • Puppeteer:提供浏览器自动化和网页抓取功能。
    • Playwright:控制浏览器
      • 可以自动化浏览器任务。填表单,截图,导航网页。还能执行JavaScript,监控控制台日志
      • 让LLM和浏览器无缝配合。API测试也变得更简单

12. 不同角色使用 MCP 的方式和逻辑

  • MCP Client 用户 (eg. 使用 Claude Desktop App 或 Cursor 的用户)
    • 只需在配置中添加要使用的 MCP Server 后,正常问问题就行
  • MCP Client 开发者 (eg. Claude Desktop App 或 Cursor的开发者)
    • 实例化 MCP 客户端
    • MCP 客户端与 MCP 服务器建立连接
    • 每当用户发起一个问题,执行以下逻辑(传给LLM的工具列表是和工具调用是由MCP负责的):
      • 拼接用户 query 为 messages
      • MCP Client 获取可用工具列表 available_tools
      • 将 messages 和 available_tools 作为入参调用 LLM
      • LLM 响应有两种:text 或 tool_use
      • 如果是 text 则将文本结果返回给用户,结束
      • 如果是 tool_use
        • MCP Client 通过MCP Server执行工具调用
        • 得到工具调用结果
        • messages 除了用户 query("role": "user")外,接着拼接 LLM 响应("role": "assistant")和工具调用结果("role": "user")为新的 messages
        • 将拼接后的 messages 和 available_tools 作为参数再次调用 LLM
        • 将 LLM 响应结果返回给用户,结束
  • MCP Server 开发者
    • 实例化 MCP 服务器
    • 定义工具,实现每个工具的执行逻辑
    • 运行 MCP 服务器
使用 MCP 的方式和逻辑
使用 MCP 的方式和逻辑

13. MCP Client 用户使用案例(Claude Desktop App)

13.1. 在 Claude Desktop App 中配置想要使用的MCP服务器

在Claude Desktop App 中添加想要使用的 MCP 服务器 "weather" 到 "mcpServers" 配置项

  • 告诉 Claude Desktop App 有一个名为 "weather" 的 MCP 服务器
{
    "mcpServers": {
        "weather": {
            "command": "uv",
            "args": [
                "--directory",
                "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather",
                "run",
                "weather.py"
            ]
        }
    }
}

保存配置后重启Claude Desktop App

13.2. 在 Claude Desktop App 中查看可用工具

可以看到配置的所有服务器提供的所有工具

  • 工具名称
  • 工具的功能描述
  • 工具来源于哪个MCP服务器

13.3. 在 Claude Desktop App 中问一些相关问题

  • 用户发起一个问题
  • Claude分析可用工具,决定是否使用工具和使用哪个工具
  • 如果确定使用MCP工具,该工具对应的MCP Client通过MCP Server执行选中的工具
  • 执行结果返回给Claude
  • Claude将执行结果转为自然语言响应
  • 用户收到响应

14. 构建 MCP 服务器案例

以下代码构建的 MCP 服务器名称为 “weather”,提供了2个工具:get-alerts 和 get-forecast

14.1. 实例化 MCP 服务器

mcp = FastMCP("weather")

14.2. 定义工具,实现每个工具的执行逻辑

  • 加上@mcp.tool()后,一个API就是一个MCP工具
  • 不需要提供一个查询可用工具列表的API
@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)
    """
    pass

@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
    """
    pass

14.3. 运行 MCP 服务器

mcp.run(transport='stdio')

15. 构建 MCP 客户端案例

15.1. 创建 .env 文件

设置Anthropic API key

ANTHROPIC_API_KEY=<your key here>

15.2. 定义 MCPClient 类

load_dotenv()  # load environment variables from .env

class MCPClient:
    def __init__(self):
        # Initialize session and client objects
        self.session: Optional[ClientSession] = None
        self.exit_stack = AsyncExitStack()
        self.anthropic = Anthropic()
    # methods will go here

15.3. 实现服务器连接管理函数 connect_to_server

async def connect_to_server(self, server_script_path: str):
    """Connect to an MCP server

    Args:
        server_script_path: Path to the server script (.py or .js)
    """
    is_python = server_script_path.endswith('.py')
    is_js = server_script_path.endswith('.js')
    if not (is_python or is_js):
        raise ValueError("Server script must be a .py or .js file")

    command = "python" if is_python else "node"
    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()

    # List available tools
    response = await self.session.list_tools()
    tools = response.tools
    print("\nConnected to server with tools:", [tool.name for tool in tools])

15.4. 实现处理查询和工具调用的函数 process_query

async def process_query(self, query: str) -> str:
    """Process a query using Claude and available tools"""
    messages = [
        {
            "role": "user",
            "content": query
        }
    ]

    response = await self.session.list_tools()
    available_tools = [{
        "name": tool.name,
        "description": tool.description,
        "input_schema": tool.inputSchema
    } for tool in response.tools]

    # Initial Claude API call
    response = self.anthropic.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=1000,
        messages=messages,
        tools=available_tools
    )

    # Process response and handle tool calls
    final_text = []

    assistant_message_content = []
    for content in response.content:
        if content.type == 'text':
            final_text.append(content.text)
            assistant_message_content.append(content)
        elif content.type == 'tool_use':
            tool_name = content.name
            tool_args = content.input

            # Execute tool call
            result = await self.session.call_tool(tool_name, tool_args)
            final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")

            assistant_message_content.append(content)
            messages.append({
                "role": "assistant",
                "content": assistant_message_content
            })
            messages.append({
                "role": "user",
                "content": [
                    {
                        "type": "tool_result",
                        "tool_use_id": content.id,
                        "content": result.content
                    }
                ]
            })

            # Get next response from Claude
            response = self.anthropic.messages.create(
                model="claude-3-5-sonnet-20241022",
                max_tokens=1000,
                messages=messages,
                tools=available_tools
            )

            final_text.append(response.content[0].text)

    return "\n".join(final_text)

15.5. 定义 chat_loop 和 cleanup 函数

async def chat_loop(self):
    """Run an interactive chat loop"""
    print("\nMCP Client Started!")
    print("Type your queries or 'quit' to exit.")

    while True:
        try:
            query = input("\nQuery: ").strip()

            if query.lower() == 'quit':
                break

            response = await self.process_query(query)
            print("\n" + response)

        except Exception as e:
            print(f"\nError: {str(e)}")

async def cleanup(self):
    """Clean up resources"""
    await self.exit_stack.aclose()

15.6. 主入口

async def main():
    if len(sys.argv) < 2:
        print("Usage: python client.py <path_to_server_script>")
        sys.exit(1)

    client = MCPClient()
    try:
        await client.connect_to_server(sys.argv[1])
        await client.chat_loop()
    finally:
        await client.cleanup()

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