mcp学习笔记

MCP(Model Context Protocol)是一种由Anthropic推出的开放协议,旨在统一大型语言模型(LLM)与外部数据源/工具之间的交互。其核心组件包括 ​MCP ClientMCP Server ​ 和 ​Function Calling​ 机制,三者协同工作以扩展LLM的能力,使大模型能够调用外部工具。

MCP协议是提示词工程和Function calling技术发展的产物,将如何调用外部工具这个事进行规范和统一。本文使用Python实现了一个简单的mcp demo程序,可实现利用自然语言来操作数据库,发送手机验证码和邮件。

一、MCP基本概念和原理

首先需要了解MCP中几个重要的概念,可参考这里MCP 简介 - MCP 中文文档

mcp中几个要素的关系如下图所示,用户通过mcp client与大模型进行交互,mcp server提供为mcp client提供外部工具。

使用mcp协议调用外部工具的过程如下:

s1:用户向mcp client提出问题;

s2:mcp client将用户的问题和tools列表一同提交给大模型;

s3:大模型会根据用户的问题,从tools列表中取出最相关最适合的工具,构造好参数,向client返回工具名和参数,也有可能找不到;

s4:mcp client调用根据工具名和参数调用对应的接口,可以是本地的函数,也可以是web api,取决于mcp server如何实现;

s5:mcp client将工具的结果提交给大模型;

s6:大模型对工具返回的结果,来回答用户的原始问题;

s7: mcp client将大模型最终的回答呈现给用户。

二、Function calling

Function Calling 允许LLM根据用户请求生成结构化参数(如JSON格式),触发预定义的外部函数或API调用,从而获取实时数据或执行具体操作。例如用户询问天气时,模型提取参数(城市、日期)并调用天气API。

原理:

  1. 开发者注册函数名称、参数类型及描述(类似JSON Schema),要符合json schema规范。

  2. 模型解析用户请求,匹配适用的函数。

  3. 提取关键信息并格式化为结构化参数(如{"location":"北京"}

  4. 外部系统调用函数后,结果返回模型生成最终回答。

Function calling功能需要对模型进行微调训练,使模型能够理解json schema格式语法,为函数构造正确的json格式参数。

目前支持Function calling功能的模型有

  • OpenAI GPT系列​:从GPT-3.5开始集成此功能,2023年就有了

  • Anthropic Claude​:支持与外部服务深度集成。

  • 国产模型​:阿里Qwen、DeepSeek v3等通过微调实现类似能力

三、Function calling示例

下面我们使用python写一个示例程序,来演示如何使用function calling功能,实现利用自然语言来操作数据库和访问第三方接口。这里我们使用的模型是qwen3-32b。

python如何调用通义模型可参考阿里百炼的官方API文档

下面是阿里官方的例子,可作为参考。

python 复制代码
import os
from openai import OpenAI

client = OpenAI(
    # 若没有配置环境变量,请用百炼API Key将下行替换为:api_key="sk-xxx",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",  # 填写DashScope SDK的base_url
)

tools = [
    # 工具1 获取当前时刻的时间
    {
        "type": "function",
        "function": {
            "name": "get_current_time",
            "description": "当你想知道现在的时间时非常有用。",
            "parameters": {}  # 因为获取当前时间无需输入参数,因此parameters为空字典
        }
    },  
    # 工具2 获取指定城市的天气
    {
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "当你想查询指定城市的天气时非常有用。",
            "parameters": {  
                "type": "object",
                "properties": {
                    # 查询天气时需要提供位置,因此参数设置为location
                    "location": {
                        "type": "string",
                        "description": "城市或县区,比如北京市、杭州市、余杭区等。"
                    }
                },
                "required": ["location"]
            }
        }
    }
]
messages = [{"role": "user", "content": "杭州天气怎么样"}]
completion = client.chat.completions.create(
    model="qwen-plus",  # 此处以qwen-plus为例,可按需更换模型名称。模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
    messages=messages,
    tools=tools
)

print(completion.model_dump_json())

1.获取阿里百炼的api key

这步比较简单不再展开,现在可领取100万的免费tokens额度。

2.mcp_client

代码如下,功能见注释。

python 复制代码
import json
import os
from openai import OpenAI
from mcp_server import *

# 模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
MODEL = "qwen3-32b"  # 模型名称

def main():
    # 参考https://bailian.console.aliyun.com/?tab=api#/api/?type=model&url=https%3A%2F%2Fhelp.aliyun.com%2Fdocument_detail%2F2712576.html&renderType=iframe
    client = OpenAI(
        # 若没有配置环境变量,请用百炼API Key将下行替换为:api_key="sk-xxx",
        api_key=os.getenv("Bailian_API_Key"),
        base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    )
    messages=[
            {"role": "system", "content": "You are a helpful assistant."}
        ]
    while True:
        query = input("请输入问题:")
        if query == "exit":
            break
        elif query == "clear":
            os.system("cls")
            continue
        elif query == "reset":
            messages = [
                {"role": "system", "content": "You are a helpful assistant."}
            ]
            continue
        else:
            messages.append({"role": "user", "content": query})

        completion = client.chat.completions.create(
            
            model=MODEL,
            messages=messages,
            extra_body={"enable_thinking": False},
            tools = json.load(open("tools.json", "r", encoding="utf-8")),# 这里加载工具列表
            # Qwen3模型通过enable_thinking参数控制思考过程(开源版默认True,商业版默认False)
            # 使用Qwen3开源版模型时,若未启用流式输出,请将下行取消注释,否则会报错
            # extra_body={"enable_thinking": False},
        )
        # 将大模型的结果保存到文件
        # 这里使用了model_dump_json()方法,返回的是一个json字符串
        res  = completion.model_dump_json()
        json.dump(completion.dict(), open("response1.json", "w", encoding="utf-8"), ensure_ascii=False, indent=4)

        
        #若大模型的结果中有tool_calls字段,则表示调用了工具
        if completion.choices[0].finish_reason == 'tool_calls':
            # 取出tool_calls字段中的第一个工具调用
            tool_call = completion.choices[0].message.tool_calls[0]

       
            py_code = 'ret = ' +  tool_call.function.name + "(**" + tool_call.function.arguments + ")" # ret = func_name(**args) 的方式传递参数
            
            exec(py_code,globals()) # 执行代码,将ret变量映射到当前程序的全局变量空间中
         
        
            # 将function calling的结果加入messages中
            messages.append(completion.choices[0].message.model_dump())
            
            # 将工具调用的结果加入messages中
            messages.append({
                'role':'tool',
                'content':  str(ret),
                'tool_call_id': tool_call.id,
            })  
            
            # 提交给大模型
            completion = client.chat.completions.create(
                model=MODEL,
                messages=messages,
                extra_body={"enable_thinking": False}
            )
            # 将大模型的结果保存到文件
            json.dump(completion.dict(), open("response2.json", "w", encoding="utf-8"), ensure_ascii=False, indent=4)
        
        # 输出大模型的最终回答
        if completion.choices[0].message.content:
            print(completion.choices[0].message.content)
            messages.append({"role": "assistant", "content": completion.choices[0].message.content})        
    
main()

2. mcp_server

在mcp_server中我们提供了三类接口,具体见附件

一是访问本地sqlite数据库的接口

  • execute_query(sql, params=None) 执行SQL查询并返回结果

  • get_all_table_structures 获取SQLite数据库中所有表的结构信息

二是获取本地公网IP和运营商信息的接口

  • get_public_ip 获取当前公网IP地址

  • get_ip_details(ip: str) 获取IP详细信息(含城市、运营商)

三是发送信息的接口

  • send_verification_code(phone_number: str, code: str) 向指定手机号发送短信验证码

  • sent_email(to,subject,body) 发送邮件

3. 接口说明

这里的所有的接口的函数和参数描述为json_schema格式,保存为tools.json,这个tools描述要提交给大模型接口。json schema语法可参考JSON Schema reference

