A hands-on workshop that teaches you how to build Model Context Protocol (MCP) clients by progressively enhancing a simple chat application. Learn how to integrate tools, resources, and prompts to create intelligent applications that can interact with external systems.
This workshop demonstrates how to transform a basic Claude chat agent into a full-featured MCP client that can:
- Connect to MCP servers via stdio
- Discover and use server-provided tools
- Load and inject contextual resources
- Apply prompt templates for behavior guidance
- Implement the agentic loop pattern
- Python 3.12 or higher
- An Anthropic API key (get one here)
- Basic familiarity with Python and async/await
This project uses uv for fast, reliable Python dependency management.
# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
# Or with pip
pip install uvgit clone <repository-url>
cd making-apps-intelligentuv sync# Copy the example environment file
cp .env.example .env
# Edit .env and add your Anthropic API key
# ANTHROPIC_API_KEY=your_api_key_hereThe workshop is organized into progressive steps, each building on the previous one:
making-apps-intelligent/
├── 00_base_application/ # Simple chat agent (baseline)
├── 01_create_client_module/ # MCP client with connection lifecycle
├── 02_supporting_tools/ # Tool discovery and execution
├── 03_supporting_resources/ # Resource loading and context injection
├── 04_supporting_prompts/ # Prompt templates and complete integration
├── calculator_server.py # Example MCP server for testing
├── pyproject.toml # Project dependencies
└── .env.example # API key template
The calculator_server.py is a simple MCP server that provides:
- Tools: Mathematical operations (add, subtract, multiply, divide)
- Resources: Math constants (pi, e, golden ratio) and formulas
- Prompts: Templates for step-by-step problem solving
This server allows you to test all MCP client functionality without needing external services.
What's New:
- Simple CLI chat interface
- Conversation history management
- Basic Claude API integration
Concepts Implemented:
- Synchronous message exchange with Claude
- User input/output handling
- Conversation context maintenance
How to Run:
uv run python 00_base_application/agent.pyExample Queries to Try:
You: Hello! How are you?
You: What is the capital of France?
You: Can you explain what gravity is?
What to Notice:
- The agent maintains conversation history
- No access to tools or external knowledge
- Simple request/response pattern
What's New:
MCPClientclass with connection management- Async/await pattern implementation
- Stdio server connection setup
Concepts Implemented:
- MCP client initialization and lifecycle
- AsyncExitStack for resource cleanup
- Subprocess-based server communication
- Session handshake and initialization
How to Run:
uv run python 01_create_client_module/agent.pyExample Queries to Try:
You: Hello! Can you help me with math?
You: What can you do?
What to Notice:
- Application converts to async (asyncio.run)
- Client connects to calculator server on startup
- Graceful disconnect on exit
- No functional difference yet - tools not used
What's New:
get_available_tools()- Discover server capabilitiesuse_tool()- Execute tools and handle results- Agentic loop pattern implementation
Concepts Implemented:
- Tool discovery and formatting for Claude API
- Tool execution via MCP protocol
- Agentic loop (Claude decides when to use tools)
- Multi-turn tool use conversations
- Content type handling (text, images, resources)
How to Run:
uv run python 02_supporting_tools/agent.pyExample Queries to Try:
You: What is 25 multiplied by 17?
You: Calculate (45 + 67) * 3
You: If I have 1000 dollars and spend 234, how much is left?
You: What's 15 divided by 3, then add 10?
What to Notice:
- Server tools are displayed on startup
[Using tool: ...]messages show tool execution- Claude decides when tools are needed
- Multiple tools can be used in sequence
- Results are integrated into Claude's response
What's New:
get_available_resources()- List available dataget_resource(uri)- Fetch resource content- Resource context injection pattern
Concepts Implemented:
- Resource discovery and metadata retrieval
- URI-based resource access
- Context injection into user messages
- Multi-modal content (text and images)
- Difference between tools (agentic) and resources (context)
How to Run:
uv run python 03_supporting_resources/agent.pyExample Queries to Try:
You: What is the value of pi?
You: Tell me about the golden ratio
You: What's the quadratic formula?
You: How does Euler's number relate to compound interest?
What to Notice:
- Resources are loaded before each query
[Loading resource: ...]messages show what's loaded- Claude has access to math constants and formulas
- Resources provide background knowledge
- Can answer questions about resource content without tool use
What's New:
get_available_prompts()- List prompt templatesload_prompt(name, arguments)- Fetch and render prompts- Complete integration of all three MCP primitives
Concepts Implemented:
- Prompt template discovery
- Parameterized prompt loading
- Prompts as system instructions
- Three MCP primitive types working together:
- Tools: Agentic execution (Claude decides when)
- Resources: Background context (pre-loaded)
- Prompts: Behavior guidance (system instructions)
How to Run:
uv run python 04_supporting_prompts/agent.pyExample Queries to Try:
You: What is 156 times 23?
You: Calculate the area of a circle with radius 5
You: Solve: 2x + 5 = 13
You: What's the difference between pi and e?
You: Help me solve: (3 + 4) * (10 - 2)
What to Notice:
- All server capabilities displayed on startup
[Loading prompt: ...]shows prompt usage- Prompts guide how Claude approaches problems
- Complete integration: prompts guide, resources inform, tools execute
- Claude can combine all three for complex queries
When to Use: For actions and computations
- Claude decides when to execute them
- Used dynamically during conversation
- Examples: Calculator functions, API calls, file operations
When to Use: For background information
- Pre-loaded before sending message
- Provide context that's always available
- Examples: Documentation, constants, configuration data
When to Use: For behavior guidance
- Define how the assistant should behave
- Can be parameterized for different scenarios
- Examples: Tone settings, response formats, domain expertise
After completing this workshop, you can:
-
Connect to Existing Servers
- Explore the MCP Servers Repository
- Connect to databases, APIs, and file systems
- Combine multiple servers in one client
-
Enhance the Client
- Add intelligent resource selection
- Implement prompt switching based on context
- Add support for streaming responses
- Build a more sophisticated UI
- Connect to the new MCP registry
- Build client-provided capabilities, like roots and elicitations. The test server can use them as-is.
Make sure you've created a .env file with your API key:
cp .env.example .env
# Edit .env and add: ANTHROPIC_API_KEY=your_actual_keyReinstall dependencies:
uv sync --forceThe server is started automatically by the client. If you see connection errors:
- Ensure
calculator_server.pyexists in the project root - Check that uv can run Python scripts:
uv run python --version