[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())    
相关推荐
xxie12379417 小时前
return与print
开发语言·python
秋917 小时前
从 Python 后端工程师转型 AI Engineer(AI 工程化)的完整补课清单(2026实战版)
开发语言·人工智能·python
humcomm18 小时前
AI编程时代新前端职位
前端·ai编程
慕木沐18 小时前
Google ADK Java 1.0版本 核心机制与实战 Demo
java·开发语言·python
Tbisnic18 小时前
AI大模型学习第十一天:技术选型、安全防护与金融实战
python·学习·ai·大模型·提示词工程
AI工具挖掘机18 小时前
Codex 桌面版上手:从安装到自己开发首个小游戏,0 基础快速入门,手把手教学
经验分享·ai·ai编程
hboot18 小时前
AI工程师第一课 - Python
前端·后端·python
坚果派·白晓明19 小时前
【鸿蒙PC】SDL3 适配:AtomCode + Skills 快速集成 NAPI 测试工具
c++·华为·ai编程·harmonyos·atomcode
许彰午19 小时前
30_Java Stream流操作全解
java·windows·python
秋919 小时前
3年经验Python后端转AI Engineer:3个月实战转型计划(2026版)
开发语言·人工智能·python