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.
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:
- Resources: Standardized gateways to various data sources.
- Tools: Functions that allow AI to take actions.
- Prompts: Templates that guide AI behavior.
- 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:
- Discovery: The AI learns what Tools are available.
- Parameter Preparation: The AI prepares the necessary inputs.
- Invocation: The AI sends a request to execute the Tool.
- Execution: The MCP server runs the Tool.
- 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:
- Content Generation: MCP sampling allows servers to request LLM completions from clients, which enables dynamic content generation.
- 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:
- Output Generation: The AI produces an output.
- Evaluation: The output is evaluated (by humans or metrics).
- Signal Processing: The evaluation is turned into a learning signal.
- Model Update: The model adjusts based on the learning signal.
- 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:
- Resources provide access to research papers.
- Tools process the papers through summarization and keyword extraction.
- Prompts guide the AI’s behavior when generating summaries and extracting keywords.
- 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.
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