---
title: Call MCP Tool
sidebarTitle: "MCP: Call Tool"
description: Call a server-side tool from a UI interaction.
icon: wrench
---

import { ComponentPreview } from '/snippets/component-preview.mdx'

`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()`.

<ComponentPreview json={{"view":{"type":"Button","label":"Refresh Data","variant":"default","size":"default","disabled":false,"onClick":{"action":"toolCall","tool":"get_latest_data","arguments":{}}}}} playground="ZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgQnV0dG9uCmZyb20gcHJlZmFiX3VpLmFjdGlvbnMubWNwIGltcG9ydCBDYWxsVG9vbAoKQnV0dG9uKCJSZWZyZXNoIERhdGEiLCBvbl9jbGljaz1DYWxsVG9vbCgiZ2V0X2xhdGVzdF9kYXRhIikpCg==">
<CodeGroup>
```python Python icon="python"
from prefab_ui.components import Button
from prefab_ui.actions.mcp import CallTool

Button("Refresh Data", on_click=CallTool("get_latest_data"))
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "type": "Button",
    "label": "Refresh Data",
    "variant": "default",
    "size": "default",
    "disabled": false,
    "onClick": {"action": "toolCall", "tool": "get_latest_data", "arguments": {}}
  }
}
```
</CodeGroup>
</ComponentPreview>

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.

<ComponentPreview json={{"view":{"cssClass":"gap-3","type":"Column","children":[{"name":"city","type":"Input","inputType":"text","placeholder":"Enter a city...","disabled":false,"readOnly":false,"required":false},{"type":"Button","label":"Get Weather","variant":"default","size":"default","disabled":false,"onClick":{"action":"toolCall","tool":"get_weather","arguments":{"location":"{{ city }}"}}}]}}} playground="ZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgSW5wdXQsIEJ1dHRvbiwgQ29sdW1uCmZyb20gcHJlZmFiX3VpLmFjdGlvbnMubWNwIGltcG9ydCBDYWxsVG9vbAoKd2l0aCBDb2x1bW4oZ2FwPTMpOgogICAgY2l0eSA9IElucHV0KG5hbWU9ImNpdHkiLCBwbGFjZWhvbGRlcj0iRW50ZXIgYSBjaXR5Li4uIikKICAgIEJ1dHRvbigKICAgICAgICAiR2V0IFdlYXRoZXIiLAogICAgICAgIG9uX2NsaWNrPUNhbGxUb29sKAogICAgICAgICAgICAiZ2V0X3dlYXRoZXIiLAogICAgICAgICAgICBhcmd1bWVudHM9eyJsb2NhdGlvbiI6IGNpdHkucnh9LAogICAgICAgICksCiAgICApCg==">
<CodeGroup>
```python Python icon="python"
from prefab_ui.components import Input, Button, Column
from prefab_ui.actions.mcp import CallTool

with Column(gap=3):
    city = Input(name="city", placeholder="Enter a city...")
    Button(
        "Get Weather",
        on_click=CallTool(
            "get_weather",
            arguments={"location": city.rx},
        ),
    )
```
```json Protocol icon="brackets-curly"
{
  "view": {
    "cssClass": "gap-3",
    "type": "Column",
    "children": [
      {
        "name": "city",
        "type": "Input",
        "inputType": "text",
        "placeholder": "Enter a city...",
        "disabled": false,
        "readOnly": false,
        "required": false
      },
      {
        "type": "Button",
        "label": "Get Weather",
        "variant": "default",
        "size": "default",
        "disabled": false,
        "onClick": {
          "action": "toolCall",
          "tool": "get_weather",
          "arguments": {"location": "{{ city }}"}
        }
      }
    ]
  }
}
```
</CodeGroup>
</ComponentPreview>

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.

```python 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:

```python 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:

```python 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](/running/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:

```python
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`.

### 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.

This only happens with callable references. If you use a string name (`CallTool("search")`), the resolver isn't involved and no unwrapping occurs — `$result` will contain the raw `structuredContent` from the server.

## API Reference

<Card icon="code" title="CallTool Parameters">
<ParamField body="tool" type="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.
</ParamField>

<ParamField body="arguments" type="dict[str, Any]" default="{}">
  Arguments to pass to the tool. Values support `{{ key }}` interpolation to reference client-side state at call time.
</ParamField>
</Card>

## Protocol Reference

```json 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](/protocol/tool-call).
