Core Concepts

Prompts

Create reusable prompts for AI assistants with optional arguments.

What are Prompts?

Prompts are reusable message templates that can be used by AI assistants. They can include dynamic arguments and return pre-formatted messages.

Why Use Prompts?

MCP prompts offer several advantages over ad-hoc instructions:

Reusability

Define once, use everywhere. Share prompts across your team for consistent AI interactions.

Standardization

Ensure consistent formatting and context for specific tasks like code reviews or documentation.

Customization

Use arguments to adapt prompts to different contexts while maintaining structure.

IDE Integration

Prompts appear in Cursor, VS Code, and Visual Studio for easy access during development.

IDE Integration

MCP prompts integrate seamlessly with modern development environments. When your MCP server is connected, prompts become available directly in your IDE.

Using Prompts in Cursor / VS Code

  1. Type /: In the chat, type / to see all available MCP prompts
  2. Select a prompt: Choose from the list (e.g., local-mcp/setup-mcp-server)
  3. Fill Arguments: For prompt templates, a dialog will appear to fill in the required arguments

Auto-Generated Name and Title

You can omit name and title - they will be automatically generated from the filename:

server/mcp/prompts/greeting.ts
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.

Simple Prompt (No Arguments)

Create a prompt without arguments:

server/mcp/prompts/greeting.ts
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?`,
        },
      }],
    }
  },
})

Prompt with Arguments

Create a prompt that accepts arguments:

server/mcp/prompts/summarize.ts
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 ? '...' : ''}`,
        },
      }],
    }
  },
})

Prompt Structure

A prompt definition consists of:

export default defineMcpPrompt({
  name: 'prompt-name',  // Unique identifier
  handler: async () => { // Handler function
    return { messages: [...] }
  },
})

Input Schema

Use Zod to define and validate prompt arguments:

server/mcp/prompts/translate.ts
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
  },
})

Common Argument Types

Zod TypeExampleDescription
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
Note: Prompt arguments must be strings. Use z.string() and convert to other types in your handler if needed.

Handler Function

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',
      },
    }],
  }
}

Message Roles

Prompts can return messages with different roles:

return {
  messages: [{
    role: 'user',
    content: {
      type: 'text',
      text: 'User message with instructions',
    },
  }],
}
Note: The MCP specification only supports user and assistant roles. To provide context or instructions, include them in the user message text.

Multiple Messages

Return multiple messages to create a conversation flow:

server/mcp/prompts/conversation.ts
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.`,
          },
        },
      ],
    }
  },
})

Use Cases

Prompts are particularly useful for:

1. Setup and Onboarding

Help new developers or AI assistants understand how to work with your codebase:

server/mcp/prompts/setup-guide.ts
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!`,
        },
      }],
    }
  },
})

2. Code Review Standards

Ensure consistent code review criteria:

server/mcp/prompts/review-standards.ts
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.`,
          },
        },
      ],
    }
  },
})

3. Documentation Generation

Standardize documentation format:

server/mcp/prompts/generate-docs.ts
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],
        },
      }],
    }
  },
})

4. Troubleshooting Workflows

Guide debugging for common issues:

server/mcp/prompts/debug-helper.ts
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.`,
        },
      }],
    }
  },
})

File Organization

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.

Type Safety

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
}

Best Practices

1. Design for AI Understanding

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 },
  }],
})

2. Use Descriptive Arguments

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(),
}

3. Use Conversation Flow

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.' } },
]

4. Keep Prompts Focused

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

5. Provide Default Values

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'),
}

6. Include Examples in Complex Prompts

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`,
    },
  }],
})

Next Steps