Skip to main content
Dynamic tools and resources let React components register MCP capabilities on mount and automatically unregister them on unmount. This enables UI-driven tool availability — tools exist only while the component that defines them is rendered. A dynamic tool is a real server tool. FrontMcpProvider registers it with the server through server.registerTool(), so it is listed like any other tool, runs through the server’s tools:call-tool flow (plugin hooks, authorities, quota and availableWhen apply), reaches every client — including in-browser agents through the WebMCP plugin — and connected clients get notifications/tools/list_changed when it appears or goes away.

useDynamicTool

Registers an MCP tool for the lifetime of the component. Supports both zod schemas (recommended) and raw JSON Schema.
When a zod schema is provided:
  • The schema is converted to JSON Schema automatically via toJSONSchema from zod/v4
  • Input is validated via safeParse before reaching your execute callback
  • Invalid input returns an error CallToolResult with issue details
  • The execute callback receives fully typed, validated args

With JSON Schema (Backward Compat)

Options

Name Conflicts

Dynamic tool names share the server’s namespace. A dynamic tool whose name a server tool (or another provider’s dynamic tool) already has is not registered: the provider reports it through its onError prop, or with a console.warn when there is none, and the component keeps rendering. Names must be 1–64 characters. Registering the tool again (a remount, say) tries it again.

Servers With Several Apps

A dynamic tool joins one of the server’s apps, whose plugin hooks then apply to it. A server made with create() has a single app, so there is nothing to choose. A server with several local apps (FrontMcpInstance.createDirect({ apps: [...] })) refuses a tool that doesn’t say which app it joins, so name the app once per server on the provider. That covers every dynamic tool: useDynamicTool, mcpComponent, store actions and useApiClient.
dynamicToolApps is keyed by server name: the primary server goes by the provider’s name ('default' unless set), and each entry of servers by its key. A tool can name its own app instead, with useDynamicTool({ ..., app: 'billing' }).

Offering a Tool Only to In-Browser Agents

With the WebMCP plugin installed, availableWhen decides which callers see a tool:

Conditional Registration

Use enabled to conditionally register/unregister tools based on application state:

Stale Closure Prevention

The execute function is stored in a ref internally, so it always captures the latest closure values. You don’t need to memoize it.

Execution

A call runs your execute through the server’s flows, but your code itself runs outside the server’s request turn. In a browser build (which has no AsyncContext, so the server serves one request at a time) a tool that calls the server back or waits on the network does not block other requests.

useDynamicResource

Registers an MCP resource for the lifetime of the component.

Options


useComponentTree

Exposes the DOM subtree under a ref as a JSON MCP resource. Useful for giving agents visibility into the rendered component hierarchy.

Options

Output Format

The resource returns a JSON tree with component, tag, children, and optional props:
Elements with data-component attributes use that value as component. Others fall back to the tag name.

Mount/Unmount Lifecycle

Dynamic tools and resources follow React’s effect lifecycle:
  1. Mount: The tool/resource is registered with the DynamicRegistry; the provider registers tools with the server
  2. Update: If dependencies change, the old registration is cleaned up and a new one is created
  3. Unmount: The tool/resource is automatically unregistered (and the tool removed from the server)
Changes are applied to the server on a microtask, so React StrictMode’s mount → unmount → mount settles into one registration. When two mounted components register the same name, the later definition replaces the earlier one on the server too. This means agents only see tools that correspond to currently rendered UI. When a user navigates away from a page, its tools disappear; when they navigate back, the tools reappear.