Response helpers format output from tools, resources, and prompts. Always use helpers instead of returning raw values.
Sign in to save downloads to your library and vote.
Preview
Response Helpers
Response helpers format output from tools, resources, and prompts. Always use helpers instead of returning raw values.
Available helpers: text(), object(), markdown(), image(), error(), widget(), mix(), resource()
Why Use Response Helpers?
// ❌ Bad - Raw return value
server.tool({ name: "get-data", schema: z.object({}) }, async () => {
return { status: "ok", data: [1, 2, 3] }; // Wrong!
});
// ✅ Good - Using helper
server.tool({ name: "get-data", schema: z.object({}) }, async () => {
return object({ status: "ok", data: [1, 2, 3] });
});
Why:
- Helpers set correct MIME types
- Ensure proper serialization
- Enable client-side rendering
- Support multi-content responses
text()
Simple string responses. Most common helper.
import { text } from "mcp-use/server";
server.tool(
{ name: "greet", schema: z.object({ name: z.string() }) },
async ({ name }) => {
return text(`Hello, ${name}!`);
},
);
// Multi-line text
server.tool(
{
name: "format-address",
schema: z.object({
address: z.object({
street: z.string(),
city: z.string(),
state: z.string(),
zip: z.string(),
country: z.string(),
}),
}),
},
async ({ address }) => {
return text(
`${address.street}\n` +
`${address.city}, ${address.state} ${address.zip}\n` +
`${address.country}`,
);
},
);
Use for:
- Simple messages
- Confirmation text
- Status updates
- Plain text output
object()
Structured JSON data. Use when AI or client needs structured information.
import { object } from "mcp-use/server";
server.tool(
{ name: "get-user", schema: z.object({ id: z.string() }) },
async ({ id }) => {
const user = await fetchUser(id);
return object({
id: user.id,
name: user.name,
email: user.email,
createdAt: user.createdAt,
settings: {
theme: user.theme,
notifications: user.notifications,
},
});
},
);
// With arrays
server.tool({ name: "list-todos", schema: z.object({}) }, async () => {
const todos = await getTodos();
return object({
total: todos.length,
items: todos.map((t) => ({
id: t.id,
title: t.title,
completed: t.completed,
dueDate: t.dueDate,
})),
});
});
Use for:
- Structured data
- Lists and arrays
- Nested objects
- Data that AI needs to parse
markdown()
Formatted text with markdown syntax. Great for documentation, reports, explanations.
import { markdown } from "mcp-use/server";
server.tool(
{ name: "generate-report", schema: z.object({ data: z.array(z.any()) }) },
async ({ data }) => {
return markdown(`
# Daily Report
## Summary
Total items processed: ${data.length}
## Breakdown
| Category | Count |
|----------|-------|
| Success | ${data.filter((d) => d.status === "ok").length} |
| Failed | ${data.filter((d) => d.status === "error").length} |
## Details
${data.map((d) => `- **${d.id}**: ${d.message}`).join("\n")}
---
*Generated at ${new Date().toISOString()}*
`);
},
);
// Code examples in markdown
server.tool(
{ name: "explain-function", schema: z.object({ name: z.string() }) },
async ({ name }) => {
return markdown(`
# ${name}() Function
## Usage
\`\`\`typescript
const result = await ${name}(params);
\`\`\`
## Description
This function performs...
## Parameters
- \`params\` - Configuration object
## Returns
Returns a Promise that resolves to...
`);
},
);
Use for:
- Documentation
- Formatted reports
- Explanations with structure
- Code examples
- Rich text output
image()
Embed images in responses. Supports URLs or base64 data.
import { image } from "mcp-use/server";
// Image from URL
server.tool(
{ name: "get-chart", schema: z.object({ data: z.array(z.number()) }) },
async ({ data }) => {
const chartUrl = await generateChart(data);
return image(chartUrl);
},
);
// Base64 image
server.tool(
{ name: "generate-qr", schema: z.object({ text: z.string() }) },
async ({ text }) => {
const qrCodeBase64 = await generateQRCode(text);
return image(`data:image/png;base64,${qrCodeBase64}`);
},
);
// Image with MIME type
server.tool(
{ name: "get-diagram", schema: z.object({ id: z.string() }) },
async ({ id }) => {
const diagramUrl = await getDiagram(id);
return image(diagramUrl, "image/svg+xml");
},
);
Use for:
- Charts and graphs
- QR codes
- Diagrams
- Generated images
- Visual output
error()
Error responses. Always use this instead of throwing exceptions.
import { error } from "mcp-use/server";
server.tool(
{ name: "fetch-data", schema: z.object({ id: z.string() }) },
async ({ id }) => {
try {
const data = await fetchData(id);
if (!data) {
return error(`No data found for ID: ${id}`);
}
return object(data);
} catch (err) {
return error(
`Failed to fetch data: ${err instanceof Error ? err.message : "Unknown error"}`,
);
}
},
);
// Multiple error cases
server.tool(
{
name: "update-user",
schema: z.object({ id: z.string(), name: z.string() }),
},
async ({ id, name }) => {
const user = await getUser(id);
if (!user) {
return error(`User not found: ${id}`);
}
if (!name.trim()) {
return error("Name cannot be empty");
}
await updateUser(id, { name });
return text("User updated successfully");
},
);
Use for:
- Validation errors
- Not found errors
- Permission errors
- Operation failures
widget()
Return visual UI alongside data. Tool must have widget: { name } config.
import { widget, text } from "mcp-use/server";
server.tool(
{
name: "search-products",
description: "Search products by query",
schema: z.object({
query: z.string().describe("Search query"),
}),
widget: {
name: "product-list", // Must match resources/product-list.tsx
invoking: "Searching...",
invoked: "Products loaded",
},
},
async ({ query }) => {
const products = await searchProducts(query);
return widget({
props: {
products,
query,
totalCount: products.length,
},
output: text(`Found ${products.length} products matching "${query}"`),
});
},
);
Widget response structure:
props- Data sent to widget componentoutput- Text/object the AI model sees
Use for:
- Browsing lists
- Comparing items
- Interactive selection
- Visual data representation
See ../widgets/basics.md for widget implementation.
mix()
Combine multiple content types in a single response.
import { mix, text, image, markdown } from "mcp-use/server";
server.tool(
{ name: "generate-report", schema: z.object({ id: z.string() }) },
async ({ id }) => {
const report = await getReport(id);
const chartUrl = await generateChart(report.data);
return mix(
markdown(`# Report: ${report.title}\n\n${report.summary}`),
image(chartUrl),
text(`Generated at ${new Date().toISOString()}`),
);
},
);
// Text + structured data
server.tool(
{ name: "analyze-code", schema: z.object({ code: z.string() }) },
async ({ code }) => {
const analysis = await analyzeCode(code);
return mix(
text(`Analysis complete. Found ${analysis.issues.length} issues.`),
object({
complexity: analysis.complexity,
issues: analysis.issues,
suggestions: analysis.suggestions,
}),
);
},
);
Use for:
- Rich responses with multiple formats
- Text + image combinations
- Summary + detailed data
- Multiple related pieces of content
Embedding Resources
Reference resources in tool responses:
import { text, resource } from "mcp-use/server";
server.tool(
{ name: "get-help", schema: z.object({ topic: z.string() }) },
async ({ topic }) => {
return mix(
text(`Help documentation for: ${topic}`),
resource(`docs://${topic}`, "text/markdown"),
);
},
);
Response Patterns
Success with Data
return object({
success: true,
data: { ... },
message: "Operation completed successfully"
});
Success with Message
return text("User created successfully");
Not Found
if (!item) {
return error(`Item not found: ${id}`);
}
Validation Error
if (!email.includes("@")) {
return error("Invalid email address");
}
Server Error
try {
// ... operation
} catch (err) {
console.error("Operation failed:", err);
return error("Operation failed. Please try again.");
}
Complete Example
import {
MCPServer,
text,
object,
markdown,
image,
error,
widget,
mix,
} from "mcp-use/server";
import { z } from "zod";
const server = new MCPServer({
name: "response-examples",
version: "1.0.0",
});
// Simple text
server.tool(
{ name: "greet", schema: z.object({ name: z.string() }) },
async ({ name }) => text(`Hello, ${name}!`),
);
// Structured data
server.tool({ name: "get-stats", schema: z.object({}) }, async () =>
object({
users: 1523,
active: 342,
growth: "+12%",
}),
);
// Markdown report
server.tool({ name: "daily-summary", schema: z.object({}) }, async () =>
markdown(`
# Daily Summary
## Metrics
- **Users**: 1,523
- **Revenue**: $4,231
- **Orders**: 87
## Top Products
1. Widget Pro
2. Gadget Plus
3. Tool Kit
`),
);
// Error handling
server.tool(
{ name: "fetch-item", schema: z.object({ id: z.string() }) },
async ({ id }) => {
try {
const item = await db.get(id);
if (!item) {
return error(`Item not found: ${id}`);
}
return object(item);
} catch (err) {
return error("Database error");
}
},
);
// Mixed content
server.tool(
{ name: "analyze", schema: z.object({ data: z.array(z.number()) }) },
async ({ data }) => {
const stats = calculateStats(data);
const chartUrl = await generateChart(data);
return mix(
markdown(`## Analysis Results\n\nProcessed ${data.length} data points`),
object(stats),
image(chartUrl),
);
},
);
// Widget response
server.tool(
{
name: "browse-items",
schema: z.object({ category: z.string() }),
widget: { name: "item-browser" },
},
async ({ category }) => {
const items = await getItems(category);
return widget({
props: { items, category },
output: text(`Found ${items.length} items in ${category}`),
});
},
);
server.listen();
Best Practices
1. Always Use Helpers
❌ return { data: "value" };
✅ return object({ data: "value" });
2. Match Helper to Content
✅ text("Simple message")
✅ object({ structured: "data" })
✅ markdown("# Formatted text")
✅ error("Error message")
3. Handle Errors Gracefully
✅ return error("User not found: userId_123");
❌ throw new Error("Raw exception");
4. Provide Context in Errors
✅ error(`User not found: ${userId}`);
✅ error(`Invalid email format: ${email}`);
❌ error("Error");
5. Use Markdown for Rich Content
✅ markdown(`# Title\n\n- Point 1\n- Point 2`);
❌ text("Title\nPoint 1\nPoint 2"); // No formatting
6. Widget Output is for AI
return widget({
props: {
/* visual data */
},
output: text("Concise summary for AI"), // AI only sees this
});
Response Helper Reference
| Helper | Returns | Use For | Example |
|---|---|---|---|
text(string) | Plain text | Simple messages | text("Hello") |
object(any) | JSON | Structured data | object({ id: 1 }) |
markdown(string) | Formatted text | Documentation, reports | markdown("# Title") |
image(url, mime?) | Image | Charts, diagrams | image(url) |
error(msg) | Error | Failures | error("Not found") |
widget(config) | UI + data | Visual interfaces | widget({ props, output }) |
mix(...results) | Multiple | Rich responses | mix(text(), image()) |
resource(uri, mime) | Resource ref | Embed resources | resource("docs://guide", "text/markdown") |
Next Steps
- Create tools → tools.md
- Add resources → resources.md
- Build widgets → ../widgets/basics.md
- See examples → ../patterns/common-patterns.md
Ingestion metadata
- Source catalog
- awesome-llm-apps
- Repository
- Shubhamsaboo/awesome-llm-apps · main
- File path
- generative_ui_agents/ai-mcp-app-builder/apps/mcp-use-server/.agent/skills/mcp-apps-builder/references/server/response-helpers.md
- Last refreshed
- 10/24/2026, 3:00:14 AM (5h ago)
- Refresh schedule
- Daily · 03:00 UTC
- Dedupe status
- Unique · deduped by (source, url)