---
title: "Response Helpers"
description: "Utility functions for creating standardized MCP tool and resource responses"
icon: "drill"
---

![mcp-use helpers](/images/ts-helpers.png)

Response helpers are utility functions that simplify creating standardized responses for MCP tools, resources, and prompts. Instead of manually constructing `CallToolResult` objects, these helpers provide a clean, type-safe API with automatic MIME type handling.

## Why Use Response Helpers?

Response helpers provide several benefits:

- **Consistency**: Standardized response format across your server
- **Type Safety**: TypeScript types ensure correct usage
- **Less Boilerplate**: No need to manually construct response objects
- **MIME Type Handling**: Automatic content type detection and metadata
- **Composability**: Easily combine multiple content types with `mix()`

## Basic Text and Content Types

### text()

Returns plain text content. The most common response type for tools and resources.

```typescript
import { text } from 'mcp-use/server';

server.tool({
  name: 'greet',
  schema: z.object({ name: z.string() }),
  async ({ name }) => text(`Hello, ${name}!`)
});

server.resource(
  { name: 'greeting', uri: 'app://greeting' },
  async () => text('Hello World!')
);
```

**Returns**: `CallToolResult` with `text/plain` MIME type

### markdown()

Returns Markdown formatted content.

```typescript
import { markdown } from 'mcp-use/server';

server.resource(
  { name: 'readme', uri: 'doc://readme' },
  async () => markdown('# Welcome\n\nGetting started with our API...')
);

server.tool({
  name: 'format-docs',
  async () => markdown('## Documentation\n\n- Item 1\n- Item 2')
});
```

**Returns**: `CallToolResult` with `text/markdown` MIME type

### html()

Returns HTML content for web display.

```typescript
import { html } from 'mcp-use/server';

server.resource(
  { name: 'page', uri: 'ui://dashboard' },
  async () => html('<h1>Dashboard</h1><p>Welcome to your dashboard</p>')
);

server.tool({
  name: 'generate-report',
  async ({ data }) => html(`
    <div class="report">
      <h2>Report</h2>
      <table>
        <tr><td>Total</td><td>${data.total}</td></tr>
      </table>
    </div>
  `)
});
```

**Returns**: `CallToolResult` with `text/html` MIME type

### xml()

Returns XML formatted content.

```typescript
import { xml } from "mcp-use/server";

server.resource({ name: "sitemap", uri: "data://sitemap" }, async () =>
  xml('<?xml version="1.0"?><urlset>...</urlset>')
);
```

**Returns**: `CallToolResult` with `text/xml` MIME type

### css()

Returns CSS stylesheet content.

```typescript
import { css } from "mcp-use/server";

server.resource({ name: "styles", uri: "asset://theme.css" }, async () =>
  css("body { margin: 0; padding: 0; font-family: sans-serif; }")
);
```

**Returns**: `CallToolResult` with `text/css` MIME type

### javascript()

Returns JavaScript code content.

```typescript
import { javascript } from "mcp-use/server";

server.resource({ name: "script", uri: "asset://main.js" }, async () =>
  javascript('console.log("Application started");')
);
```

**Returns**: `CallToolResult` with `text/javascript` MIME type

## Structured Data

### object()

Returns JSON object with both text representation and typed structured content.

```typescript
import { object } from 'mcp-use/server';

server.tool({
  name: 'get-user-info',
  schema: z.object({ userId: z.string() }),
  async ({ userId }) => {
    const user = await fetchUser(userId);
    return object({
      userId: user.id,
      email: user.email,
      name: user.name,
      createdAt: user.createdAt
    });
  }
});

server.resource(
  { name: 'config', uri: 'config://settings' },
  async () => object({ theme: 'dark', version: '1.0', debug: false })
);
```

**Returns**: `TypedCallToolResult<T>` with:

- Text content: Pretty-printed JSON string
- Structured content: Typed object data
- MIME type: `application/json`

<Tip>
  The `object()` helper includes both stringified JSON for backwards
  compatibility as specified by the protocol and the new structured data
  property.
</Tip>

### array()

Returns array data wrapped in an object with a `data` field.

```typescript
import { array } from 'mcp-use/server';

server.tool({
  name: 'list-items',
  async () => {
    const items = await getItems();
    return array(items);
  }
});
```

**Returns**: `TypedCallToolResult<{ data: T }>` with structured content

<Note>
  Arrays are automatically wrapped in an object with a `data` field. If you pass
  an array to `object()`, it will automatically call `array()` internally.
</Note>

## Media Content

### image()

Returns image content with base64 data or data URL.

```typescript
import { image } from 'mcp-use/server';

server.tool({
  name: 'generate-chart',
  async ({ data }) => {
    const chartImage = await generateChart(data);
    return image(chartImage, 'image/png');
  }
});

server.resource(
  { name: 'logo', uri: 'asset://logo' },
  async () => image(base64LogoData, 'image/png')
);
```

**Parameters**:

- `data`: Base64 string or data URL
- `mimeType`: Image MIME type (default: `'image/png'`)

**Returns**: `CallToolResult` with image content and metadata

### audio()

Returns audio content. Supports both base64 data and file paths.

```typescript
import { audio } from 'mcp-use/server';

// With base64 data (synchronous)
server.tool({
  name: 'generate-audio',
  async () => audio(base64AudioData, 'audio/wav')
});

// With file path (asynchronous)
server.resource(
  { name: 'notification', uri: 'audio://notification' },
  async () => await audio('./sounds/notification.wav')
);
```

**Parameters**:

- `dataOrPath`: Base64 audio data or file path
- `mimeType`: Audio MIME type (optional, inferred from extension for files)

**Returns**: `CallToolResult` (sync) or `Promise<CallToolResult>` (async for files)

**Supported formats**: WAV, MP3, OGG, M4A, WebM, FLAC, AAC

<Warning>
  When using file paths, make sure to `await` the result as the file is read
  asynchronously.
</Warning>

### binary()

Returns arbitrary binary content as base64.

```typescript
import { binary } from "mcp-use/server";

server.resource({ name: "document", uri: "file://document.pdf" }, async () => {
  const pdfData = await readFile("./document.pdf");
  const base64 = pdfData.toString("base64");
  return binary(base64, "application/pdf");
});
```

**Parameters**:

- `base64Data`: Base64-encoded binary data
- `mimeType`: Content MIME type

**Returns**: `CallToolResult` with binary metadata

## Resource Embedding

### resource()

Creates embedded resource content. Supports two patterns:

**Pattern 1: Three arguments (uri, mimeType, text)**

```typescript
import { resource } from 'mcp-use/server';

server.tool({
  name: 'get-config',
  async () => resource(
    'config://app',
    'application/json',
    JSON.stringify({ api: 'v2', timeout: 30 })
  )
});
```

**Pattern 2: Two arguments (uri, content helper result)**

```typescript
import { resource, text, object } from 'mcp-use/server';

// With text helper
server.tool({
  name: 'get-greeting',
  async () => resource('test://greeting', text('Hello'))
});

// With object helper
server.tool({
  name: 'get-data',
  async () => resource('data://user', object({ id: 1, name: 'Alice' }))
});
```

**Returns**: `CallToolResult` with embedded resource content

## Error Handling

### error()

Returns an error response with the `isError` flag set.

```typescript
import { error, text } from 'mcp-use/server';

server.tool({
  name: 'divide',
  schema: z.object({
    a: z.number(),
    b: z.number()
  }),
  async ({ a, b }) => {
    if (b === 0) {
      return error('Division by zero is not allowed');
    }
    return text(`Result: ${a / b}`);
  }
});

server.tool({
  name: 'fetch-data',
  async ({ url }) => {
    try {
      const data = await fetch(url);
      return object(data);
    } catch (err) {
      return error(`Failed to fetch data: ${err.message}`);
    }
  }
});
```

