diff --git a/docs/handlers/mcp-server-prompts.mdx b/docs/handlers/mcp-server-prompts.mdx new file mode 100644 index 00000000..c237c83c --- /dev/null +++ b/docs/handlers/mcp-server-prompts.mdx @@ -0,0 +1,285 @@ +--- +title: MCP Server Prompts +sidebar_label: MCP Server Prompts +--- + +The MCP (Model Context Protocol) Server handler supports prompts in addition to +tools, enabling you to provide reusable, parameterized prompt templates through +the MCP protocol. + +MCP prompts allow AI clients to request and execute structured prompt templates +with dynamic parameters, making it easy to standardize and share prompt patterns +and context across different AI workflows. + +## Overview + +Much like tools, Zuplo's MCP prompts work by utilizing structured API routes as +prompt generators that return formatted messages for AI consumption. When an MCP +client calls a prompt, your route handler returns a structured message array +that the AI can use directly. + +But unlike MCP tools that perform actions and return data, MCP prompts return +formatted instructions or context that guide AI reasoning and responses. + +## Configuration + +### Route Configuration + +Configure a route in your OpenAPI doc: + +```json +{ + "/greeting": { + "post": { + "operationId": "greeting", + "summary": "Generate a personalized greeting", + "description": "Creates a customized greeting for a given person", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the person to greet" + } + }, + "required": ["name"] + } + } + } + }, + "x-zuplo-route": { + "corsPolicy": "none", + "handler": { + "export": "default", + "module": "$import(./modules/greeting)" + } + } + } + } +} +``` + +To provide MCP specific metadata for the prompt, use `x-zuplo-mcp-prompt`: + +```json +"x-zuplo-mcp-prompt": { + "name": "greeting_generator", + "description": "Utilize this prompt to generate a personalized greeting message" +}, +``` + +The `x-zuplo-mcp-prompt` extension supports: + +- `name` - The identifier for the MCP prompt +- `description` - Description of what the prompt generates + +Without `x-zuplo-mcp-prompt`, the MCP server will use the route's `operationId` +as the prompt name and the `summary` as the prompt description. + +### MCP Server Handler Configuration + +Add prompt configuration to your MCP Server handler options: + +```json +{ + "paths": { + "/mcp": { + "post": { + "x-zuplo-route": { + "handler": { + "export": "mcpServerHandler", + "module": "$import(@zuplo/runtime)", + "options": { + "name": "example-mcp-server", + "version": "1.0.0", + "prompts": [ + { + "path": "./config/routes.oas.json", + "operationIds": ["greeting"] + } + ] + } + } + } + } + } + } +} +``` + +The `prompts` array supports: + +- `path` - Path to the OpenAPI specification file containing prompt routes +- `operationIds` - Array of operation IDs to include as MCP prompts + +## Route Handler Implementation + +Your route handler must return a structured response with a `messages` array +containing properly formatted message objects: + +```typescript +export default async function (request: ZuploRequest, context: ZuploContext) { + const { name } = await request.json(); + + return { + messages: [ + { + role: "assistant", + content: { + type: "text", + text: `Create a personalized greeting for ${name}. Make it friendly and welcoming!`, + }, + }, + ], + }; +} +``` + +### Message Structure + +Each message in the `messages` array should follow this structure: + +- `role` - The role of the message (`"assistant"`, `"user"`, or `"system"`) +- `content` - The content object with: + - `type` - Content type (`"text"` for text content) + - `text` - The actual text content + +For more information, review +[the `PromptMessage` type described in the MCP docs](https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#promptmessage). + +### Multiple Messages + +You can return multiple messages to create complex and dynamic templates: + +```typescript +return { + messages: [ + { + role: "system", + content: { + type: "text", + text: "You are a helpful assistant that generates personalized greetings.", + }, + }, + { + role: "assistant", + content: { + type: "text", + text: `Create a warm greeting for ${name} in ${location}. Consider local customs and time of day.`, + }, + }, + ], +}; +``` + +## Testing MCP Prompts + +### List Available Prompts + +Use the MCP `prompts/list` method to see available prompts: + +```bash +curl localhost:9000/mcp \ + -X POST \ + -H 'accept: application/json, text/event-stream' \ + -d '{ + "jsonrpc": "2.0", + "id": "1", + "method": "prompts/list" + }' +``` + +Response: + +```json +{ + "jsonrpc": "2.0", + "id": "1", + "result": { + "prompts": [ + { + "name": "greeting_generator", + "description": "Generate a personalized greeting message for someone in a specific location", + "arguments": [ + { + "name": "name", + "description": "The name of the person to greet", + "required": true + } + ] + } + ] + } +} +``` + +### Execute a Prompt + +Use the MCP `prompts/get` method to execute a prompt with parameters: + +```bash +curl localhost:9000/mcp \ + -X POST \ + -H 'accept: application/json, text/event-stream' \ + -d '{ + "jsonrpc": "2.0", + "id": "1", + "method": "prompts/get", + "params": { + "name": "greeting_generator", + "arguments": { + "name": "john" + } + } + }' +``` + +Response: + +```json +{ + "jsonrpc": "2.0", + "id": "1", + "result": { + "description": "Generate a personalized greeting message for someone in a specific location", + "messages": [ + { + "role": "assistant", + "content": { + "type": "text", + "text": "Create a personalized greeting for john. Make it friendly and welcoming!" + } + } + ] + } +} +``` + +## Best Practices + +### Prompt Design + +- Write clear, specific prompt instructions that guide AI behavior +- Use parameter interpolation to create dynamic, contextual prompts +- Include relevant context and constraints in your prompt text +- Consider the target AI model's strengths and prompt formatting preferences + +### Parameter Schema + +- Define comprehensive JSON schemas for prompt parameters - this _must_ appear + as a `application/json` request body in a `POST` to your route. Typically, + this will point to a module that programmatically can craft the prompt. +- Include helpful descriptions for each parameter +- Mark required parameters appropriately +- Use validation to ensure parameter quality + +### Message Organization + +- Use `system` messages for general behavior instructions +- Use `assistant` messages for specific task guidance +- Structure complex prompts as multiple focused messages +- Keep individual messages concise and purposeful diff --git a/docs/handlers/mcp-server.mdx b/docs/handlers/mcp-server.mdx index d9b801b6..5f085d3c 100644 --- a/docs/handlers/mcp-server.mdx +++ b/docs/handlers/mcp-server.mdx @@ -18,9 +18,10 @@ functionality or rebuild business logic in your backend. Each MCP Server handler has a 1:1 relationship with a route. That means one route can host one server. -A single MCP server may have many tools, where each tool interfaces with an API -route in your gateway. You can compose multiple MCP servers on different routes -to tailor MCP tools for each server's specific purpose. +A single MCP server may have many tools and prompts, where each tool interfaces +with an API route in your gateway. You can compose multiple MCP servers on +different routes to tailor MCP tools and prompts for each server's specific +purpose. ## Setup via Portal @@ -39,8 +40,8 @@ Configure the handler with the following required options: ![MCP Server Handler Portal](../../public/media/mcp/portal-handler.png) -Next, configure your routes to be transformed into MCP tools (see Configuration -section below). +Next, configure your routes to be transformed into MCP tools or prompts (see +Configuration section below). ## Setup via routes.oas.json @@ -225,6 +226,42 @@ each endpoint becomes an MCP tool with custom TypeScript handlers. See [MCP Server Custom Tools](./mcp-server-custom-tools.mdx) for detailed documentation. +### MCP Prompts + +In addition to tools, MCP servers can expose prompts - reusable, parameterized +prompt templates that AI clients can request and execute. Configure prompts +using the `prompts` array in your MCP server options: + +```json +"paths": { + "/mcp": { + "post": { + "x-zuplo-route": { + "handler": { + "export": "mcpServerHandler", + "module": "$import(@zuplo/runtime)", + "options": { + "name": "My MCP Server", + "version": "1.0.0", + "prompts": [ + { + "path": "./config/routes.oas.json", + "operationIds": ["greeting_generator", "content_template"] + } + ] + } + } + } + } + } +} +``` + +Routes configured as MCP prompts may include the `x-zuplo-mcp-prompt` extension +in their OpenAPI definition in order to provide additional MCP metadata. These +routes should return structured message arrays from their handlers. See +[MCP Server Prompts](./mcp-server-prompts.mdx) for detailed documentation. + ### Tool names and descriptions Regardless of which option you use, MCP tools are configured as follows: @@ -505,6 +542,21 @@ curl https://my-gateway.zuplo.dev/mcp \ }' ``` +#### List prompts + +To see what prompts a server has available: + +```sh +curl https://my-gateway.zuplo.dev/mcp \ + -X POST \ + -H 'accept: application/json, text/event-stream' \ + -d '{ + "jsonrpc": "2.0", + "id": "0", + "method": "prompts/list" +}' +``` + #### Call tool To @@ -529,6 +581,30 @@ For more complex tools, you'll need to provide the schema compliant `arguments`. Note the `inputSchema` for the tool from `tools/list` to appropriately craft the `arguments`. +#### Get prompt + +To execute a prompt with parameters: + +```sh +curl https://my-gateway.zuplo.dev/mcp \ + -X POST \ + -H 'accept: application/json, text/event-stream' \ + -d '{ + "jsonrpc": "2.0", + "id": "1", + "method": "prompts/get", + "params": { + "name": "my_prompt", + "arguments": { + "param1": "value1" + } + } +}' +``` + +For prompts with parameters, provide the required `arguments` object based on +the prompt's schema from `prompts/list`. + :::tip Read more about how calling tools works in diff --git a/sidebar.ts b/sidebar.ts index 13d8d9a0..e98750e3 100644 --- a/sidebar.ts +++ b/sidebar.ts @@ -615,7 +615,10 @@ export const policies: Navigation = [ type: "category", label: "MCP Server", link: "handlers/mcp-server", - items: ["handlers/mcp-server-custom-tools"], + items: [ + "handlers/mcp-server-custom-tools", + "handlers/mcp-server-prompts", + ], }, "handlers/redirect", "handlers/openapi",