Unlocking AI's Full Potential: Understanding the Model Context Protocol

January 1, 2024

                                                                           

Unlocking AI’s Full Potential: Understanding the Model Context Protocol

Discover how the Model Context Protocol (MCP) allows AI applications to access data, perform actions, and learn from feedback in real time. Learn about the four key components that make AI systems more powerful and adaptable.

Core MCP Concepts: Resources, Tools, Prompts, and Sampling

Imagine giving your AI assistant not just a brain, but also hands and feet. What if your AI could not only think about data but also retrieve it, transform it, and learn from how people interact with it? This is exactly what the Model Context Protocol (MCP) aims to do. While large language models (LLMs) have impressive reasoning abilities, they are often limited by their inability to access real-time data or perform actions in the world. MCP solves this problem by providing a standard framework that allows AI to interact with data, perform tasks, and continuously improve through feedback.

ChatGPT Image May 4, 2025, 10_22_17 PM.png

In this article, we will explore the four pillars of MCP that make this possible: Resources, Tools, Prompts, and Sampling. Whether you are building a customer service chatbot, a research assistant, or any other AI-powered application, understanding these concepts will help you create more powerful and flexible solutions.

The Four Pillars of MCP

MCP has four interconnected components that work together to create intelligent, adaptable AI applications:

  1. Resources: Standardized gateways to various data sources.
  2. Tools: Functions that allow AI to take actions.
  3. Prompts: Templates that guide AI behavior.
  4. Sampling: Mechanisms for collecting feedback and improving AI over time.

Let’s look at each one to understand how they work and how you can implement them in your AI applications.

Resources: The AI’s Access to Data

What Are Resources?

In MCP, a Resource is any data source that an AI agent can access. Think of databases, filesystems, APIs, and cloud storage—any place where information is stored. MCP hides the complexities of different data storage systems, providing a single, unified interface for AI to interact with data.

This abstraction is powerful because it means your AI does not need to know the specific details of how to connect to a PostgreSQL database versus a MongoDB database or a REST API. It simply uses a standard protocol.

How Resources Work: The Power of URLs

MCP uses Resource URLs to identify and access different data sources. These URLs follow a specific structure:

scheme://authority/path?query_parameters

For example:

database://my_database/customers?api_key=YOUR_API_KEY

file://path/to/my/file.txt?version=1.2

api://weather_api/current_conditions?location=NewYork&units=metric&api_key=YOUR_API_KEY

vector://embedding_db/documents?search=similarto:query_embedding&limit=5

Each part of the URL has a specific purpose:

  • Scheme: Identifies the Resource type (database, file, api, etc.).
  • Authority: Specifies the Resource server or service.
  • Path: Indicates the location within the Resource.
  • Query Parameters: Provides optional configuration.

Resource Implementation Example

Let’s look at how you might implement access to a customer database:

import os
from mcp_client import MCPClient

# Get server URL and API key from env vars
server_url = os.environ.get("MCP_SERVER_URL")
api_key = os.environ.get("DATABASE_API_KEY")

# Initialize MCP client
client = MCPClient(server_url)

# Create Resource URL with API key
resource_url = (
    f"database://crm_db/customers"
    f"?api_key={api_key}"
    f"&fields=id,name,email,last_purchase_date"
    f"&filter=status:active"
)

try:
    # Get customer data
    customer_data = client.get_resource(resource_url)

    # Display results
    print(f"Retrieved {len(customer_data)} active customers")
    for customer in customer_data:
        print(f"ID: {customer['id']}, Name: {customer['name']}")

except Exception as e:
    print(f"Error: {e}")

This code shows how MCP Resources allow for precise data retrieval without exposing the underlying implementation details. You simply specify what data you want through the URL, and the MCP client does the rest.

Tools: Enabling AI to Take Action

What Are Tools?

While Resources allow AI to access data, Tools allow it to perform actions. In MCP, a Tool is a function that an AI agent can execute. Think of it as giving your AI “hands” to interact with the world.

Tools can be anything from simple calculators to complex workflow engines. They might perform mathematical operations, search for information, send notifications, or interact with external services.

Tool Invocation Process

When an AI agent wants to use a Tool, it follows a specific protocol:

  1. Discovery: The AI learns what Tools are available.
  2. Parameter Preparation: The AI prepares the necessary inputs.
  3. Invocation: The AI sends a request to execute the Tool.
  4. Execution: The MCP server runs the Tool.
  5. Result Handling: The AI receives and processes the results.

Simple Tool Example: Rectangle Area Calculator

Let’s implement a simple Tool that calculates the area of a rectangle:

from mcp.server import MCPServer
from mcp.tools import Tool

class RectangleAreaCalculator(Tool):
    def execute(
        self,
        width: float,
        height: float
    ) -> float:
        """Calculates rectangle area.

        Args:
            width: Rectangle width
            height: Rectangle height

        Returns:
            float: Rectangle area
        """
        return width * height

server = MCPServer("Area Calculator Server")
server.register_tool(
    "calculate_rectangle_area",
    RectangleAreaCalculator()
)

if __name__ == "__main__":
    server.start()

And here is how a client might call this Tool:

from mcp.client import MCPClient

# Initialize client
client = MCPClient("http://localhost:8000")

# Set tool name and parameters
tool_name = "calculate_rectangle_area"
params = {
    "width": 5.0,
    "height": 10.0
}

try:
    # Execute tool and get result
    result = client.execute_tool(
        tool_name,
        params
    )
    print(f"The area is: {result}")

except Exception as e:
    print(f"Error: {e}")

Advanced Tool Example: Sentiment Analysis

Tools can also integrate with complex AI services. Here is an example of a sentiment analysis Tool:

from mcp.server import MCPServer
from mcp.tools import Tool
from transformers import pipeline

class SentimentAnalyzer(Tool):
    def __init__(self):
        # Initialize the sentiment analysis pipeline
        self.analyzer = pipeline(
            "sentiment-analysis"
        )

    def execute(self, text: str) -> dict:
        """Analyzes the sentiment of text.

        Args:
            text (str): Text to analyze.

        Returns:
            dict: Sentiment analysis results including
                  label (positive/negative) and score.
        """
        # Perform the analysis
        result = self.analyzer(text)[0]

        # Return a more detailed result
        return {
            "label": result['label'],
            "score": result['score'],
            "text_analyzed": text[:50] + "..." if len(text) > 50 else text
        }

# Create the server
server = MCPServer("Sentiment Analysis Server")

# Register the tool
server.register_tool(
    "analyze_sentiment",
    SentimentAnalyzer()
)

# Start the server
if __name__ == "__main__":
    server.start()

Complex Document Retrieval System

One particularly powerful use of Tools is in document management and retrieval. Here is an example of a Tool that implements an advanced document retrieval system:

from mcp.server import MCPServer
from mcp.tools import Tool

class DocumentRetriever(Tool):
    def __init__(self, vector_db, graph_db, keyword_searcher):
        self.vector_db = vector_db
        self.graph_db = graph_db
        self.keyword_searcher = keyword_searcher

    def execute(
        self,
        query: str,
        filters: dict = None,
        limit: int = 5
    ) -> list:
        """Retrieves documents using multiple search methods.

        Args:
            query: User's search query
            filters: Optional metadata filters
            limit: Maximum number of results

        Returns:
            list: Relevant document chunks with metadata
        """
        # Get vector search results
        vector_results = self.vector_db.search(
            query=query,
            filters=filters,
            limit=limit*2  # Get more for reranking
        )

        # Get graph-based results
        graph_results = self.graph_db.search(
            query=query,
            filters=filters,
            limit=limit*2
        )

        # Get keyword search results
        keyword_results = self.keyword_searcher.search(
            query=query,
            filters=filters,
            limit=limit*2
        )

        # Combine and rerank results
        all_results = self._combine_results(
            vector_results,
            graph_results,
            keyword_results
        )
        reranked_results = self._rerank_results(
            query,
            all_results
        )

        # Return top results
        return reranked_results[:limit]

    def _combine_results(self, *result_sets):
        """Combines results from different search methods."""
        # Implementation details...
        pass

    def _rerank_results(self, query, results):
        """Reranks results based on relevance to query."""
        # Implementation details...
        pass

# Server setup
server = MCPServer("Document Search Server")
server.register_tool(
    "retrieve_documents",
    DocumentRetriever(
        vector_db=VectorDatabase(),
        graph_db=GraphDatabase(),
        keyword_searcher=KeywordSearcher()
    )
)

if __name__ == "__main__":
    server.start()

This document retrieval system does more than just simple search. It takes in a large collection of documents, stores metadata and extracted fields in a graph database, and keeps document paragraphs in a vector database with links to related sections and pages. It combines multiple search methods—reranking, keyword search, BM25, HyDE, and vector similarity search—to provide precise results.

What is powerful about MCP is that this complex system is exposed through a simple Tool interface. Your AI agent does not need to understand the implementation details. It just needs to provide a query and filters to get relevant results. This abstraction allows you to continuously improve the underlying retrieval system without changing how AI agents interact with it.

Prompts: Guiding AI Behavior

The Power of Prompts in MCP

Prompts are structured instructions that guide AI models within MCP. Think of them as precise search queries for an AI “search engine” or detailed instruction manuals for an expert. Well-crafted prompts are essential for getting accurate, relevant responses.

Prompts have two main functions:

  • Guiding Behavior: They tell the AI what to do and how (e.g., desired tone, format).
  • Extracting Information: They specify what information is needed from the AI’s response.

In MCP, prompts are often templates with placeholders that are filled in dynamically. This allows for flexibility and reuse.

Prompt Templates and Formatting

Here is a simple example of a translation prompt template:

Translate the following text from {source_lang} to {target_lang}: {text}

At runtime, the placeholders ({source_lang}, {target_lang}, and {text}) are replaced with actual values.

The art of designing effective prompts is called prompt engineering. It involves understanding how AI models interpret language and structuring requests to achieve specific goals.

Document Summarization Example

Let’s see how we might implement document summarization using MCP prompts:

from typing import Dict, Any
from pydantic import BaseModel

class SummaryResponse(BaseModel):
    summary: str

def summarize_document(
    mcp_client: Any,
    document_text: str
) -> str:
    """Summarizes text using an AI model via MCP."""

    # Define the prompt template
    prompt_template = (
        "Summarize the following document in "
        "three sentences or less, focusing on "
        "the key arguments and conclusions: "
        "{document}"
    )

    # Format prompt with document
    formatted_prompt = prompt_template.format(
        document=document_text
    )

    # Call AI model's tool via MCP client
    try:
        response: Dict[str, Any] = mcp_client.call_tool(
            "generate_text",
            prompt=formatted_prompt
        )
        validated_response = SummaryResponse(**response)
        return validated_response.summary

    except Exception as e:
        print(f"Error calling MCP tool: {e}")
        return "Error: Could not generate summary."

Here is an asynchronous version of the same function:

import asyncio

class SummaryResponse(BaseModel):
    summary: str

async def summarize_document_async(
    mcp_client: Any,
    document_text: str
) -> str:
    """Async document summarization via MCP."""

    prompt_template = (
        "Summarize the following document in "
        "three sentences or less, focusing on "
        "the key arguments and conclusions: "
        "{document}"
    )

    formatted_prompt = prompt_template.format(
        document=document_text
    )

    try:
        response = await mcp_client.call_tool(
            "generate_text",
            prompt=formatted_prompt
        )
        validated_response = SummaryResponse(**response)
        return validated_response.summary

    except Exception as e:
        print(f"Error calling MCP tool: {e}")
        return "Error: Could not generate summary."

These examples show how prompts can be combined with Tools to create powerful document processing capabilities.

Sampling: The AI Feedback Mechanism

Understanding Sampling in MCP

Sampling in MCP is a complex mechanism that allows AI systems to collect, analyze, and use feedback data. It is what turns static AI systems into dynamic, evolving agents that get better over time.

Sampling has two main functions:

  1. Content Generation: MCP sampling allows servers to request LLM completions from clients, which enables dynamic content generation.
  2. Feedback Collection: It involves collecting and analyzing user interactions (e.g., ratings, comments) to improve model performance.

The Science of Feedback Loops

AI feedback loops have several interconnected components:

  1. Output Generation: The AI produces an output.
  2. Evaluation: The output is evaluated (by humans or metrics).
  3. Signal Processing: The evaluation is turned into a learning signal.
  4. Model Update: The model adjusts based on the learning signal.
  5. Iteration: The process repeats, creating continuous improvement.

Sampling Strategies

Different situations require different sampling techniques.

Data Collection Methods

  • Passive Sampling: Automatically collecting data during normal operation.
  • Active Sampling: Deliberately asking for feedback from users.
  • Implicit Sampling: Inferring feedback from user behavior.
  • Explicit Sampling: Directly asking users for ratings or comments.

Selection Strategies

  • Random Sampling: Data points are selected randomly.
  • Stratified Sampling: Samples are drawn from subgroups to ensure a balanced representation.
  • Uncertainty-Based Sampling: Prioritizes data points that the AI is most uncertain about.
  • Information-Directed Sampling: Balances exploration and exploitation.

Let’s implement some of these strategies.

Random Sampling Example

import random

def random_sample(data, n):
    """Returns a random sample of size n
    from the given data."""
    if not data:
        return []  # Empty list if data is empty

    if n > len(data):
        raise ValueError(
            "Sample size cannot be larger than the data size"
        )

    return random.sample(data, n)

# Example usage
data = list(range(100))
sample = random_sample(data, 10)
print(f"Random Sample: {sample}")

Stratified Sampling Example

import pandas as pd

def stratified_sample_pandas(
    data: list,
    strata_keys: list,
    n: int
) -> list:
    """Get a stratified sample from the data."""
    # Create a DataFrame
    df = pd.DataFrame(data)

    # Group by the keys
    grouped = df.groupby(
        strata_keys,
        group_keys=False
    )

    # Items per group
    items = n // len(grouped)

    # Sample from the groups
    sampled = grouped.apply(
        lambda x: x.sample(
            min(len(x), items)
        )
    )

    return sampled.to_dict('records')

# Example data
data = [
    {"group": "A", "value": 1},
    {"group": "B", "value": 2},
    {"group": "A", "value": 3},
    {"group": "B", "value": 4},
    {"group": "A", "value": 5}
]

keys = ["group"]
sample = stratified_sample_pandas(
    data,
    keys,
    3
)

print(f"Sample: {sample}")

Comprehensive Feedback Pipeline

For production systems, you might implement a more complete feedback pipeline:

class FeedbackPipeline:
    def __init__(self, mcp_client, model_id, storage_client):
        self.mcp_client = mcp_client
        self.model_id = model_id
        self.storage = storage_client
        self.metrics = MetricsTracker()

    async def collect_feedback(self, output, context):
        """Collects explicit or implicit feedback on model output"""
        feedback_data = {
            "model_id": self.model_id,
            "output": output,
            "context": context,
            "timestamp": datetime.now().isoformat(),
        }

        # Try to collect explicit feedback
        try:
            user_feedback = await self.mcp_client.call_tool(
                "request_user_feedback",
                {
                    "output": output,
                    "feedback_type": "rating",
                    "scale": "1-5"
                }
            )
            feedback_data["explicit_feedback"] = user_feedback
        except Exception:
            # Fall back to implicit feedback
            implicit_feedback = await self.mcp_client.call_tool(
                "measure_user_engagement",
                {"output": output, "context": context}
            )
            feedback_data["implicit_feedback"] = implicit_feedback

        # Store feedback for model improvement
        await self.mcp_client.call_tool(
            "store_feedback",
            feedback_data
        )

        return feedback_data

    async def analyze_feedback(self, time_period="day"):
        """Analyzes recent feedback for patterns"""
        # Retrieve recent feedback
        recent_feedback = await self.mcp_client.call_tool(
            "retrieve_feedback",
            {
                "model_id": self.model_id,
                "time_period": time_period
            }
        )

        # Analyze the feedback
        analysis = {
            "average_rating": self._calculate_average_rating(recent_feedback),
            "issue_categories": self._identify_issue_categories(recent_feedback),
            "improvement_areas": self._identify_improvement_areas(recent_feedback)
        }

        return analysis

    def _calculate_average_rating(self, feedback_list):
        """Calculate the average rating from explicit feedback"""
        explicit_feedback = [f for f in feedback_list
                            if "explicit_feedback" in f]
        if not explicit_feedback:
            return None

        ratings = [f["explicit_feedback"]["rating"] for f in explicit_feedback]
        return sum(ratings) / len(ratings)

    def _identify_issue_categories(self, feedback_list):
        """Identify common issues from feedback"""
        # Implementation would categorize feedback comments
        pass

    def _identify_improvement_areas(self, feedback_list):
        """Identify areas for model improvement"""
        # Implementation would analyze patterns in low-rated interactions
        pass

Bringing It All Together: The Interplay of MCP Components

The true power of MCP comes when Resources, Tools, Prompts, and Sampling work together. Let’s look at an example of a simplified research assistant that finds, summarizes, and extracts key information from research papers:

import asyncio
import json
import logging
import requests
from pydantic import BaseModel, HttpUrl, validator
from datetime import datetime

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

# Define data models for validation
class Paper(BaseModel):
    url: HttpUrl
    title: str

    @validator('url')
    def url_must_be_from_papers_domain(cls, url):
        if "papers.example.com" not in str(url):
            raise ValueError(
                "URL must be from papers.example.com"
            )
        return url

class SummaryResult(BaseModel):
    summary: str
    keywords: list[str]

# Resource: Paper database (simulated)
PAPER_DATABASE = [
    {
        "url": "https://papers.example.com/mcp_paper",
        "title": "Mastering the Model Context Protocol"
    }
]

# Tool URLs
SUMMARIZE_TOOL_URL = (
    "https://summarization-service.example.com"
    "/summarize"
)

KEYWORD_TOOL_URL = (
    "https://keyword-extraction-service.example.com"
    "/keywords"
)

# Prompts
SUMMARY_PROMPT = (
    "Summarize the following document:\n"
    "{document}"
)

KEYWORD_PROMPT = (
    "Extract keywords from the following\n"
    "document: {document}"
)

async def access_resource(topic: str) -> list[Paper]:
    """Simulates accessing a resource."""
    logging.info(f"Accessing resource: {topic}")
    await asyncio.sleep(0.1)  # Simulate I/O delay
    papers = [Paper(**paper) for paper in PAPER_DATABASE]
    return papers

async def execute_tool(
    tool_url: str,
    prompt: str
) -> dict:
    """Executes a tool."""
    logging.info(f"Executing tool: {tool_url}")
    try:
        response = requests.post(
            tool_url,
            json={"prompt": prompt},
            timeout=10
        )
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        logging.error(f"Tool error: {e}")
        raise

async def research_assistant(
    topic: str
) -> SummaryResult:
    """Research assistant using MCP components."""
    try:
        # 1. Access papers (Resource)
        papers = await access_resource(topic)
        if not papers:
            return SummaryResult(
                summary="No papers found.",
                keywords=[]
            )

        # 2. Summarize first paper (Tool)
        first_paper = papers[0]
        summary_prompt = SUMMARY_PROMPT.format(
            document=first_paper.title
        )
        summary_data = await execute_tool(
            SUMMARIZE_TOOL_URL,
            summary_prompt
        )
        summary = summary_data.get("summary", "")

        # 3. Extract keywords (Tool)
        keyword_prompt = KEYWORD_PROMPT.format(
            document=first_paper.title
        )
        keyword_data = await execute_tool(
            KEYWORD_TOOL_URL,
            keyword_prompt
        )
        keywords = keyword_data.get("keywords", [])

        # 4. Collect feedback (Sampling)
        await collect_feedback(
            "user123",
            {
                "summary": summary,
                "keywords": keywords
            }
        )

        return SummaryResult(
            summary=summary,
            keywords=keywords
        )
    except Exception as e:
        logging.exception("Research assistant error")
        raise

async def collect_feedback(
    user_id: str,
    result: dict
) -> None:
    """Feedback collection for continuous improvement."""
    logging.info(f"Collecting feedback from user {user_id}")

    # In a real application, this would store feedback in a database
    # or send it to an analytics service
    feedback_data = {
        "user_id": user_id,
        "result": result,
        "timestamp": datetime.now().isoformat(),
        "feedback_type": "implicit"  # No explicit rating collected yet
    }

    # Here we would typically store the feedback for later analysis
    logging.info(f"Feedback recorded: {json.dumps(feedback_data, indent=2)}")

    return feedback_data

if __name__ == "__main__":
    async def main():
        try:
            result = await research_assistant(
                "MCP Protocol"
            )
            print(result.json(indent=4))
        except Exception as e:
            logging.error(f"Main error: {e}")

    asyncio.run(main())

This example shows the seamless integration of MCP’s core components:

  1. Resources provide access to research papers.
  2. Tools process the papers through summarization and keyword extraction.
  3. Prompts guide the AI’s behavior when generating summaries and extracting keywords.
  4. Sampling collects feedback on the results for future improvements.

Conclusion: The Future of AI with MCP

The Model Context Protocol provides a complete framework for building intelligent, adaptable AI applications. By standardizing how AI systems access data, perform actions, generate responses, and learn from feedback, MCP allows for the development of more powerful and flexible AI solutions.

The key advantages of using MCP include:

  • Modularity: Each component can be developed independently.
  • Standardization: Consistent interfaces simplify development.
  • Flexibility: It is easy to add new capabilities or data sources.
  • Continuous Improvement: Built-in feedback mechanisms allow for learning.

As AI continues to evolve, frameworks like MCP will be increasingly important for managing the complexity of AI systems while maximizing their capabilities. By understanding and implementing the four core concepts—Resources, Tools, Prompts, and Sampling—developers can create AI applications that not only think but also act and learn in the world.

Whether you are building customer service chatbots, research assistants, document management systems, or any other AI-powered application, MCP provides the architectural foundation for success. The future of AI is not just about smarter models. It is about creating ecosystems where those models can access information, take actions, and continuously improve through feedback. MCP is a big step toward that future.

Glossary of Key Terms

  • MCP (Model Context Protocol): A standardized framework for AI systems to access data, perform actions, generate responses, and learn from feedback.
  • Resource: A standardized gateway to a data source (database, filesystem, API) that is accessible via the MCP protocol.
  • Resource URL: A URL format for addressing and accessing Resources in MCP (e.g., database://my_database/customers?api_key=KEY).
  • Tool: An executable function that is accessible through MCP and allows AI agents to perform actions.
  • Prompt Template: A structured instruction with placeholders that can be filled in dynamically to guide AI model behavior.
  • Prompt Engineering: The practice of designing effective prompts for AI models to get the best responses.
  • Sampling: The MCP component that allows for two-way communication through content generation and feedback collection.
  • Feedback Loop: A cycle where AI outputs are evaluated, feedback is collected, the model is updated, and new outputs are generated.

If you would like more details, check out this chapter in this book.

About the Author

Rick Hightower is a seasoned technologist and AI expert with extensive experience in software development and AI. As a prolific writer and thought leader in the tech industry, he regularly shares insights about artificial intelligence, software architecture, and emerging technologies through his articles and publications.

His work focuses on making complex technical concepts accessible to developers and technology professionals, particularly in the areas of AI implementation and software development best practices. Rick maintains an active presence in the tech community through his writing, speaking engagements, and collaborative projects.

Connect with Rick:

  • Articles: Regular publications on AI and software development.
  • Expertise: Artificial Intelligence, Data Engineering, Software Architecture, ETL, Event-driven software / streaming, Cloud Computing.
                                                                           
comments powered by Disqus

Apache Spark Training
Kafka Tutorial
Akka Consulting
Cassandra Training
AWS Cassandra Database Support
Kafka Support Pricing
Cassandra Database Support Pricing
Non-stop Cassandra
Watchdog
Advantages of using Cloudurable™
Cassandra Consulting
Cloudurable™| Guide to AWS Cassandra Deploy
Cloudurable™| AWS Cassandra Guidelines and Notes
Free guide to deploying Cassandra on AWS
Kafka Training
Kafka Consulting
DynamoDB Training
DynamoDB Consulting
Kinesis Training
Kinesis Consulting
Kafka Tutorial PDF
Kubernetes Security Training
Redis Consulting
Redis Training
ElasticSearch / ELK Consulting
ElasticSearch Training
InfluxDB/TICK Training TICK Consulting