Prompts
Reusable conversation templates the MCP client can hydrate with variables. Learn handler signatures, the conversation() builder, and how to inject user context.
A prompt is a reusable conversation template the MCP client can hydrate with variables and use to start a conversation. Users invoke prompts explicitly — through slash commands or menu options in their client — making them a good fit for recurring workflows like code reviews, debugging sessions, or report generation.
Register a prompt with server.prompt(name, handler):
import { createServer, T } from "@mctx-ai/mcp";
const server = createServer();
const codeReview = (args) =>
`Review this ${args.language} code for bugs, performance issues, and style:\n\n${args.code}`;
codeReview.description = "Review code for quality issues";
codeReview.input = {
code: T.string({ required: true, description: "Code to review" }),
language: T.string({ default: "JavaScript", description: "Programming language" }),
};
server.prompt("code-review", codeReview);
export default { fetch: server.fetch };Handler signature
handler(args, ask?, ctx?) => string | ConversationResult | Message[] | Promise<...>| Parameter | Type | Notes |
|---|---|---|
args | Record<string, any> | Validated input from the client. Shape matches your .input definition. |
ask | AskFunction | null | LLM sampling function. null when the client doesn't support sampling. Always guard before calling. |
ctx | McpContext | Request context. Contains ctx.userId — the authenticated subscriber identity injected by the mctx platform. |
The handler returns one of:
- string — becomes a single user message
Message[]— an array of MCP message objects with explicit rolesConversationResult— the output ofconversation(), which wraps aMessage[]
Handler properties
| Property | Type | Notes |
|---|---|---|
.description | string | What this prompt does — shown to the client |
.input | Record<string, SchemaDefinition> | Input schema built with T. Same system as tools. |
The conversation() builder
For multi-message prompts, use the conversation() builder. It gives you user and ai role helpers to construct a structured dialogue:
import { conversation } from "@mctx-ai/mcp";
const debugSession = (args) =>
conversation(({ user, ai }) => [
user.say(`I am seeing this error:\n\n${args.error}`),
...(args.stackTrace ? [user.say(`Stack trace:\n${args.stackTrace}`)] : []),
ai.say("I will analyze the error and provide step-by-step debugging guidance."),
]);
debugSession.description = "Start a guided debugging session";
debugSession.input = {
error: T.string({ required: true, description: "Error message or description" }),
stackTrace: T.string({ description: "Full stack trace if available" }),
};
server.prompt("debug", debugSession);Role helpers on user and ai:
| Method | What it produces |
|---|---|
.say(text) | A text message from this role |
.attach(data, mimeType) | An image message from this role (base64 data) |
.embed(uri) | A resource embed from this role — the client resolves the URI |
Pre-filling an ai turn seeds the conversation with context or a persona before the LLM responds. This is useful for few-shot examples or role-setting.
Use cases
Single-message prompts — a string return works for simple template injection: code review requests, report starters, task descriptions.
Multi-message prompts with few-shot examples — use conversation() to show the LLM how to respond before it generates:
const sqlHelper = (args) =>
conversation(({ user, ai }) => [
user.say("Convert this to SQL: show me all users who signed up last week"),
ai.say("SELECT * FROM users WHERE created_at >= NOW() - INTERVAL 7 DAY;"),
user.say(`Now convert this: ${args.request}`),
]);Persona templates — seed an ai turn to establish a role:
const techWriter = (args) =>
conversation(({ user, ai }) => [
ai.say("I am a technical writer. I turn complex topics into clear, concise documentation."),
user.say(`Write documentation for: ${args.topic}`),
]);Injecting ctx.userId
ctx.userId is the authenticated subscriber identity the mctx platform injects on every request. Use it to personalize a prompt for the calling user:
const myStandup = (args, ask, ctx) => {
const userId = ctx?.userId;
const greeting = userId
? `User ${userId}, here is your standup template:`
: "Here is your standup template:";
return conversation(({ user }) => [
user.say(
`${greeting}\n\n` +
`Yesterday: ${args.yesterday}\n` +
`Today: ${args.today}\n` +
`Blockers: ${args.blockers || "None"}`,
),
]);
};
myStandup.description = "Generate a personalized daily standup summary";
myStandup.input = {
yesterday: T.string({ required: true, description: "What you did yesterday" }),
today: T.string({ required: true, description: "What you plan to do today" }),
blockers: T.string({ description: "Anything blocking your progress" }),
};
server.prompt("my-standup", myStandup);ctx may be undefined when running outside the mctx platform (local dev, HTTP tests). Always guard with optional chaining before accessing ctx.userId.
See also
- Tools — actions the client can invoke
- Resources — named data the client can read
- Concepts — handlers, the server object, and how it all fits together
- Framework API Reference — every export, type, and option
See something wrong? Report it or suggest an improvement — your feedback helps make these docs better.