> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metabind.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Build an Interactive Tool

> Create a Type backed by a layout component that returns schema-validated rendered UI

An Interactive Tool is a Type backed by a layout component. The AI calls it with structured JSON; the platform validates against the schema, compiles to BindJS, and returns a rendered UI bundle. This guide walks through building one end-to-end.

## Prerequisites

* A project with `type: "mcp"`. See [Project setup](/guides/building/project-setup).
* Familiarity with [Tools and Types](/guides/concepts/types) and [Components and Packages](/guides/concepts/components).

## 1. Author a layout component

A layout component is the BindJS code that:

* Declares the tool's input schema (via `properties`).
* Defines what the AI is allowed to render (via component allowlists).
* Renders the validated input as a layout.

A minimal layout component:

```ts theme={null}
const properties = {
  title: {
    type: "string",
    title: "Title",
    description: "Main heading",
    inspector: { control: "singleline", placeholder: "Lounge Chair" }
  },
  price: {
    type: "string",
    title: "Price",
    inspector: { control: "singleline", placeholder: "$128.99" }
  },
  description: {
    type: "string",
    title: "Description",
    inspector: { control: "multiline", numberOfLines: 4 }
  }
} satisfies ComponentProperties;

const body = (props: InferProps<typeof properties>): Component => {
  return VStack({ spacing: 16, alignment: "leading" }, [
    Text(props.title).font("title2").fontWeight("bold"),
    Text(props.price).foregroundStyle(Color("#7A7A7A")),
    Text(props.description).font("body")
  ])
    .padding(24)
    .background(Color("white"))
    .cornerRadius(12);
};

export default defineComponent({
  metadata: {
    title: "Product Detail",
    description: "Image, title, price, and description"
  },
  properties,
  body
});
```

For the property schema language and the BindJS layout primitives, see the [BindJS Reference](/bindjs/introduction).

### Add a `previews` array

Previews are sample inputs that MCP App Studio uses to render the component without an AI call. Always include at least one — it's how you'll test changes.

```ts theme={null}
const previews = [
  Self({
    title: "Lounge Chair",
    price: "$128.99",
    description: "A generous, hand-finished blanket woven from merino wool and alpaca."
  })
    .previewName("With description")
];

export default defineComponent({
  metadata,
  properties,
  body,
  previews
});
```

## 2. Create a Type that points at the component

A Type registers the component with the MCP server. In MCP App Studio:

1. Open **UI Tools** in the project sidebar and click **+** to add one.
2. Select the layout component you just authored.
3. Set the Type's name (becomes the slug, the MCP tool name) and description (becomes the LLM-facing tool description).
4. Save.

The Type's input schema is auto-generated from the component's `properties`. The Type's name slugifies to lowercase-underscore — `Product Detail` → `product_detail`. The slug is immutable after first publish.

<Frame>
  <img src="https://mintcdn.com/yapstudios/ZJLavl8Q7LnCwqCq/images/building/interactive-tools/type-editor.png?fit=max&auto=format&n=ZJLavl8Q7LnCwqCq&q=85&s=a3582d95bbe87363d3b0ef1268498659" alt="The Interactive Tool edit page in MCP App Studio with Tool Name, Title, Description, and Component fields visible" width="3680" height="2264" data-path="images/building/interactive-tools/type-editor.png" />
</Frame>

## 3. Configure allowed components (for layouts that compose children)

If your layout accepts children — for example, a layout that renders a list of arbitrary product cards — declare the allowed children on the property:

```ts theme={null}
properties: {
  components: {
    type: "array",
    title: "Components",
    description: "Components rendered in the body",
    allowedComponents: ["ProductCard", "ProductCarousel", "ProductComparison"],
    minItems: 1
  }
}
```

The AI can only compose the listed components inside `components`. Any other reference is rejected during schema validation. See [Component allowlists](/guides/building/allowed-components).

## 4. Test the tool inline

In MCP App Studio, click the Type. The center pane shows a live preview; the right pane is the test panel.

1. Fill the test inputs (or use the defaults from your `previews` array).
2. Click **Run**.
3. The platform validates your input against the schema, compiles BindJS, resolves the package, and renders the result.

This is the same code path that runs when an AI calls the tool from a connected MCP host.

<Frame>
  <img src="https://mintcdn.com/yapstudios/ZJLavl8Q7LnCwqCq/images/getting-started/your-first-mcp-app/test-product-detail.png?fit=max&auto=format&n=ZJLavl8Q7LnCwqCq&q=85&s=279fca14467a8aa6c0c75ab064b9d166" alt="The product_detail Interactive Tool tested inside MCP App Studio — a live Lounge Chair preview on the left and a populated test panel on the right" width="3680" height="2264" data-path="images/getting-started/your-first-mcp-app/test-product-detail.png" />
</Frame>

## 5. Iterate and refine

A few common refinements once the basic tool works:

* **Tighten the description.** The Type's description is the tool's MCP description — what the AI reads when deciding whether to call it. Make it specific: *"Render a single product detail card. Use when the user wants to view one product in depth, not when comparing multiple products."*
* **Add validation to properties.** `minLength`, `maxLength`, `min`, `max`, `pattern`. Schema violations return clear errors to the AI on retry.
* **Constrain allowed components tightly.** The smaller the allowlist, the more predictable the AI's output.
* **Update the project Instructions** to reference the new tool and describe when it should be used. See [Project setup](/guides/building/project-setup#project-instructions).

## 6. Publish

Once the tool works on the draft endpoint, [publish the server](/guides/publishing/publishing-the-server) to promote it to production.

## Related

<CardGroup cols={2}>
  <Card title="Authoring BindJS components" icon="code" href="/guides/building/authoring-components">
    Component primitives, modifiers, and the inspector schema.
  </Card>

  <Card title="Component allowlists" icon="filter" href="/guides/building/allowed-components">
    Constrain what the AI can render.
  </Card>

  <Card title="Testing tools in MCP App Studio" icon="flask" href="/guides/building/testing-tools">
    The test panel, preview gallery, and live device preview.
  </Card>

  <Card title="Tools and Types" icon="wrench" href="/guides/concepts/types">
    Conceptual: how Types become MCP tools.
  </Card>
</CardGroup>
