MCP 協議的動態發現能力

MCP(Model Context Protocol) 與傳統的 API 相比,一個重要的能力是動態發現:MCP 允許 AI 模型動態發現並與可用工具交互,無需預先設定每個集成的固定代碼。

這個動態發現分兩個層次:

一、工具發現和更新

MCP 支持動態工具發現。

https://modelcontextprotocol.io/docs/concepts/tools#tool-discovery-and-updates

1、客戶端可以隨時列出可用的工具

以下是一個展示動態發現功能的簡單代碼示例,包括服務器端和客戶端:

服務器端代碼示例(server.py)

from mcp.server.fastmcp import FastMCP

# 創建一個MCP服務器
mcp = FastMCP("Demo")

# 添加一個加法工具
@mcp.tool()
def add(a:int, b:int)->int:
    """Add two numbers"""
    return a + b

# 添加一個動態問候資源
@mcp.resource("greeting://{name}")
def get_greeting(name:str)->str:
    """Get a personalized greeting"""
    return f"Hello, {name}!"

# 運行服務器
if __name__ =="__main__":
    mcp.run()

客戶端代碼示例(main.py)

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

async def main():
    # 配置服務器
    server_params = StdioServerParameters(
        command="python",
        args=["server.py"]
    )

    # 創建客戶端會話
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()

            # 列出服務器提供的工具
            tools = await session.list_tools()
            print("Available tools:", tools)

            # 執行工具
            result = await session.call_tool("add",{"a":5,"b":3})
            print("Result of add tool:", result)

            # 獲取動態問候
            greeting = await session.read_resource("greeting://Alice")
            print("Greeting:", greeting)

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

這裏用了 Stdio 類型的 Server,MCP 目前支持兩種通訊機制,他們的區別可以看 MCP 的通信機制

上述代碼示例展示瞭如何在服務器端動態創建工具和資源,並在客戶端動態發現並調用這些工具和資源。

2、服務器端通知客戶端工具變化

當工具發生變化時,服務器可以使用 notifications/tools/list_changed 通知客戶端

這樣就可以:

代碼示例,服務器端新加了一個乘法工具,通知客戶端變化

服務器端最初版本代碼跟上個例子一樣,變化成如下:

from mcp.server.fastmcp import FastMCP
from mcp import types

# 創建一個MCP服務器
mcp = FastMCP("Demo")

# 加法工具
@mcp.tool()
def add(a:int, b:int)->int:
    """Add two numbers"""
    return a + b

# 動態問候資源
@mcp.resource("greeting://{name}")
def get_greeting(name:str)->str:
    """Get a personalized greeting"""
    return f"Hello, {name}!"

# 添加一個乘法工具
@mcp.tool()
def multiply(a:int, b:int)->int:
    """Multiply two numbers"""
    return a * b

# 每次工具變化時通知客戶端
async def notify_tool_changes():
    await mcp.notify(types.ListToolsChangedNotification())

# 運行服務器
if __name__ =="__main__":
    import asyncio

    async def main():
        await notify_tool_changes()
        mcp.run()

    asyncio.run(main())

客戶端代碼示例(main.py)

客戶端會監聽工具變化通知,並動態更新工具列表。

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

async def main():
    # 配置服務器
    server_params = StdioServerParameters(
        command="python",
        args=["server.py"]
    )

    # 創建客戶端會話
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()

            # 列出服務器提供的工具
            tools = await session.list_tools()
            print("Available tools:", tools)

            # 監聽工具變化通知
            async def on_tool_change(notification):
                print("Tools have changed:", notification)
                # 列出更新後的工具
                updated_tools = await session.list_tools()
                print("Updated tools:", updated_tools)

            session.subscribe("notifications/tools/list_changed", on_tool_change)

            # 執行加法工具
            result = await session.call_tool("add",{"a":5,"b":3})
            print("Result of add tool:", result)

            # 執行乘法工具
            result = await session.call_tool("multiply",{"a":5,"b":3})
            print("Result of multiply tool:", result)

            # 獲取動態問候
            greeting = await session.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 決定是否需要與該工具集成、何時使用它等...

大致的流程是:

  1. agent 遇到自定義 URI,例如 mcp://api.myservice.com

  2. agent 解析 URI 並獲取有關服務的詳細信息。例如,它對預定義端點執行 HTTP GET 請求,例如: https://api.myservice.com/llms.txt

  3. 該服務以包含所有相關元數據的 JSON 或文本文件進行響應,其中包括:身份驗證、服務及其功能描述、提供的功能 / 工具 / 資源列表、完整的 API 文檔、定價、付款方式或支持的付款協議的詳細信息。

  4. 基於上面檢索到的信息,Agent 做權限驗證、功能映射、啓動交互。

通過上面方式,完成對服務的動態發現。

總結:動態發現是 MCP 的關鍵能力

從本質上講:當你想爲一個你不能控制的 Agent 引入工具時,MCP 就會派上用場。

而如何讓不能控制的 Agent 發現你的服務,動態發現就是必須的能力,從上面的講解看,MCP 這個能力將很快就健全了,非常值得期待。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/lZGIsQwdoXqhyorcwX4qWA