Learn MCP server development through progressive discovery. This tutorial takes you from basic concepts to advanced features in three phases.
- Rust installed (1.70 or later)
- Claude Code or another MCP client
- 30 minutes
| Phase | Template | What You'll Learn | Duration |
|---|---|---|---|
| 1 | Simple Calculator | Basic MCP: tools, requests, responses | 10 min |
| 2 | Complete Calculator | Advanced: validation, errors, resources, prompts | 10 min |
| 3 | SQLite Explorer | Expert: workflows, step bindings, databases | 10 min |
Goal: Understand basic MCP concepts with a single add tool.
cargo pmcp new my-mcp-learning
cd my-mcp-learningThis creates a workspace with server-common (shared HTTP bootstrap).
cargo pmcp add server calculator --template calculatorWhat happened:
- Created
mcp-calculator-core(business logic) - Created
calculator-server(HTTP binary) - Assigned port 3000 automatically
- Saved configuration to
.pmcp-config.toml
cargo pmcp dev --server calculatorWhat to observe:
- Server builds and starts on port 3000
- Shows "Server URL: http://0.0.0.0:3000"
- Logs show MCP server is ready
In a new terminal (keep server running):
cargo pmcp connect --server calculator --client claude-codeThis configures Claude Code to connect to your server.
In Claude Code, try:
- "Add 5 and 3"
- "What's 42 plus 17?"
- "Calculate the sum of 123 and 456"
What's happening:
- Claude Code sends MCP request to your server
- Server receives JSON-RPC request for
addtool - Tool executes:
{ a: 5, b: 3 }→{ result: 8 } - Claude Code receives response and answers
Open crates/mcp-calculator-core/src/lib.rs and examine:
// Tool Input - Auto-generates JSON schema
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct AddInput {
pub a: f64,
pub b: f64,
}
// Tool Handler - Async function
async fn add_tool(input: AddInput, _extra: RequestHandlerExtra) -> Result<AddResult> {
Ok(AddResult {
result: input.a + input.b,
})
}
// Server Registration
Server::builder()
.tool("add", TypedTool::new("add", |input, extra| {
Box::pin(add_tool(input, extra))
}))
.build()Key Concepts:
- Tools = Functions Claude can call
- TypedTool = Type-safe tool with automatic JSON schema
- Input/Output = Defined by Rust types (validated automatically)
- MCP servers provide tools to AI agents
- Tools have typed inputs and outputs
-
cargo-pmcphandles HTTP bootstrap - Servers run on specific ports (3000 by default)
Stop the server (Ctrl+C) and proceed to Phase 2.
Goal: Learn advanced patterns—multiple tools, validation, resources, prompts.
cargo pmcp add server calculator --template complete-calculator --replaceWhat happened:
- Prompted for confirmation (shows current vs new template)
- Deleted old
mcp-calculator-coreandcalculator-server - Generated new crates with complete template
- Kept port 3000 (no config change needed in Claude Code!)
cargo pmcp dev --server calculatorServer starts on same port (3000)—Claude Code reconnects automatically!
In Claude Code, try:
- "Multiply 7 by 8"
- "Divide 100 by 4"
- "What's 2 to the power of 10?"
- "Calculate the square root of 144"
New capabilities:
add,subtract,multiply(basic arithmetic)divide(with zero-division validation!)power(exponentiation)sqrt(square root)
Try: "Divide 10 by 0"
Observe Claude's response—the server returned a validation error!
// In divide_tool():
if input.b == 0.0 {
return Err(Error::validation("Cannot divide by zero"));
}In Claude Code, use the /prompts command:
/quadratic a: 1 b: -5 c: 6
What's happening:
- Prompt orchestrates multiple tools
- Calculates discriminant (b² - 4ac)
- Determines if real roots exist
- Calculates roots using quadratic formula
- Returns step-by-step solution
Ask Claude: "Show me the quadratic formula guide"
Resources provide:
- Static knowledge/documentation
- Context for the AI
- Educational material
- Servers can have multiple tools
- Validation returns errors to the client
- Prompts orchestrate multi-step workflows
- Resources provide static knowledge
-
--replaceupgrades servers in-place
Stop the server and proceed to Phase 3.
Goal: Master workflows with step bindings, resources, and real databases.
cargo pmcp add server explorer --template sqlite-explorerWhat happened:
- Created explorer server
- Auto-assigned port 3001 (next available)
- Both servers in workspace:
calculator(port 3000)explorer(port 3001)
cd crates/mcp-explorer-core
curl -L https://github.com/lerocha/chinook-database/raw/master/ChinookDatabase/DataSources/Chinook_Sqlite.sqlite -o chinook.db
cd ../..This downloads the Chinook database (music store with 11 tables, 3500+ tracks).
cargo pmcp dev --server explorerServer starts on port 3001.
In a new terminal:
cargo pmcp connect --server explorer --client claude-codeNow you have TWO servers configured in Claude Code:
calculator(port 3000) - Offexplorer(port 3001) - Running
In Claude Code, try:
- "Show me all tables in the database"
- "Get sample rows from the Track table"
- "List customers and their countries"
Tools available:
execute_query- Run SELECT queries (read-only)list_tables- Show all tables with row countsget_sample_rows- Preview table data
Try the workflow prompts:
/monthly_sales_report month: 3 year: 2021
What's different from Phase 2:
- Workflows use SQL template substitution:
WHERE month = {month} - Multi-step workflows with bindings: Step 1 output → Step 2 input
- Real database queries (not just calculations)
/analyze_customer customer_id: 5
This runs a 3-step workflow:
- Get customer info
- Get purchase history
- Calculate lifetime value
All results are bound and composed automatically!
/customers_who_bought_top_tracks limit: 10
How it works:
- Step 1: Query top 10 tracks by purchase count → binds as
top_tracks - Client (Claude) receives
top_tracksbinding - Claude extracts track IDs
- Claude generates SQL to find customers
- Claude calls
execute_querywith generated SQL
This demonstrates client-side orchestration!
You can run both servers simultaneously:
Terminal 1:
cargo pmcp dev --server calculatorTerminal 2:
cargo pmcp dev --server explorerBoth servers run on different ports—Claude Code can use both at once!
- Multiple servers can coexist (different ports)
- Workflows can use template substitution
- Multi-step workflows with bindings
- Resources provide database schemas
- Real-world use case: database operations
| Concept | Phase 1 | Phase 2 | Phase 3 |
|---|---|---|---|
| Tools | 1 (add) | 6 (arithmetic) | 3 (database ops) |
| Validation | None | Division by zero | SQL safety |
| Prompts | None | Quadratic solver | 3 workflows |
| Resources | None | Formula guide | DB schema |
| Bindings | No | No | Yes |
| Complexity | ⭐ Simple | ⭐⭐ Intermediate | ⭐⭐⭐ Advanced |
-
Add a new tool to calculator:
// In mcp-calculator-core/src/lib.rs async fn factorial_tool(input: FactorialInput, _extra: RequestHandlerExtra) -> Result<FactorialResult> { // Your implementation }
-
Create a custom workflow for explorer:
fn create_genre_analysis_workflow() -> SequentialWorkflow { // Your workflow }
-
Build your own server from scratch:
cargo pmcp add server my-server --template minimal
See the Deployment Guide for:
- Docker containerization
- Environment configuration
- Production logging
- Monitoring and metrics
- Authentication: Add auth middleware
- Rate Limiting: Protect your APIs
- Custom Transport: Beyond HTTP
- Testing: Write integration tests
Error: Port 3000 is already in useSolution: Stop the other server or use a different port:
cargo pmcp dev --server calculator --port 3005- Check
.pmcp-config.tomlhas correct port - Restart Claude Code
- Run connect command again:
cargo pmcp connect --server explorer --client claude-code
Make sure you're using the latest version:
cargo install --path /path/to/rust-mcp-sdk/cargo-pmcp --forceFound an issue or have a suggestion?
Congratulations! 🎉 You've completed the MCP Server Development Tutorial. You now understand:
- Basic MCP architecture (tools, prompts, resources)
- Progressive server development (simple → complete → advanced)
- Multi-server workspaces with port management
- Production patterns with cargo-pmcp
Happy building! 🚀