作为一个MCP服务器,FastMCP在底层需要建立一个面向传输(STDIO、SSE和Streamable-HTTP)的服务器用于建立和维护于客户端的连接,并在此基础上完成与客户端的消息交互。具体的消息交互具有如下几种模式:
- 从客户端到服务器的请求-回复模式:这是主要的消息交换模式,客户端请求服务端获取组件列表、读取资源、渲染提示词和工具调用都采用此种模式;
- 从服务端到客户端的请求-回复模式:服务端在执行某些操作的时候需要反向请求客户,并要求对方提供某些信息,比如Elicitation就是这类模式的代表;
- 从服务端到客户端的单向通知模式:当某些状态发生改变时,服务端单向发送通知给客户端,比如当组件列表改变的通知发送便采用这种模式;
由于采用JSON-RPC协议,服务端接收到的每个请求均携带一个具体的方法名称,方法名称关联的操作对请求和响应数据结构均有确定性的描述,所以请求可以精准地被反序列化成操作对应的输入类型,也就是mcp库中定义的各种请求类型。FastMCP会针对具体的请求类型注册对应的处理器,反序列化生成的请求会交给对应的处理器进行处理,返回的结果被序列化成标准的JSON-RPC内容响应给客户端。前面介绍的Provider、Transform和Middleware的应用最终体现在这个处理器的实现上。
python
class FastMCP(
AggregateProvider,
LifespanMixin,
MCPOperationsMixin,
TransportMixin,
Generic[LifespanResultT],
):
def __init__(
self,
name: str | None = None,
instructions: str | None = None,
*,
version: str | None = None,
website_url: str | None = None,
icons: list[mcp.types.Icon] | None = None,
auth: AuthProvider | None = None,
middleware: Sequence[Middleware] | None = None,
providers: Sequence[Provider] | None = None,
transforms: Sequence[Transform] | None = None,
lifespan: LifespanCallable | Lifespan | None = None,
tools: Sequence[Tool | Callable[..., Any]] | None = None,
on_duplicate: DuplicateBehavior | None = None,
mask_error_details: bool | None = None,
dereference_schemas: bool = True,
strict_input_validation: bool | None = None,
list_page_size: int | None = None,
tasks: bool | None = None,
session_state_store: AsyncKeyValue | None = None,
sampling_handler: SamplingHandler | None = None,
sampling_handler_behavior: Literal["always", "fallback"] | None = None,
**kwargs: Any,
)
LifespanCallable = Callable[["FastMCP[LifespanResultT]"], AbstractAsyncContextManager[LifespanResultT]]
DuplicateBehavior = Literal["warn", "error", "replace", "ignore"]
上面给出了FastMCP构造函数的定义,我们来看看构造一个FastMCP可以提供哪些参数:
- name: 服务器名称。如果不传,会自动生成一个随机 ID;
- instructions:给LLM 的"系统提示词"。告诉模型这个服务器是做什么的、应该如何使用它;
- version:版本号(支持字符串、整数、浮点数);
- website_url: 服务器相关的官方链接;
- icons: 在客户端 UI 中显示的图标列表;
- auth:处理客户端的身份验证;
- middleware: 注册的Middleware序列;
- providers: 注册的Provider序列;
- transforms: 注册的Transform序列;
- lifespan:生命周期管理函数。用于在服务器启动/关闭时执行初始化(如连数据库)和清理工作;
- tools: 初始化时直接注册的工具列表(支持原始函数或 Tool 对象);
- on_duplicate: 冲突处理策略(如 warn 或 error)。当注册了同名的工具或资源时,决定是警告还是报错;
- mask_error_details:错误脱敏。开启后,详细的 Python 报错不会发给客户端,防止泄露后端代码细节。
- dereference_schemas: 是否Schema进行解引用(引用变成内联)。默认开启,将复杂的JSON引用展开,方便LLM理解;
- strict_input_validation: 严格输入校验。是否强制要求客户端传参完全符合 JSON Schema。
- list_page_size: 分页大小。当工具或资源非常多时,控制单次列表返回的数量;
- tasks: 是否启用后台任务支持;
- session_state_store: 状态存储引擎(默认是内存存储)。用于在不同请求间共享数据;
- sampling_handler: 采样处理器。当模型需要调用其他模型的能力(Sampling)时,由它处理;
- sampling_handler_behavior: 采样行为。always(总是使用处理器)或fallback(仅在必要时回退);
FastMCP继承了如下四个基类:
- AggregateProvider:这是一个
Provider,同时也是一个Provider的组合,它重写了Provider的方法并将其转发给作为组合成员的每个Provider; - LifespanMixin:这是异步服务的状态控制器。管理服务器"从启动到关闭"的全过程。它提供了钩子是我们能在服务器真正开始处理请求前执行初始化代码,并在关闭时优雅地实施回收;
- MCPOperationsMixin:针对MCP协议的操作定义了对应的方法,上面提到的
FastMCP针对每个请求类型注册的处理器指的就是这些方法; - TransportMixin:定义了相应的方法以不同的方式启动面向传输的底层服务器;
1. AggregateProvider
AggregateProvider采用典型的组合 设计模式,它自身维护了一组Provider对象(通过构造函数或者调用add_provider方法提供),而自身也是一个Provider对象,这也是我们为什么说FastMCP自身就是一个Provider的原因。AggregateProvider重写了真正用于提供组件的所有九个方法(带下划线前缀的方法),并将方法调用分发给作为组成成员的Provider。对于_list方法,它不会进行去重和过滤处理,返回的是所有Provider提供列表的合并。对于提供指定组件的_get方法,它会选择具有最高版本的那一个(如果具有多个选项,就随机选一个)。
python
class AggregateProvider(Provider):
def __init__(self, providers: Sequence[Provider] | None = None) -> None
def add_provider(self, provider: Provider, *, namespace: str = "") -> None
async def _list_tools(self) -> Sequence[Tool]
async def _get_tool(self, name: str, version: VersionSpec | None = None) -> Tool | None
async def _list_resources(self) -> Sequence[Resource]
async def _get_resource(self, uri: str, version: VersionSpec | None = None) -> Resource | None
async def _list_resource_templates(self) -> Sequence[ResourceTemplate]
async def _get_resource_template(self, uri: str, version: VersionSpec | None = None) -> ResourceTemplate | None
async def _list_prompts(self) -> Sequence[Prompt]
async def _get_prompt(self, name: str, version: VersionSpec | None = None) -> Prompt | None
async def get_tasks(self) -> Sequence[FastMCPComponent]
@asynccontextmanager
async def lifespan(self) -> AsyncIterator[None]
正是因为FastMCP继承了AggregateProvider,让它对组件具有分而治之 的能力。也就是说,如果提供的组件过多,我们可以将其分类,每个类别的组件由对应的Provider来提供。不仅如此,这还赋予了FastMCP可以挂载(Mount)其他FastMCP的能力,谁叫FastMCP就是一个Provider呢。对于需要跨进程或者跨网络远程调用的MCP服务器,我们还可以采用代理的形式进行挂载(将代理定义成Provider)。
2. TransportMixin
FastMCP在底层需要建立一个面向传输的服务器(对应的类型为fastmcp.server.LowLevelServer)用于建立和维护于客户端的连接,并在此基础上完成与客户端的消息交互。TransportMixin定义了相应的方法以不同的模式启动服务器。
python
class TransportMixin:
async def run_async(
self: FastMCP,
transport: Transport | None = None,
show_banner: bool | None = None,
**transport_kwargs: Any,
) -> None
def run(
self: FastMCP,
transport: Transport | None = None,
show_banner: bool | None = None,
**transport_kwargs: Any,
) -> None
async def run_stdio_async(
self: FastMCP,
show_banner: bool = True,
log_level: str | None = None,
stateless: bool = False,
) -> None
async def run_http_async(
self: FastMCP,
show_banner: bool = True,
transport: Literal["http", "streamable-http", "sse"] = "http",
host: str | None = None,
port: int | None = None,
log_level: str | None = None,
path: str | None = None,
uvicorn_config: dict[str, Any] | None = None,
middleware: list[ASGIMiddleware] | None = None,
json_response: bool | None = None,
stateless_http: bool | None = None,
stateless: bool | None = None,
) -> None
def http_app(
self: FastMCP,
path: str | None = None,
middleware: list[ASGIMiddleware] | None = None,
json_response: bool | None = None,
stateless_http: bool | None = None,
transport: Literal["http", "streamable-http", "sse"] = "http",
event_store: EventStore | None = None,
retry_interval: int | None = None,
) -> StarletteWithLifespan
def custom_route(
self: FastMCP,
path: str,
methods: list[str],
name: str | None = None,
include_in_schema: bool = True,
) -> Callable[
[Callable[[Request], Awaitable[Response]]],
Callable[[Request], Awaitable[Response]],
]
TransportMixin定义了如下的方法:
- run_async: 异步启动。在已有
asyncio事件循环的环境(如脚本内部)使用。它会根据transport参数决定调用run_stdio_async还是run_http_async; - run: 同步封装。这是最常用的入门方法,它内部会自动调用
asyncio.run来启动服务,适合简单的独立脚本; - run_http_async:一键启动一个HTTP服务器。它内部集成了
Uvicorn,可以直接指定host和port; - http_app:它不直接启动服务器,而是返回一个
Starlette (ASGI)应用对象。这让你可以把FastMCP集成到现有的FastAPI或自定义Web后端中; - custom_route:这是一个用于注册额外的路由的装饰器,它让
FastMCP不仅仅是一个MCP服务器,还能当成一个微型Web框架;
在下面演示实例中,我们利用@custom_route装饰器注册了一个用于健康检查的终结点。原则上我们可以采用这种方式利用FastMCP作为一个Web框架来构建一个Web应用。
python
from fastmcp import FastMCP
from httpx import AsyncClient
from starlette.requests import Request
from starlette.responses import Response,PlainTextResponse
import asyncio
mcp = FastMCP("Server")
@mcp.custom_route("/health", methods=["GET"])
async def health_check(_: Request) -> Response:
return PlainTextResponse("healthy", status_code=200)
async def main():
async with AsyncClient() as client:
asyncio.create_task(mcp.run_async(transport="streamable-http", host="0.0.0.0", port=3721))
await asyncio.sleep(2) # Wait for the server to start
response = await client.get("http://localhost:3721/health")
assert response.status_code == 200
assert response.text == "healthy"
if __name__ == "__main__":
asyncio.run(main())
3. MCPOperationsMixin
当底层服务器从传输层读取请求,并根据JSON-RPC的方法名反序列化成对应的请求类型后,会根据请求类型提取对应的处理器进行处理,处理器与请求类型的映射关系就是在MCPOperationsMixin中建立的。如下面的代码片段所示,这个类型针对四种组件(工具、静态资源、动态资源模板和提示词,没有后台任务)的七种操作(四种List加上工具执行、资源读取和提示词渲染)定义了对应的处理器函数,它们会在_setup_handlers方法中映射给对应的请求类型。
python
class MCPOperationsMixin:
def _setup_handlers(self: FastMCP) -> None
async def _list_tools_mcp(self, request: mcp.types.ListToolsRequest) -> mcp.types.ListToolsResult
async def _list_resources_mcp(self, request: mcp.types.ListResourcesRequest) -> mcp.types.ListResourcesResult
async def _list_resource_templates_mcp(self, request: mcp.types.ListResourceTemplatesRequest) -> mcp.types.ListResourceTemplatesResult
async def _list_prompts_mcp(self, request: mcp.types.ListPromptsRequest) -> mcp.types.ListPromptsResult
async def _call_tool_mcp(self, key: str, arguments: dict[str, Any]) -> (
list[ContentBlock]
| tuple[list[ContentBlock], dict[str, Any]]
| mcp.types.CallToolResult
| mcp.types.CreateTaskResult
)
async def _read_resource_mcp(self, uri: AnyUrl | str) -> mcp.types.ReadResourceResult | mcp.types.CreateTaskResult
async def _get_prompt_mcp(
self, name: str, arguments: dict[str, Any] | None
) -> mcp.types.GetPromptResult | mcp.types.CreateTaskResult
4. LifespanMixin
LifespanMixin的核心任务是管理 "环境准备" 与 "后台任务"。它确保在服务器处理第一个请求前,所有依赖(如数据库、任务队列)都已就绪,并在关闭时安全退出。
python
class LifespanMixin:
@property
def docket(self: FastMCP) -> Docket | None
@asynccontextmanager
async def _docket_lifespan(self: FastMCP) -> AsyncIterator[None]
@asynccontextmanager
async def _lifespan_manager(self: FastMCP) -> AsyncIterator[None]
def _setup_task_protocol_handlers(self: FastMCP) -> None
LifespanMixin具有如下的类型成员:
- docket: 返回作为后台任务调度器的
Docket对象; - _docket_lifespan:是专门为后台任务准备的,负责管理对象
Docket和Worker相关资源的生命周期; - _lifespan_manager:它是整个服务器启动的唯一入口,管理服务器整体状态、引用计数、所有子组件的生命周期;
- _setup_task_protocol_handlers:在后台任务开关被开启后(
Docket对象可用),用于注册基于后台任务执行方法的处理器;
5. LocalProvider
FastMCP利用字段_local_provider返回的LocalProvider作为本地资源提供者,我们利用@tool、@resource和@propmt装饰器注册的组件实际上全部存储在这个LocalProvider对象中。LocalProvider类型定义如下,它继承自Provider类型,我们可以调用四个remove方法删除注册的组件,还可以调用get_tasks方法返回后台任务列表。
python
class LocalProvider(
Provider,
ToolDecoratorMixin,
ResourceDecoratorMixin,
PromptDecoratorMixin,
):
def remove_tool(self, name: str, version: str | None = None) -> None
def remove_resource(self, uri: str, version: str | None = None) -> None
def remove_template(self, uri_template: str, version: str | None = None) -> None
def remove_prompt(self, name: str, version: str | None = None) -> None
async def get_tasks(self) -> Sequence[FastMCPComponent]
用于注册相应资源的装饰器就定义在它继承的三个混入类中。以如下所示的ToolDecoratorMixin为例,标注在工具函数上用于注册工具的@tool装饰器函数就定义在这里。除此之外,它还定义了add_tool方法手工添加工具。其他的混入类(ResourceDecoratorMixin和PromptDecoratorMixin)的定义与之类似。
python
class ToolDecoratorMixin:
def add_tool(self: LocalProvider, tool: Tool | Callable[..., Any]) -> Tool
@overload
def tool(
self: LocalProvider,
name_or_fn: F,
*,
name: str | None = None,
version: str | int | None = None,
title: str | None = None,
description: str | None = None,
icons: list[mcp.types.Icon] | None = None,
tags: set[str] | None = None,
output_schema: dict[str, Any] | NotSetT | None = NotSet,
annotations: ToolAnnotations | dict[str, Any] | None = None,
exclude_args: list[str] | None = None,
meta: dict[str, Any] | None = None,
enabled: bool = True,
task: bool | TaskConfig | None = None,
serializer: ToolResultSerializerType | None = None, # Deprecated
timeout: float | None = None,
auth: AuthCheck | list[AuthCheck] | None = None,
) -> F: ...
@overload
def tool(
self: LocalProvider,
name_or_fn: str | None = None,
*,
name: str | None = None,
version: str | int | None = None,
title: str | None = None,
description: str | None = None,
icons: list[mcp.types.Icon] | None = None,
tags: set[str] | None = None,
output_schema: dict[str, Any] | NotSetT | None = NotSet,
annotations: ToolAnnotations | dict[str, Any] | None = None,
exclude_args: list[str] | None = None,
meta: dict[str, Any] | None = None,
enabled: bool = True,
task: bool | TaskConfig | None = None,
serializer: ToolResultSerializerType | None = None, # Deprecated
timeout: float | None = None,
auth: AuthCheck | list[AuthCheck] | None = None,
) -> Callable[[F], F]: ...
def tool(
self: LocalProvider,
name_or_fn: str | AnyFunction | None = None,
*,
name: str | None = None,
version: str | int | None = None,
title: str | None = None,
description: str | None = None,
icons: list[mcp.types.Icon] | None = None,
tags: set[str] | None = None,
output_schema: dict[str, Any] | NotSetT | None = NotSet,
annotations: ToolAnnotations | dict[str, Any] | None = None,
exclude_args: list[str] | None = None,
meta: dict[str, Any] | None = None,
enabled: bool = True,
task: bool | TaskConfig | None = None,
serializer: ToolResultSerializerType | None = None, # Deprecated
timeout: float | None = None,
auth: AuthCheck | list[AuthCheck] | None = None,
) -> (
Callable[[AnyFunction], FunctionTool]
| FunctionTool
| partial[Callable[[AnyFunction], FunctionTool] | FunctionTool]
)