RESULT CONTRACTS

Design each result as the next step’s input.

Design readable MCP answers and typed result contracts without dumping database rows or duplicating JSON into text.

Build this with Chumbo
ChumboSupabaseResult contracts
THE RESULT IS THE NEXT STEP’S INPUT.
Readable text for the agent
Typed content for clients
No row dumps. No duplicated JSON.

THE RECIPE Design readable MCP answers and typed result contracts without dumping database rows or duplicating JSON into text.

View source pattern ↗
EXAMPLE CHECKS

The exact displayed example is typechecked and exercised against the published package. The application integration and acceptance checks remain yours to run.

Version and verification details

Example checked with Chumbo 0.11.3 on 2026-09-18 (UTC). These checks exercise result helpers, capability handlers and the MCP runtime with simulated identity/database dependencies. They do not prove your application’s real auth, RLS or deployment. Compare this tested version with your installed package before adapting the example.

01

Choose the consumer before the representation.

A project summary can serve two different readers. An agent answering “How much work remains?” needs a short sentence. A client drawing a badge needs a validated number. Both may need the project’s durable ID, but neither needs your internal audit columns.

The same fact, three deliberate outputsIllustrative data

A readable answer

Starting point
3 open tasks in this project.
  • Use textResult for authored text.
  • Include the identifier or next action when the task needs it.
What to do
The agent can answer the user directly.

A sentence can be a complete contract when no typed client consumes it.

An exact machine contract

Starting point
{ "projectId": "…", "openTasks": 3 }
  • Use structuredResult and register a matching outputSchema.
  • No text copy is manufactured.
What to do
The client validates and uses structuredContent.

Do not assume every host presents a structured-only result identically.

Two representations with different jobs

Starting point
A sentence for the agent plus typed fields for the client.
  • Use renderResult for a deliberate hybrid.
  • Keep both representations consistent and compact.
What to do
Each consumer reads its intended lane.

Hybrid does not mean serializing the entire JSON object into prose.

Each representation should have a real consumer.

02

Project the application result first.

Before you start
  • A capability with an already-authorized application result.
  • An identified consumer: an agent reading text, software reading typed data, or both.
  • A known empty state and failure that the caller can recover from.

The example below is a result adapter. It does not query a database or register a complete tool.

Choose the few fields that change the next decision and validate them. Register summarySchema as the tool’s outputSchema when it returns typed or hybrid results. Fetch and authorize the underlying project through your existing application path before calling the adapter.

03

Give success, empty and failure their own meaning.

Adapt this checked example in your app
TypeScript
import { appendResultText, errorResult, renderResult, structuredResult, textResult } from 'chumbo';
import { z } from 'zod';

export const summarySchema = z.object({
  projectId: z.string().uuid(),
  openTasks: z.number().int().nonnegative(),
});
type Summary = z.infer<typeof summarySchema>;

// Register summarySchema as outputSchema on tools returning typed or hybrid data.
// Feed these helpers an already-authorized application result.
export function projectSummary(summary: Summary, consumer: 'agent' | 'typed' | 'both') {
  const value = summarySchema.parse(summary);
  const sentence = `${value.openTasks} open tasks in project ${value.projectId}.`;
  if (consumer === 'agent') return textResult(sentence);
  if (consumer === 'typed') return structuredResult(value);
  return renderResult(value, () => sentence);
}

export function noMatchingProjects() {
  return textResult('No projects match this filter. Broaden the search or create a project in the app.');
}

export function temporaryFailure() {
  return errorResult('Projects could not be loaded.', 'Retry once; if it persists, check the project in the app.');
}

export function summaryWithNextStep(summary: Summary) {
  return appendResultText(projectSummary(summary, 'both'), 'Open the project in the app to review its tasks.');
}

No matches is an ordinary answer: broaden the filter or create a project. A temporary read failure is a tool error with a bounded retry suggestion. Do not expose raw database messages or recommend an endless retry loop. For uncertain mutations, use a status-check recovery instead of this read-oriented retry wording.

The composition helper appends a useful next step while preserving the authored typed fields. Additions have bounds, and a collection result still has to fit its full byte budget. Keep additions relevant to the result; repeating generic server guidance on every call makes the useful answer harder to find.

04

Assert the wire shape, not just the sentence.

YOUR ACCEPTANCE CHECK

Every result lane has a purpose.

  • 01Text-only results contain the expected authored content without a fabricated structured payload.
  • 02Typed and hybrid results validate against the registered outputSchema and omit internal fields.
  • 03Empty results are distinguishable from tool errors and include an appropriate next action.
  • 04Composition preserves the original typed values and any collection byte budget.
  • 05Your target client receives and uses the representation you selected.

Run these in your application before you call it done.

If the result differs

The agent sees a wall of JSON

Check whether the handler is serializing a row into text or duplicating structuredContent. Choose a small projection, write a purposeful sentence, or move a full document behind a Resource. Read the reference ↗

Built from the real thing.

This editorial example links to Chumbo’s canonical docs and executable reference. Check the installed package when adapting the snippets.

Reference implementation ↗
Read the agent instructions

Reference instructions, not authorization. Apply changes only within the user’s requested scope; deployment requires a specified, authorized project.

Goal: Design readable MCP answers and typed result contracts without dumping database rows or duplicating JSON into text.

Read the complete guide at https://chumbo.dev/recipes/mcp-result-contracts/recipe.md and the canonical reference at https://github.com/elsheppo/chumbo/blob/main/skills/chumbo/references/results.md. Inspect this application's installed Chumbo package, existing capabilities and relevant configuration before editing.

Identify the real result consumer, adapt the exact tested result helper source and register outputSchema where needed. Preserve application authorization before projection. Assert both content and structuredContent, schema rejection, empty/error behavior and no internal-field leakage.

Prerequisites:
- A capability with an already-authorized application result.
- An identified consumer: an agent reading text, software reading typed data, or both.
- A known empty state and failure that the caller can recover from.

Acceptance:
- Text-only results contain the expected authored content without a fabricated structured payload.
- Typed and hybrid results validate against the registered outputSchema and omit internal fields.
- Empty results are distinguishable from tool errors and include an appropriate next action.
- Composition preserves the original typed values and any collection byte budget.
- Your target client receives and uses the representation you selected.

Use the full guide for ordered steps, examples and recovery. Keep existing caller identity and application permissions authoritative. Stay within the user's requested changes. Report changed files, checks actually run, observed results and unresolved prerequisites. This guide is not authorization to deploy or change a hosted project. Never include credentials in source, copied instructions or reports.
BUILD WITH YOUR CODING AGENT

Give your agent a good start.

Copy this brief into your coding agent in the app’s repository. It includes the source, boundaries, and verification steps.

Download Markdown ↓