sample lets an MCP tool call a model for a draft, such as a short note. The tool itself has no model.
Call it as ctx.context.sample inside the tool.
You need an MCP server first. If you do not have one, start with Serve Tools over HTTP.
import { toolDefinition } from '@tanstack/ai'
import { createMCPServer } from '@tanstack/ai-mcp/server'
import type { MCPToolContext } from '@tanstack/ai-mcp/server'
import { z } from 'zod'
const draftNote = toolDefinition({
name: 'draft_note',
description: 'Draft a short note about a topic',
inputSchema: z.object({
topic: z.string(),
}),
}).server<MCPToolContext>(async (args, ctx) => {
const draft = await ctx.context.sample({
messages: [
{
role: 'user',
content: `Write a short note about ${args.topic}.`,
},
],
})
if (typeof draft !== 'string') {
throw new Error('The sample result is not text.')
}
return draft
})
const server = createMCPServer({
name: 'notes',
version: '1.0.0',
tools: [draftNote],
sample: async (request) => {
const first = request.messages[0]
const content = first === undefined ? '' : first.content
return `Draft: ${content}`
},
})
export function handleMcp(request: Request) {
return server.fetch(request)
}Call draft_note with a topic. The tool result is the draft text. In this example, the sample function returns Draft: plus the message. In your app, call your model there.
| Spec | Result |
|---|---|
| Spec 2025 | ctx.context.sample asks the MCP client. It does not call the sample function. |
| Spec 2026 | ctx.context.sample calls the sample function. It does not ask the client. |
| Spec 2026 with no sample | ctx.context.sample throws an Error. The message names sample. |
On spec 2025, the server can ask the client only when it keeps a session. Without a session, ctx.context.sample calls the sample function. See MCP Server Sessions.