**Returns**: `CallToolResult` with `isError: true`

<Tip>
  Using `error()` provides a standardized way to communicate failures to clients
  while maintaining consistent response structure.
</Tip>

## Mixed Content

### mix()

Combines multiple content types into a single response. Perfect for returning text with images, resources, or multiple content items together.

```typescript
import { mix, text, image, resource, object } from 'mcp-use/server';

// Text + Image
server.tool({
  name: 'generate-report',
  async ({ data }) => {
    const chart = await generateChart(data);
    return mix(
      text('Analysis complete. See chart below:'),
      image(chart, 'image/png')
    );
  }
});

// Text + Image + Resource
server.tool({
  name: 'test_multiple_content_types',
  async () => mix(
    text('Multiple content types test:'),
    image(RED_PIXEL_PNG, 'image/png'),
    resource(
      'test://mixed-content-resource',
      object({ test: 'data', value: 123 })
    )
  )
});

// Multiple text messages
server.tool({
  name: 'batch-process',
  async ({ items }) => {
    const results = [];
    for (const item of items) {
      const result = await processItem(item);
      results.push(text(`Processed ${item}: ${result}`));
    }
    return mix(...results);
  }
});
```

**Returns**: `CallToolResult` with merged content array and combined metadata

<Note>
  The `mix()` helper merges content arrays and combines `structuredContent` and
  `_meta` objects from all inputs, allowing complex multi-part responses.
</Note>

## Widget Responses

### widget()

Creates a response for a tool that returns a widget. Used in conjunction with the `widget` config on your tool definition.