json 复制代码
[
    {
        "type": "function",
        "function": {
            "name": "execute_query",
            "description": "执行SQL查询并返回结果",
            "parameters": {
                "type": "object",
                "properties": {
                    "sql": {
                        "type": "string",
                        "description": "要执行的SQL语句"
                    },
                    "params": {
                        "type": "array",
                        "description": "SQL语句的参数",
                        "items": {
                            "type": "string"
                        }
                    }
                },
                "required": [
                    "sql",
                    "params"
                ]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_all_table_structures",
            "description": "获取所有表的结构信息",
            "parameters": {}
        }
    },

    {
        "type": "function",
        "function": {
            "name": "get_public_ip",
            "description": "获取当前公网IP地址",
            "parameters": {}
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_ip_details",
            "description": "获取IP详细信息(含城市、运营商)",
            "parameters": {
                "type": "object",
                "properties": {
                    "ip": {
                        "type": "string",
                        "description": "要查询的IP地址"
                    }
                },
                "required": [
                    "ip"
                ]
            }
        }
    }, 
    {
        "type": "function",
        "function": {
            "name": "send_verification_code",
            "description": "发送短信验证码",
            "parameters": {
                "type": "object",
                "properties": {
                    "phone_number": {
                        "type": "string",
                        "description": "手机号"
                    },
                    "code": {
                        "type": "string",
                        "description": "验证码(需为6位数字)",
                        "pattern": "^\\d{6}$",
                        "minLength": 6,
                        "maxLength": 6
                    }
                },
                "required": [
                    "phone_number",
                    "code"
                ]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "sent_email",
            "description": "发送电子邮件",
            "parameters": {
                "type": "object",
                "properties": {
                    "to": {
                        "type": "string",
                        "description": "收件人邮箱地址"
                    },
                    "subject": {
                        "type": "string",
                        "description": "邮件主题"
                    },
                    "body": {
                        "type": "string",
                        "description": "邮件正文"
                    }
                },
                "required": [
                    "to",
                    "subject",
                    "body"
                ]
            }
        }
    }
]

四、测试

为了测试数据操作,在本地创建了一个sqlite数据库,并创建了两个表students和scores,用来存储学生的个人信息和成绩,填充了一些假数据。

students表

scores表

1.测试数据库接口

读取表结构

select操作

提问和回答如下,获得的数据完全正确。

看一下response1.json内容,如下所示,可以看到function calling的响应中,大模型建议调用execute_query函数,并给出了正确的sql语句。

json 复制代码
{
    "id": "chatcmpl-e0d887bb-3954-9585-9594-ae39ccbe5f75",
    "choices": [
        {
            "finish_reason": "tool_calls",
            "index": 0,
            "logprobs": null,
            "message": {
                "content": "",
                "refusal": null,
                "role": "assistant",
                "audio": null,
                "function_call": null,
                "tool_calls": [
                    {
                        "id": "call_ca7257e92b9a412d8c1553",
                        "function": {
                            "arguments": "{\"sql\": \"SELECT * FROM scores WHERE 学号 = (SELECT 学号 FROM students WHERE 姓名 = ?)\", \"params\": [\"张晓萌\"]}",
                            "name": "execute_query"
                        },
                        "type": "function",
                        "index": 0
                    }
                ],
                "reasoning_content": ""
            }
        }
    ],
    "created": 1747480960,
    "model": "qwen3-32b",
    "object": "chat.completion",
    "service_tier": null,
    "system_fingerprint": null,
    "usage": {
        "completion_tokens": 49,
        "prompt_tokens": 1319,
        "total_tokens": 1368,
        "completion_tokens_details": null,
        "prompt_tokens_details": null
    }
}

update操作

这个问题client也是调用execute_query函数。

在navicat中查看数据库,可以看到数据直接的被修改了。

2. web接口测试

如下图所示,这两个问题也完全正确,分别调用get_public_ip和get_ip_details函数。

3.信息发送接口

提问如下 ,大模型回复都完成了。

这里我填写的是本人的手机号和邮箱,手机收到了短信。

收到了邮件

五、如何在通义灵码中使用mcp

可参考配置 MCP 服务_通义灵码_智能编码助手_智能编码助手通义灵码(Lingma)-阿里云帮助中心

需要在vscode中安装通义灵码插件,具体怎么配置和登陆不再展开,可参考上面的官方文档。

我从MCP广场中添加了几个mcp server,比如MCP 中文趋势聚合,感觉就是个爬虫。

提供的工具还挺多。

测试一下它的功能,感觉很准确。

总结

本文使用python写了一个最简单的mcp demo,没有严格的按照mcp协议来,本质上还是使用function calling功能来调用函数。

现在互联网上有很多mcp_server市场,比如阿里百炼mcp市场,还有魔搭社区MCP 广场

很多服务提供端也提供了自己的mcp server 如百度、高德、支付宝等,以后估计会越来越多。

mcp_client有Claude Desktop**、Cursor、阿里通义灵码等,cursor和通义灵码可vscode的插件市场中安装。

安全领域奇安信也发布了自己的威胁情况MCP server。

看来拥抱mcp已经成为大势所趋。

参考资料

相关推荐
FAREWELL000756 分钟前
Unity基础学习(十五)核心系统——音效系统
学习·unity·c#·游戏引擎
lwewan6 分钟前
26考研408目录汇总~
笔记·考研
岁岁岁平安8 分钟前
Vue3学习(组合式API——Watch侦听器、watchEffect()详解)
前端·javascript·vue.js·学习·watch侦听器·组合式api
ljt272496066115 分钟前
Compose笔记(二十三)--多点触控
笔记·android jetpack
虾球xz35 分钟前
游戏引擎学习第281天:在房间之间为摄像机添加动画效果
c++·人工智能·学习·游戏引擎
Y3174291 小时前
Python Day25 学习
python·学习
菜鸟蹦迪1 小时前
学习记录:mybatis和jdbc实现数据表作为参数的相关的sql操作
sql·学习·mybatis
清晨朝暮1 小时前
【Linux 学习计划】-- yum
学习
oneDay++2 小时前
# IntelliJ IDEA企业版安装与配置全指南:避坑详解
java·开发语言·经验分享·学习·学习方法
2303_Alpha2 小时前
深度学习入门:深度学习(完结)
人工智能·笔记·python·深度学习·神经网络·机器学习