Automatización de Workflows con MCP Prompts
Introducción
Los MCP Prompts son mucho más que simples plantillas de texto. Son puntos de entrada para automatización compleja, combinando instrucciones de texto con contexto dinámico, recursos incrustados y parámetros personalizables.
En este artículo aprenderás a construir workflows automatizados que aprovechan todo el potencial de MCP.
¿Qué son los MCP Prompts?
Un prompt MCP es una combinación de:
Prompt MCP = Instrucciones + Contexto Dinámico + Recursos + Parámetros
Componentes
- Instrucciones de texto: El prompt base
- Plantillas de recursos: URIs dinámicos con parámetros
- Completaciones: Sugerencias mientras se escribe
- Argumentos tipados: Entrada estructurada del usuario
Anatomía de un prompt
Prompt básico
import { ListPromptsRequestSchema, GetPromptRequestSchema } from "@modelcontextprotocol/sdk/types.js";
server.setRequestHandler(ListPromptsRequestSchema, async () => {
return {
prompts: [
{
name: "generate-readme",
description: "Genera un README para el proyecto",
arguments: [],
},
],
};
});
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
const { name } = request.params;
if (name === "generate-readme") {
return {
messages: [
{
role: "user",
content: {
type: "text",
text: "Genera un README completo para este proyecto basándote en el código fuente.",
},
},
],
};
}
});
Prompt con parámetros
server.setRequestHandler(ListPromptsRequestSchema, async () => {
return {
prompts: [
{
name: "code-review",
description: "Revisa código con criterios específicos",
arguments: [
{
name: "file_path",
description: "Ruta del archivo a revisar",
required: true,
},
{
name: "focus",
description: "Área de enfoque (security, performance, style)",
required: false,
},
],
},
],
};
});
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "code-review") {
const { file_path, focus = "general" } = args as {
file_path: string;
focus?: string;
};
return {
messages: [
{
role: "user",
content: {
type: "text",
text: `Revisa el archivo ${file_path} con enfoque en ${focus}.
Identifica problemas potenciales y sugiere mejoras.`,
},
},
],
};
}
});
Plantillas de recursos
Las plantillas permiten URIs dinámicos con parámetros.
Definir plantillas
import { ListResourcesRequestSchema, ListResourceTemplatesRequestSchema } from "@modelcontextprotocol/sdk/types.js";
// Listar plantillas disponibles
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
return {
resourceTemplates: [
{
uriTemplate: "recipe://{cuisine}/{meal_type}",
name: "Receta por tipo",
description: "Obtiene receta basada en cocina y tipo de comida",
mimeType: "text/markdown",
},
],
};
});
// Resolver plantilla a recurso concreto
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
const match = uri.match(/^recipe:\/\/(\w+)\/(\w+)$/);
if (match) {
const [, cuisine, mealType] = match;
const recipe = await fetchRecipe(cuisine, mealType);
return {
contents: [
{
uri,
mimeType: "text/markdown",
text: recipe,
},
],
};
}
});
Usar en prompts
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "plan-meal") {
const { cuisine, meal_type } = args as {
cuisine: string;
meal_type: string;
};
return {
messages: [
{
role: "user",
content: {
type: "resource",
resource: {
uri: `recipe://${cuisine}/${meal_type}`,
text: `Usa esta receta para crear un plan de comida`,
},
},
},
],
};
}
});
Completaciones (Autocompletado)
Proporciona sugerencias mientras el usuario escribe.
Implementar completaciones
import { CompleteRequestSchema } from "@modelcontextprotocol/sdk/types.js";
server.setRequestHandler(CompleteRequestSchema, async (request) => {
const { ref, argument } = request.params;
// Autocompletar tipo de cocina
if (argument.name === "cuisine") {
const cuisines = ["italiana", "mexicana", "japonesa", "francesa", "india"];
const query = argument.value.toLowerCase();
return {
completion: {
values: cuisines
.filter((c) => c.startsWith(query))
.map((value) => ({ value, label: value })),
hasMore: false,
},
};
}
// Autocompletar tipo de comida
if (argument.name === "meal_type") {
return {
completion: {
values: [
{ value: "desayuno", label: "Desayuno" },
{ value: "almuerzo", label: "Almuerzo" },
{ value: "cena", label: "Cena" },
{ value: "snack", label: "Snack" },
],
hasMore: false,
},
};
}
return { completion: { values: [], hasMore: false } };
});
Experiencia de usuario
Usuario: /plan-meal
CLI: Cocina? (escribe para buscar)
Usuario: ita
CLI: [italiana] ← autocompletado
Usuario: <Enter>
CLI: Tipo de comida?
> Desayuno
Almuerzo
Cena
Snack
Ejemplo práctico: Planificador de comidas
Construyamos un sistema completo de planificación de comidas.
Estructura del servidor
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
ListPromptsRequestSchema,
GetPromptRequestSchema,
ListResourceTemplatesRequestSchema,
ReadResourceRequestSchema,
CompleteRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{
name: "meal-planner-mcp",
version: "1.0.0",
},
{
capabilities: {
prompts: {},
resources: {},
},
}
);
Base de datos de recetas
interface Recipe {
name: string;
cuisine: string;
mealType: string;
ingredients: string[];
instructions: string[];
prepTime: number;
difficulty: "easy" | "medium" | "hard";
}
const recipes: Recipe[] = [
{
name: "Pasta Carbonara",
cuisine: "italiana",
mealType: "almuerzo",
ingredients: ["pasta", "huevos", "panceta", "queso parmesano"],
instructions: [
"Cocinar la pasta al dente",
"Freír la panceta",
"Mezclar huevos con queso",
"Combinar todo fuera del fuego",
],
prepTime: 20,
difficulty: "medium",
},
// ... más recetas
];
function getRecipe(cuisine: string, mealType: string): Recipe | undefined {
return recipes.find(
(r) => r.cuisine === cuisine && r.mealType === mealType
);
}
Prompts del sistema
server.setRequestHandler(ListPromptsRequestSchema, async () => {
return {
prompts: [
{
name: "weekly-meal-plan",
description: "Genera plan semanal de comidas",
arguments: [
{
name: "cuisine",
description: "Tipo de cocina preferida",
required: true,
},
{
name: "people",
description: "Número de personas",
required: false,
},
],
},
{
name: "shopping-list",
description: "Crea lista de compras para la semana",
arguments: [
{
name: "plan_uri",
description: "URI del plan de comidas",
required: true,
},
],
},
],
};
});
Implementación de “weekly-meal-plan”
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "weekly-meal-plan") {
const { cuisine, people = "2" } = args as {
cuisine: string;
people?: string;
};
const mealTypes = ["desayuno", "almuerzo", "cena"];
const days = [
"lunes",
"martes",
"miércoles",
"jueves",
"viernes",
"sábado",
"domingo",
];
// Recursos de recetas para cada comida
const resources = [];
for (const day of days) {
for (const mealType of mealTypes) {
resources.push({
uri: `recipe://${cuisine}/${mealType}`,
text: `Receta de ${mealType} (${cuisine}) para ${day}`,
});
}
}
return {
messages: [
{
role: "user",
content: {
type: "text",
text: `Crea un plan semanal de comidas para ${people} personas.
Preferencia de cocina: ${cuisine}.
Usa las recetas adjuntas y organiza:
- Variedad balanceada
- Aprovecha ingredientes comunes
- Indica tiempo de preparación total por día`,
},
},
...resources.map((r) => ({
role: "user" as const,
content: {
type: "resource" as const,
resource: r,
},
})),
],
};
}
});
Flujo completo
1. Usuario ejecuta: /weekly-meal-plan
→ Selecciona cocina: "italiana"
→ Selecciona personas: "4"
2. Sistema adjunta recursos:
→ recipe://italiana/desayuno
→ recipe://italiana/almuerzo
→ recipe://italiana/cena
(× 7 días = 21 recetas)
3. IA procesa y genera:
┌─────────────────────────────┐
│ Plan Semanal de Comidas │
├─────────────────────────────┤
│ Lunes: │
│ - Desayuno: Frittata │
│ - Almuerzo: Carbonara │
│ - Cena: Risotto │
│ │
│ Martes: │
│ - Desayuno: Bruschetta │
│ ... │
└─────────────────────────────┘
4. Usuario ejecuta: /shopping-list
→ Pasa URI del plan generado
5. Sistema genera lista optimizada:
┌─────────────────────────────┐
│ Lista de Compras │
├─────────────────────────────┤
│ Pasta: 2kg │
│ Huevos: 2 docenas │
│ Queso parmesano: 500g │
│ ... │
└─────────────────────────────┘
Casos de uso avanzados
1. Generación de documentación
{
name: "doc-generator",
description: "Genera documentación desde código",
arguments: [
{ name: "module_path", required: true },
{ name: "format", required: false } // markdown, html, pdf
]
}
// Adjunta recursos:
// - code://src/module.ts
// - code://tests/module.test.ts
// - dependencies://package.json
2. Code review automatizado
{
name: "auto-review",
description: "Revisa PR automáticamente",
arguments: [
{ name: "pr_number", required: true },
{ name: "checks", required: false } // security, performance, style
]
}
// Adjunta recursos:
// - git://diff/${pr_number}
// - git://files-changed/${pr_number}
// - ci://test-results/${pr_number}
3. Análisis de logs
{
name: "analyze-errors",
description: "Analiza errores en logs",
arguments: [
{ name: "timeframe", required: true }, // last-hour, last-day
{ name: "severity", required: false } // error, warning, critical
]
}
// Adjunta recursos:
// - logs://errors?timeframe=${timeframe}&severity=${severity}
// - metrics://error-rate
// - alerts://active
Mejores prácticas
1. Diseño de prompts
// ❌ Mal: Vago y sin contexto
"Ayúdame con el código"
// ✅ Bien: Específico y contextual
`Revisa este archivo para problemas de seguridad:
- SQL injection
- XSS vulnerabilities
- Hardcoded credentials
- Insecure dependencies
Archivo: ${file_path}
Contexto: ${project_context}`
2. Recursos incrementales
// ✅ Carga recursos bajo demanda
const resources = args.detailed
? [mainResource, ...detailedResources]
: [mainResource];
3. Validación de entrada
function validateCuisine(cuisine: string): void {
const valid = ["italiana", "mexicana", "japonesa"];
if (!valid.includes(cuisine)) {
throw new Error(`Cocina inválida: ${cuisine}. Opciones: ${valid.join(", ")}`);
}
}
4. Caching de recursos
const cache = new Map<string, Recipe>();
function getCachedRecipe(uri: string): Recipe {
if (!cache.has(uri)) {
cache.set(uri, fetchRecipe(uri));
}
return cache.get(uri)!;
}
Testing de prompts
import { describe, it, expect } from "vitest";
describe("Meal Planner Prompts", () => {
it("should generate weekly plan", async () => {
const response = await server.handleRequest({
method: "prompts/get",
params: {
name: "weekly-meal-plan",
arguments: { cuisine: "italiana", people: "4" },
},
});
expect(response.messages).toHaveLength(22); // 1 prompt + 21 recursos
expect(response.messages[0].content.text).toContain("plan semanal");
});
it("should provide completions", async () => {
const response = await server.handleRequest({
method: "completion/complete",
params: {
ref: { type: "ref/prompt", name: "weekly-meal-plan" },
argument: { name: "cuisine", value: "ita" },
},
});
expect(response.completion.values).toContainEqual({
value: "italiana",
label: "italiana",
});
});
});
Conclusión
Los MCP Prompts proporcionan herramientas poderosas para automatizar workflows complejos:
- ✅ Contexto dinámico: Recursos que se adaptan a parámetros
- ✅ Tipado fuerte: Argumentos validados y autocompletados
- ✅ Modularidad: Prompts reutilizables y componibles
- ✅ Escalabilidad: Desde simples hasta flujos multi-paso
Con prompts bien diseñados, puedes construir sistemas de automatización sofisticados que aprovechan al máximo las capacidades de la IA.
Post anterior: Nuevas Características de MCP
Serie completa:
- Introducción al MCP
- Primeros Pasos con MCP
- El Registry de MCP
- Nuevas Características MCP
- Automatización con MCP Prompts (este post)
Recursos: