|
MCP(Model Context Protocol)与传统的API相比,一个重要的能力是动态发现:MCP允许AI模型动态发现并与可用工具交互,无需预先设定每个集成的固定代码。 这个动态发现分两个层次:
一、工具发现和更新MCP 支持动态工具发现。 https://modelcontextprotocol.io/docs/concepts/tools#tool-discovery-and-updates 1、客户端可以随时列出可用的工具以下是一个展示动态发现功能的简单代码示例,包括服务器端和客户端: 服务器端代码示例(server.py)frommcp.server.fastmcpimportFastMCP
# 创建一个MCP服务器 mcp=FastMCP("Demo")
# 添加一个加法工具 @mcp.tool() defadd(a:int,b:int)->int: """Add two numbers""" returna+b
# 添加一个动态问候资源 @mcp.resource("greeting://{name}") defget_greeting(name:str)->str: """Get a personalized greeting""" returnf"Hello,{name}!"
# 运行服务器 if__name__=="__main__": mcp.run()
客户端代码示例(main.py)importasyncio frommcpimportClientSession,StdioServerParameters frommcp.client.stdioimportstdio_client
asyncdefmain(): # 配置服务器 server_params=StdioServerParameters( command="python", args=["server.py"] )
# 创建客户端会话 asyncwithstdio_client(server_params)as(read,write): asyncwithClientSession(read,write)assession: awaitsession.initialize()
# 列出服务器提供的工具 tools=awaitsession.list_tools() print("Available tools:",tools)
# 执行工具 result=awaitsession.call_tool("add",{"a":5,"b":3}) print("Result of add tool:",result)
# 获取动态问候 greeting=awaitsession.read_resource("greeting://Alice") print("Greeting:",greeting)
if__name__=="__main__": asyncio.run(main())
这里用了 Stdio 类型的Server,MCP目前支持两种通讯机制,他们的区别可以看MCP的通信机制。 上述代码示例展示了如何在服务器端动态创建工具和资源,并在客户端动态发现并调用这些工具和资源。
2、服务器端通知客户端工具变化当工具发生变化时,服务器可以使用notifications/tools/list_changed通知客户端 这样就可以: 代码示例,服务器端新加了一个乘法工具,通知客户端变化 服务器端最初版本代码跟上个例子一样,变化成如下: frommcp.server.fastmcpimportFastMCP frommcpimporttypes
# 创建一个MCP服务器 mcp=FastMCP("Demo")
# 加法工具 @mcp.tool() defadd(a:int,b:int)->int: """Add two numbers""" returna+b
# 动态问候资源 @mcp.resource("greeting://{name}") defget_greeting(name:str)->str: """Get a personalized greeting""" returnf"Hello,{name}!"
# 添加一个乘法工具 @mcp.tool() defmultiply(a:int,b:int)->int: """Multiply two numbers""" returna*b
# 每次工具变化时通知客户端 asyncdefnotify_tool_changes(): awaitmcp.notify(types.ListToolsChangedNotification())
# 运行服务器 if__name__=="__main__": importasyncio
asyncdefmain(): awaitnotify_tool_changes() mcp.run()
asyncio.run(main())
客户端代码示例(main.py)客户端会监听工具变化通知,并动态更新工具列表。 importasyncio frommcpimportClientSession,StdioServerParameters frommcp.client.stdioimportstdio_client
asyncdefmain(): # 配置服务器 server_params=StdioServerParameters( command="python", args=["server.py"] )
# 创建客户端会话 asyncwithstdio_client(server_params)as(read,write): asyncwithClientSession(read,write)assession: awaitsession.initialize()
# 列出服务器提供的工具 tools=awaitsession.list_tools() print("Available tools:",tools)
# 监听工具变化通知 asyncdefon_tool_change(notification): print("Tools have changed:",notification) # 列出更新后的工具 updated_tools=awaitsession.list_tools() print("Updated tools:",updated_tools)
session.subscribe("notifications/tools/list_changed",on_tool_change)
# 执行加法工具 result=awaitsession.call_tool("add",{"a":5,"b":3}) print("Result of add tool:",result)
# 执行乘法工具 result=awaitsession.call_tool("multiply",{"a":5,"b":3}) print("Result of multiply tool:",result)
# 获取动态问候 greeting=awaitsession.read_resource("greeting://Alice") print("Greeting:",greeting)
if__name__=="__main__": asyncio.run(main())
上述代码示例展示了如何在服务器端添加一个新的乘法工具,并通知客户端这些变化。客户端会监听这些通知,并动态更新工具列表。
二、动态发现服务按照规划是25年上半年要完成的,参看:https://modelcontextprotocol.io/development/roadmap。 按照https://github.com/modelcontextprotocol/specification/discussions/69这里的讨论,是准备: 用一种与 MCP 重叠的协议,并且使用自定义 URI 解决了这个限制,这样就允许在聊天中粘贴 URI 等流程,并让 LLM 决定是否需要与该工具集成、何时使用它等... 大致的流程是: agent 遇到自定义 URI,例如 mcp://api.myservice.com agent 解析 URI 并获取有关服务的详细信息。例如,它对预定义端点执行 HTTP GET 请求,例如:https://api.myservice.com/llms.txt 该服务以包含所有相关元数据的 JSON 或文本文件进行响应,其中包括:身份验证、服务及其功能描述、提供的功能/工具/资源列表、完整的 API 文档、定价、付款方式或支持的付款协议的详细信息。 基于上面检索到的信息,Agent 做权限验证、功能映射、启动交互。
通过上面方式,完成对服务的动态发现。
总结:动态发现是MCP的关键能力从本质上讲:当你想为一个你不能控制的 Agent 引入工具时,MCP 就会派上用场。 而如何让不能控制的Agent发现你的服务,动态发现就是必须的能力,从上面的讲解看,MCP这个能力将很快就健全了,非常值得期待。 |