设计指南:enable/disable 层级过滤与迁移实践)
FastMCP 3.0 组件可见性Visibility设计指南enable/disable 层级过滤与迁移实践【免费下载链接】fastmcp The fast, Pythonic way to build MCP servers and clients.项目地址: https://gitcode.com/GitHub_Trending/fa/fastmcp组件可见性Visibility是 FastMCP 3.0 中控制哪些工具、资源、提示词对外可见的核心机制。本文以仓库内设计笔记 dev-docs/v3-notes/visibility.md 为主线结合用户文档 docs/servers/visibility.mdx 与源码实现完整讲解enable()/disable()的层级过滤模型、Blocklist/Allowlist 语义、组件键格式、会话级可见性以及从 2.x 迁移的注意事项。读完本文你将能精确控制 MCP 服务器对外暴露的组件集合实现运行时访问控制、特性开关与按会话定制的渐进式披露。核心原则组件描述能力服务器与 Provider 控制可用性FastMCP 3.0 可见性设计的第一条铁律是Components describe capabilities. Servers and providers control availability.组件负责描述能力服务器与 Provider 负责控制可用性。在 2.x 时代每个组件Component自身带有一个enabled字段用户可以直接修改它。这带来一个根本性问题当组件经过 Provider尤其是 TransformingProvider时你拿到的是副本copy——修改副本上的enabled字段不会影响原始组件导致禁用不生效这类难以排查的 bug。3.0 的解决方案是把可见性状态从组件内部抽离出去交给组件链条上的拥有者统一管理即下文介绍的**层级可见性Hierarchical Visibility**模型。层级可见性Provider → Server → Client 的过滤链服务器Server和 Provider 各自维护独立的可见性过滤状态。如果组件在任何一层被禁用它在整条链路上就是禁用的——层层过滤最终客户端只会看到所有层级都放行的组件Provider A (filters) → Provider B (filters) → Server (filters) → Client sees only enabled components对应到最终实现可见性通过Visibilitytransform 表达见 fastmcp_slim/fastmcp/server/transforms/visibility.pyProvider 级 transform 先运行Server 级 transform 后运行后运行的标记覆盖先运行的标记later marks win因此Server 拥有最终决定权可以覆盖 Provider 层的禁用决定。从 fastmcp_slim/fastmcp/server/providers/base.py 的源码可以看到Provider 在list_tools时按序应用自身 transforms但只做标记、不过滤——最终过滤发生在 Server 层这样做是为了让会话级 transform 有机会覆盖 Provider 层与 Server 层的全局规则async def list_tools(self) - Sequence[Tool]: tools await self._list_tools() for transform in self.transforms: tools await transform.list_tools(tools) return tools设计笔记中的 VisibilityFilter 与最终实现的演化设计笔记 dev-docs/v3-notes/visibility.md 中规划了一个VisibilityFilter类位于src/fastmcp/utilities/visibility.py负责维护 blocklist/allowlist 状态并直接发送变更通知。在最终代码中这一设计演化为位于 fastmcp_slim/fastmcp/server/transforms/visibility.py 的Visibilitytransform 类 模块级is_enabled()辅助函数Visibility只负责在组件元数据meta[fastmcp][_internal][visibility]上打标记is_enabled()负责读取该标记最终过滤逻辑收敛在 Provider/Server 层。设计意图分层过滤、状态集中管理与最终实现完全一致。enable / disableBlocklist 与 Allowlist 两种模式enable()与disable()是 3.0 统一对外的方法同时存在于服务器与 Provider 上fastmcp_slim/fastmcp/server/providers/base.py 定义了 Provider 的实现Server 层入口在 fastmcp_slim/fastmcp/server/server.py。Blocklist按需禁用默认模式默认情况下所有组件都是启用的除非被显式禁用server.disable(keys[tool:my_tool]) # 隐藏指定组件 server.disable(tags{internal}) # 隐藏所有带该标签的组件Allowlist只保留白名单组件onlyTrueserver.enable(tags{public}, onlyTrue) # 只显示带 public 标签的组件onlyTrue会把默认可见状态切换为禁用同时清空之前的 allowlist只让匹配条件的组件保持启用。从 fastmcp_slim/fastmcp/server/providers/base.py 的实现可以看到其内部机制先追加一个Visibility(False, match_allTrue)禁用一切再追加一个Visibility(True, ...)启用匹配项利用后标记覆盖先标记的语义实现白名单if only: # Allowlist: disable everything, then enable matching self._transforms.append(Visibility(False, match_allTrue)) self._transforms.append( Visibility(True, namesnames, keyskeys, versionversion, componentsset(components) if components else None, tagsset(tags) if tags else None) )Blocklist 优先Blocklist Wins如果一个组件同时命中 blocklist 和 allowlistblocklist 胜出。这保证了你总能隐藏某个组件而不必担心其他过滤器把它放出来。变化检测只在状态真正变化时通知VisibilityFilter只在可见性实际发生变化时才发送通知禁用已经禁用的组件不发送通知启用已经启用的组件不发送通知状态真正改变发送通知这避免了冗余的list_changed通知风暴客户端不会因为无意义的变更而反复刷新组件列表。过滤条件的匹配语义enable()/disable()接受统一的过滤参数所有条件之间是交集AND语义——组件必须满足全部条件才会命中。参数匹配对象说明names组件名称资源/模板为 URI最常见的匹配方式跨组件类型生效tags组件标签组件命中任一标签即被匹配OR 语义keys组件键tool:namev1唯一能精确定位某个版本的某个组件的过滤器versionVersionSpec按版本区间匹配components组件类型集合{tool, resource, template, prompt}match_all全部组件匹配一切组件主要用于 allowlist 初始化从 fastmcp_slim/fastmcp/server/transforms/visibility.py 的_matches()实现可以看出类型、键、名称、版本、标签逐项检查任何一项不满足即不匹配空规则未指定任何条件默认匹配不到任何组件避免误伤。需要注意的几个陷阱用户文档 docs/servers/visibility.mdx 中有专门警告名称跨类型生效names匹配所有组件类型同名工具和提示词会一起被影响只想影响其中一种请加componentsmcp.disable(names{config}, components{tool}) # 只禁用名为 config 的工具交集 ≠ 并集disable(names{debug_info}, tags{dangerous})只会禁用同时满足两个条件的组件如果想禁用或关系请分两次调用。交集的静默失败条件组合后如果匹配不到任何组件操作会静默失败不会报错——这意味着你以为被隐藏的组件可能仍然暴露。调试时务必检查条件组合是否真的能命中目标。标签是 OR 语义一个组件只要带有任一被禁用标签就会被禁用不需要同时带有全部标签。版本化组件的可见性控制当组件注册了多个版本时names会匹配到所有版本。需要针对特定版本操作时用version参数配合VersionSpec参考 docs/servers/versioning.mdxfrom fastmcp.utilities.versions import VersionSpec # 退役所有版本化组件的 v1保留后续版本 mcp.disable(versionVersionSpec(eqv1))注意未版本化组件versionNone不会命中任何版本规格——这是 fastmcp_slim/fastmcp/server/transforms/visibility.py 中match_noneFalse的语义。组件键Component Keys精确定位单个组件组件键是可见性控制的精确地址格式统一为{type}:{identifier}{version}其中是恒定的分隔符组件类型键格式示例Tooltool:{name}{version}tool:delete_everythingResourceresource:{uri}{version}resource:data://configTemplatetemplate:{uri_template}{version}template:file://{path}Promptprompt:{name}{version}prompt:analyze之所以必须恒存在是因为资源 URI 本身可能包含如data://userexample.com/profile。始终输出后解析键时只需按最后一个切分即可无歧义。关键注意点未版本化组件的键以裸结尾tool:delete_everything才是合法键tool:delete_everything缺少尾部按精确字符串匹配会命中不了任何组件。源码 fastmcp_slim/fastmcp/server/transforms/visibility.py 会校验键中必须包含否则发出UserWarning提示该键永远无法匹配。优先用names除非你需要版本级精度从component.key读取键而不是手工拼装避免格式错误。# 只禁用 search 的 v1不影响其他组件的 v1 mcp.disable(keys{tool:searchv1})Server 级与 Provider 级两层控制与覆盖顺序可见性状态作用于两个层级Server 级作用于所有 Provider 的所有组件是客户端最终看到的视图。mcp.disable(tags{internal})会过滤掉来自所有来源的内部组件包括挂载的子服务器。Provider 级每个 Provider 可以设置自己的可见性 transform先于 Server 级运行因此 Server 可以覆盖 Provider 的禁用决定。典型用法是Provider 设置默认可见性Server 选择性覆盖from fastmcp import FastMCP from fastmcp.server.providers import LocalProvider admin_tools LocalProvider() admin_tools.tool(tags{admin}) def admin_action() - str: ... admin_tools.tool def regular_action() - str: ... admin_tools.disable(tags{admin}) # Provider 层禁用 admin 标签 mcp FastMCP(Server, providers[admin_tools]) mcp.enable(names{admin_action}) # Server 层重新启用后标记覆盖先标记分层叠加示例Provider 用onlyTrue只启用feature标签Server 随后禁用beta标签——由于 Server 层 transform 后运行new_feature同时带feature和beta标签最终是禁用的provider.enable(tags{feature}, onlyTrue) mcp FastMCP(Server, providers[provider]) mcp.disable(tags{beta}) # 后运行覆盖 provider 的启用标记会话级可见性Per-Session VisibilityServer 级可见性变更会同时影响所有已连接客户端。当需要不同客户端看到不同组件时使用会话级可见性规则只作用于当前会话其他会话仍看到全局默认值。这为渐进式披露、基于角色的访问控制、按需功能激活提供了实现基础。实现位于 fastmcp_slim/fastmcp/server/transforms/visibility.py 与 fastmcp_slim/fastmcp/server/context.py入口是Context上的三个方法await ctx.enable_components(...)为当前会话启用匹配组件await ctx.disable_components(...)为当前会话禁用匹配组件await ctx.reset_visibility()清空当前会话全部规则回到全局默认from fastmcp import FastMCP from fastmcp.server.context import Context mcp FastMCP(Session-Aware Server) mcp.tool(tags{premium}) def premium_analysis(data: str) - str: return fPremium analysis of: {data} mcp.tool async def unlock_premium(ctx: Context) - str: await ctx.enable_components(tags{premium}) return Premium features unlocked mcp.tool async def reset_features(ctx: Context) - str: await ctx.reset_visibility() return Features reset to defaults # 全局默认禁用 premium 标签组件 mcp.disable(tags{premium})所有会话初始都看不到premium_analysis某会话调用unlock_premium后只有该会话获得 premium 工具的访问权调用reset_features则回到全局默认。仓库中的完整示例见 examples/namespace_activation/server.py 及配套的 examples/namespace_activation/client.py。会话规则的机制要点均有对应测试见 tests/server/test_session_visibility.py会话规则覆盖全局 transform列出组件时先应用全局规则再叠加会话规则。规则累积后写覆盖先写每次调用都向会话追加一条规则同一组件上后添加的规则胜出。await ctx.enable_components(tags{finance}) # 启用 finance await ctx.enable_components(tags{admin}) # 再启用 admin await ctx.disable_components(names{dangerous_admin_tool}) # 最后禁用单个工具dangerous_admin_tool最终被禁用因为它的禁用规则添加在 admin 启用规则之后。自动通知会话规则变化时FastMCP 自动向该会话发送ToolListChangedNotification、ResourceListChangedNotification、PromptListChangedNotification。指定components参数可优化通知范围见 fastmcp_slim/fastmcp/server/transforms/visibility.pyawait ctx.enable_components(tags{finance}, components{tool}) # 只发 ToolListChanged await ctx.enable_components(tags{finance}) # 三类通知都发命名空间激活模式Namespace Activation Pattern会话级可见性最常见的实战模式用标签前缀把工具组织成命名空间全局禁用再提供激活工具按需解锁server FastMCP(Multi-Domain Assistant) server.tool(tags{namespace:finance}) def analyze_portfolio(symbols: list[str]) - str: ... server.tool(tags{namespace:admin}) def list_users() - list[str]: ... # 激活工具——始终可见 server.tool async def activate_finance(ctx: Context) - str: await ctx.enable_components(tags{namespace:finance}) return Finance tools activated server.tool async def deactivate_all(ctx: Context) - str: await ctx.reset_visibility() return All namespaces deactivated # 全局禁用命名空间工具 server.disable(tags{namespace:finance, namespace:admin})会话初始只看到激活工具调用activate_finance后仅该会话看到 finance 工具多个命名空间可独立激活deactivate_all恢复初始状态。完整可运行示例见 examples/namespace_activation/。客户端通知机制可见性状态变化时FastMCP 自动通知已连接的客户端无需手动触发mcp.disable(tags{maintenance}) # 客户端自动收到 tools/list_changed、resources/list_changed 等通知设计笔记 dev-docs/v3-notes/visibility.md 提到VisibilityFilter通过_send_notification()直接处理通知先获取当前请求上下文若有再排队对应的 list-changed 通知在请求上下文之外优雅地 no-op。这种设计简化了代码——无需在 VisibilityFilter 与其所属对象之间做回调接线。过滤逻辑is_enabled() 与元数据标记最终过滤判定依赖is_enabled()fastmcp_slim/fastmcp/server/transforms/visibility.py它检查组件内部元数据meta[fastmcp][_internal][visibility]若标记为False组件被禁用若标记为True组件被启用若未设置任何标记默认启用多个enable()/disable()调用按顺序追加 transform后添加的 transform 覆盖先添加的因此最后一个匹配的标记决定最终状态。这也解释了为什么enable(onlyTrue)内部要先追加禁用一切再追加启用匹配项——利用覆盖顺序实现白名单。从 2.x 迁移三处 API 变化迁移指南来自设计笔记 dev-docs/v3-notes/visibility.md1. 组件级 enable/disable 已移除会触发 NotImplementedError# Before (2.x) - BROKEN: 修改的是副本不生效 tool.disable() # After (3.x) server.disable(keys[tool:my_tool])源码中 fastmcp_slim/fastmcp/utilities/components.py 的Component.enable()/Component.disable()现在直接抛出NotImplementedError并提示改用server.enable(keys[...])/server.disable(keys[...])。2. 装饰器 enabled 参数已移除# Before (2.x) mcp.tool(enabledFalse) def my_tool(): ... # After (3.x) mcp.tool def my_tool(): ... mcp.disable(keys[tool:my_tool])3. include_tags / exclude_tags 已废弃# Before (deprecated) mcp FastMCP(server, exclude_tags{internal}) # After mcp FastMCP(server) mcp.disable(tags{internal})实现文件索引作用路径设计笔记本文主体dev-docs/v3-notes/visibility.md用户文档完整示例docs/servers/visibility.mdxVisibilitytransform、is_enabled()、会话级可见性函数fastmcp_slim/fastmcp/server/transforms/visibility.pyProvider.enable/disable/add_transformfastmcp_slim/fastmcp/server/providers/base.pyFastMCP.enable/disable/add_transformfastmcp_slim/fastmcp/server/server.pyComponent.enable/disable抛 NotImplementedErrorfastmcp_slim/fastmcp/utilities/components.pyContext.enable_components/disable_componentsfastmcp_slim/fastmcp/server/context.py会话级可见性测试tests/server/test_session_visibility.py命名空间激活完整示例examples/namespace_activation/server.py小结FastMCP 3.0 的可见性体系围绕一条主线展开可见性状态从组件本身剥离交给 Provider 与 Server 分层管理。理解四个关键点即可掌握全部用法——后标记覆盖先标记的 transform 顺序、onlyTrue的白名单模式、Blocklist 优先的冲突仲裁以及会话级规则的累积覆盖语义。无论是做运行时访问控制、按版本灰度、特性开关还是构建按命名空间渐进披露的多域服务器这套 API 都提供了统一而精确的控制面。【免费下载链接】fastmcp The fast, Pythonic way to build MCP servers and clients.项目地址: https://gitcode.com/GitHub_Trending/fa/fastmcp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考