大模型能力扩展的"万能插座":MCP协议客户端实现技术全景透视

背景

在人工智能技术日新月异的今天,MCP(Model Context Protocol)的协议正在悄然重塑AI应用的开发范式。它的核心优势在于通过标准化协议安全高效地连接AI模型与多元数据工具,打破数据孤岛并实现灵活扩展。由于使用的是标准协议,使得不同厂商的模型、工具和数据源能够无缝对接,编写的MCP服务可以无缝集成到任何支持MCP协议的客户端中,比如Claude Desktop、Cline、5ire等客户端。

▍ 实际应用案例

  • 获取实时时间:通过开发和部署获取当前时间的MCP server,AI能准确回答当前时间。
  • 电商直播管理:AI通过MCP server访问本地数据库,管理商品库存和价格。
  • 素材整理:AI利用MCP server整理视频素材,提高视频剪辑效率。
  • 阅读网页:通过部署MCP server,AI能读取并总结网页内容。

▍ 疑惑

尽管MCP展现出巨大潜力,但像对于上篇文章MCP初体验,也留下了一个疑惑:对于DeepSeek R1这类大模型,它本身是不支持Function Calling,也不支持像Claude模型的Tool use能力,那它是怎么打通MCP协议,从而可以调用mcp定义的各种能力呢?; 本文将简单总结下常见的MCP客户端的实现原理,主要列举了5ire和我们上篇使用的Cline的实现原理,顺便也解答了上篇留下的疑惑。

官方MCP客户端实现

根据官方文档For Client Developers,开发MCP客户端主要有以下几个步骤:

  1. 服务器连接配置
  2. 建立通信会话
  3. 实现查询处理逻辑
  4. 构建交互流程oooo

一条Query的查询如下:

sequenceDiagram participant User as 用户 participant Client as 客户端 participant Claude as Claude处理模块 participant ToolCheck as 工具判断模块 participant MCPServer as MCP服务端 User->>Client: 输入查询请求 activate Client Client->>Claude: 转发用户请求 activate Claude Claude->>ToolCheck: 解析请求内容 activate ToolCheck ToolCheck-->>Claude: 是否需要工具调用? deactivate ToolCheck alt 需要工具调用 Claude->>MCPServer: 工具调用请求 activate MCPServer MCPServer-->>Claude: 工具执行结果 deactivate MCPServer else 无需工具调用 Claude-->>Client: 直接生成响应 end Claude-->>Client: 最终响应内容 deactivate Claude Client-->>User: 展示响应结果 deactivate Client

常见的客户端实现

5ire

查看源码可以看出来,5ire做了一层封装,将OpenAI和Claude的接口做了统一(NextCharService虚类),然后在下层实现中来区分是OpenAI还是Claude接口。

sequenceDiagram participant User as 用户 participant Chat as Chat页面 participant DS as DeepSeekChatService participant OAI as OpenAIChatService participant NCS as NextChatService participant MCP as MCP服务 participant API as DeepSeek API User->>Chat: 发送消息 Note over Chat: 创建消息记录 Chat->>DS: chat(messages) DS->>OAI: 继承 OAI->>NCS: 继承 NCS->>DS: makeRequest() DS->>OAI: makePayload() alt model.toolEnabled = true OAI->>MCP: window.electron.mcp.listTools() MCP-->>OAI: 返回可用工具列表 OAI->>OAI: 过滤&转换工具格式 OAI->>OAI: 添加到payload end DS->>API: 发送请求 API-->>DS: 流式响应 loop 处理响应 DS->>Chat: onReading(chunk) alt 检测到工具调用 DS->>MCP: window.electron.mcp.callTool() MCP-->>DS: 工具执行结果 DS->>DS: makeToolMessages() DS->>DS: 递归调用chat() end end DS->>Chat: onComplete(result) Chat->>User: 展示结果

OpenAI兼容协议实现

  • 对于OpenAI兼容协议接口,工具调用如下:
ts 复制代码
// src/intellichat/services/OpenAIChatService.ts
export default class OpenAIChatService
  extends NextChatService
  implements INextChatService {
  
  ...
	protected makeTool(
		tool: IMCPTool,
	  ): IOpenAITool | IAnthropicTool | IGoogleTool {
		return {
		  type: 'function',
		  function: {
			name: tool.name,
			description: tool.description.substring(0, 1000), // some models have a limit on the description length, like gpt series, so we truncate it
			parameters: {
			  type: tool.inputSchema.type,
			  properties: tool.inputSchema.properties,
			  required: tool.inputSchema.required,
			  additionalProperties: tool.inputSchema.additionalProperties,
			},
		  },
		};
	  }
	  
	  protected makeToolMessages(
		tool: ITool,
		toolResult: any,
	  ): IChatRequestMessage[] {
		return [
		  {
			role: 'assistant',
			tool_calls: [
			  {
				id: tool.id,
				type: 'function',
				function: {
				  arguments: JSON.stringify(tool.args),
				  name: tool.name,
				},
			  },
			],
		  },
		  {
			role: 'tool',
			name: tool.name,
			content:
			  typeof toolResult === 'string' ? toolResult : toolResult.content,
			tool_call_id: tool.id,
		  },
		];
	  }
	  ...
}

可以看到OpenAI兼容协议接口,使用的是Function Calling的能力,因此对于不支持Function Calling的大模型,是没法开启MCP的

请求示例
  • 请求体
json 复制代码
{
  "model": "deepseek-chat",
  "messages": [
    {"role": "user", "content": "查询北京的天气"},
    // 可能包含历史工具调用消息...
  ],
  "temperature": 1,
  "stream": true,
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "Weather--getCurrent",
        "description": "获取当前天气",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "城市名称"
            }
          },
          "required": ["location"]
        }
      }
    }
  ],
  "tool_choice": "auto"
}
  • 响应体
json 复制代码
[
  {
    "role": "assistant",
    "tool_calls": [
      {
        "id": "call_abc123", 
        "type": "function",
        "function": {
          "name": "Weather--getCurrent",
          "arguments": "{\"location\":\"北京\"}"
        }
      }
    ]
  }
]
  • 系统执行工具后生成新消息:
json 复制代码
[
  {
    "role": "user", 
    "content": "查询北京的天气"
  },
  {
    "role": "assistant",
    "tool_calls": [
      {
        "id": "call_abc123",
        "type": "function", 
        "function": {
          "name": "Weather--getCurrent",
          "arguments": "{\"location\":\"北京\"}"
        }
      }
    ]
  },
  {
    "role": "tool",
    "name": "Weather--getCurrent", 
    "content": "北京 晴 25℃",
    "tool_call_id": "call_abc123"
  }
]

Anthropic协议实现

  • 对于Anthropic协议接口,工具调用如下:
ts 复制代码
// src/intellichat/services/AnthropicChatService.ts
export default class AnthropicChatService
  extends NextChatService
  implements INextChatService
{
...
protected makeToolMessages(
    tool: ITool,
    toolResult: any
  ): IChatRequestMessage[] {
    return [
      {
        role: 'assistant',
        content: [
          {
            type: 'tool_use',
            id: tool.id,
            name: tool.name,
            input: tool.args ?? {},
          },
        ],
      },
      {
        role: 'user',
        content: [
          {
            type: 'tool_result',
            tool_use_id: tool.id,
            content:
              typeof toolResult === 'string' ? toolResult : toolResult.content,
          },
        ],
      },
    ];
  }

  protected makeTool(tool: IMCPTool): IOpenAITool | IAnthropicTool {
    return {
      name: tool.name,
      description: tool.description,
      input_schema: {
        type: tool.inputSchema.type,
        properties: tool.inputSchema.properties,
        required: tool.inputSchema.required,
        additionalProperties: tool.inputSchema.additionalProperties,
      },
    };
  }
...
}

同样的Anthropic的Claude模型使用的是Tool Use能力。

请求示例
  • 请求体
