$ desarrollomcp
Volver al blog
Avanzado

Automatización de Workflows con MCP Prompts

Por DesarrolloMCP
|
#mcp #prompts #automatizacion #workflows

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

  1. Instrucciones de texto: El prompt base
  2. Plantillas de recursos: URIs dinámicos con parámetros
  3. Completaciones: Sugerencias mientras se escribe
  4. 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:

  1. Introducción al MCP
  2. Primeros Pasos con MCP
  3. El Registry de MCP
  4. Nuevas Características MCP
  5. Automatización con MCP Prompts (este post)

Recursos: