Introduction

Prompts are the third MCP primitive, alongside tools and resources. While tools let LLMs perform actions and resources provide data, prompts are reusable message templates that guide how an LLM should approach a task. They are particularly useful for encoding expert workflows — code review checklists, data analysis patterns, or troubleshooting procedures — as structured prompt sequences.

In the TypeScript SDK, you register prompts using server.registerPrompt() with a name, configuration object, and handler function.

Key Concepts

  • server.registerPrompt() takes three arguments: a name, a config object, and a handler function
  • Config object includes title, description, and argsSchema (a Zod schema defining the prompt's input parameters)
  • GetPromptResult is the return type, containing a messages array of prompt messages
  • Messages have a role ('user' or 'assistant') and content (with type and text or image fields)
  • Prompts vs tools: prompts produce messages for the LLM to process; tools perform actions and return results

Real World Context

Prompts encode reusable workflows that would otherwise be copy-pasted or memorized:

  • Code review prompts that check for security issues, performance, and style
  • Data analysis templates that structure how an LLM examines a dataset
  • Debugging prompts that walk through a systematic troubleshooting process
  • Documentation generation templates with consistent structure

Clients like Claude Desktop display available prompts as slash commands, so users can invoke them directly.

Deep Dive

Here is a complete example registering a code review prompt with typed arguments.

typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';

const server = new McpServer({ name: 'dev-tools', version: '1.0.0' });

server.registerPrompt(
  'review-code',
  {
    title: 'Code Review',
    description: 'Review code for best practices',
    argsSchema: z.object({
      code: z.string().describe('The code to review'),
      language: z.string().describe('Programming language')
    })
  },
  ({ code, language }): GetPromptResult => ({
    messages: [{
      role: 'user',
      content: { type: 'text', text: `Review this ${language} code:\n\n${code}` }
    }]
  })
);

The first argument 'review-code' is the prompt name — clients use this to request the prompt. The config object defines the title, description, and an argsSchema built with Zod. The handler receives the validated arguments and returns a GetPromptResult.

The argsSchema uses Zod's .describe() method on each field to provide descriptions that clients can display to users. When a client calls prompts/get with arguments, the SDK validates them against this schema before invoking the handler.

The handler returns a messages array. Each message has a role and content. The content object uses type: 'text' for text content.

typescript
server.registerPrompt(
  'explain-error',
  {
    title: 'Explain Error',
    description: 'Explain an error message in plain language',
    argsSchema: z.object({
      error: z.string().describe('The error message or stack trace'),
      context: z.string().optional().describe('Additional context about what was happening')
    })
  },
  ({ error, context }): GetPromptResult => {
    let text = `Explain this error in plain language and suggest how to fix it:\n\n${error}`;
    if (context) {
      text += `\n\nContext: ${context}`;
    }
    return {
      messages: [{ role: 'user', content: { type: 'text', text } }]
    };
  }
);

Optional arguments are supported through Zod's .optional() modifier. The handler checks whether the optional argument was provided before including it in the prompt text.

Common Pitfalls

  • Confusing prompts with tools: prompts return messages for the LLM to process, not action results — use tools when you need to perform operations
  • Overly complex prompt text: keep individual messages focused; use multiple messages for multi-step instructions
  • Forgetting .describe() on schema fields: without descriptions, clients cannot show users what each argument means
  • Not validating argument combinations: if certain argument combinations are invalid, validate them in the handler and return clear error messages

Best Practices

  • Use descriptive prompt names that read naturally as commands (e.g., review-code, explain-error)
  • Always include title and description in the config so clients can display the prompt in menus
  • Use .describe() on every Zod field to document what each argument expects
  • Keep prompts focused on a single task — create separate prompts for different workflows
  • Test prompts locally by calling the handler directly with sample arguments

Summary

server.registerPrompt() exposes reusable prompt templates with typed arguments validated by Zod. The handler returns GetPromptResult containing a messages array that structures how the LLM should approach the task. Prompts encode expert workflows as shareable, discoverable templates.

Code Examples

typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';

server.registerPrompt(
  'review-code',
  {
    title: 'Code Review',
    description: 'Review code for best practices',
    argsSchema: z.object({
      code: z.string().describe('The code to review'),
      language: z.string().describe('Programming language')
    })
  },
  ({ code, language }): GetPromptResult => ({
    messages: [{
      role: 'user',
      content: { type: 'text', text: `Review this ${language} code:\n\n${code}` }
    }]
  })
);
typescript
server.registerPrompt(
  'explain-error',
  {
    title: 'Explain Error',
    description: 'Explain an error message in plain language',
    argsSchema: z.object({
      error: z.string().describe('The error message'),
      context: z.string().optional().describe('Additional context')
    })
  },
  ({ error, context }): GetPromptResult => ({
    messages: [{
      role: 'user',
      content: { type: 'text', text: `Explain this error:\n\n${error}${context ? `\n\nContext: ${context}` : ''}` }
    }]
  })
);
✓ Completed