理论+代码一文带你深入浅出MCP:人工智能大模型与外部世界交互的革命性突破

前言

在人工智能技术飞速发展的今天,大语言模型不再局限于对话问答,Function Calling技术让大语言模型的能力边界不断拓展。(关于Function Calling技术大家不了解的可阅读我的文章从0到1开发DeepSeek天气助手智能体------你以为大模型只会聊天?Function Calling让它"上天入地")。然而,Function Calling复杂的开发逻辑始终困扰着程序员和企业------如何让"聪明"的模型高效可靠地与外部世界进行交互呢?2024年底,由Anthropic(开发出Claude大模型的公司)提出了 模型上下文协议(Model Context Protocol,简称MCP) ,为解决这一难题提供了标准化方案,迅速成为AI领域的热门话题。下面我将深入剖析MCP技术的诞生背景、核心优势,以及与传统的Function Calling技术的本质区别,并通过MCP客户端和服务端开发的代码案例带大家走进这一重塑AI应用的开发范式的重要协议。

一、MCP协议诞生的背景:大模型交互的"巴别塔困境"

能调用外部工具是大模型从聊天机器人进化成AI Agent的关键,不过由于底层技术限制,大模型本身是无法和外部工具直接通信的,连查询天气都做不到。为解决这个问题,2023年3月OpenAI提出了Function Calling的思路:通过创建一个外部函数为中介,一边传递大模型请求,另一边调用外部工具,从而使大模型能间接处理外部信息,相当于大模型具备外部工具调用能力。

Function Calling是个非常不错的技术设计,自诞生以来一直被奉为圭臬。但该技术也有很严重的问题------编写外部函数的工作量太大了,一个简单的外部函数往往就得上百行代码,而且为了让大模型认识这些函数,还需要额外为每个外部函数编写一个JSON Schema格式的功能说明,此外,我们还需要为函数设计一个提示词模板description, 才能提高Function Calling响应的准确率。这种开发模式会使开发者为每个系统单独编写适配接口,处理不同的认证方式、数据格式和调用规范,即使同样的功能,不同的人也会有N种开发版本,造成开发过程费时费力且不利于后期维护。以我们上一篇Function Calling开发天气助手为例,需要完成下图所示的所有繁琐逻辑:

这种困境在业界被称为"巴别塔困境"------每个开发者都"说着不同语言",用着不同的"开发习惯",正是在这样的背景下。2024年11月 Anthropic发布了MCP(Model Context Protocol)模型上下文协议 ,它不是一种特定技术,而是一种协议,是智能体AI Agent开发过程中共同约定的一种规范。MCP统一规范采用分布式架构,分为Client客户端Server服务端两部分,客户端用户基于大模型编写,服务端暴露扩展大模型功能的相关函数,通常来说用户只需要编写一套客户端代码,就可以接入符合MCP开发规范的所有服务端(不管是自己编写的,还是利用别人编写上传的服务端)。这种统一的开发模式就好比秦始皇的"书同文、车同轨",有了统一的规范,大家的协作效率就能大幅度提升,最终提升智能体AI Agent的开发效率。

二、MCP协议的技术架构与核心优势

2.1 MCP标准化接口:一次开发,多人复用

MCP最突出的优势在于它的标准化程度 , 与Function Calling需要为每个API单独适配不同,MCP提供了一套统一的调用规范,涵盖接口定义、异常处理、方法参数、返回值和文档等各个方面。开发者按照MCP标准开发完成服务端后,其它任何想使用同样功能的开发者都可以通过大模型客户端访问,无需额外的适配工作。举个例子:如果我们按照MCP服务端开发方法开发出MCP天气服务并发布,其它所有开发者都可以访问服务器的get_weather()方法获得天气情况,不需要重复编写类似功能的函数。

访问 MCP.So网站,可以看到知名公司已经开放了多种多样的MCP SERVER, 这里以百度地图MCP为例,只要按照文档中的操作接入百度地图MCP服务, 就可以访问百度地图开放的查询位置、寻找路径等多种方法:

现在只要你本地运行的环境支持MCP协议,也就是安装MCP相关的库,仅需几行代码就可以接入海量的外部MCP工具,是不是感觉Agent开发门槛瞬间降低了。

2.2 MCP架构设计:分布式与异步

MCPFunction Calling两种技术目标都是为了增强大模型的外部交互能力,但Function Calling技术采用集中式架构,函数定义,模型调用和执行逻辑是紧密耦合在单一应用中的。设计简单直接,适合小型项目但难以拓展,一遇到复杂项目代码就变得很臃肿(我们一个简单天气智能体都写了150行代码)。

MCP则采用了分布式架构,明确分离了工具提供者(MCP Server)和工具消费者(MCP Client),是不是很像现在开发项目的前后端分离模式,这种解耦使各组件能够独立演化,更适合企业级应用。

还有一点很重要,Function Calling通常采用同步执行模型,模型发起请求后等待响应,这在处理耗时操作时一定会导致性能瓶颈,比如当一个用户查询天气时,另一个用户只能排队等他查询完。MCP原生就支持异步操作,允许长时间任务在后台执行,通过回调机制通知用户结果。这种模式更符合真实业务场景,使AI能并行处理多个请求,显著提升吞吐量。

2.3 MCP开发生态:快速开发的强大生态支持

既然MCP是协议,必然是使用的人越多越有用,为了进一步普及MCP,Anthropic还提供了一整套MCP客户端、服务端SDK等各种开发工具,并且支持Python, TypeScript和Java等多种语言,借助SDK, 仅需几行代码就可以快速开发一个MCP服务器了。

三、Python MCP 开发简单示例

分享了这么多理论的东西,下面我们使用Python编写一个MCP的小例子让大家直观了解MCP客户端和服务端的开发流程,全部代码可见codecopy.cn/post/0ja6zf

3.1 环境准备

MCP开发要求借助uv进行虚拟环境创建和依赖管理,uv是一个Python依赖管理工具(类似于anaconda, 但更轻量级)。

  1. pip install uv安装uv
  1. 使用uv init mcp-demo初始化项目,然后cd mcp-demo进入新创建的文件夹, 可以看到新建的项目已经有一些默认文件
  1. 创建并激活MCP客户端虚拟环境:
python 复制代码
uv venv #创建虚拟环境
.venv\Scripts\activate #windows下激活虚拟环境的方式
source .venv/bin/activate # linux下激活虚拟环境的方式

3.2 编写MCP Server

  1. 编写服务端之前,首先cd mcp-demo进入服务端目录,执行.venv\Scripts\activate激活服务端环境,执行如下命令安装依赖包
python 复制代码
# uv使用uv add添加python 软件包
uv add mcp # 添加mcp依赖包,用于mcp服务端快速创建
  1. 因为大模型的算术计算能力是比较差的,我们这里编写一个加法函数供大模型调用,在mcp-demo目录下面新建server.py,编写如下代码。
python 复制代码
from mcp.server.fastmcp import FastMCP

# 创建MCP 服务
mcp = FastMCP('Demo')

@mcp.tool()
def add(a:int, b:int) ->int:
  """"
  计算两个整数的和并返回
  """
  return a+b

if __name__ == "__main__":
  # 以标准 I/O 方式运行 MCP 服务器
  mcp.run(transport='stdio')
  1. 以上通过Anthropic的python sdk开发MCP服务器是不是很简单? 代码中有两点注意事项:(1)代码中add函数一定要写清注释, @mcp.tool()会根据add函数的注释自动添加函数的JSON_Schema说明格式,决定了大模型是否认识并选择调用该函数。(2)本次代码以标准I/O的方式进行MCP服务端和客户端交互,MCP客户端和服务器通信有两种方式,标准I/O更适用于本地编写或把服务端代码Pull到本地访问的场景,Http SSE更适用于远程访问MCP服务端的场景,这部分内容笔者以后会出详细教程分享~

3.3 编写MCP 客户端代码

  1. 编写客户端代码之前,首先执行如下命令安装依赖包
python 复制代码
uv add openai # 添加openai依赖包,用于调用大模型
  1. mcp-demo文件夹下新建client.py编写客户端代码,客户端代码的格式如下, __init__初始化MCP客户端,定义访问大模型的参数。connect_to_server链接到MCP服务端并列出MCP服务端可用的工具函数, process_query调用大模型查询并调用可用的服务端MCP工具, chat_loop运行交互式聊天循环,其余具体细节我都写在代码注释中
python 复制代码
import asyncio
import json
from typing import Optional
from contextlib import AsyncExitStack
from openai import OpenAI

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

