Model Context Protocol al Descubierto: El Poder de Resources y Prompts más allá de las Tools

Model Context Protocol al Descubierto: El Poder de Resources y Prompts más allá de las Tools

Una guía práctica para dejar de centrarte solo en 'function calling' y construir servidores MCP de gran valor con el poder de Resources y Prompts

Como desarrollador en el mundo de la IA, seguro que te has enfrentado a este problema: conectar un LLM con aplicaciones y datos del mundo real es un proceso repetitivo y fragmentado. Cada integración parece una odisea de conectores a medida y APIs personalizadas. Es como si cada vez que quisiéramos enchufar un dispositivo, tuviéramos que inventar un nuevo tipo de conector.

La raíz del problema es la falta de estandarización. Y es aquí donde el Model Context Protocol (MCP) entra en juego, no como una mejora incremental, sino como una solución definitiva.

Pensemos en MCP como el USB-C del mundo de la IA. Así como USB-C unificó la conexión de periféricos, MCP estandariza cómo los modelos de IA se conectan a herramientas y fuentes de datos. Este post es la guía a fondo que no he encontrado en otros sitios. No nos quedaremos en la superficie hablando solo de Tools; vamos a sumergirnos en la arquitectura completa de MCP y a desvelar el poder oculto de Resources y Prompts con ejemplos prácticos y robustos.

Arquitectura de MCP

Para entender MCP, primero debemos conocer su arquitectura cliente-servidor y su estructura en capas.

Participantes: Host, Cliente y Servidor

La arquitectura de MCP se compone de tres participantes clave:

MCP Host: Es la aplicación de IA principal que orquesta todo el proceso (por ejemplo, una aplicación de escritorio como Claude Desktop o un IDE como Visual Studio Code). El Host gestiona y coordina uno o varios clientes MCP.

MCP Cliente: Es un componente dentro del Host que establece y mantiene una conexión uno a uno con un servidor MCP. El Host crea un cliente por cada servidor al que se conecta.

MCP Servidor: Es el programa que proporciona el contexto (datos, herramientas, etc.) a un cliente. Un servidor puede ejecutarse de forma local en la misma máquina que el Host o de forma remota en otra plataforma.

Por ejemplo, si Claude Code (el host) — en este post hablo sobre Claude Code — se conecta al servidor MCP de DeepWiki y también a un servidor MCP local de Context7, instanciará dos Clientes MCP diferentes, cada uno manteniendo su propia conexión dedicada a su respectivo Servidor.

Las Dos Capas de MCP: Datos y Transporte

Conceptualmente, el protocolo se divide en dos capas que trabajan juntas:

Capa de Transporte (Externa): Gestiona los canales de comunicación y la autenticación. Se encarga de establecer la conexión, enmarcar los mensajes y asegurar la comunicación. MCP soporta dos mecanismos de transporte:

  • Transporte Stdio (Standard I/O): Usa los flujos de entrada/salida estándar para la comunicación directa entre procesos en la misma máquina. Es ideal para servidores locales por su máximo rendimiento y nula sobrecarga de red.
  • Transporte HTTP Transmitible (Streamable HTTP): Emplea HTTP POST para los mensajes, con la capacidad opcional de usar Server-Sent Events (SSE) para streaming. Permite la comunicación con servidores remotos y soporta métodos de autenticación estándar (OAuth, Bearer Tokens, API keys).

Capa de Datos (Interna): Define el protocolo de comunicación basado en JSON-RPC 2.0. Esta es la capa que nos interesa como desarrolladores, ya que define la estructura y semántica de los mensajes. Incluye:

  • Gestión del ciclo de vida: Negociación de capacidades al iniciar y finalizar la conexión.
  • Primitivas del Servidor: Las funcionalidades que un servidor puede ofrecer, que son los tres pilares que exploraremos: Tools, Resources y Prompts.
  • Primitivas del Cliente: Capacidades que el servidor puede solicitar al cliente, como pedir una finalización del LLM (sampling) o solicitar información al usuario (elicitation).

Esta arquitectura en capas permite que el mismo protocolo de datos funcione de manera idéntica sin importar el mecanismo de transporte subyacente.

🔎
Toda esta información, y más conceptos, la puedes encontrar en su documentación oficial.
Architecture Overview - Model Context Protocol

Los Tres Pilares del Contexto en MCP

La capa de datos de MCP define tres primitivas o conceptos fundamentales que un servidor puede ofrecer, Tools, Resources y Prompts. Aquí es donde MCP redefine el juego, yendo mucho más allá del simple function calling.

Para ilustrar estos conceptos, construiremos un Product Catalog Manager para e-commerce que demuestre cómo cada pilar aporta valor único al sistema.

Pilar 1: Tools - Dándole Manos a tu IA 🛠️

Las Tools son funciones que el modelo de IA puede decidir ejecutar de forma autónoma para realizar una acción o modificar un estado. El control lo tiene el modelo.

Son el concepto más conocido. Si has trabajado con “function calling” de OpenAI, ya estás familiarizado con la idea.

Características Clave

  • Control del modelo: El LLM decide cuándo y cómo invocar la herramienta.
  • Orientadas a la acción: Su propósito es HACER algo (crear, actualizar, enviar, calcular).
  • Esquemas definidos: Tienen parámetros de entrada y salida estrictamente tipados.

Ejemplos Prácticos

Para definir los parámetros de forma robusta, usaremos la librería zod para la validación de esquemas.

Herramienta de Actualización de Precios:

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';

// Zod schema for input validation
const UpdatePriceSchema = z.object({
  productId: z.string().min(1, 'Product ID is required'),
  newPrice: z.number().positive('Price must be greater than 0'),
  currency: z.enum(['EUR', 'USD', 'GBP']).default('EUR'),
  reason: z.enum(['competitor_match', 'cost_increase', 'promotion', 'market_adjustment']),
  effectiveDate: z.string().datetime().optional(),
  notifySubscribers: z.boolean().default(false)
}).refine((data) => {
  // Business validation: products over 1000€ require special approval
  if (data.newPrice > 1000 && data.currency === 'EUR') {
    return data.reason !== 'promotion';
  }
  return true;
}, {
  message: 'Products over 1000€ cannot have promotional pricing without approval'
});

// Tool definition
const updateProductPriceTool: Tool = {
  name: 'updateProductPrice',
  description: 'Updates product pricing with comprehensive business validations',
  inputSchema: z.toJSONSchema(UpdatePriceSchema)
};

// Handler function
export async function handleUpdatePrice(args: UpdatePriceArgs) {
  // Automatic validation with Zod before calling handler
  const validatedArgs = UpdatePriceSchema.parse(args);
  
  const product = mockProducts[validatedArgs.productId];
  if (!product) {
    return {
      content: [{
        type: 'text' as const,
        text: `❌ Error: Product ${validatedArgs.productId} not found`
      }],
      isError: true
    };
  }

  // Simulate price update
  product.price = validatedArgs.newPrice;
  product.currency = validatedArgs.currency;
  product.priceUpdated = new Date().toISOString();
  
  const reasonText = {
    'competitor_match': 'competitor match',
    'cost_increase': 'cost increase',
    'promotion': 'special promotion',
    'market_adjustment': 'market adjustment'
  }[validatedArgs.reason];

  return {
    content: [{
      type: 'text' as const,
      text: `✅ Price updated successfully:
      
📦 **Product**: ${product.name}
💰 **New price**: ${validatedArgs.newPrice} ${validatedArgs.currency}
📊 **Reason**: ${reasonText}
${validatedArgs.effectiveDate ? `📅 **Effective date**: ${new Date(validatedArgs.effectiveDate).toLocaleDateString()}` : ''}
${validatedArgs.notifySubscribers ? '📧 **Notification**: Subscribers notified' : ''}

The price has been updated in the system and will be effective immediately.`
    }]
  };
}

Cuándo Implementar Tools

Ideal para:

  • Operaciones que modifican estado (crear, actualizar, eliminar productos).
  • Cálculos complejos que requieren lógica empresarial.
  • Integraciones con APIs externas.
  • Acciones que requieren lógica compleja e integraciones con APIs externas.

No ideal para:

  • Consulta de datos estáticos (para eso están los Resources)
  • Flujos de trabajo complejos multi-paso (mejor usar Prompts)

Pilar 2: Resources - El Contexto Que Lo Cambia Todo 📚

Los Resources son datos o contenido que el modelo puede consultar para informat sus respuestas. El control lo tiene usuario/modelo, pero el acceso es a petición.

Esta es la funcionalidad menos entendida pero más poderosa de MCP. Un Resource no es solo un archivo; es información estructurada, dinámica y contextual que enriquece las respuestas del modelo.

Características Clave

  • Sólo lectura: Para consultar, no para modificar.
  • Direccionamiento dinámico: Soportan plantillas URI con parámetros.
  • Contenido rico: Pueden incluir texto, JSON, imágenes, etc.
  • Contextualización inteligente: Se integran automáticamente en el contexto del modelo.

Ejemplos Prácticos

Resource Estático: Estructura de Categorías

const categoriesResource: ResourceTemplate = {
  uri: 'catalog://categories',
  name: 'Category Structure',
  description: 'Complete catalog category hierarchy',
  mimeType: 'application/json'
};

async function handleCategoriesResource() {
  return {
    contents: [{
      uri: 'catalog://categories',
      mimeType: 'application/json',
      text: JSON.stringify({
        categories: [
          {
            id: 'electronics',
            name: 'Electronics',
            subcategories: ['smartphones', 'laptops', 'tablets'],
            productCount: 1247,
            averagePrice: 450.32
          },
          {
            id: 'clothing',
            name: 'Clothing',
            subcategories: ['men', 'women', 'children'],
            productCount: 2891,
            averagePrice: 67.89
          }
        ],
        lastUpdated: new Date().toISOString()
      }, null, 2)
    }]
  };
}

Resource Dinámico: Detalles de Producto

const productDetailsTemplate: ResourceTemplate = {
  uriTemplate: 'product://details/{productId}',
  name: 'Product Details',
  description: 'Complete information for any product by ID',
  mimeType: 'application/json'
};

async function handleProductDetails(params: { productId: string }) {
  const product = await getProductById(params.productId);
  
  return {
    contents: [{
      uri: `product://details/${params.productId}`,
      mimeType: 'application/json',
      text: JSON.stringify({
        product: {
          id: product.id,
          name: product.name,
          description: product.description,
          price: {
            amount: product.price,
            currency: product.currency,
            lastUpdated: product.priceUpdated
          },
          inventory: {
            stock: product.stock,
            reserved: product.reserved,
            available: product.stock - product.reserved,
            reorderPoint: product.reorderPoint,
            status: product.stock <= product.reorderPoint ? 'low_stock' : 'in_stock'
          }
        },
        lastUpdated: new Date().toISOString()
      }, null, 2)
    }]
  };
}

Resource Template: Analytics de Ventas

const salesAnalyticsTemplate: ResourceTemplate = {
  uriTemplate: 'analytics://sales/{period}',
  name: 'Sales Analytics',
  description: 'Sales data by period (daily, weekly, monthly)',
  mimeType: 'application/json'
};

async function handleSalesAnalytics(params: { period: string }) {
  const analyticsData = await getSalesAnalytics(params.period);
  
  return {
    contents: [{
      uri: `analytics://sales/${params.period}`,
      mimeType: 'application/json',
      text: JSON.stringify({
        period: params.period,
        dateRange: {
          from: analyticsData.dateRange.from,
          to: analyticsData.dateRange.to
        },
        overview: {
          totalRevenue: analyticsData.totalRevenue,
          totalOrders: analyticsData.totalOrders,
          averageOrderValue: analyticsData.averageOrderValue,
          topSellingProducts: analyticsData.topProducts
        },
        trends: analyticsData.trends,
        categoryBreakdown: analyticsData.categoryBreakdown
      }, null, 2)
    }]
  };
}

Cuándo Implementar Resources

Ideal para:

  • Documentos y archivos existentes (catálogos, políticas, manuales)
  • Datos de consulta frecuente que fundamentan las respuestas del LLM
  • Contenido que debe ser indexado o buscado

No ideal para:

  • Acciones que modifican estado (para eso están las Tools)
  • Operaciones complejas que requieren lógica de negocio

Pilar 3: Prompts - Empaquetando Workflows Complejos ⚡

Los Prompts en MCP son plantillas de interacción predefinidas y parametrizadas que encapsulan flujos de trabajo comunes. El control lo tiene el usuario.

Este es, quizás, el concepto más innovador. Un Prompt en MCP no es solo una cadena de texto, es un workflow reutilizable que puede orquestar Tools y Resources para cumplir un objetivo complejo.

Características Clave

  • Control del usuario: El usuario final elige explícitamente qué Prompt ejecutar.
  • Plantillas parametrizadas: Aceptan argumentos para personalizar la ejecución.
  • Reutilizables: Diseñados para encapsular tareas repetitivas de alto nivel.
  • Contexto integrado: Un Prompt puede pre-configurar la conversación con Resources específicos.

Ejemplos Prácticos

Prompt para Optimización de Precios:

import { Prompt } from '@modelcontextprotocol/sdk/types.js';

interface PriceOptimizationArgs {
  category: string;
  strategy: 'competitive' | 'profit_max' | 'market_penetration';
  timeframe?: string;
}

const priceOptimizationPrompt: Prompt = {
  name: 'priceOptimization',
  description: 'Analyzes and suggests price optimizations for a category',
  arguments: [
    { 
      name: 'category', 
      description: 'Product category to analyze', 
      required: true 
    },
    { 
      name: 'strategy', 
      description: 'Strategy: competitive, profit_max, market_penetration', 
      required: true 
    },
    { 
      name: 'timeframe', 
      description: 'Analysis period (e.g., "30d", "quarterly")', 
      required: false 
    }
  ]
};

async function generatePriceOptimizationPrompt(args: PriceOptimizationArgs) {
  const timeframeText = args.timeframe ? ` over the last ${args.timeframe}` : '';
  
  return {
    messages: [{
      role: 'user' as const,
      content: {
        type: 'text',
        text: `Act as an expert e-commerce pricing analyst with complete access to the MCP system.

CONTEXT: Analyze the "${args.category}" category with "${args.strategy}" strategy${timeframeText}.

AVAILABLE DATA:
- Query inventory://stock/${args.category} for current inventory status
- Query analytics://sales/${args.timeframe || 'monthly'} for sales data
- For specific products, use product://details/{productId}

TASKS:
1. Analyze current pricing performance in the category
2. Identify products with optimization opportunities
3. Provide specific recommendations based on ${args.strategy} strategy
4. Include estimated impact on revenue and margin
5. Suggest implementation timeline

RESPONSE FORMAT:
- Executive summary
- Top 5 products to optimize with suggested prices
- Justification for each change
- Risks and mitigations
- Recommended tracking metrics

If you need to update any prices, use the updateProductPrice tool with appropriate parameters.`
      }
    }]
  };
}

Prompt para Revisión de Inventario:

interface InventoryReviewArgs {
  urgency: 'routine' | 'urgent' | 'critical';
  categories?: string[];
  includeRecommendations?: boolean;
}

const inventoryReviewPrompt: Prompt = {
  name: 'inventoryReview',
  description: 'Generates a comprehensive inventory review report',
  arguments: [
    { 
      name: 'urgency', 
      description: 'Urgency level: routine, urgent, critical', 
      required: true 
    },
    { 
      name: 'categories', 
      description: 'Specific categories to review (optional)', 
      required: false 
    },
    { 
      name: 'includeRecommendations', 
      description: 'Include restocking recommendations', 
      required: false 
    }
  ]
};

async function generateInventoryReviewPrompt(args: InventoryReviewArgs) {
  const categoriesText = args.categories?.length 
    ? ` focusing on: ${args.categories.join(', ')}`
    : ' for all categories';
  
  const recommendationsText = args.includeRecommendations 
    ? '\n- Generate automatic restock recommendations using manageInventory tool where needed'
    : '';

  return {
    messages: [{
      role: 'user' as const,
      content: {
        type: 'text',
        text: `Act as an expert inventory manager. Perform a ${args.urgency} review${categoriesText}.

DATA TO QUERY:
- Category structure: catalog://categories
- Inventory status by category: inventory://stock/{category}
- Recent sales data: analytics://sales/weekly

URGENCY LEVEL: ${args.urgency.toUpperCase()}
${args.urgency === 'critical' ? '⚠️  CRITICAL: Focus on out-of-stock and immediate low stock' : ''}
${args.urgency === 'urgent' ? '⚡ URGENT: Prioritize products near reorder point' : ''}
${args.urgency === 'routine' ? '📊 ROUTINE: Complete analysis and preventive planning' : ''}

REQUIRED ANALYSIS:
1. General inventory status
2. Products with critical stock (< reorder point)
3. Products with overstock (> 3 months inventory)
4. Rotation trends by category
5. Sales impact from stock-outs${recommendationsText}

OUTPUT FORMAT:
- Executive dashboard with key metrics
- List of immediate actions required
- Category alerts with priority levels
- Future review schedule

For products requiring immediate action, provide the exact manageInventory command that should be executed.`
      }
    }]
  };
}

Prompt para Análisis de Competencia:

interface CompetitorAnalysisArgs {
  category: string;
  competitors?: string[];
  focus: 'pricing' | 'features' | 'positioning' | 'comprehensive';
}

const competitorAnalysisPrompt: Prompt = {
  name: 'competitorAnalysis',
  description: 'Performs deep competitive analysis for a category',
  arguments: [
    { 
      name: 'category', 
      description: 'Product category to analyze', 
      required: true 
    },
    { 
      name: 'competitors', 
      description: 'List of specific competitors (optional)', 
      required: false 
    },
    { 
      name: 'focus', 
      description: 'Focus: pricing, features, positioning, comprehensive', 
      required: true 
    }
  ]
};

async function generateCompetitorAnalysisPrompt(args: CompetitorAnalysisArgs) {
  const competitorsText = args.competitors?.length 
    ? ` specifically comparing with: ${args.competitors.join(', ')}`
    : '';

  return {
    messages: [{
      role: 'user' as const,
      content: {
        type: 'text',
        text: `Act as an expert e-commerce competitive intelligence analyst.

OBJECTIVE: ${args.focus} analysis of "${args.category}" category${competitorsText}

INTERNAL DATA AVAILABLE:
- Category inventory: inventory://stock/${args.category}
- Sales data: analytics://sales/monthly
- Product details: product://details/{productId} for specific products

ANALYSIS FOCUS: ${args.focus.toUpperCase()}

${args.focus === 'pricing' ? `
🏷️ PRICING FOCUS:
- Map prices of similar products
- Identify positioning opportunities
- Calculate price gaps and estimated elasticity
- Propose competitive pricing strategy
` : ''}

${args.focus === 'features' ? `
⚙️ FEATURES FOCUS:
- Compare specifications and functionalities
- Identify unique competitive advantages
- Detect gaps in our catalog
- Suggest product improvements
` : ''}

${args.focus === 'positioning' ? `
🎯 POSITIONING FOCUS:
- Analyze messaging and value propositions
- Evaluate market segmentation
- Identify underserved niches
- Recommend differentiation strategy
` : ''}

${args.focus === 'comprehensive' ? `
🔍 COMPREHENSIVE ANALYSIS:
- All aspects above
- Detailed SWOT per competitor
- Threat and opportunity analysis
- Recommended strategic roadmap
` : ''}

EXPECTED DELIVERABLES:
1. Executive summary with key insights
2. Detailed competitive matrix
3. Prioritized identified opportunities
4. Specific actionable recommendations
5. Competitive tracking metrics

If you identify products needing price adjustments based on competitive analysis, prepare corresponding updateProductPrice commands.`
      }
    }]
  };
}

Cuándo Implementar Prompts

Ideal para:

  • Flujos de trabajo comunes y repetitivos (revisión diaria, resumen de proyecto)
  • Tareas que requieren múltiples pasos coordinados
  • Simplificar interacciones complejas para el usuario final

No ideal para:

  • Acciones simples que pueden ser Tools directas
  • Consultas únicas que no se repiten

Guía de Decisión: ¿Tool, Resource o Prompt?

La clave del éxito con MCP es saber elegir el componente adecuado para cada tarea.

Si necesitas… Usa un Resource 📚 Usa una Tool 🛠️ Usa un Prompt ⚡
…consultar información de productos (detalles, categorías).
…obtener datos que cambian (inventario, analytics, precios actuales).
…actualizar un precio o crear un producto.
…ejecutar lógica de negocio compleja (validaciones, cálculos).
…encapsular un flujo de varios pasos (optimización de precios, revisión de inventario).
…simplificar una tarea repetitiva para el usuario final.

Leyenda: ✅ Opción principal / ⚡ Puede formar parte de la solución / ❌ No es la herramienta adecuada

En resumen:

  • Empieza con Resources: Expón todos los datos y conocimientos que tu aplicación ya posee (catálogo, inventarios, analytics).
  • Añade Tools: Implementa las acciones y verbos clave que definen la funcionalidad de tu dominio (crear, actualizar, gestionar).
  • Crea Prompts: Identifica los flujos de trabajo más comunes de tus usuarios y empaquétalos como interacciones de un solo clic.

Implementación Completa del Servidor MCP

Ahora que hemos explorado cada pilar por separado, es hora de integrar todo en un servidor MCP completo y funcional.

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
  ListResourcesRequestSchema,
  ReadResourceRequestSchema,
  ListPromptsRequestSchema,
  GetPromptRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

export class ProductCatalogServer {
  private server: Server;
  private tools = new Map<string, ToolDefinition>();
  private resources = new Map<string, ResourceDefinition>();
  private prompts = new Map<string, PromptDefinition>();

  constructor() {
    this.server = new Server(
      {
        name: 'product-catalog-manager',
        version: '1.0.0'
      },
      {
        capabilities: {
          tools: {},
          resources: {},
          prompts: {}
        }
      }
    );

    this.initializeComponents();
    this.setupHandlers();
  }

  private initializeComponents() {
    this.setupTools();
    this.setupResources();
    this.setupPrompts();
  }

  private setupTools() {
    // Register all the tools we defined earlier
    this.tools.set('updateProductPrice', {
      name: 'updateProductPrice',
      description: 'Updates product pricing with comprehensive business validations',
      schema: UpdatePriceSchema,
      handler: this.handleUpdatePrice.bind(this)
    });

    this.tools.set('createProduct', {
      name: 'createProduct',
      description: 'Creates a new product in the catalog',
      schema: CreateProductSchema,
      handler: this.handleCreateProduct.bind(this)
    });

    this.tools.set('manageInventory', {
      name: 'manageInventory',
      description: 'Manages product inventory operations',
      schema: ManageInventorySchema,
      handler: this.handleManageInventory.bind(this)
    });
  }

  private setupResources() {
    // Direct resource: category structure
    this.resources.set('catalog://categories', {
      uri: 'catalog://categories',
      name: 'Category Structure',
      description: 'Complete catalog category hierarchy',
      mimeType: 'application/json',
      handler: this.handleCategoriesResource.bind(this)
    });

    // Dynamic resource templates
    this.resources.set('product://details/{productId}', {
      uri: 'product://details/{productId}',
      name: 'Product Details',
      description: 'Complete information for any product by ID',
      mimeType: 'application/json',
      isTemplate: true,
      handler: this.handleProductDetails.bind(this)
    });

    this.resources.set('inventory://stock/{category}', {
      uri: 'inventory://stock/{category}',
      name: 'Inventory by Category',
      description: 'Inventory status filtered by category',
      mimeType: 'application/json',
      isTemplate: true,
      handler: this.handleInventoryByCategory.bind(this)
    });

    this.resources.set('analytics://sales/{period}', {
      uri: 'analytics://sales/{period}',
      name: 'Sales Analytics',
      description: 'Sales data by period',
      mimeType: 'application/json',
      isTemplate: true,
      handler: this.handleSalesAnalytics.bind(this)
    });
  }

  private setupPrompts() {
    this.prompts.set('priceOptimization', {
      name: 'priceOptimization',
      description: 'Analyzes and suggests price optimizations for a category',
      arguments: [
        { name: 'category', description: 'Product category to analyze', required: true },
        { name: 'strategy', description: 'Strategy: competitive, profit_max, market_penetration', required: true },
        { name: 'timeframe', description: 'Analysis period', required: false }
      ],
      handler: generatePriceOptimizationPrompt
    });

    this.prompts.set('inventoryReview', {
      name: 'inventoryReview',
      description: 'Generates a comprehensive inventory review report',
      arguments: [
        { name: 'urgency', description: 'Urgency level: routine, urgent, critical', required: true },
        { name: 'categories', description: 'Specific categories to review', required: false },
        { name: 'includeRecommendations', description: 'Include restocking recommendations', required: false }
      ],
      handler: generateInventoryReviewPrompt
    });

    this.prompts.set('competitorAnalysis', {
      name: 'competitorAnalysis',
      description: 'Performs deep competitive analysis for a category',
      arguments: [
        { name: 'category', description: 'Product category to analyze', required: true },
        { name: 'competitors', description: 'List of specific competitors', required: false },
        { name: 'focus', description: 'Analysis focus', required: true }
      ],
      handler: generateCompetitorAnalysisPrompt
    });
  }

  private setupHandlers() {
    // Handlers for Tools
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: Array.from(this.tools.values()).map(tool => ({
        name: tool.name,
        description: tool.description,
        inputSchema: z.toJSONSchema(tool.schema)
      }))
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;
      const tool = this.tools.get(name);
      if (!tool) throw new Error(`Tool not found: ${name}`);

      try {
        const validatedArgs = tool.schema.parse(args || {});
        return await tool.handler(validatedArgs);
      } catch (error) {
        if (error instanceof z.ZodError) {
          throw new Error(`Validation errors: ${error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`);
        }
        throw error;
      }
    });

    // Handlers for Resources
    this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
      resources: Array.from(this.resources.values()).map(resource => ({
        uri: resource.uri,
        name: resource.name,
        description: resource.description,
        mimeType: resource.mimeType
      }))
    }));

    this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
      const { uri } = request.params;
      
      // Search direct resource
      const directResource = this.resources.get(uri);
      if (directResource) {
        return await directResource.handler();
      }

      // Search resource templates
      for (const [template, resource] of this.resources.entries()) {
        if (resource.isTemplate) {
          const match = this.matchUriTemplate(uri, template);
          if (match) {
            return await resource.handler(match);
          }
        }
      }

      throw new Error(`Resource not found: ${uri}`);
    });

    // Handlers for Prompts
    this.server.setRequestHandler(ListPromptsRequestSchema, async () => ({
      prompts: Array.from(this.prompts.values()).map(prompt => ({
        name: prompt.name,
        description: prompt.description,
        arguments: prompt.arguments
      }))
    }));

    this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;
      const prompt = this.prompts.get(name);
      if (!prompt) throw new Error(`Prompt not found: ${name}`);

      return await prompt.handler(args || {});
    });
  }

  private matchUriTemplate(uri: string, template: string): any {
    // Simplified implementation of URI template matching
    const templateParts = template.split('/');
    const uriParts = uri.split('/');
    
    if (templateParts.length !== uriParts.length) return null;
    
    const params: any = {};
    for (let i = 0; i < templateParts.length; i++) {
      const templatePart = templateParts[i];
      const uriPart = uriParts[i];
      
      if (templatePart.startsWith('{') && templatePart.endsWith('}')) {
        const paramName = templatePart.slice(1, -1);
        params[paramName] = uriPart;
      } else if (templatePart !== uriPart) {
        return null;
      }
    }
    
    return params;
  }

  // Handler implementations (simplified for the example)
  private async handleUpdatePrice(args: UpdatePriceArgs) {
    // Price update logic
    return {
      content: [{
        type: 'text',
        text: `Price updated: Product ${args.productId} → ${args.newPrice} ${args.currency}`
      }]
    };
  }

  private async handleCreateProduct(args: CreateProductArgs) {
    // Product creation logic
    const newId = `prod_${Date.now()}`;
    return {
      content: [{
        type: 'text', 
        text: `Product "${args.name}" created with ID: ${newId}`
      }]
    };
  }

  private async handleManageInventory(args: ManageInventoryArgs) {
    // Inventory management logic
    return {
      content: [{
        type: 'text',
        text: `Inventory ${args.operation}: ${args.quantity} units of product ${args.productId}`
      }]
    };
  }

  private async handleCategoriesResource() {
    // Returns category structure
    return handleCategoriesResource();
  }

  private async handleProductDetails(params: { productId: string }) {
    // Returns product details
    return handleProductDetails(params.productId);
  }

  private async handleInventoryByCategory(params: { category: string }) {
    // Returns inventory by category
    return handleInventoryByCategory(params.category);
  }

  private async handleSalesAnalytics(params: { period: string }) {
    // Returns sales analytics
    return handleSalesAnalytics(params.period);
  }

  async start() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
  }
}

