Model Context Protocol Uncovered: The Power of Resources and Prompts Beyond Tools

Model Context Protocol Uncovered: The Power of Resources and Prompts Beyond Tools

A practical guide to move beyond 'function calling' and build high-value MCP servers with the power of Resources and Prompts.

As a developer in the AI world, you've surely faced this problem: connecting an LLM with real-world applications and data is a repetitive and fragmented process. Every integration seems like an odyssey of custom connectors and bespoke APIs. It's as if we had to invent a new type of plug every time we wanted to connect a device.

The root of the problem is the lack of standardization. And this is where the Model Context Protocol (MCP) comes into play, not as an incremental improvement, but as a definitive solution.

Think of MCP as the USB-C of the AI world. Just as USB-C unified peripheral connections, MCP standardizes how AI models connect to tools and data sources. This post is the in-depth guide I couldn't find elsewhere. We won't just scratch the surface by talking only about Tools; we're going to dive deep into the complete architecture of MCP and unveil the hidden power of Resources and Prompts with practical and robust examples.

MCP Architecture

To understand MCP, we first need to know its client-server architecture and its layered structure.

Participants: Host, Client, and Server

The MCP architecture is composed of three key participants:

MCP Host: This is the main AI application that orchestrates the entire process (for example, a desktop application like Claude Desktop or an IDE like Visual Studio Code). The Host manages and coordinates one or more MCP clients.

MCP Client: This is a component within the Host that establishes and maintains a one-to-one connection with an MCP server. The Host creates one client for each server it connects to.

MCP Server: This is the program that provides the context (data, tools, etc.) to a client. A server can run locally on the same machine as the Host or remotely on another platform.

For example, if Claude Code (the Host) – in this post I've talk a little bit more about Claude Code – connects to the DeepWiki MCP server and also to a local Context7 MCP server, it will instantiate two different MCP Clients, each maintaining its own dedicated connection to its respective Server.

The Two Layers of MCP: Data and Transport

Conceptually, the protocol is divided into two layers that work together:

Transport Layer (Outer): Manages communication channels and authentication. It is responsible for establishing the connection, framing messages, and ensuring secure communication. MCP supports two transport mechanisms:

  • Stdio Transport (Standard I/O): Uses standard input/output streams for direct communication between processes on the same machine. It's ideal for local servers due to its maximum performance and zero network overhead.
  • Streamable HTTP Transport: Employs HTTP POST for messages, with the optional ability to use Server-Sent Events (SSE) for streaming. It allows communication with remote servers and supports standard authentication methods (OAuth, Bearer Tokens, API keys).

Data Layer (Inner): Defines the communication protocol based on JSON-RPC 2.0. This is the layer that interests us as developers, as it defines the structure and semantics of the messages. It includes:

  • Lifecycle Management: Negotiation of capabilities when initiating and terminating the connection.
  • Server Primitives: The functionalities a server can offer, which are the three pillars we will explore: Tools, Resources, and Prompts.
  • Client Primitives: Capabilities that the server can request from the client, such as asking for an LLM completion (sampling) or requesting information from the user (elicitation).

This layered architecture allows the same data protocol to work identically regardless of the underlying transport mechanism.

🔎
You can find all this information, and more concepts, in their official documentation.
Architecture Overview - Model Context Protocol

The Three Pillars of Context in MCP

The MCP data layer defines three fundamental primitives or concepts that a server can offer: Tools, Resources, and Prompts. This is where MCP redefines the game, going far beyond simple function calling.

To illustrate these concepts, we will build a Product Catalog Manager for e-commerce that demonstrates how each pillar brings unique value to the system.

Pillar 1: Tools - Giving Your AI Hands 🛠️

Tools are functions that the AI model can decide to execute autonomously to perform an action or modify a state. The model is in control.

This is the most well-known concept. If you've worked with OpenAI's "function calling", you're already familiar with the idea.

Key Features

  • Model Control: The LLM decides when and how to invoke the tool.
  • Action-Oriented: Their purpose is to DO something (create, update, send, calculate).
  • Defined Schemas: They have strictly typed input and output parameters.

Practical Examples

To define parameters robustly, we'll use the Zod library for schema validation.

Price Update Tool:

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.`
    }]
  };
}

When to Implement Tools

Ideal for:

  • Operations that modify state (creating, updating, deleting products).
  • Complex calculations that require business logic.
  • Integrations with external APIs.
  • Actions that require complex logic and integrations with external APIs.

Not ideal for:

  • Querying static data (that's what Resources are for).
  • Complex multi-step workflows (better to use Prompts).

Pillar 2: Resources - The Context That Changes Everything 📚

Resources are data or content that the model can query to inform its responses. The user/model is in control, but access is on-demand.

This is the least understood yet most powerful feature of MCP. A Resource is not just a file; it is structured, dynamic, and contextual information that enriches the model's responses.

Key Features

  • Read-only: For querying, not modifying.
  • Dynamic addressing: They support URI templates with parameters.
  • Rich content: They can include text, JSON, images, etc.
  • Intelligent contextualization: They are automatically integrated into the model's context.

Practical Examples

Static Resource: Category Structure

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)
    }]
  };
}

Dynamic Resource: Product Details

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: Sales Analytics

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)
    }]
  };
}

When to Implement Resources

Ideal for:

  • Existing documents and files (catalogs, policies, manuals).
  • Frequently queried data that grounds the LLM's responses.
  • Content that needs to be indexed or searched.

Not ideal for:

  • Actions that modify state (that's what Tools are for).
  • Complex operations that require business logic.

Pillar 3: Prompts - Packaging Complex Workflows ⚡

Prompts in MCP are predefined, parameterized interaction templates that encapsulate common workflows. The user is in control.

This is perhaps the most innovative concept. A Prompt in MCP is not just a string of text; it is a reusable workflow that can orchestrate Tools and Resources to achieve a complex goal.

Key Features

  • User Control: The end-user explicitly chooses which Prompt to execute.
  • Parameterized Templates: They accept arguments for customization.
  • Reusable: Designed to encapsulate repetitive, high-level tasks.
  • Integrated Context: A Prompt can pre-configure the conversation with specific Resources.

Practical Examples

Prompt for Price Optimization:

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 for Inventory Review:

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 for Competitor Analysis:

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.`
      }
    }]
  };
}

When to Implement Prompts

Ideal for:

  • Common and repetitive workflows (daily review, project summary).
  • Tasks that require multiple coordinated steps.
  • Simplifying complex interactions for the end-user.

Not ideal for:

  • Simple actions that can be direct Tools.
  • One-off queries that are not repeated.

Decision Guide: Tool, Resource, or Prompt?

The key to success with MCP is knowing how to choose the right component for each task.

If You Need to… Use a Resource 📚 Use a Tool 🛠️ Use a Prompt ⚡
…query product information (details, categories).
…get data that changes (inventory, analytics, current prices).
…update a price or create a product.
…execute complex business logic (validations, calculations).
…encapsulate a multi-step flow (price optimization, inventory review).
…simplify a repetitive task for the end-user.

Legend: ✅ Primary choice / ⚡ Can be part of the solution / ❌ Not the right tool

In summary:

  • Start with Resources: Expose all the data and knowledge your application already has (catalog, inventory, analytics).
  • Add Tools: Implement the key actions and verbs that define your domain's functionality (create, update, manage).
  • Create Prompts: Identify the most common workflows your users perform and package them as single-click interactions.

Complete MCP Server Implementation

Now that we've explored each pillar separately, it's time to integrate everything into a complete and functional MCP server.

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);

Conclusion: The Future is Contextual

The Model Context Protocol represents a paradigm shift in how we think about AI integration. While most of the focus is solely on Tools (function calling), the real power lies in the strategic combination of all three pillars:

  • Tools: For specific actions and business logic.
  • Resources: To provide rich and dynamic context.
  • Prompts: For complex workflows and simplified experiences.

Mastering this triad is what separates a simple AI integration from a truly intelligent application. The future of AI does not lie in isolated models, but in connected ecosystems. MCP is the standard that is making this future possible.

The standardization that MCP proposes is just the beginning. The real challenge now is to identify what unique knowledge (Resources) and workflows (Prompts) from our own systems can be exposed to generate real value.

This is a rapidly evolving field, and shared knowledge is key. Do you have a question or a specific use case you'd like to discuss? Let me know in the comments.


If you want to see the complete MCP server code, here is the link to the GitHub repository:

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…
You've successfully subscribed to The Dave Stack
Great! Next, complete checkout for full access to The Dave Stack
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.
Success! Your billing info is updated.
Billing info update failed.