Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
DEPRECATED: Replaced by mcp-app-builder. Previously used to build ChatGPT apps with interactive React widgets via mcp-use.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/server/response-helpers.md
1# Response Helpers23Response helpers format output from tools, resources, and prompts. Always use helpers instead of returning raw values.45**Available helpers:** `text()`, `object()`, `markdown()`, `image()`, `error()`, `widget()`, `mix()`, `resource()`67---89## Why Use Response Helpers?1011```typescript12// ❌ Bad - Raw return value13server.tool(14{ name: "get-data", schema: z.object({}) },15async () => {16return { status: "ok", data: [1, 2, 3] }; // Wrong!17}18);1920// ✅ Good - Using helper21server.tool(22{ name: "get-data", schema: z.object({}) },23async () => {24return object({ status: "ok", data: [1, 2, 3] });25}26);27```2829**Why:**30- Helpers set correct MIME types31- Ensure proper serialization32- Enable client-side rendering33- Support multi-content responses3435---3637## text()3839Simple string responses. Most common helper.4041```typescript42import { text } from "mcp-use/server";4344server.tool(45{ name: "greet", schema: z.object({ name: z.string() }) },46async ({ name }) => {47return text(`Hello, ${name}!`);48}49);5051// Multi-line text52server.tool(53{ name: "format-address", schema: z.object({54address: z.object({55street: z.string(),56city: z.string(),57state: z.string(),58zip: z.string(),59country: z.string()60})61}) },62async ({ address }) => {63return text(64`${address.street}\n` +65`${address.city}, ${address.state} ${address.zip}\n` +66`${address.country}`67);68}69);70```7172**Use for:**73- Simple messages74- Confirmation text75- Status updates76- Plain text output7778---7980## object()8182Structured JSON data. Use when AI or client needs structured information.8384```typescript85import { object } from "mcp-use/server";8687server.tool(88{ name: "get-user", schema: z.object({ id: z.string() }) },89async ({ id }) => {90const user = await fetchUser(id);9192return object({93id: user.id,94name: user.name,95email: user.email,96createdAt: user.createdAt,97settings: {98theme: user.theme,99notifications: user.notifications100}101});102}103);104105// With arrays106server.tool(107{ name: "list-todos", schema: z.object({}) },108async () => {109const todos = await getTodos();110111return object({112total: todos.length,113items: todos.map(t => ({114id: t.id,115title: t.title,116completed: t.completed,117dueDate: t.dueDate118}))119});120}121);122```123124**Use for:**125- Structured data126- Lists and arrays127- Nested objects128- Data that AI needs to parse129130---131132## markdown()133134Formatted text with markdown syntax. Great for documentation, reports, explanations.135136```typescript137import { markdown } from "mcp-use/server";138139server.tool(140{ name: "generate-report", schema: z.object({ data: z.array(z.any()) }) },141async ({ data }) => {142return markdown(`143# Daily Report144145## Summary146Total items processed: ${data.length}147148## Breakdown149| Category | Count |150|----------|-------|151| Success | ${data.filter(d => d.status === "ok").length} |152| Failed | ${data.filter(d => d.status === "error").length} |153154## Details155${data.map(d => `- **${d.id}**: ${d.message}`).join('\n')}156157---158*Generated at ${new Date().toISOString()}*159`);160}161);162163// Code examples in markdown164server.tool(165{ name: "explain-function", schema: z.object({ name: z.string() }) },166async ({ name }) => {167return markdown(`168# ${name}() Function169170## Usage171\`\`\`typescript172const result = await ${name}(params);173\`\`\`174175## Description176This function performs...177178## Parameters179- \`params\` - Configuration object180181## Returns182Returns a Promise that resolves to...183`);184}185);186```187188**Use for:**189- Documentation190- Formatted reports191- Explanations with structure192- Code examples193- Rich text output194195---196197## image()198199Embed images in responses. Supports URLs or base64 data.200201```typescript202import { image } from "mcp-use/server";203204// Image from URL205server.tool(206{ name: "get-chart", schema: z.object({ data: z.array(z.number()) }) },207async ({ data }) => {208const chartUrl = await generateChart(data);209return image(chartUrl);210}211);212213// Base64 image214server.tool(215{ name: "generate-qr", schema: z.object({ text: z.string() }) },216async ({ text }) => {217const qrCodeBase64 = await generateQRCode(text);218return image(`data:image/png;base64,${qrCodeBase64}`);219}220);221222// Image with MIME type223server.tool(224{ name: "get-diagram", schema: z.object({ id: z.string() }) },225async ({ id }) => {226const diagramUrl = await getDiagram(id);227return image(diagramUrl, "image/svg+xml");228}229);230```231232**Use for:**233- Charts and graphs234- QR codes235- Diagrams236- Generated images237- Visual output238239---240241## error()242243Error responses. Always use this instead of throwing exceptions.244245```typescript246import { error } from "mcp-use/server";247248server.tool(249{ name: "fetch-data", schema: z.object({ id: z.string() }) },250async ({ id }) => {251try {252const data = await fetchData(id);253254if (!data) {255return error(`No data found for ID: ${id}`);256}257258return object(data);259} catch (err) {260return error(261`Failed to fetch data: ${err instanceof Error ? err.message : "Unknown error"}`262);263}264}265);266267// Multiple error cases268server.tool(269{ name: "update-user", schema: z.object({ id: z.string(), name: z.string() }) },270async ({ id, name }) => {271const user = await getUser(id);272273if (!user) {274return error(`User not found: ${id}`);275}276277if (!name.trim()) {278return error("Name cannot be empty");279}280281await updateUser(id, { name });282return text("User updated successfully");283}284);285```286287**Use for:**288- Validation errors289- Not found errors290- Permission errors291- Operation failures292293---294295## widget()296297Return visual UI alongside data. Tool must have `widget: { name }` config.298299```typescript300import { widget, text } from "mcp-use/server";301302server.tool(303{304name: "search-products",305description: "Search products by query",306schema: z.object({307query: z.string().describe("Search query")308}),309widget: {310name: "product-list", // Must match resources/product-list.tsx311invoking: "Searching...",312invoked: "Products loaded"313}314},315async ({ query }) => {316const products = await searchProducts(query);317318return widget({319props: {320products,321query,322totalCount: products.length323},324output: text(`Found ${products.length} products matching "${query}"`)325});326}327);328```329330**Widget response structure:**331- `props` - Data sent to widget component332- `output` - Text/object the AI model sees333334**Use for:**335- Browsing lists336- Comparing items337- Interactive selection338- Visual data representation339340See [../widgets/basics.md](../widgets/basics.md) for widget implementation.341342---343344## mix()345346Combine multiple content types in a single response.347348```typescript349import { mix, text, image, markdown } from "mcp-use/server";350351server.tool(352{ name: "generate-report", schema: z.object({ id: z.string() }) },353async ({ id }) => {354const report = await getReport(id);355const chartUrl = await generateChart(report.data);356357return mix(358markdown(`# Report: ${report.title}\n\n${report.summary}`),359image(chartUrl),360text(`Generated at ${new Date().toISOString()}`)361);362}363);364365// Text + structured data366server.tool(367{ name: "analyze-code", schema: z.object({ code: z.string() }) },368async ({ code }) => {369const analysis = await analyzeCode(code);370371return mix(372text(`Analysis complete. Found ${analysis.issues.length} issues.`),373object({374complexity: analysis.complexity,375issues: analysis.issues,376suggestions: analysis.suggestions377})378);379}380);381```382383**Use for:**384- Rich responses with multiple formats385- Text + image combinations386- Summary + detailed data387- Multiple related pieces of content388389---390391## Embedding Resources392393Reference resources in tool responses:394395```typescript396import { text, resource } from "mcp-use/server";397398server.tool(399{ name: "get-help", schema: z.object({ topic: z.string() }) },400async ({ topic }) => {401return mix(402text(`Help documentation for: ${topic}`),403resource(`docs://${topic}`, "text/markdown")404);405}406);407```408409---410411## Response Patterns412413### Success with Data414```typescript415return object({416success: true,417data: { ... },418message: "Operation completed successfully"419});420```421422### Success with Message423```typescript424return text("User created successfully");425```426427### Not Found428```typescript429if (!item) {430return error(`Item not found: ${id}`);431}432```433434### Validation Error435```typescript436if (!email.includes("@")) {437return error("Invalid email address");438}439```440441### Server Error442```typescript443try {444// ... operation445} catch (err) {446console.error("Operation failed:", err);447return error("Operation failed. Please try again.");448}449```450451---452453## Complete Example454455```typescript456import {457MCPServer,458text,459object,460markdown,461image,462error,463widget,464mix465} from "mcp-use/server";466import { z } from "zod";467468const server = new MCPServer({469name: "response-examples",470version: "1.0.0"471});472473// Simple text474server.tool(475{ name: "greet", schema: z.object({ name: z.string() }) },476async ({ name }) => text(`Hello, ${name}!`)477);478479// Structured data480server.tool(481{ name: "get-stats", schema: z.object({}) },482async () => object({483users: 1523,484active: 342,485growth: "+12%"486})487);488489// Markdown report490server.tool(491{ name: "daily-summary", schema: z.object({}) },492async () => markdown(`493# Daily Summary494495## Metrics496- **Users**: 1,523497- **Revenue**: $4,231498- **Orders**: 87499500## Top Products5011. Widget Pro5022. Gadget Plus5033. Tool Kit504`)505);506507// Error handling508server.tool(509{ name: "fetch-item", schema: z.object({ id: z.string() }) },510async ({ id }) => {511try {512const item = await db.get(id);513514if (!item) {515return error(`Item not found: ${id}`);516}517518return object(item);519} catch (err) {520return error("Database error");521}522}523);524525// Mixed content526server.tool(527{ name: "analyze", schema: z.object({ data: z.array(z.number()) }) },528async ({ data }) => {529const stats = calculateStats(data);530const chartUrl = await generateChart(data);531532return mix(533markdown(`## Analysis Results\n\nProcessed ${data.length} data points`),534object(stats),535image(chartUrl)536);537}538);539540// Widget response541server.tool(542{543name: "browse-items",544schema: z.object({ category: z.string() }),545widget: { name: "item-browser" }546},547async ({ category }) => {548const items = await getItems(category);549550return widget({551props: { items, category },552output: text(`Found ${items.length} items in ${category}`)553});554}555);556557server.listen();558```559560---561562## Best Practices563564### 1. Always Use Helpers565```typescript566❌ return { data: "value" };567✅ return object({ data: "value" });568```569570### 2. Match Helper to Content571```typescript572✅ text("Simple message")573✅ object({ structured: "data" })574✅ markdown("# Formatted text")575✅ error("Error message")576```577578### 3. Handle Errors Gracefully579```typescript580✅ return error("User not found: userId_123");581❌ throw new Error("Raw exception");582```583584### 4. Provide Context in Errors585```typescript586✅ error(`User not found: ${userId}`);587✅ error(`Invalid email format: ${email}`);588❌ error("Error");589```590591### 5. Use Markdown for Rich Content592```typescript593✅ markdown(`# Title\n\n- Point 1\n- Point 2`);594❌ text("Title\nPoint 1\nPoint 2"); // No formatting595```596597### 6. Widget Output is for AI598```typescript599return widget({600props: { /* visual data */ },601output: text("Concise summary for AI") // AI only sees this602});603```604605---606607## Response Helper Reference608609| Helper | Returns | Use For | Example |610|--------|---------|---------|---------|611| `text(string)` | Plain text | Simple messages | `text("Hello")` |612| `object(any)` | JSON | Structured data | `object({ id: 1 })` |613| `markdown(string)` | Formatted text | Documentation, reports | `markdown("# Title")` |614| `image(url, mime?)` | Image | Charts, diagrams | `image(url)` |615| `error(msg)` | Error | Failures | `error("Not found")` |616| `widget(config)` | UI + data | Visual interfaces | `widget({ props, output })` |617| `mix(...results)` | Multiple | Rich responses | `mix(text(), image())` |618| `resource(uri, mime)` | Resource ref | Embed resources | `resource("docs://guide", "text/markdown")` |619620---621622## Next Steps623624- **Create tools** → [tools.md](tools.md)625- **Add resources** → [resources.md](resources.md)626- **Build widgets** → [../widgets/basics.md](../widgets/basics.md)627- **See examples** → [../patterns/common-patterns.md](../patterns/common-patterns.md)628