// Support interfaces
interface ToolDefinition {
  name: string;
  description: string;
  schema: z.ZodSchema;
  handler: (args: any) => Promise<any>;
}

interface ResourceDefinition {
  uri: string;
  name: string;
  description: string;
  mimeType: string;
  isTemplate?: boolean;
  handler: (params?: any) => Promise<any>;
}

interface PromptDefinition {
  name: string;
  description: string;
  arguments: Array<{
    name: string;
    description: string;
    required: boolean;
  }>;
  handler: (args: any) => Promise<any>;
}

// Launch the server
const server = new ProductCatalogServer();
server.start().catch(console.error);

Conclusión: El Futuro es Contextual

El Model Context Protocol representa un cambio paradigmático en cómo pensamos sobre la integración de IA. Mientras que la mayoría se enfoca únicamente en Tools (function calling), el verdadero poder reside en la combinación estratégica de los tres pilares:

  • Tools: Para acciones específicas y lógica de negocio.
  • Resources: Para proporcionar contexto rico y dinámico.
  • Prompts: Para workflows complejos y experiencias simplificadas.

Dominar esta tríada es lo que diferencia una simple integración de IA de una aplicación verdaderamente inteligente. El futuro de la IA no reside en modelos aislados, sino en ecosistemas conectados. MCP es el estándar que lo está haciendo posible.

La estandarización que propone MCP es solo el comienzo. El verdadero desafío ahora es identificar qué conocimiento único (Resources) y flujos de trabajo (Prompts) de nuestros propios sistemas pueden ser expuestos para generar un valor real.

Este es un campo en plena evolución y el conocimiento compartido es clave. ¿Tienes alguna pregunta o un caso de uso específico que te gustaría debatir? Te leo en los comentarios.


Si quieres ver el código del servidor mcp al completo, aquí os dejo el enlace al repositorio de Github:

GitHub - The-Dave-Stack/product-catalog-mcp: Advanced MCP server showcasing Resources and Prompts for intelligent e-commerce catalog management. Demonstrates the power of Model Context Protocol beyond simple function calling.
Advanced MCP server showcasing Resources and Prompts for intelligent e-commerce catalog management. Demonstrates the power of Model Context Protocol beyond simple function calling. - The-Dave-Stack…
Te suscribiste correctamente a The Dave Stack
¡Excelente! A continuación, complete el proceso de pago para acceder a The Dave Stack
¡Dar una buena acogida! Has iniciado sesión correctamente.
¡Éxito! Su cuenta está completamente activada, ahora tiene acceso a todo el contenido.
¡Éxito! Su información de facturación está actualizada.
Error al actualizar la información de facturación.