[FastMCP设计、原理与应用-15]挂载一个MCP服务器就像挂载一个目录一样容易

随着应用程序的增长,我们需要将其拆分为多个功能专一的服务器(例如一个用于天气,一个用于日历,一个用于管理后台),并通过挂载(Mount)的方式将它们合并成一个客户端连接的单一服务器。挂载服务器后,其所有工具、资源和提示都会通过父进程变为可用状态。连接是实时的,挂载后向子进程添加工具,该工具会立即在父进程中可见。

1. 挂载一个FastMCP对象

以如下这个演示程序为例,我们创建的名为ServerFastMCP对象通过调用mount方法挂载了两个FastMCP对象:命名为Weather的服务器提供了返回天气信息的工具get_weather,命名为OA的服务器提供的工具is_available用于确定某个人在指定的日志是否由任务安排。

python 复制代码
from fastmcp import FastMCP
from fastmcp.client import Client
from datetime import date
import asyncio

weather = FastMCP("Weather")
@weather.tool()
async def get_weather(location: str) -> str:
    """Get the current weather for a given location."""
    return f"The current weather in {location} is sunny."

oa = FastMCP("OA")
@oa.tool()
async def is_available(name: str, date: date) -> bool:
    """Check if a person is available on a given date."""
    return True

async def main():
    server = FastMCP("Server")
    server.mount(server = weather)
    server.mount(server = oa)
    async with Client(server) as client:
        tools = await client.list_tools()
        tool_names = set([tool.name for tool in tools])
        assert tool_names == {"get_weather", "is_available"}

    server = FastMCP("Server")
    server.mount(server=weather,namespace="weather")
    server.mount(server=oa,namespace="oa")
    async with Client(server) as client:
        tools = await client.list_tools()
        tool_names = set([tool.name for tool in tools])
        assert tool_names == {"weather_get_weather", "oa_is_available"}

asyncio.run(main())  

上面的例子演示了针对mount方法的两次调用,第一次仅仅指定被挂载的FastMCP对象,所以工具名称保持不变。第二次指定了命名空间(weatheros),它会作为工具的名称前缀(weather_get_weatheroa_is_available)。

python 复制代码
class FastMCP(
    AggregateProvider,
    LifespanMixin,
    MCPOperationsMixin,
    TransportMixin,
    Generic[LifespanResultT],
):
    def mount(
        self,
        server: FastMCP[LifespanResultT],
        namespace: str | None = None,
        as_proxy: bool | None = None,
        tool_names: dict[str, str] | None = None,
        prefix: str | None = None, 
    ) -> None

上面给出了mount方法的定义,它的参数说明如下:

  • server:被挂载的作为MCP服务器的FastMCP对象;
  • namespace:命名空间。工具和提示词的名称将以{namespace}_为前缀,资源将其作为前置路径;
  • as_proxy:以代理的形式被挂载,这个参数已经废弃,因为我们倾向于自行创建代理;
  • tool_names: 提供一个字典来修改工具的名称;
  • prefix: 命名前缀,已经废弃,并使用namespace代替;

2. 挂载一个代理

由于mount方法只支持针对FastMCP对象的挂载。对于一个需要通过客户端远程调用MCP服务器,我们可以采用代理的形式进行挂载。作为MCP服务器代理的FastMCPProxy对象之所以能够被挂载,是因为它自身就是一个FastMCP对象。FastMCPProxy对象利用ProxyProvider提供组件,后者又利用Client远程获取组件,构造函数参数client_factory用于提供创建Client的工厂。出于性能的考虑,远程获取的组件会在本地进行缓存,缓存的过期时间默认为300秒。如果手工创建ProxyProvider,可以利用构造函数的cache_ttl参数对缓存过期时间进行设置。

python 复制代码
class FastMCPProxy(FastMCP):
    def __init__(
        self,
        *,
        client_factory: ClientFactoryT,
        **kwargs,
    ):
        super().__init__(**kwargs)
        self.client_factory = client_factory
        provider: Provider = ProxyProvider(client_factory)
        self.add_provider(provider)

class ProxyProvider(Provider):
    def __init__(
        self,
        client_factory: ClientFactoryT,
        cache_ttl: float | None = None,
    )

ClientFactoryT = Callable[[], Client] | Callable[[], Awaitable[Client]]

FastMCPProxy可以通过如下这个全局函数fastmcp.server.create_proxy来创建。

python 复制代码
def create_proxy(
    target: (
        Client[ClientTransportT]
        | ClientTransport
        | FastMCP[Any]
        | FastMCP1Server
        | AnyUrl
        | Path
        | MCPConfig
        | dict[str, Any]
        | str
    ),
    **settings: Any,
) -> FastMCPProxy

如下的程序演示了以代理的形式挂载远程MCP服务器。

python 复制代码
from fastmcp import FastMCP
from fastmcp.client import Client
from fastmcp.server import create_proxy
from datetime import date
import asyncio

server = FastMCP("Server")
@server.tool()
async def get_weather(location: str) -> str:
    """Get the current weather for a given location."""
    return f"The current weather in {location} is sunny."

@server.tool()
async def is_available(name: str, date: date) -> bool:
    """Check if a person is available on a given date."""
    return True

async def main():
    asyncio.create_task(server.run_async(transport="streamable-http", host="0.0.0.0", port=3721))
    await asyncio.sleep(5)  # Wait for the server to start
    
    mcp = FastMCP()
    mcp.mount(create_proxy("http://localhost:3721/mcp"))
    async with Client(mcp) as client:      
        tools = await client.list_tools()
        tool_names = set([tool.name for tool in tools])
        print(f"Tools: {tool_names}")
        assert tool_names == {"get_weather", "is_available"} 

asyncio.run(main())    
相关推荐
名字不好奇2 小时前
LangGraph 记忆系统设计实战
人工智能·langchain·ai编程·langgraph
甄心爱学习2 小时前
【项目实训(个人4)】
前端·vue.js·python
西兰先森2 小时前
使用Antv G6渲染neo4j知识图谱数据
python·知识图谱·neo4j
weixin_307779132 小时前
SparkPySetup:基于Python的Windows 11 PySpark环境自动化搭建工具
大数据·开发语言·python·spark
m0_738120722 小时前
渗透基础知识ctfshow——Web应用安全与防护(完结:第八章)
前端·python·sql·安全·web安全·网络安全
雷帝木木2 小时前
Python 并发编程高级技巧详解:从原理到实践
人工智能·python·深度学习·机器学习
devnullcoffee2 小时前
亚马逊 Movers and Shakers 数据采集实战:用 Python + Scrape API 构建实时榜单监控系统
python·亚马逊数据采集·scrape api·亚马逊数据 api·pangolinfo api·amazon 爬虫工具·实时榜单监控
一个天蝎座 白勺 程序猿2 小时前
AI入门踩坑实录:我换了3种语言才敢说,Python真的是入门唯一选择吗?
开发语言·人工智能·python·ai
Hui_AI7202 小时前
保险条款NLP解析与知识图谱搭建:让AI准确理解保险产品的技术方案
开发语言·人工智能·python·算法·自然语言处理·开源·开源软件