Serve resources and prompts from the same MCP server as your tools. A host can then read those documents and start from those prompts.
Define each document with resourceDefinition and each prompt with promptDefinition. Then pass them to createMCPServer.
You need an MCP server first. If you do not have one, start with Serve Tools over HTTP.
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)
}Now the host gets these results:
A resource has one of two addresses:
read returns the body of the document:
A prompt parses its arguments with argsSchema. Then render returns an array of messages. Each message has a role (user or assistant) and a content string.
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.
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 } })
}The host lists Item 1 and Item 2. A read of myapp://items/1/summary returns First item plus the user id.
read gets three arguments:
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.