Prompts are reusable message templates that can be used by AI assistants. They can include dynamic arguments and return pre-formatted messages.
MCP prompts offer several advantages over ad-hoc instructions:
Reusability
Standardization
Customization
IDE Integration
MCP prompts integrate seamlessly with modern development environments. When your MCP server is connected, prompts become available directly in your IDE.
/: In the chat, type / to see all available MCP promptslocal-mcp/setup-mcp-server)You can omit name and title - they will be automatically generated from the filename:
export default defineMcpPrompt({
// name and title are auto-generated from filename:
// name: 'greeting'
// title: 'Greeting'
description: 'Generate a personalized greeting message',
handler: async () => {
// ...
},
})
The filename greeting.ts automatically becomes:
name: greeting (kebab-case)title: Greeting (title case)You can still provide name or title explicitly to override the auto-generated values.
Create a prompt without arguments:
export default defineMcpPrompt({
name: 'greeting',
title: 'Greeting',
description: 'Generate a personalized greeting message',
handler: async () => {
const hour = new Date().getHours()
const timeOfDay = hour < 12 ? 'morning' : hour < 18 ? 'afternoon' : 'evening'
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `Good ${timeOfDay}! How can I help you today?`,
},
}],
}
},
})
Create a prompt that accepts arguments:
import { z } from 'zod'
export default defineMcpPrompt({
name: 'summarize',
title: 'Text Summarizer',
description: 'Summarize any text content',
inputSchema: {
text: z.string().describe('The text to summarize'),
maxLength: z.string().optional().describe('Maximum length of summary in words'),
},
handler: async ({ text, maxLength }) => {
const words = text.split(/\s+/)
const maxWords = maxLength ? Number.parseInt(maxLength) : Math.ceil(words.length * 0.3)
const summary = words.slice(0, maxWords).join(' ')
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `Summary (${maxWords} words): ${summary}${words.length > maxWords ? '...' : ''}`,
},
}],
}
},
})
A prompt definition consists of:
export default defineMcpPrompt({
name: 'prompt-name', // Unique identifier
handler: async () => { // Handler function
return { messages: [...] }
},
})
export default defineMcpPrompt({
name: 'prompt-name',
title: 'Prompt Title', // Human-readable title
description: 'Description', // What the prompt does
inputSchema: { ... }, // Zod schema for arguments
handler: async (args) => { // Handler with arguments
return { messages: [...] }
},
})
Use Zod to define and validate prompt arguments:
import { z } from 'zod'
export default defineMcpPrompt({
name: 'translate',
inputSchema: {
// Required string argument
text: z.string().describe('Text to translate'),
// Required enum argument
targetLanguage: z.enum(['en', 'fr', 'es', 'de']).describe('Target language'),
// Optional argument
sourceLanguage: z.string().optional().describe('Source language (auto-detect if not provided)'),
// Optional with default
formality: z.enum(['formal', 'informal']).default('formal'),
},
handler: async ({ text, targetLanguage, sourceLanguage, formality }) => {
// Implementation
},
})
| Zod Type | Example | Description |
|---|---|---|
z.string() | z.string().min(1) | String with validation |
z.enum() | z.enum(['a', 'b']) | Enumeration |
z.optional() | z.string().optional() | Optional field |
z.default() | z.string().default('value') | Field with default |
z.string() and convert to other types in your handler if needed.The handler receives validated arguments (if inputSchema is provided) and returns messages:
// Prompt without arguments
handler: async () => {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: 'Message text',
},
}],
}
}
// Prompt with arguments
handler: async (args, extra) => {
// args: Validated arguments matching inputSchema
// extra: Request handler extra information
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: 'Message text',
},
}],
}
}
Prompts can return messages with different roles:
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: 'User message with instructions',
},
}],
}
return {
messages: [{
role: 'assistant',
content: {
type: 'text',
text: 'Pre-filled assistant response',
},
}],
}
user and assistant roles. To provide context or instructions, include them in the user message text.Return multiple messages to create a conversation flow:
export default defineMcpPrompt({
name: 'conversation-starter',
inputSchema: {
topic: z.string().describe('Conversation topic'),
},
handler: async ({ topic }) => {
return {
messages: [
{
role: 'user',
content: {
type: 'text',
text: `You are a helpful assistant. Let's discuss ${topic}.`,
},
},
{
role: 'assistant',
content: {
type: 'text',
text: `I'd be happy to discuss ${topic} with you.`,
},
},
],
}
},
})
Prompts are particularly useful for:
Help new developers or AI assistants understand how to work with your codebase:
export default defineMcpPrompt({
description: 'Provide complete setup instructions for this project',
handler: async () => {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `You are setting up this Nuxt project. Here's what you need to know:
1. Install dependencies: \`pnpm install\`
2. Start dev server: \`pnpm dev\`
3. Project structure follows Nuxt conventions
4. MCP tools are available in server/mcp/
Ask me what you'd like to build!`,
},
}],
}
},
})
Ensure consistent code review criteria:
import { z } from 'zod'
export default defineMcpPrompt({
description: 'Apply team code review standards',
inputSchema: {
focus: z.enum(['security', 'performance', 'maintainability', 'all']).default('all'),
},
handler: async ({ focus }) => {
return {
messages: [
{
role: 'user',
content: {
type: 'text',
text: `You are a code reviewer following our team standards. Focus on: ${focus}.
Review the code I provide, checking for best practices and potential issues.`,
},
},
],
}
},
})
Standardize documentation format:
import { z } from 'zod'
export default defineMcpPrompt({
description: 'Generate documentation in team format',
inputSchema: {
type: z.enum(['api', 'component', 'function']).describe('What to document'),
},
handler: async ({ type }) => {
const templates = {
api: 'Document this API endpoint with: endpoint, method, parameters, response format, and examples.',
component: 'Document this Vue component with: props, emits, slots, and usage examples.',
function: 'Document this function with: parameters, return value, and usage examples.',
}
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: templates[type],
},
}],
}
},
})
Guide debugging for common issues:
import { z } from 'zod'
export default defineMcpPrompt({
description: 'Help debug common issues',
inputSchema: {
area: z.enum(['api', 'auth', 'database', 'frontend']).describe('Area of the issue'),
},
handler: async ({ area }) => {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `You are debugging a ${area} issue. Ask clarifying questions and suggest diagnostic steps.`,
},
}],
}
},
})
Organize your prompts in the server/mcp/prompts/ directory:
server/
└── mcp/
└── prompts/
├── greeting.ts
├── summarize.ts
└── translate.ts
Each file should export a default prompt definition.
The module provides full TypeScript type inference:
// Argument types are inferred from inputSchema
handler: async ({ text, maxLength }) => {
// text is typed as string
// maxLength is typed as string | undefined
}
Write prompts that give the AI clear context and expectations:
// Good: Clear context and instructions
handler: async ({ code }) => ({
messages: [{
role: 'user',
content: {
type: 'text',
text: `You are a senior developer reviewing code for a Nuxt application.
Review this code for Vue 3 best practices:\n\n${code}`,
},
}],
})
// Less effective: Vague instructions
handler: async ({ code }) => ({
messages: [{
role: 'user',
content: { type: 'text', text: code },
}],
})
Always use .describe() on Zod fields to help both users and AI understand what's expected:
inputSchema: {
// Good: Clear descriptions
language: z.enum(['typescript', 'javascript']).describe('Programming language of the code'),
strict: z.boolean().default(true).describe('Whether to enforce strict TypeScript rules'),
// Less helpful: No descriptions
lang: z.string(),
s: z.boolean(),
}
Use user and assistant messages to guide the AI:
// Effective: User provides context, assistant acknowledges
messages: [
{ role: 'user', content: { type: 'text', text: 'You are an expert in accessibility. Review this HTML for a11y issues.' } },
{ role: 'assistant', content: { type: 'text', text: 'I\'ll analyze the HTML for accessibility issues.' } },
]
Each prompt should have a single, clear purpose. Create multiple prompts instead of one complex one:
// Good: Separate focused prompts
// server/mcp/prompts/review-security.ts
// server/mcp/prompts/review-performance.ts
// server/mcp/prompts/review-style.ts
// Less maintainable: One complex prompt trying to do everything
Use .default() for optional arguments to improve usability:
inputSchema: {
format: z.enum(['brief', 'detailed']).default('detailed').describe('Output format'),
language: z.string().default('en').describe('Response language'),
}
For prompts that need specific output formats, include examples:
handler: async () => ({
messages: [{
role: 'user',
content: {
type: 'text',
text: `Generate a commit message following this format:
type(scope): description
Example:
feat(auth): add OAuth2 login support
Types: feat, fix, docs, style, refactor, test, chore`,
},
}],
})