Lesson

Building Prompts

You have built tools that LLMs can call and resources that clients can load. Now you will learn about prompts - reusable templates that guide users in how to interact with your MCP server. Unlike tools and resources, prompts are user-controlled - users explicitly choose when to use them through a UI menu or slash command.

Creating Prompts

The TypeScript MCP SDK provides registerPrompt() to create prompts:

typescript
server.registerPrompt("movieRecommendation", {
  description: "Get movie recommendations based on your preferences",
}, () => ({
  messages: [{
    role: "user",
    content: {
      type: "text",
      text: `I'd like to discover new movies to watch.
 
Please ask me about:
1. What genres I enjoy
2. Any specific movies I've loved
3. My preferred movie era or style
 
Then recommend 5 movies I might enjoy and explain why each one would be a good fit.`,
    },
  }],
}));

The registerPrompt() method takes three arguments:

  1. Name - A unique identifier for the prompt ("movieRecommendation")
  2. Options - Metadata including description and optionally argsSchema
  3. Handler - A function that returns an object with a messages array

Prompts with Parameters

Prompts can accept parameters to customize the template. Use argsSchema with Zod schemas to define typed parameters:

typescript
import { z } from "zod";
 
server.registerPrompt("similarMovies", {
  description: "Find movies similar to one you enjoyed",
  argsSchema: {
    movieTitle: z.string().describe("Title of the movie you enjoyed"),
    count: z.string().default("5").describe("Number of recommendations"),
  },
}, ({ movieTitle, count }) => ({
  messages: [{
    role: "user",
    content: {
      type: "text",
      text: `I really enjoyed the movie "${movieTitle}".
 
Can you recommend ${count} similar movies and explain why each one is similar?
 
Consider factors like:
 
- Genre and themes
- Director or actors
- Mood and tone
- Era and style`,
    },
  }],
}));

Users provide the parameters when invoking the prompt, and the template is filled in with their values.

Parameter Types

Prompt parameters defined in argsSchema are always received as strings in the handler function. If you need numeric values, parse them inside the handler. Use z.string() for all parameters, with .default() for optional ones.

Multi-Message Prompts

Prompts can return multiple messages to create a conversation flow:

typescript
server.registerPrompt("analyzePreferences", {
  description: "Analyze your movie preferences and recommend genres",
  argsSchema: {
    favoriteMovies: z.string().describe("Comma-separated list of your favorite movies"),
  },
}, ({ favoriteMovies }) => ({
  messages: [
    {
      role: "user",
      content: {
        type: "text",
        text: `Here are my favorite movies: ${favoriteMovies}`,
      },
    },
    {
      role: "assistant",
      content: {
        type: "text",
        text: "I'll analyze your movie preferences. Let me look at the genres and themes...",
      },
    },
    {
      role: "user",
      content: {
        type: "text",
        text: "Based on my favorites, what genres should I explore next?",
      },
    },
  ],
}));

This creates a conversation starter that includes both user and assistant messages, guiding the interaction toward a specific outcome.

Prompt Best Practices

1. Be specific and actionable:

typescript
// Good  - specific guidance
server.registerPrompt("movieNightPlanner", {
  description: "Plan a themed movie night",
}, () => ({
  messages: [{
    role: "user",
    content: {
      type: "text",
      text: `Help me plan a movie night with these details:
 
1. Theme or genre I want to explore
2. Number of movies (2-4 recommended)
3. Any constraints (runtime, rating, era)
 
Create a curated list with:
 
- Movie titles and years
- Why they fit the theme
- Suggested watching order
- Total runtime`,
    },
  }],
}));
 
// Avoid  - too vague
server.registerPrompt("movies", {
  description: "Movies",
}, () => ({
  messages: [{
    role: "user",
    content: { type: "text", text: "Tell me about movies" },
  }],
}));

2. Provide structure:

typescript
server.registerPrompt("movieReviewTemplate", {
  description: "Write a structured movie review",
  argsSchema: {
    movieTitle: z.string().describe("Title of the movie to review"),
  },
}, ({ movieTitle }) => ({
  messages: [{
    role: "user",
    content: {
      type: "text",
      text: `Write a review of "${movieTitle}" covering:
 
**Plot Summary** (no spoilers)
- Brief overview in 2-3 sentences
 
**Strengths**
- What worked well?
- Standout performances?
 
**Weaknesses**
- What could be improved?
 
**Overall Verdict**
- Rating out of 10
- Who would enjoy this movie?`,
    },
  }],
}));

3. Use clear parameters:

typescript
server.registerPrompt("discoveryPrompt", {
  description: "Discover hidden gems in a specific genre",
  argsSchema: {
    genre: z.string().describe("Genre to explore"),
    decade: z.string().default("any").describe("Preferred decade (e.g., 1990, 2000)"),
    mood: z.string().default("any").describe("Preferred mood (e.g., uplifting, dark, funny)"),
  },
}, ({ genre, decade, mood }) => {
  const filters: string[] = [];
  if (decade !== "any") filters.push(`from the ${decade}s`);
  if (mood !== "any") filters.push(`with a ${mood} mood`);
 
  const filterText = filters.length > 0 ? filters.join(" ") : "from any era";
 
  return {
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: `Help me discover lesser-known ${genre} movies ${filterText}.
 
Find me 5 hidden gems that:
 
- Have high ratings but are under-appreciated
- Represent the genre well
- Offer something unique
 
For each movie, explain:
 
- Why it is worth watching
- What makes it special
- Who would enjoy it`,
      },
    }],
  };
}),

When to Use Prompts

Prompts are ideal for common workflows and complex requests. In a movie server, they help with tasks like:

  • Movie recommendations - "Find movies based on my favorites"
  • Themed planning - "Plan a movie marathon with specific criteria"
  • Guided discovery - "Help me explore new genres with structured questions"
  • Analysis templates - "Compare two movies using standard criteria"

Prompts vs Tools

While tools are functions that execute code when the LLM needs them, prompts are pre-written templates that users explicitly select. They work together - a prompt might guide the user to ask questions that lead the LLM to call specific tools. For example, a movie recommendation prompt could guide the conversation that leads to calling the getMoviesByGenre tool.

Adding Prompts to Your Server

Prompts are straightforward to add - just use registerPrompt():

typescript
server.registerPrompt("movieDiscovery", {
  description: "Discover new movies in a genre",
  argsSchema: {
    genre: z.string().default("any").describe("Genre to explore"),
  },
}, ({ genre }) => {
  if (genre === "any") {
    return {
      messages: [{
        role: "user",
        content: {
          type: "text",
          text: "Help me discover new movies! What genres do I enjoy? What recent movies have I loved? Do I prefer classics or new releases?",
        },
      }],
    };
  }
 
  return {
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: `Recommend 5 diverse ${genre} movies that span different styles and eras. Explain why each is a great example of the genre.`,
      },
    }],
  };
});

You can test your prompts in MCP Inspector by clicking the Prompts tab, selecting a prompt, filling in any parameters, and viewing the generated messages.