TypeScriptTools

Tool Widgets

Return interactive UI with a tool result

A tool can render a widget: a small React page that shows its result as a card, a chart, or a form. Clients that support MCP UI display the widget; others still receive the data.

Attach visual components to tool responses using the @Widget decorator.

Basic Widget Connection

Add @Widget with the widget's name, which matches a folder in src/widgets/app/. The tool's return value becomes the widget's data.

import { Widget } from '@nitrostack/core';

@Tool({
  name: 'get_order_summary',
  description: 'Get order summary with visual breakdown',
  inputSchema: z.object({
    orderId: z.string().describe('Order ID')
  })
})
@Widget('order-summary')  // Maps to src/widgets/app/order-summary/page.tsx
async getOrderSummary(input: { orderId: string }, ctx: ExecutionContext) {
  return {
    id: input.orderId,
    items: await this.orderService.getItems(input.orderId),
    total: await this.orderService.getTotal(input.orderId),
    status: await this.orderService.getStatus(input.orderId)
  };
}

Widget with Invocation Messages and Examples

For the best user experience, provide invocation messages and example data:

@Tool({
  name: 'get_dashboard',
  title: 'User Dashboard',
  description: 'Get personalized dashboard with stats and recent activity',
  inputSchema: z.object({
    userId: z.string().describe('User ID')
  }),
  // Status messages shown in the UI during execution
  invocation: {
    invoking: 'Loading dashboard...',   // Shown while tool runs
    invoked: 'Dashboard ready'          // Shown when complete
  },
  // IMPORTANT: Example data is used for widget preview!
  examples: {
    request: { userId: 'user-123' },
    response: {
      user: { name: 'John Doe', avatar: '/avatars/john.jpg' },
      stats: { orders: 42, totalSpent: 1234.56 },
      recentOrders: [
        { id: 'order-1', total: 99.99, date: '2026-01-30' }
      ]
    }
  }
})
@Widget('user-dashboard')
async getDashboard(input: { userId: string }, ctx: ExecutionContext) {
  // Return data matching the example structure
  return { user: {...}, stats: {...}, recentOrders: [...] };
}

Important

The examples.response data is used by clients to render widget previews before the tool executes. Without it, the widget preview may not appear.

How Widget Metadata is Exposed

NitroStack exposes widget metadata in the tool's _meta field in multiple formats for compatibility:

{
  "name": "get_dashboard",
  "_meta": {
    "ui/template": "ui://widget/next-user-dashboard.html",
    "ui": { "resourceUri": "ui://widget/next-user-dashboard.html" },
    "openai/outputTemplate": "ui://widget/next-user-dashboard.html",
    "openai/toolInvocation/invoking": "Loading dashboard...",
    "openai/toolInvocation/invoked": "Dashboard ready",
    "tool/examples": { "request": {...}, "response": {...} }
  }
}

See the UI Widgets Guide for complete widget development documentation.

Next steps

On this page