```
## `widget()` Response Helper
Used in tool callbacks to send data to the widget:
```typescript
import { widget, text } from "mcp-use/server";
return widget({
props: { city: "Tokyo", temp: 25 }, // Sent to widget via useWidget().props
output: text("Weather in Tokyo: 25°C"), // What the AI model sees
message: "Current weather for Tokyo", // Optional text override
});
```
| Field | Type | Description |
| --------- | --------------------- | ------------------------------------------------------------------ |
| `props` | `Record
` | Data for the widget UI (hidden from model) |
| `output` | `CallToolResult` | Response helper result the model sees (`text()`, `object()`, etc.) |
| `message` | `string` | Optional text message override |
## Tool `widget` Config
```typescript
server.tool({
name: "tool-name",
schema: z.object({ ... }),
widget: {
name: "widget-name", // Must match resources/ file/folder name
invoking: "Loading...", // Text shown while tool runs
invoked: "Ready", // Text shown when complete
widgetAccessible: true, // Widget can call other tools (default: true)
},
}, async (input) => { ... });
```
## Complete End-to-End Example
**`index.ts`:**
```typescript
import { MCPServer, widget, text, object } from "mcp-use/server";
import { z } from "zod";
const server = new MCPServer({
name: "recipe-finder",
version: "1.0.0",
baseUrl: process.env.MCP_URL || "http://localhost:3000",
});
const mockRecipes = [
{
id: "1",
name: "Pasta Carbonara",
cuisine: "Italian",
time: 30,
ingredients: ["pasta", "eggs", "bacon", "parmesan"],
},
{
id: "2",
name: "Chicken Tikka",
cuisine: "Indian",
time: 45,
ingredients: ["chicken", "yogurt", "spices", "rice"],
},
{
id: "3",
name: "Sushi Rolls",
cuisine: "Japanese",
time: 60,
ingredients: ["rice", "nori", "fish", "avocado"],
},
];
server.tool(
{
name: "search-recipes",
description: "Search for recipes by query or cuisine",
schema: z.object({
query: z.string().describe("Search query (e.g., 'pasta', 'chicken')"),
cuisine: z.string().optional().describe("Filter by cuisine"),
}),
widget: {
name: "recipe-list",
invoking: "Searching recipes...",
invoked: "Recipes found",
},
},
async ({ query, cuisine }) => {
const results = mockRecipes.filter(
(r) =>
r.name.toLowerCase().includes(query.toLowerCase()) ||
(cuisine && r.cuisine.toLowerCase() === cuisine.toLowerCase()),
);
return widget({
props: { recipes: results, query },
output: text(`Found ${results.length} recipes for "${query}"`),
});
},
);
server.listen();
```
**`resources/recipe-list.tsx`:**
```tsx
import { McpUseProvider, useWidget, type WidgetMetadata } from "mcp-use/react";
import { z } from "zod";
export const widgetMetadata: WidgetMetadata = {
description: "Display recipe search results",
props: z.object({
recipes: z.array(
z.object({
id: z.string(),
name: z.string(),
cuisine: z.string(),
time: z.number(),
ingredients: z.array(z.string()),
}),
),
query: z.string(),
}),
exposeAsTool: false,
};
export default function RecipeList() {
const { props, isPending } = useWidget();
if (isPending) {
return (
Searching...
);
}
return (
Recipes for "{props.query}"
{props.recipes.length === 0 ? (
No recipes found.
) : (
{props.recipes.map((recipe) => (
{recipe.name}
{recipe.cuisine} · {recipe.time} min ·{" "}
{recipe.ingredients.join(", ")}
))}
)}
);
}
```