You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A user report on #3754 shows that @mcp.tool(app=PrefabAppConfig(csp=...)) still leaks CSP into tool metadata and never propagates frameDomains to the renderer resource — the same symptom originally reported in #3735. The immediate fix is simple to describe but hard to implement cleanly, because the underlying architecture hardcodes a single renderer resource at ui://prefab/renderer.html that every prefab-using tool shares. There's nowhere for per-tool CSP to live, and no way to distinguish one prefab tool's rendering configuration from another's.
FastMCPApp's ___ separator and get_app_tool fallback are a hand-rolled workaround for a related problem: the need for stable internal identity that survives display transforms. Both issues share a missing primitive — a deterministic, internal addressing system for mount points in the provider tree.
Goals
Introduce provider addressing so every mount point has a stable, dot-separated address. Addresses are internal bookkeeping, distinct from MCP tool names; they do not affect what clients see. With addressing in place, the architecture gains a single coherent identity layer that several existing workarounds can collapse into.
Design
Naming. Every provider has a name. User-supplied when present; defaulted from FastMCP.name / FastMCPApp.name / class name otherwise. Siblings with colliding names get deterministic -N suffixes based on registration order.
Dot separator. Dots separate address segments externally; they are invalid inside tool keys (new validation). URIs serialize segments as slashes.
Addresses are properties of mount points, not providers. A provider mounted at multiple positions has one address per mount. Providers stay stateless about their location; the root server owns the walk and the registry.
Launch-time freeze. At launch, the root walks its provider graph, assigns addresses, and builds address_path → mount_point_info. Addressing is frozen from that point. Post-launch dynamic tool additions look up their provider in the registry and fan out to every mount point.
Call-time context. Tools learn their current mount path from Context at invocation. Peer-tool references (e.g., a Prefab component's onClick) serialize using that path, so the same tool called through two different mounts produces two different references — correct, because each mount is a distinct round-trippable address.
Per-mount metadata transform. Launch installs a transform at each mount point that rewrites tool.meta["ui"]["resourceUri"] to the materialized URI for that mount. This is a new transform, not a modification to Namespace.
Outcomes
When this lands:
Identity is addressable. Every mount point in the provider tree has a stable dot-separated address, computed deterministically at launch. The same code produces the same addresses on every replica, so the scheme survives horizontal sharding without coordination.
Tool references are location-aware. When a tool returns a Prefab component that references a peer tool, the reference serializes with the calling mount path as its prefix. Calls round-trip back through the same mount even when the same underlying provider is mounted elsewhere in the tree.
FastMCPApp stops being a special case. The ___ separator, app_name uniqueness check, and get_app_tool transform-bypass fallback all fold into the general dot-address resolver.
Per-mount rendering works.@mcp.tool(app=PrefabAppConfig(csp=...)) materializes a dedicated renderer resource at the tool's mount address, with the user-supplied CSP living on the resource where the spec wants it — and surviving user customization of frame_domains, base_uri_domains, and the other fields currently dropped on the floor. Mounting the same provider at two addresses produces two resources, one per mount, independently configurable.
Display transforms are unchanged.Namespace and friends still prefix names for clients; they don't touch identity or addressing, because identity now lives in a parallel internal system.
Non-goals
Backward compatibility for ___. It's internal, never crosses the wire, and has no external consumers.
Changes to Namespace or other display-layer transforms.
Changes to how users register tools or write @mcp.tool(app=...). Existing code continues to work unchanged; PrefabAppConfig(csp=...) simply starts doing what its name implies.
Context
A user report on #3754 shows that
@mcp.tool(app=PrefabAppConfig(csp=...))still leaks CSP into tool metadata and never propagatesframeDomainsto the renderer resource — the same symptom originally reported in #3735. The immediate fix is simple to describe but hard to implement cleanly, because the underlying architecture hardcodes a single renderer resource atui://prefab/renderer.htmlthat every prefab-using tool shares. There's nowhere for per-tool CSP to live, and no way to distinguish one prefab tool's rendering configuration from another's.FastMCPApp's___separator andget_app_toolfallback are a hand-rolled workaround for a related problem: the need for stable internal identity that survives display transforms. Both issues share a missing primitive — a deterministic, internal addressing system for mount points in the provider tree.Goals
Introduce provider addressing so every mount point has a stable, dot-separated address. Addresses are internal bookkeeping, distinct from MCP tool names; they do not affect what clients see. With addressing in place, the architecture gains a single coherent identity layer that several existing workarounds can collapse into.
Design
Naming. Every provider has a name. User-supplied when present; defaulted from
FastMCP.name/FastMCPApp.name/ class name otherwise. Siblings with colliding names get deterministic-Nsuffixes based on registration order.Dot separator. Dots separate address segments externally; they are invalid inside tool keys (new validation). URIs serialize segments as slashes.
Addresses are properties of mount points, not providers. A provider mounted at multiple positions has one address per mount. Providers stay stateless about their location; the root server owns the walk and the registry.
Launch-time freeze. At launch, the root walks its provider graph, assigns addresses, and builds
address_path → mount_point_info. Addressing is frozen from that point. Post-launch dynamic tool additions look up their provider in the registry and fan out to every mount point.Call-time context. Tools learn their current mount path from
Contextat invocation. Peer-tool references (e.g., a Prefab component'sonClick) serialize using that path, so the same tool called through two different mounts produces two different references — correct, because each mount is a distinct round-trippable address.Per-mount metadata transform. Launch installs a transform at each mount point that rewrites
tool.meta["ui"]["resourceUri"]to the materialized URI for that mount. This is a new transform, not a modification toNamespace.Outcomes
When this lands:
FastMCPAppstops being a special case. The___separator,app_nameuniqueness check, andget_app_tooltransform-bypass fallback all fold into the general dot-address resolver.@mcp.tool(app=PrefabAppConfig(csp=...))materializes a dedicated renderer resource at the tool's mount address, with the user-supplied CSP living on the resource where the spec wants it — and surviving user customization offrame_domains,base_uri_domains, and the other fields currently dropped on the floor. Mounting the same provider at two addresses produces two resources, one per mount, independently configurable.app=Truepath, not theapp=PrefabAppConfig(...)path the user on fix: remove CSP from tool metadata, keep on resource only #3754 reported.Namespaceand friends still prefix names for clients; they don't touch identity or addressing, because identity now lives in a parallel internal system.Non-goals
___. It's internal, never crosses the wire, and has no external consumers.Namespaceor other display-layer transforms.@mcp.tool(app=...). Existing code continues to work unchanged;PrefabAppConfig(csp=...)simply starts doing what its name implies.