Persistence (Advanced)
- Overview
- Quick Start
- Providers
- Harnesses
- Harness Auth
- Workspace
- Tools
- Policy
- Lifecycle & Snapshots
- Portable Snapshots
- Keep Files After Reload
- Save a Named Version
- Branch From a Version
- Send a Frozen File
- Let the Agent Save and Fork
- Pick Which Files to Keep
- What a Snapshot Stores
- Instance Durability
- Durable Runs
- Events
Sandboxes (Advanced)
Serve Resources and Prompts
Until you serve a resource, a host cannot read your document. Until you serve a prompt, a host cannot start from that prompt.
Define Resources and Prompts#
- Define each document with resourceDefinition.
- Define each prompt with promptDefinition.
- Pass resources and prompts to createMCPServer.
import {
createMCPServer,
promptDefinition,
resourceDefinition,
} from '@tanstack/ai-mcp/server'
import { z } from 'zod'
const readme = resourceDefinition({
name: 'readme',
mimeType: 'text/markdown',
uri: 'file:///readme.md',
}).read(async () => ({ text: '# Hello' }))
const file = resourceDefinition({
name: 'file',
mimeType: 'text/plain',
uriTemplate: 'file:///{path}',
argsSchema: z.object({ path: z.string() }),
}).read(async (_uri, { path }) => ({ text: `The body of ${path}` }))
const summarize = promptDefinition({
name: 'summarize',
description: 'Summarize a topic',
argsSchema: z.object({ topic: z.string() }),
}).render(async (args) => [{ role: 'user', content: args.topic }])
const server = createMCPServer({
name: 'library',
version: '1.0.0',
resources: [readme, file],
prompts: [summarize],
})
export function handleMcp(request: Request) {
return server.fetch(request)
}Resources#
A resource must have uri or uriTemplate.
- uri: one document. An example is file:///readme.md.
- uriTemplate: a URI pattern. An example is file:///{path}.
If the resource has no uri and no uriTemplate, resourceDefinition throws This resource has no uri and no uriTemplate. Pass a uri or a uriTemplate.
If you pass uri and uriTemplate, the server uses uri.
read returns { text } for a text document. For a binary document, read returns { blob } with a base64 string. Add mimeType to that object when one template serves files of different types.
Serve One Resource per Item#
A template such as myapp://items/{itemId}/summary serves many documents. The read must know which item the host asked for, and which user asks. A host also needs a list of the items that exist.
- Read itemId from the variables argument of read.
- Read the user from ctx.context.
- Add list to return the concrete resources for resources/list.
import { createMCPServer, resourceDefinition } from '@tanstack/ai-mcp/server'
const summaries = new Map([
['1', 'First item'],
['2', 'Second item'],
])
const summary = resourceDefinition({
name: 'item-summary',
mimeType: 'text/plain',
uriTemplate: 'myapp://items/{itemId}/summary',
list: async () => ({
resources: [...summaries.keys()].map((id) => ({
uri: `myapp://items/${id}/summary`,
name: `Item ${id}`,
})),
}),
}).read(async (uri, variables, ctx) => {
const itemId = String(variables.itemId)
const userId = String(ctx.context.userId)
return { text: `${summaries.get(itemId) ?? 'Unknown'} (for ${userId})` }
})
const server = createMCPServer({
name: 'items',
version: '1.0.0',
resources: [summary],
})
export function handleMcp(request: Request, userId: string) {
return server.handle(request, { context: { userId } })
}read gets three arguments:
- uri: the URI the host asked for, as a URL.
- variables: the values from the template. A resource with uri gets {}. When the template has argsSchema, argsSchema.parse runs first, so read gets the parsed values. For file:///notes.md, path is notes.md.
- ctx.context: the values from handle(request, { context }), plus the verified token as authInfo. A tool gets the same values, plus requestInput and sample.
list gets the same ctx. It returns { resources }, with a uri and a name for each resource. Only a resource with uriTemplate can have list.
Prompts#
argsSchema.parse runs first. Then render receives the parsed arguments.
render returns an array of messages. Each message has these fields:
- role: user or assistant.
- content: the message text.
If role is not user or assistant, the server sends that message as user.
What the Host Gets#
The host reads file:///readme.md. The text is # Hello.
The host reads file:///notes.md. The text is The body of notes.md.
The host starts from the summarize prompt with topic weather. The role is user. The content string is weather.