Per the [MCP Apps specification](https://github.com/modelcontextprotocol/ext-apps), the tool result has three fields with different visibility:

| Field | LLM sees it? | Widget sees it? | Populated by |
|---|---|---|---|
| `content` | **Yes** | Yes | `output` or `message` |
| `structuredContent` | **No** | Yes (as `props`) | `props` |
| `_meta` | **No** | Yes (as `metadata`) | `metadata` |

<Note>
  The `widget()` helper only handles **runtime data**. Widget configuration
  (name, invoking status, etc.) must be set on the tool's `widget` config
  property at registration time.
</Note>

```typescript
import { widget, text } from "mcp-use/server";
import z from "zod";

server.tool(
  {
    name: "show-weather",
    schema: z.object({ city: z.string() }),
    widget: {
      name: "weather-display",
      invoking: "Fetching weather data...",
      invoked: "Weather data loaded",
    },
  },
  async ({ city }) => {
    const weatherData = await fetchWeather(city);

    return widget({
      props: {
        city,
        temperature: weatherData.temp,
        conditions: weatherData.conditions,
      },
      output: text(
        `Weather in ${city}: ${weatherData.temp}°C, ${weatherData.conditions}`
      ),
    });
  }
);
```

With extra metadata for the widget:

```typescript
return widget({
  props: { items: filteredItems },
  metadata: { totalCount: 1000, nextCursor: "abc123" },
  output: text(`Showing ${filteredItems.length} of 1000 results`),
});
```

**Parameters:**

| Parameter | Type | Description |
|---|---|---|
| `props` | `Record<string, any>` | Widget rendering data. Sent as `structuredContent` (LLM does **not** see). Widget reads via `useWidget().props`. |
| `output` | `CallToolResult` | Response helper (text(), object(), etc.) defining what the LLM sees in `content`. |
| `metadata` | `Record<string, unknown>` | Extra data for the widget in `_meta`. Widget reads via `useWidget().metadata`. |
| `message` | `string` | Optional text override for `content` (used instead of `output`). |

**Returns**: `CallToolResult` with `structuredContent` (props), `content` (LLM text), and `_meta` (metadata)

<Tip>
  The widget name must match a `.tsx` file or folder in your `resources/`
  directory. The widget receives `props` via `useWidget().props` and `metadata`
  via `useWidget().metadata`, while the model only sees the `output` text.
</Tip>

## Using Response Helpers in Prompts and Resources

Response helpers aren't just for tools - they work seamlessly with prompts and resources too. The server automatically converts `CallToolResult` to the appropriate format (`GetPromptResult` for prompts, `ReadResourceResult` for resources), allowing you to use the same familiar API across all MCP primitives.

### Prompts with Response Helpers

Instead of manually constructing message objects, you can use the same helpers you use in tools. The server automatically converts them to the required `GetPromptResult` format:

```typescript
import { text, object, mix, image } from "mcp-use/server";
import { z } from "zod";

// Simple text prompt
server.prompt(
  {
    name: "greeting",
    description: "Generate a greeting message",
    schema: z.object({ name: z.string() }),
  },
  async ({ name }) => {
    return text(`Hello, ${name}! How can I help you today?`);
  }
);

// Structured data prompt
server.prompt(
  {
    name: "user_context",
    description: "Provide user context data",
    schema: z.object({ userId: z.string() }),
  },
  async ({ userId }) => {
    const userData = await fetchUser(userId);
    return object({
      userId: userData.id,
      email: userData.email,
      preferences: userData.preferences,
    });
  }
);

// Mixed content prompt
server.prompt(
  {
    name: "analysis_report",
    description: "Generate analysis report with visualization",
    schema: z.object({ dataId: z.string() }),
  },
  async ({ dataId }) => {
    const data = await fetchData(dataId);
    const chart = await generateChart(data);

    return mix(
      text("Analysis Report"),
      object({ summary: data.summary, metrics: data.metrics }),
      image(chart, "image/png")
    );
  }
);
```

**How it works**: When you return a response helper from a prompt handler, the server automatically converts it to a `GetPromptResult` with properly formatted messages. The content becomes message content, and any structured data is preserved.

### Resources with Response Helpers

Resources fully support response helpers (you've seen examples throughout this guide). The automatic conversion ensures consistent behavior:

```typescript
import { text, object, markdown } from "mcp-use/server";

// Text resource
server.resource(
  {
    name: "readme",
    uri: "doc://readme",
  },
  async () => markdown("# Welcome\n\nGetting started...")
);

// Structured resource
server.resource(
  {
    name: "config",
    uri: "config://app",
  },
  async () => object({ version: "1.0.0", features: ["auth", "api"] })
);

// Resource template with helpers
server.resourceTemplate(
  {
    name: "user_profile",
    uriTemplate: "user://{userId}/profile",
  },
  async (uri, { userId }) => {
    const profile = await fetchProfile(userId);
    return object(profile);
  }
);
```

**Automatic Conversion**: The server converts `CallToolResult` to `ReadResourceResult` format, extracting content and metadata appropriately. This means you can use the same response helpers for tools, prompts, and resources without worrying about format differences.

<Tip>
  This unified API makes it easy to share response-building logic across tools,
  prompts, and resources. For example, a helper function that returns `object()`
  can be used in all three contexts.
</Tip>

## Type Safety

Response helpers are fully typed for TypeScript users:

```typescript
import { object, TypedCallToolResult } from "mcp-use/server";

// Type inference
const response = object({ userId: "123", name: "Alice" });
// response type: TypedCallToolResult<{ userId: string; name: string }>

// Explicit typing
interface UserData {
  userId: string;
  email: string;
  role: "admin" | "user";
}

function getUserResponse(user: UserData): TypedCallToolResult<UserData> {
  return object(user);
}
```

## Next Steps

- [Tools](./tools) - Learn about creating MCP tools
- [Resources](./resources) - Managing static and dynamic content
- [Prompts](./prompts) - Building interactive prompts
- [UI Widgets](./mcp-apps) - Creating OpenAI Apps SDK widgets