json 复制代码
{
  "model": "claude-3-5-sonnet-20240620",
  "messages": [
    {"role": "user", "content": "查询上海未来三天的天气"},
    // 可能包含历史工具调用消息...
  ],
  "temperature": 0.7,
  "stream": true,
  "tools": [
    {
      "name": "Weather--getForecast",
      "description": "获取天气预报",
      "input_schema": {
        "type": "object",
        "properties": {
          "location": {"type": "string", "description": "城市名称"},
          "days": {"type": "number", "description": "预报天数"}
        },
        "required": ["location"]
      }
    }
  ],
  "tool_choice": {
    "type": "auto",
    "disable_parallel_tool_use": true
  }
}
  • 响应
json 复制代码
{
  "content": [
    {
      "type": "tool_use",
      "id": "toolu_01",
      "name": "Weather--getForecast",
      "input": {
        "location": "上海",
        "days": 3
      }
    }
  ]
}
  • 系统执行工具后生成新消息:
json 复制代码
[
  {
    "role": "assistant",
    "content": [
      {
        "type": "tool_use",
        "id": "toolu_01",
        "name": "Weather--getForecast",
        "input": {
          "location": "上海",
          "days": 3
        }
      }
    ]
  },
  {
    "role": "user",
    "content": [
      {
        "type": "tool_result",
        "tool_use_id": "toolu_01",
        "content": "上海未来三天:晴转多云,25-32℃"
      }
    ]
  }
]

DeepSeek的实现

比如下面的DeepSeek的实现中,就可以看出来DeepSeek Reaonser由于不支持Function Calling,它的toolEnabled=false,因此它在5ire中就是不能开启MCP的。

yaml 复制代码
// src/providers/DeepSeek.ts
export default {
  name: 'DeepSeek',
  apiBase: 'https://api.deepseek.com',
  currency: 'CNY',
  options: {
    apiBaseCustomizable: true,
    apiKeyCustomizable: true,
  },
  chat: {
    apiSchema: ['base', 'key', 'model'],
    presencePenalty: { min: -2, max: 2, default: 0 },
    topP: { min: 0, max: 1, default: 1 },
    temperature: { min: 0, max: 2, default: 1 },
    options: {
      modelCustomizable: true,
    },
    models: {
      'deepseek-chat': {
        name: 'deepseek-chat',
        contextWindow: 65536,
        maxTokens: 8192,
        defaultMaxTokens: 2048,
        inputPrice: 0.0006,
        outputPrice: 0.002,
        isDefault: true,
        description: `60 tokens/second, Enhanced capabilities,API compatibility intact`,
        toolEnabled: true,
        group: 'DeepSeek',
      },
      'deepseek-reasoner': {
        name: 'deepseek-reasoner',
        contextWindow: 65536,
        maxTokens: 8192,
        defaultMaxTokens: 2048,
        inputPrice: 0.003,
        outputPrice: 0.016,
        isDefault: true,
        description: `Performance on par with OpenAI-o1`,
        toolEnabled: false,
        group: 'DeepSeek',
      },
    },
  },
} as IServiceProvider;

RooCode\Cline

在RooCode\Cline中可以它们使用的是不同的方式,它没有使用模型自带的FunctionCalling或者ToolUse能力,而是完全使用Prompt来实现,整个MCP的System Prompt非常长(这也是为什么它非常费token的原因之一,如果请求中不用MCP的话,关闭MCP可以大大节约tokne),详细的可以参考Cline System Prompt

实现原理

  • Cline调用MCP的流程图
flowchart TB start[模型返回消息] --> parseMessage[parseAssistantMessage解析] parseMessage --> generateBlocks[生成ContentBlocks数组] generateBlocks --> presentMessage[presentAssistantMessage处理] presentMessage --> blockType{block类型判断} blockType -->|text| handleText[外理文本内容] blockType -->|tool_use| handleTool[外理工具调用] handleTool --> toolType{工具类型判断} toolType --> useMcpTool[use_mcp_tool] useMcpTool --> validate[参数验证] validate --> executeTask[执行工具] executeTask --> outputResult[处理结果] outputResult --> respondToHuman[更新状态]

比如对于下面大模型返回结果:

xml 复制代码
1. 任务要求获取指定URL的文章内容并总结要点
2. 可以使用MCP服务器的fetch工具来获取网页内容
3. 需要提供URL参数,用户已明确给出
4. 其他参数可以使用默认值

<use_mcp_tool>
	<server_name>fetch</server_name>
	<tool_name>fetch</tool_name>
	<arguments>
		{
		 "url": "https://juejin.cn/post/7430620710605422644"
		}
	</arguments>
</use_mcp_tool>
  • 解析后的block数组如下
csharp 复制代码
[
  {
    type: "text",
    content: "1. 任务要求获取指定URL的文章内容并总结要点...4. 其他参数可以使用默认值",
    partial: false
  },
  {
    type: "tool_use",
    name: "use_mcp_tool",
    params: {
      server_name: "fetch",
      tool_name: "fetch",
      arguments: '{"url": "https://juejin.cn/post/7430620710605422644"}'
    },
    partial: false
  }
]

多种类型时,text类型直接展示给用户(只是展示),tool_use会继续调用工具,然后请求大模型

其中content Blocks解析的流程如下:

flowchart TD A[parseAssistantMessage返回contentBlocks] --> B{遍历contentBlocks} B --> C{检查block类型} C -->|text| D[处理文本内容] C -->|tool_use| E{检查工具类型} E --> F{检查是否已拒绝工具} F -->|是| G[跳过工具执行] F -->|否| H{工具类型判断} H --> I[execute_command] H --> J[read_file] H --> K[write_to_file] H --> L[use_mcp_tool] H --> M[其他工具...] I --> N[验证参数] J --> N K --> N L --> N M --> N N -->|参数有效| O[执行工具] N -->|参数无效| P[错误处理] O --> Q[处理工具结果] Q --> R[更新状态] R --> B

cline是将流式文本按照字符遍历来处理的,没有使用正则表达式。

请求示例

  • 请求体
json 复制代码
{
  "model": "deepseek-r1",
  "temperature": 0,
  "messages": [
    {
		"role": "system",
      	"content":"You are Roo,...(long text)"
	},
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "<task>\n测试下,获取https://juejin.cn/post/7430620710605422644正文,总结要点\n</task>"
        },
        {
          "type": "text",
          "text": "<environment_details>\n# VSCode Visible Files\n..."
		}
      ]
    }
  ],
  "stream": true,
  "stream_options": {
    "include_usage": true
  }
}
  • 响应
json 复制代码
<thinking>
1. 任务要求获取指定URL的文章内容并总结要点
2. 可以使用MCP服务器的fetch工具来获取网页内容
3. 需要提供URL参数,用户已明确给出
4. 其他参数可以使用默认值
</thinking>

<use_mcp_tool>
<server_name>fetch</server_name>
<tool_name>fetch</tool_name>
<arguments>
{
 "url": "https://juejin.cn/post/7430620710605422644"
}
</arguments>
</use_mcp_tool>

只把content中的内容摘取出来

  • 再次请求
swift 复制代码
{
  "messages": [
    // 历史消息...
    {
      "role": "assistant",
      "content": "\n\n<use_mcp_tool>\n<server_name>fetch</server_name>\n<tool_name>fetch</tool_name>\n<arguments>\n{\n  \"url\": \"https://juejin.cn/post/7430620710605422644\",\n  \"raw\": false\n}\n</arguments>\n</use_mcp_tool>"
    },
    {
      "role": "user", 
      "content": [
        { "type": "text", "text": "[use_mcp_tool for 'github'] Result:" },
        {
          "type": "text",
          "text": "Contents of https://juejin.cn/post/7430620710605422644:\n        \n## 环境安装\n\n### 安装ollama..."}
      ]
    },
	{
          "type": "text",
          "text": "<environment_details>\n# VSCode Visible Files\n..."
	}
  ]
}

其实通过Claude Tool use system prompt的官方文档可以发现,它也是通过设置了System Prompt来实现的,只不过如果是使用了tools参数,系统帮你自动加上了这些System Prompt。其实和Cline是类似的思路。

方案对比与选型建议

