Skip to main content
CallTool is the bridge between your UI and your server logic. When a user clicks a button or submits a form, CallTool invokes a server tool with the arguments you specify — the same tools you define with @mcp.tool(). The renderer proxies the call through the Prefab SDK, so the tool executes server-side with full access to your backend.

Passing Arguments

Use .rx or Rx("key") to pass client state to the tool. Form controls with a name automatically sync to state — Input(name="city") updates city on every keystroke, no SetState needed. Type a city name, click the button, and the current input value flows into the location argument.

Combined with Client Actions

Pass a list to execute multiple actions from a single interaction. A common pattern: update UI state before making the server call, so the user sees immediate feedback.
Combined Actions
from prefab_ui.components import Button
from prefab_ui.actions import SetState
from prefab_ui.actions.mcp import CallTool
from prefab_ui.rx import Rx

dataset = Rx("dataset")

Button("Analyze", on_click=[
    SetState("status", "analyzing..."),
    CallTool("run_analysis", arguments={"data": dataset}),
])
The SetState executes first (instant), then CallTool fires the server request.

Handling Results

The tool’s return value is available as $result (Python: RESULT) inside on_success callbacks. Use SetState to write it into client-side state:
Writing results to state
from prefab_ui.components import Button
from prefab_ui.actions import SetState
from prefab_ui.actions.mcp import CallTool
from prefab_ui.rx import RESULT

Button(
    "Search",
    on_click=CallTool(
        "search",
        arguments={"q": "{{ query }}"},
        on_success=SetState("results", RESULT),
    ),
)
$result is the success counterpart of $error: one is available in on_success, the other in on_error. You can combine them with any action — ShowToast, AppendState, or chain multiple actions in a list:
Callbacks with result
from prefab_ui.components import Button
from prefab_ui.actions import AppendState, SetState, ShowToast
from prefab_ui.actions.mcp import CallTool
from prefab_ui.rx import ERROR, RESULT

Button(
    "Add Item",
    on_click=CallTool(
        "create_item",
        arguments={"name": "{{ item_name }}"},
        on_success=[
            AppendState("items", RESULT),
            ShowToast("Item created!", variant="success"),
        ],
        on_error=ShowToast(ERROR, variant="error"),
    ),
)

Callable References (FastMCP)

When using Prefab with FastMCP, you can pass a function reference instead of a tool name string. The framework resolves it to the correct tool name at serialization time, including any namespace prefixes or global keys:
from fastmcp import FastMCP, FastMCPApp
from prefab_ui.actions import SetState
from prefab_ui.actions.mcp import CallTool
from prefab_ui.rx import RESULT

app = FastMCPApp("My App")

@app.tool()
def search(q: str) -> list[dict]:
    return [{"name": "Arthur"}, {"name": "Ford"}]

@app.ui()
def browse():
    # Function reference — resolved automatically
    Button("Search", on_click=CallTool(
        search,
        arguments={"q": "{{ query }}"},
        on_success=SetState("results", RESULT),
    ))
Callable references provide two advantages over string names. First, the resolver handles name mangling (global keys, namespace prefixes) so your code doesn’t need to know the final tool name. Second, the resolver can inspect the tool’s metadata and inject hints into the serialized action — for example, unwrapResult. String names are also resolved when a resolver is active. CallTool("save_contact") will pass the string "save_contact" through the resolver, which can map it to a global key like "save_contact-a1b2c3d4". This means you can use string names in a FastMCPApp context and still get correct name resolution — useful when you don’t have a direct reference to the tool function (e.g., tools defined in mounted sub-apps).

Automatic Result Unwrapping

MCP requires structuredContent to be a JSON object, so FastMCP wraps non-object tool returns (lists, strings, numbers) in a {"result": X} envelope. Without correction, $result in your on_success callback would contain this envelope instead of the actual data. When you use a callable reference, the resolver detects this wrapping and sets unwrapResult: true on the serialized action. The renderer sees the flag and automatically extracts the inner value, so $result contains exactly what your tool returned. String names go through the same resolver, so they also benefit from unwrapResult when the resolver can identify the tool. However, if the resolver doesn’t recognize a string name (e.g., a dynamically constructed name), it may not be able to determine whether unwrapping is needed.

API Reference

CallTool Parameters

tool
str | Callable
required
Name of the server tool to call, or a callable reference to the tool function. Can be passed as a positional argument.
arguments
dict[str, Any]
default:"{}"
Arguments to pass to the tool. Values support {{ key }} interpolation to reference client-side state at call time.

Protocol Reference

CallTool
{
  "action": "toolCall",
  "tool": "string (required)",
  "arguments?": "object",
  "unwrapResult?": "boolean"
}
When unwrapResult is true, the renderer extracts $result from a {"result": X} envelope in the tool’s structuredContent. This is set automatically by the callable resolver when the tool wraps its return value. For the complete protocol schema, see CallTool.