class MCPClient:
    def __init__(self):
        """初始化MCP客户端"""
        self.exit_stack = AsyncExitStack()
        self.opanai_api_key = "替换你的api_key" # 调用模型的api_key
        self.base_url = "https://api.deepseek.com" # 调用模型url, 这里以deepseek作演示
        self.model = "deepseek-chat" # 调用deepseek-v3模型
        self.client = OpenAI(api_key=self.opanai_api_key, base_url=self.base_url)
        self.session: Optional[ClientSession] = None # Optional提醒用户该属性是可选的,可能为None
        self.exit_stack = AsyncExitStack() # 用来存储和清楚对话中上下文的,提高异步资源利用率

    async def connect_to_server(self, server_script_path):
        """连接到MCP服务器并列出MCP服务器的可用工具函数"""
        server_params = StdioServerParameters(
        command="python",
            args=[server_script_path],
            env=None
        ) # 设置启动服务器的参数, 这里是要用python执行server.py文件

        # 启动MCP服务器并建立通信
        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() # 与服务器建立stdio连接

        # 列出MCP服务器上的工具
        response = await self.session.list_tools()
        tools = response.tools
        print("\n已连接到服务器,支持以下工具:", [tool.name for tool in tools])#打印服务端可用的工具
  
    async def process_query(self, query:str)->str:
        """使用大模型处理查询并调用MCP Server可用的MCP工具"""
        messages = [{"role":"user", "content":query}]
        response = await self.session.list_tools()

        available_tools = [{
            "type": "function",
            "function": {
                "name": tool.name,
                "description": tool.description,
                "input_schema": tool.inputSchema
            }
        } for tool in response.tools]

        response = self.client.chat.completions.create(
            model=self.model,
            messages=messages,
            tools=available_tools
        )

        # 处理返回内容
        content = response.choices[0]
        if content.finish_reason == "tool_calls":
            # 返回结果是使用工具的建议,就解析并调用工具
            tool_call = content.message.tool_calls[0]
            tool_name = tool_call.function.name
            tool_args = json.loads(tool_call.function.arguments)
            # 执行工具
            result = await self.session.call_tool(tool_name, tool_args)
            print(f"\n\n[Calling tool {tool_name} with args {tool_args}]\n\n")
            # 将模型返回的调用工具的对话记录保存在messages中
            messages.append(content.message.model_dump())
            messages.append({
                "role": "tool",
                "content": result.content[0].text,
                "tool_call_id": tool_call.id,
            })
            # 将上面的结果返回给大模型用于生产最终结果
            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages
            )
            return response.choices[0].message.content
        return content.message.content
  
    async def chat_loop(self):
        """运行交互式聊天"""
        print("\n MCP客户端已启动!输入quit退出")

        while True:
            try:
                query = input("\n用户:").strip()
                if query.lower() == 'quit':
                    break
                response = await self.process_query(query)
                print(f"\nDeepSeek-V3-0324: {response}")
            except Exception as e:
                print(f"发生错误: {str(e)}")

    async def clean(self):
        """清理资源"""
        await self.exit_stack.aclose()

async def main():
    if len(sys.argv) < 2:
        print("使用方法是: python client.py server.py")
        sys.exit(1)

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

if __name__ == "__main__":
    import sys
    asyncio.run(main())
  1. 大家有没有发现该客户端代码最大的优势就是完全没有绑定具体函数调用和大模型的逻辑,也就是说该客户端代码无论面对哪种类型的MCP Server都可以成功执行,这样我们在以后开发中可以保持MCP Client的代码不变,专注于快速开发或者访问不同的MCP Server,可以便捷的使用大模型开发不同能力的AI Agent智能体,这就是MCP相比于Function Calling最大的技术优势!分布式的架构,解耦的代码等于更便捷快速的开发速度。

3.4 测试运行

mcp-demo路径下使用.venv\Scripts\activate激活虚拟环境,执行 python client.py server.py

可以看到大模型成功调用了MCP Server的add函数并返回了执行处理的结果,我们通过Python开发MCP的简单示例,向大家展示MCP客户端和服务端的开发全貌,帮助大家更全面理解MCP特性~

四、未来展望:MCP是AI应用的新纪元

本次分享中我们对MCP的优势和标准化进行深入分析,同时通过代码实践进一步感受到了MCP开发相比Function Calling的巨大优势。

回顾计算机发展史,TCP/IP、HTTP等标准协议的建立曾催生互联网革命;在AI时代,MCP协议有望扮演类似的角色,成为智能应用互联互通的基石。正如原美团联合创始人王慧文所言:"越来越觉得,Anthropic会替代OpenAI成为AGI担当"

在AI从"玩具"走向"工具"的关键转折点上,MCP协议消除了模型与真实世界之间的隔阂,让智能真正服务于业务需求。这场由Anthropic发起的"标准之战",或许正是AI技术普惠化的关键一步。

接下来笔者会分享MCP的更多内容,让大家都能快速上手MCP, 开发属于自己的AI Agent智能体!感兴趣大家可关注我的掘金账号,同名wx公众号:大模型真好玩, 免费分享工作生活中遇到的大模型相关知识和教程,大家感兴趣可关注一下~

相关推荐
Mengke6 小时前
以「asset-price-mcp」为例,从 0 开发 MCP Server
llm·mcp
阿坡RPA11 小时前
手搓MCP客户端&服务端:从零到实战极速了解MCP是什么?
人工智能·aigc
用户277844910499312 小时前
借助DeepSeek智能生成测试用例:从提示词到Excel表格的全流程实践
人工智能·python
机器之心12 小时前
刚刚,DeepSeek公布推理时Scaling新论文,R2要来了?
人工智能
算AI14 小时前
人工智能+牙科:临床应用中的几个问题
人工智能·算法
JavaEdge在掘金14 小时前
ssl.SSLCertVerificationError报错解决方案
python
我不会编程55514 小时前
Python Cookbook-5.1 对字典排序
开发语言·数据结构·python
凯子坚持 c15 小时前
基于飞桨框架3.0本地DeepSeek-R1蒸馏版部署实战
人工智能·paddlepaddle
老歌老听老掉牙15 小时前
平面旋转与交线投影夹角计算
python·线性代数·平面·sympy
满怀101515 小时前
Python入门(7):模块
python