实现方式对比

5ire技术方案
graph TD A[模型选择] --> B{支持工具调用} B -->|是| C[使用原生能力] B -->|否| D[无法启用MCP] C --> E[OpenAI Function Calling] C --> F[Claude Tool Use] E --> G[动态注册工具] F --> G G --> H[自动递归处理]
Cline技术方案
graph LR A[Prompt工程] --> B[长System Prompt] B --> C[自定义XML标签] C --> D[流式解析引擎] D --> E[混合内容处理] E --> F[文本即时呈现] E --> G[工具调用执行]

核心差异对比

维度 5ire方案 Cline方案
技术路径 模型原生能力 Prompt工程
协议兼容性 严格遵循OpenAI/Anthropic规范 自定义交互协议
工具发现 动态获取MCP服务端注册工具 硬编码在System Prompt中
执行效率 单次交互完成工具调用 需多轮消息交互
适用模型 GPT-4/Claude等新一代模型 任意文本生成模型

选型决策树

flowchart TD Start[模型选择] --> A{是否支持原生工具调用?} A -->|是| B[推荐5ire方案] A -->|否| C{是否需要快速验证?} C -->|是| D[选择Cline方案] C -->|否| E[考虑模型升级] B --> F[获得最佳性能] D --> G[牺牲效率换取兼容性]

开发者建议

  • 🚀 短期项目优先考虑Cline的灵活性
  • ⚖️ 折中方案可组合使用两种客户端

总结

  • 对文解答了上篇文章【AIGC】MCP初体验留下的疑问:对于DeepSeek R1这类大模型,它本身是不支持Function Calling,也不支持像Claude模型的Tool use能力,那它是怎么打通MCP协议,从而可以调用mcp定义的各种能力呢?,分析了RooCode\Cline是如何实现MCP调用的;
  • 对比分析了两种MCP客户端实现方案:基于模型原生工具调用能力的5ire方案(OpenAI/Claude适配)和通过Prompt工程实现的Cline方案(通用模型适配),解析了二者的技术原理、优劣差异及适用场景。

参考

官方实现的客户端示例

通过prompt来实现MCP,强制其返回json数据格式,和Cline类似。

使用OpenAI SDK来实现,从而可以让DeepSeek Chat等支持Function Calling的大模型可以接入MCP,和5ire的原理类似,通过代码来将Claude的协议兼容到OpenAI标准协议接口中。但是对于不支持Function Calling的大模型,是没法支持的。

将不同模型统一成OpenAI兼容的FunctionCalling定义格式,使用的是原生的FunctionCalling或者ToolUse能力,然后再统一调用Tool

参考了上面mcp-cli的实现,也是使用了原生的FunctionCalling或者ToolUse能力

相关推荐
Captaincc2 小时前
MCP开发实战-如何使用MCP真正加速UE项目开发
ai编程·mcp
货拉拉技术5 小时前
LLM 驱动前端创新:AI 赋能营销合规实践
前端·程序员·llm
yaocheng的ai分身6 小时前
用cursor操作figma,设计师的春天也来了
cursor·mcp
xidianjiapei0017 小时前
LLM架构解析:词嵌入模型 Word Embeddings(第二部分)—— 从基础原理到实践应用的深度探索
llm·bert·word2vec·elmo·cbow·llm架构·词嵌入模型
xidianjiapei0018 小时前
构建大语言模型应用:句子转换器(Sentence Transformers)(第三部分)
人工智能·语言模型·自然语言处理·llm·transformer
白云千载尽10 小时前
AI时代下的编程——matlib与blender快捷编程化、初始MCP
java·人工智能·大模型·llm·blender
浪漫程序10 小时前
OWL 简明指南:快速上手
人工智能·llm·aigc
Canace11 小时前
用 Cursor 提高工作效率实战笔记
前端·cursor·mcp
Captaincc1 天前
🔥A16z最近报告原因深入探讨MCP及其在未来AI工具中的作用
mcp
墨风如雪1 天前
MCP服务宝库:让AI从聊天到实干的「技能超市」全解析
aigc·mcp