Skip to content

Provider addressing to enable per-mount resource materialization #3805

@jlowin

Description

@jlowin

Context

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.
  • Tool metadata stops carrying CSP. This completes the invariant PR fix: remove CSP from tool metadata, keep on resource only #3754 established but only enforced on the app=True path, not the app=PrefabAppConfig(...) path the user on fix: remove CSP from tool metadata, keep on resource only #3754 reported.
  • 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementImprovement to existing functionality. For issues and smaller PR improvements.mcp appsRelated to MCP Apps - user-facing applications with frontend bundles served by MCP servers.proposalA proposal for a feature or enhancement either requiring or seeking comments on its design.serverRelated to FastMCP server implementation or server-side functionality.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions