Introduction to Model Context Protocol - Certification Study Notes
Table of Contents
- Introduction to Model Context Protocol - Certification Study Notes
- Module 1: Introduction to MCP
- Module 2: Hands-On MCP Server Development
- Module 3: Connecting with MCP Clients
- Module 4: Assessment Concepts
- Key Learning Objectives Summary
- Practice Exercises
- Additional Resources
- Study Tips for Certification
Introduction to Model Context Protocol - Certification Study Notes
Course: Anthropic’s “Introduction to Model Context Protocol” Date: April 2026 Difficulty: Intermediate Focus: Building MCP servers and clients, understanding protocol fundamentals
Module 1: Introduction to MCP
What is MCP?
Model Context Protocol (MCP) is an open-source standard for connecting AI applications to external systems. Think of it like USB-C for AI — providing a standardized interface for AI apps (like Claude) to access data, tools, and workflows.
Core Purpose:
- Enable AI applications to connect to data sources (databases, files, APIs)
- Allow AI to invoke actions via tools (execute functions, make API calls)
- Provide interaction templates via prompts (pre-crafted instructions)
Ecosystem Support: MCP is supported by Claude, ChatGPT, VS Code, Cursor, and many other tools. Build once, integrate everywhere.
Why MCP Matters
For Developers: Reduced complexity when integrating with AI applications. Standard interfaces eliminate custom integrations.
For AI Applications: Access to diverse data, tools, and workflows enhances capabilities and user experience.
For End Users: More capable AI assistants that can access their data and take actions securely.
MCP Architecture Diagram (ASCII)
┌─────────────────────────────────────────────────────────────┐
│ MCP HOST (AI Application) │
│ (Claude, ChatGPT, VS Code) │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ MCP Client Manager │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐│ │
│ │ │ MCP Client 1 │ │ MCP Client 2 │ │MCP Client N││ │
│ │ └──────┬───────┘ └──────┬───────┘ └──────┬──────┘│ │
│ └─────────┼──────────────────┼──────────────────┼──────┘ │
│ │ │ │ │
└────────────┼──────────────────┼──────────────────┼─────────┘
│ │ │
┌────────▼────────┐ ┌───────▼───────┐ ┌──────▼─────────┐
│ MCP Server A │ │ MCP Server B │ │ MCP Server C │
│ (Local STDIO) │ │(Local STDIO) │ │(Remote HTTP) │
│ │ │ │ │ │
│ - Tools │ │ - Tools │ │ - Tools │
│ - Resources │ │ - Resources │ │ - Resources │
│ - Prompts │ │ - Prompts │ │ - Prompts │
└─────────────────┘ └────────────────┘ └────────────────┘
Each MCP Client maintains a dedicated connection to its server.
Key Participants
- MCP Host — The AI application coordinating connections (Claude, VS Code, etc.)
- MCP Client — Component maintaining connection to a server (one per server)
- MCP Server — Program providing context (runs locally or remotely)
The 3 Core Primitives
MCP defines three fundamental primitives that servers expose to clients:
1. Tools — Model-Controlled Actions
What they are: Executable functions that the AI can invoke to perform actions.
Who controls: The language model (with user approval).
Analogy: Like POST endpoints in REST APIs — model requests execution.
When to use:
- Actions that require LLM reasoning
- Operations the AI should execute based on context
- Anything that modifies external state
How they work:
- Server defines tools with JSON Schema
- Client lists available tools
- LLM decides which tools to call
- Client executes the tool
- Results returned to LLM
Example Use Cases:
- Database queries
- API calls
- File operations
- Calculations
- External service integrations
2. Resources — Application-Controlled Data
What they are: File-like data sources that clients can read (static or templated).
Who controls: The application/server (client reads only).
Analogy: Like GET endpoints — immutable information lookup.
When to use:
- Configuration data
- Reference information
- Database schemas
- API documentation
- Static files
How they work:
- Server defines resources (with optional URI templates)
- Client lists available resources
- Client reads specific resources by URI
- Server returns data with MIME type
Example Use Cases:
- Database schema documentation
- Configuration files
- API reference materials
- Log files
- Cached data
3. Prompts — User-Controlled Templates
What they are: Pre-written instruction templates for accomplishing specific tasks.
Who controls: The user (selects which prompt to use).
Analogy: Like workflow templates — user-driven structured interactions.
When to use:
- Standardized workflows
- Expert prompts for specific domains
- Few-shot examples
- System prompts for specialized tasks
How they work:
- Server defines prompts with optional arguments
- Client lists available prompts
- User selects a prompt
- Server returns the prompt template
- User/LLM uses it as context
Example Use Cases:
- Code review prompts
- Writing assistance workflows
- Domain-specific expert prompts
- Standardized analysis templates
Comparison Table: Tools vs Resources vs Prompts
| Aspect | Tools | Resources | Prompts |
|---|---|---|---|
| Who Controls | LLM (automated) | Application (read-only) | User (interactive) |
| Analogy | POST endpoint | GET endpoint | Template library |
| Example | Calculate sum | Fetch schema | Code review guide |
| Execution | Automatic (LLM decides) | On-demand read | User-selected |
| State Change | Can modify external state | Read-only | No direct action |
| Discovery | tools/list | resources/list | prompts/list |
| Invocation | tools/call | resources/read | prompts/get |
Best Practices
- Protocol Version Negotiation — Both client and server must agree on compatible versions during initialization
- Capability Discovery — Clients should discover what features servers support (tools, resources, prompts, notifications)
- Error Handling — Always validate responses and handle connection failures gracefully
- Logging Strategy — Use stderr for STDIO servers, never write to stdout
- Security — Validate all external inputs, use proper authentication mechanisms
Module 2: Hands-On MCP Server Development
Project Setup
System Requirements
- Python 3.10+ (or equivalent Node.js/TypeScript)
uvpackage manager (ornpmfor Node.js)- Basic understanding of async/await patterns
Quick Start (Python)
# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# Create project
uv init my-mcp-server
cd my-mcp-server
# Set up environment
uv venv
source .venv/bin/activate
# Install MCP SDK
uv add "mcp[cli]" httpx
Building a Complete MCP Server
Core Concepts
FastMCP Framework:
- Simplifies MCP server development using Python decorators
- Automatically generates JSON Schema from type hints
- Handles JSON-RPC protocol complexity
- Supports async/await natively
Basic Server Structure
from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP
# Initialize FastMCP server
mcp = FastMCP("my-server")
# Constants
API_BASE = "https://api.example.com"
USER_AGENT = "my-server/1.0"
def main():
# Start the server
mcp.run(transport="stdio")
if __name__ == "__main__":
main()
Key Points:
FastMCPtakes a server name as argumenttransport="stdio"uses standard input/output- The server handles all JSON-RPC protocol details automatically
Defining Tools
Tools are the most common primitive. They enable the LLM to execute actions.
Tool Definition Using Decorators
@mcp.tool()
async def add(a: int, b: int) -> int:
"""Add two numbers together.
Args:
a: First number
b: Second number
Returns:
The sum of a and b
"""
return a + b
@mcp.tool()
async def get_weather(latitude: float, longitude: float) -> str:
"""Get weather forecast for a location.
Args:
latitude: Latitude of location (-90 to 90)
longitude: Longitude of location (-180 to 180)
Returns:
Weather forecast as formatted string
"""
url = f"https://api.weather.gov/points/{latitude},{longitude}"
async with httpx.AsyncClient() as client:
response = await client.get(url)
data = response.json()
forecast_url = data["properties"]["forecast"]
forecast_response = await client.get(forecast_url)
forecast_data = forecast_response.json()
periods = forecast_data["properties"]["periods"]
result = []
for period in periods[:5]:
result.append(f"{period['name']}: {period['detailedForecast']}")
return "\n".join(result)
Decorator Mechanics:
@mcp.tool()registers the function as a callable tool- Type hints become JSON Schema constraints
- Docstring becomes tool description
- Parameter descriptions in docstring become JSON Schema
- Return type defines output schema
Important Rules:
- Tool names must be valid identifiers (lowercase_with_underscores)
- All parameters must have type hints
- Docstrings are mandatory (describe purpose and parameters)
- Return values should be simple types (str, int, float) or JSON-serializable
Real-World Tool Example: Weather Server
async def make_nws_request(url: str) -> dict[str, Any] | None:
"""Make request to National Weather Service API with error handling."""
headers = {
"User-Agent": "weather-app/1.0",
"Accept": "application/geo+json"
}
async with httpx.AsyncClient() as client:
try:
response = await client.get(url, headers=headers, timeout=30.0)
response.raise_for_status()
return response.json()
except Exception:
return None
def format_alert(feature: dict) -> str:
"""Format alert feature into readable string."""
props = feature["properties"]
return f"""
Event: {props.get("event", "Unknown")}
Area: {props.get("areaDesc", "Unknown")}
Severity: {props.get("severity", "Unknown")}
Description: {props.get("description", "No description available")}
Instructions: {props.get("instruction", "No instructions provided")}
"""
@mcp.tool()
async def get_alerts(state: str) -> str:
"""Get weather alerts for a US state.
Args:
state: Two-letter US state code (e.g., CA, NY, TX)
Returns:
Formatted list of active weather alerts
"""
NWS_API_BASE = "https://api.weather.gov"
url = f"{NWS_API_BASE}/alerts/active/area/{state}"
data = await make_nws_request(url)
if not data or "features" not in data:
return "Unable to fetch alerts or no alerts found."
if not data["features"]:
return "No active alerts for this state."
alerts = [format_alert(feature) for feature in data["features"]]
return "\n---\n".join(alerts)
@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
"""Get weather forecast for a location.
Args:
latitude: Latitude of the location (range: -90 to 90)
longitude: Longitude of the location (range: -180 to 180)
Returns:
5-period weather forecast
"""
NWS_API_BASE = "https://api.weather.gov"
# Get forecast grid endpoint
points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
points_data = await make_nws_request(points_url)
if not points_data:
return "Unable to fetch forecast data for this location."
# Get actual forecast
forecast_url = points_data["properties"]["forecast"]
forecast_data = await make_nws_request(forecast_url)
if not forecast_data:
return "Unable to fetch detailed forecast."
# Format periods
periods = forecast_data["properties"]["periods"]
forecasts = []
for period in periods[:5]:
forecast = f"""{period["name"]}:
Temperature: {period["temperature"]}°{period["temperatureUnit"]}
Wind: {period["windSpeed"]} {period["windDirection"]}
Forecast: {period["detailedForecast"]}"""
forecasts.append(forecast)
return "\n---\n".join(forecasts)
Defining Resources
Resources expose read-only data to clients.
Resource Definition Using Decorators
@mcp.resource("config://app")
def get_config() -> str:
"""Get application configuration.
Returns:
Current configuration as formatted string
"""
config = {
"version": "1.0.0",
"debug": True,
"timeout": 30,
"max_retries": 3
}
return "\n".join(f"{k}: {v}" for k, v in config.items())
@mcp.resource("schema://database")
def get_schema() -> str:
"""Get database schema documentation.
Returns:
Schema definition and table descriptions
"""
schema = """
DATABASE SCHEMA
================
Tables: users, products, orders
USERS TABLE:
- id (INTEGER PRIMARY KEY)
- name (VARCHAR(255))
- email (VARCHAR(255) UNIQUE)
- created_at (TIMESTAMP)
PRODUCTS TABLE:
- id (INTEGER PRIMARY KEY)
- name (VARCHAR(255))
- price (DECIMAL(10, 2))
- inventory (INTEGER)
ORDERS TABLE:
- id (INTEGER PRIMARY KEY)
- user_id (INTEGER FOREIGN KEY)
- product_id (INTEGER FOREIGN KEY)
- quantity (INTEGER)
- order_date (TIMESTAMP)
"""
return schema
@mcp.resource("logs://app/{date}")
def get_logs(date: str) -> str:
"""Get application logs for specific date.
Args:
date: Date in YYYY-MM-DD format
Returns:
Log entries for that date
"""
# In real implementation, read from file or database
return f"Logs for {date}:\n[2026-04-24 10:00] Server started\n[2026-04-24 10:05] Request processed"
Resource URI Format:
config://app— Read configurationschema://database— Read database schemalogs://app/{date}— Templated resource with parameters
Key Differences from Tools:
- Resources are read-only (no side effects)
- Parameters are in URI, not function arguments
- Return values should be strings or JSON-serializable data
- Clients don’t require “approval” to read resources
Defining Prompts
Prompts are user-controlled interaction templates.
Prompt Definition Using Decorators
@mcp.prompt()
def code_review_prompt(code: str) -> str:
"""Code review expert prompt.
Args:
code: Code snippet to review
Returns:
Expert code review prompt template
"""
return f"""You are an expert code reviewer. Review the following code for:
1. Performance issues
2. Security vulnerabilities
3. Code style and readability
4. Potential bugs
5. Best practices
CODE TO REVIEW:
{code}
Provide detailed feedback with suggestions for improvement."""
@mcp.prompt()
def writing_assistant_prompt(topic: str) -> str:
"""Writing assistant prompt for blog posts.
Args:
topic: Blog topic
Returns:
Structured writing prompt
"""
return f"""You are a professional blog writer. Write a comprehensive blog post about "{topic}" with:
1. Engaging introduction (2-3 paragraphs)
2. Clear main sections (4-5 major points)
3. Real-world examples and case studies
4. Actionable takeaways
5. Compelling conclusion
Target audience: Technical professionals
Tone: Informative yet accessible
Length: 1500-2000 words"""
@mcp.prompt()
def sql_expert_prompt(question: str, schema: str) -> str:
"""SQL query expert prompt.
Args:
question: Natural language question
schema: Database schema information
Returns:
Expert SQL prompt
"""
return f"""You are a SQL expert. Answer the following question by writing an efficient SQL query.
DATABASE SCHEMA:
{schema}
QUESTION: {question}
Provide:
1. The SQL query
2. Explanation of how it works
3. Performance considerations
4. Alternative approaches if applicable"""
Key Characteristics:
- Accept parameters from users
- Return formatted instruction strings
- Help structure LLM interactions
- No side effects (purely informational)
Server Inspector for Debugging
The MCP Inspector is a development tool for testing servers without building a full client.
Starting the Inspector
# Install MCP CLI tools
uv add mcp
# Start inspector for your server
mcp run server.py
Inspector Features
- Tool Discovery — Lists all available tools with schemas
- Tool Testing — Execute tools with test parameters
- Resource Access — Browse and read resources
- Protocol Debugging — View JSON-RPC messages
- Error Analysis — See detailed error responses
Example Inspector Session
MCP Server Inspector
====================
Available Tools:
1. get_alerts (state: str) -> str
2. get_forecast (latitude: float, longitude: float) -> str
Available Resources:
1. config://app
2. schema://database
3. logs://app/{date}
Available Prompts:
1. code_review_prompt
2. writing_assistant_prompt
3. sql_expert_prompt
>> Tool: get_alerts
Parameter: state = "CA"
Result: Event: Winter Storm...
Severity: Moderate...
>> Resource: config://app
Result: version: 1.0.0
debug: True...
Best Practices for Servers
- Error Handling — Always catch exceptions and return meaningful error messages
- Type Hints — Use precise type hints for automatic schema generation
- Documentation — Write clear docstrings (becomes user-facing)
- Logging — Use stderr for STDIO servers, never stdout
- Async/Await — Use async properly for I/O-bound operations
- Resource Cleanup — Close connections, clean up files
- Input Validation — Validate parameters before processing
- Timeout Handling — Set reasonable timeouts on external API calls
Running the Server
def main():
# With stdio transport (for Claude Desktop, local clients)
mcp.run(transport="stdio")
# Or with SSE transport (for HTTP-based clients)
# mcp.run(transport="sse", host="localhost", port=8000)
if __name__ == "__main__":
main()
Module 3: Connecting with MCP Clients
Building an MCP Client
System Requirements
- Python 3.8+ or Node.js 16+
- Anthropic API key for Claude integration
- Understanding of async programming
Client Setup (Python)
# Create project
uv init mcp-client
cd mcp-client
# Set up environment
uv venv
source .venv/bin/activate
# Install dependencies
uv add mcp anthropic python-dotenv
Complete Python Client Example
Basic Client Structure
import asyncio
from typing import Optional
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from anthropic import Anthropic
from dotenv import load_dotenv
load_dotenv()
class MCPClient:
def __init__(self):
self.session: Optional[ClientSession] = None
self.exit_stack = AsyncExitStack()
self.anthropic = Anthropic()
async def connect_to_server(self, server_script_path: str):
"""Connect to an MCP server via stdio transport.
Args:
server_script_path: Path to server script (.py or .js)
"""
is_python = server_script_path.endswith('.py')
is_js = server_script_path.endswith('.js')
if not (is_python or is_js):
raise ValueError("Server script must be .py or .js")
command = "python" if is_python else "node"
# Create server parameters
server_params = StdioServerParameters(
command=command,
args=[server_script_path],
env=None
)
# Set up transport
stdio_transport = await self.exit_stack.enter_async_context(
stdio_client(server_params)
)
self.stdio, self.write = stdio_transport
# Create session
self.session = await self.exit_stack.enter_async_context(
ClientSession(self.stdio, self.write)
)
# Initialize connection
await self.session.initialize()
# List available tools
response = await self.session.list_tools()
tools = response.tools
print(f"\nConnected with tools: {[tool.name for tool in tools]}")
Query Processing Logic
async def process_query(self, query: str) -> str:
"""Process a query using Claude and available MCP tools.
Args:
query: User's natural language query
Returns:
Final response from Claude
"""
messages = [
{
"role": "user",
"content": query
}
]
# Get available tools
response = await self.session.list_tools()
available_tools = [{
"name": tool.name,
"description": tool.description,
"input_schema": tool.inputSchema
} for tool in response.tools]
# Initial Claude API call
response = self.anthropic.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1000,
messages=messages,
tools=available_tools
)
# Process response and handle tool calls
final_text = []
for content in response.content:
if content.type == 'text':
final_text.append(content.text)
elif content.type == 'tool_use':
tool_name = content.name
tool_args = content.input
# Execute tool through MCP server
result = await self.session.call_tool(tool_name, tool_args)
final_text.append(f"[Executed {tool_name}]")
# Build tool result message
messages.append({
"role": "assistant",
"content": [content]
})
messages.append({
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": content.id,
"content": result.content
}
]
})
# Get Claude's response to tool result
response = self.anthropic.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1000,
messages=messages,
tools=available_tools
)
final_text.append(response.content[0].text)
return "\n".join(final_text)
Interactive Chat Loop
async def chat_loop(self):
"""Run interactive chat interface."""
print("\nMCP Client Started!")
print("Type your queries or 'quit' to exit.\n")
while True:
try:
query = input("Query: ").strip()
if query.lower() == 'quit':
break
response = await self.process_query(query)
print(f"\n{response}\n")
except Exception as e:
print(f"\nError: {str(e)}\n")
async def cleanup(self):
"""Clean up resources."""
await self.exit_stack.aclose()
Main Entry Point
async def main():
if len(sys.argv) < 2:
print("Usage: python client.py <path_to_server_script>")
sys.exit(1)
client = MCPClient()
try:
await client.connect_to_server(sys.argv[1])
await client.chat_loop()
finally:
await client.cleanup()
if __name__ == "__main__":
import sys
asyncio.run(main())
Running the Client
# Run with Python server
uv run client.py ./server/weather.py
# Run with Node.js server
uv run client.py ./server/build/index.js
# With absolute path
uv run client.py /Users/username/mcp-servers/weather.py
Transport Mechanisms
MCP supports two transport protocols:
1. Stdio Transport (Local)
Use When:
- Server and client on same machine
- Developing/testing locally
- Using Claude Desktop
- Low-latency required
Advantages:
- Zero network overhead
- Optimal performance
- Simple process management
- No authentication needed
Implementation:
from mcp.client.stdio import stdio_client
from mcp import StdioServerParameters
server_params = StdioServerParameters(
command="python",
args=["server.py"]
)
transport = await stdio_client(server_params)
2. Streamable HTTP Transport (Remote)
Use When:
- Server runs remotely
- Multiple clients accessing one server
- Cloud deployment required
- Need HTTP authentication
Advantages:
- Remote server support
- Multiple concurrent clients
- Standard HTTP authentication
- Scalable architecture
Supports:
- Bearer token authentication
- API key authentication
- OAuth (recommended)
- Custom headers
Implementation:
from mcp.client.sse import sse_client
session = SSEClientSession(
uri="https://mcp-server.example.com",
auth=BearerToken("your-api-key")
)
await session.initialize()
tools = await session.list_tools()
Client Best Practices
- Error Handling — Wrap tool calls in try-catch, handle timeouts
- Resource Management — Use async context managers for cleanup
- Tool Validation — Validate tool names match server’s tools
- Message Flow — Build proper message history for context
- Security — Store API keys in environment variables
- Testing — Test with MCP Inspector before building client
Module 4: Assessment Concepts
Understanding Protocol Flow
Complete Request-Response Cycle
CLIENT SERVER
| |
|------ initialize ------> |
| |
|<----- initialize result -----|
| |
|------ tools/list ----> |
| |
|<----- tools result ---------|
| |
|------ tools/call ------> |
| (execute get_weather) |
| |
| [processing...]
| |
|<----- tool result ----------|
| |
JSON-RPC 2.0 Message Format
Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}
Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "get_weather",
"description": "Get weather for location",
"inputSchema": {
"type": "object",
"properties": {
"location": {"type": "string"}
},
"required": ["location"]
}
}
]
}
}
Integration with Claude Code
Claude Code MCP Configuration (settings.json)
{
"mcpServers": {
"weather": {
"command": "python",
"args": ["/absolute/path/to/weather.py"],
"env": {
"API_KEY": "your-key"
}
},
"database": {
"command": "uv",
"args": ["--directory", "/path/to/db-server", "run", "db.py"]
},
"remote-service": {
"url": "https://api.example.com/mcp",
"auth": "bearer",
"token": "your-token"
}
}
}
Key Configuration Points:
command— How to start the server (python, node, uv, etc.)args— Command-line argumentsenv— Environment variables for the serverurl— For remote (HTTP) serversauth— Authentication type (bearer, api-key, oauth)
Security Considerations
- Input Validation — Always validate parameters before processing
- API Key Management — Use environment variables, never hardcode
- HTTPS for Remote Servers — Always use TLS for HTTP transport
- Authentication — Implement proper auth (OAuth, bearer tokens)
- Error Messages — Don’t expose sensitive info in error responses
- Logging — Be careful not to log API keys or credentials
- Tool Permissions — Consider which tools users should access
Common Pitfalls and Solutions
| Pitfall | Problem | Solution |
|---|---|---|
| stdout logging | Corrupts JSON-RPC messages in STDIO | Use stderr or logging library |
| Type hint missing | Auto-schema fails, tools not registered | Add type hints to all parameters |
| Docstring missing | No description in tool list | Always write docstrings |
| Unhandled exceptions | Server crashes silently | Wrap code in try-catch |
| Missing await | Async function not executed | Use await for all async calls |
| Hardcoded paths | Breaks on different machines | Use absolute paths or relative to cwd |
| No timeouts | Hanging requests | Set timeouts on external API calls |
| Resource leaks | Files/connections not closed | Use context managers |
| Invalid JSON | Protocol errors | Ensure all responses are JSON-serializable |
| Tool name conflicts | Tools shadow each other | Use unique, descriptive names |
Debugging Tips
- Use MCP Inspector — Test tools without building full client
- Enable Logging — Log to stderr to see execution flow
- Test Schemas — Validate tool input schemas with sample data
- Check Types — Ensure return values match declared types
- Test Edge Cases — Empty inputs, null values, large data
- Monitor Performance — Check timeout and response times
- Verify Connections — Test both stdio and HTTP transports
- Review Messages — Examine JSON-RPC messages for errors
Key Learning Objectives Summary
By completing this course, you should understand:
✓ MCP architecture and how clients connect to servers ✓ The 3 core primitives: Tools, Resources, Prompts ✓ When and how to use each primitive ✓ Building MCP servers with FastMCP (decorators, type hints) ✓ Implementing tools, resources, and prompts ✓ Testing servers with MCP Inspector ✓ Building MCP clients that integrate with Claude ✓ Handling tool execution and message flows ✓ Transport mechanisms (STDIO vs HTTP) ✓ Security considerations and best practices ✓ Debugging and troubleshooting servers and clients ✓ Integrating MCP with Claude Code (settings.json configuration)
Practice Exercises
Exercise 1: Build a Simple Calculator Server
Create an MCP server with tools for:
add(a, b)— Add two numbersmultiply(a, b)— Multiply two numbersdivide(a, b)— Divide two numbers (with error handling for zero)
Include a resource config://calculator that returns calculator settings.
Exercise 2: Build a Client for Calculator Server
Create a client that:
- Connects to the calculator server
- Lists available tools
- Allows user to input calculations
- Executes tools and displays results
- Handles errors gracefully
Exercise 3: Extend Weather Server with Prompts
Add prompts to the weather server:
severe_weather_alert— Analyzes severe weather and suggests precautionstravel_planning— Suggests travel timing based on forecasts
Exercise 4: Test with Claude Desktop
- Build a simple MCP server (any domain)
- Configure Claude Desktop with
claude_desktop_config.json - Test server integration with Claude
- Verify tools appear in Claude interface
- Execute tools from Claude conversation
Additional Resources
Official Documentation:
- https://modelcontextprotocol.io/introduction
- https://docs.anthropic.com/en/docs/claude-code/mcp
- https://modelcontextprotocol.io/quickstart/server
- https://modelcontextprotocol.io/quickstart/client
- https://modelcontextprotocol.io/docs/concepts/architecture
Reference Implementations:
- Weather Server: https://github.com/modelcontextprotocol/quickstart-resources
- Official Servers: https://github.com/modelcontextprotocol/servers
- MCP Inspector: https://github.com/modelcontextprotocol/inspector
Related Concepts to Study:
- JSON-RPC 2.0 specification
- Async/await patterns
- REST API design principles
- Protocol buffers and serialization
- Process management and IPC
Study Tips for Certification
- Build First, Then Integrate — Create a simple server before testing with Claude
- Use the Inspector — Test every tool with MCP Inspector before deploying
- Read the Spec — Understand JSON-RPC and protocol details
- Practice Error Handling — Test with invalid inputs, network errors
- Review Type Hints — Understand how Python type hints map to JSON Schema
- Study Async Patterns — Master async/await for I/O operations
- Test Transport Mechanisms — Try both STDIO and HTTP transports
- Review Security — Understand authentication and input validation
- Build Real Servers — Connect to actual APIs and services
- Document Everything — Write clear docstrings and comments
Last Updated: April 2026 Course Status: Available Certification Available: Yes