Content
# Mastra MCP Workshop: Best Practices
A hands-on workshop demonstrating MCP (Model Context Protocol) best practices using Mastra. Build a "Customer Analytics" MCP server that showcases workflow-oriented tools, authentication patterns, and multi-system integration.
## 🎯 Workshop Goals
- **Minimize surface area**: 2 tools that handle many use cases
- **Workflow-shaped tools**: Capabilities (explore schema, run queries) vs endpoints (getUsers)
- **Model compatibility**: Test with multiple models, ensure consistent behavior
- **Exploration**: Let AI discover capabilities through resources
- **Guardrails**: Safe, deterministic, read-only by default
## 🏗️ What We'll Build
**Customer Analytics MCP Server** featuring:
- 📊 **2 Tools**: `compute_account_health` (multi-system workflow), `run_sql` (database queries)
- 📚 **1 Resource**: `schema://main` (discovery & exploration)
- 🔄 **Workflow Patterns**: External API integration, data fusion, scoring algorithms
- 🛡️ **Built-in Safety**: SELECT-only, implicit LIMIT, parsed queries, API rate limits
- 🔐 **Authentication**: Transparent role-based access control (admin/user/readonly)
## 🚀 Quick Start
### Prerequisites
```bash
# Required
node >= 20.9.0
pnpm >= 8.0.0
# Optional: OpenAI API key for demo
export OPENAI_API_KEY="your-key-here"
```
### Installation
```bash
# Clone and install
git clone <this-repo>
cd mcp-server-workshop-best-practices
pnpm install
```
```bash
# Terminal 1: Start HTTP MCP server with real auth
pnpm mcp-http-server
# Terminal 2: Run HTTP workshop demo
pnpm workshop-demo-http
# Server runs on http://localhost:3001 with endpoints:
# - /mcp (MCP endpoint with auth)
# - /health (health check)
```
## 🛠️ Technical Deep Dive
### MCP Server Implementation
```typescript
// src/mastra/mcp/server.ts
import { MCPServer } from "@mastra/mcp";
import { computeAccountHealthTool, runSqlTool } from "../tools";
const server = new MCPServer({
name: "customer-analytics",
version: "0.5.0",
description: "Customer analytics MCP server with multi-system workflows",
tools: {
compute_account_health: computeAccountHealthTool,
run_sql: runSqlTool,
},
resources: resourceHandlers,
});
```
**Key Features:**
- ✅ **Multi-system workflows**: `compute_account_health` combines database, external APIs, and business logic
- ✅ **Resource-based discovery**: Schema exploration via `schema://main` resource
- ✅ **Transparent authentication**: MCP-compliant auth context passed to tools
- ✅ **Role-based access**: admin/user/readonly permissions enforced per tool call
- ✅ **Guardrails built-in**: SELECT-only, auto-LIMIT, permission checking
- ✅ **Error teaching**: Structured, helpful error messages
### Mastra Integration
```typescript
// src/workshop-demo.ts
import { MCPClient } from "@mastra/mcp";
import { Agent } from "@mastra/core/agent";
const mcpClient = new MCPClient({
servers: {
customerAnalytics: {
command: "pnpm",
args: ["mcp-server"],
env: { DEMO_API_KEY: "api_key_user_456" }, // Auth context
},
},
});
const agent = new Agent({
name: "Customer Analytics Agent",
model: openai("gpt-4o-mini"),
});
// Use MCP tools with agent
const response = await agent.generate(task, {
toolsets: await mcpClient.getToolsets(),
});
```
### HTTP Authentication (Production Pattern)
The workshop includes a production-ready HTTP server with real authentication:
```typescript
// src/mastra/mcp/http-server.ts
import { MCPServer } from "@mastra/mcp";
import { server } from "./server";
import type { AuthInfo, DemoUserInfo } from "./utils";
// Authentication middleware
function authenticateRequest(req: http.IncomingMessage): AuthInfo | null {
const authHeader = req.headers.authorization;
// Support JWT Bearer tokens
if (authHeader?.startsWith("Bearer ")) {
return validateJWT(authHeader.slice(7));
}
// Support API keys
if (authHeader?.startsWith("ApiKey ")) {
return validateApiKey(authHeader.slice(7));
}
return null;
}
// HTTP request handler with MCP-compliant auth
const handleRequest = async (req, res) => {
const authInfo = authenticateRequest(req);
if (!authInfo) {
res.writeHead(401, { "Content-Type": "application/json" });
res.end(
JSON.stringify({
error: "Authentication required",
message: "Please provide a valid Authorization header",
}),
);
return;
}
// Attach MCP-compliant auth to request
(req as any).auth = authInfo;
await server.startHTTP({
url: new URL(`http://localhost:${PORT}`),
httpPath: "/mcp",
req,
res,
});
};
```
**Key Features:**
- ✅ **JWT & API Key Support**: Multiple authentication methods
- ✅ **MCP-Compliant Auth**: Uses official `AuthInfo` type from MCP specification
- ✅ **Real Auth Context**: Authentication info passed to tools via `options.extra.authInfo`
- ✅ **Session Management**: Unique session IDs for each connection
- ✅ **CORS Support**: Web client compatibility
- ✅ **Structured Errors**: Clear auth failure responses
**Test Credentials:**
```bash
# Health check (no auth required)
curl localhost:3001/health
# MCP endpoint (requires auth)
curl -H "Authorization: ApiKey sk-user-987654321" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}' \
localhost:3001/mcp
# Available credentials:
# API Keys: sk-admin-123456789, sk-user-987654321, sk-readonly-555666777
# JWT Tokens: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.admin, eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.user
```
## 🛠️ Tools Overview
### 1. `compute_account_health` - Multi-System Workflow
**Purpose**: Demonstrates complex workflow patterns that combine multiple data sources for business insights.
**Data Flow**:
```
Internal DB (orders) → External NPS API → External Support API → Risk Scoring → Actionable Insights
```
**Key Features**:
- **Multi-source data fusion**: Order history + NPS scores + support tickets
- **Business logic**: Configurable scoring weights (recency 30%, momentum 30%, satisfaction 25%, reliability 15%)
- **Segmentation**: Filter by customer value, activity patterns, risk levels
- **External API simulation**: Realistic delays, missing data, enterprise customer patterns
- **Role-based limits**: Readonly users limited to 10 accounts
**Input Parameters**:
- `segment`: "all" | "inactive" | "highValue"
- `windowDays`: Analysis period (default 90 days)
- `limit`: Maximum accounts to analyze (default 50)
- `includeReasons`: Include risk factor explanations
**Output Structure**:
```json
{
"accounts": [
{
"accountId": "1",
"name": "Alice Johnson",
"healthScore": 85,
"tier": "good",
"metrics": { "lastOrderDays": 5, "spendDeltaPct": 15.2, "nps": 72 },
"reasons": []
}
],
"summary": {
"totalAnalyzed": 20,
"segmentBreakdown": { "good": 15, "watch": 3, "at_risk": 2 },
"avgHealthScore": 75,
"externalDataCoverage": { "npsAvailable": 18, "supportDataAvailable": 20 }
}
}
```
### 2. `run_sql` - Database Query Tool
**Purpose**: Safe, controlled database access with automatic guardrails.
**Key Features**:
- SELECT-only validation
- Automatic LIMIT injection
- Role-based row limiting
- Permission checking based on query content
## 🧪 Testing & Validation
### Model Compatibility Test
Run the same task with multiple models:
```
Task: "Analyze customer health for high-value accounts and show database schema"
Expected flow:
1. Schema resource → understand available data structure
2. compute_account_health → multi-system customer analysis
3. run_sql → supplementary database queries if needed
4. Valid JSON response with insights and recommendations
```
**Testing Matrix:**
- ✅ Right tool chosen? (compute_account_health for analysis, run_sql for queries)
- ✅ Args valid? (proper segment filtering, reasonable limits)
- ✅ Result shape? (structured health scores, actionable insights)
- ✅ Authentication? (role-based access control working)
### Demo Workflows
1. **Schema Exploration**: "What data is available?" (via schema resource)
2. **Customer Health Analysis**: "Show me at-risk customers" (compute_account_health)
3. **Database Queries**: "Show me recent high-value orders" (run_sql)
4. **Multi-system Integration**: "Combine order data with support tickets" (workflow tool)
5. **Guardrails Test**: "Try to delete users" (safely fails with helpful error)
6. **Authentication Test**: Different access levels (admin/user/readonly)
## 📊 Best Practices Demonstrated
### ✅ Tool Design
- **Capability-oriented**: `run_sql` (workflow) vs `getUserById` (endpoint)
- **Self-documenting**: Descriptions include examples
- **Composable**: One general tool > many specific ones
### ✅ Safety & Reliability
- **Read-only default**: Only SELECT operations allowed
- **Implicit limits**: Auto-add LIMIT clauses
- **Structured errors**: "Only SELECT queries allowed" (teaches the model)
### ✅ Model Compatibility
- **Cross-model testing**: GPT-4, GPT-3.5, etc.
- **Consistent behavior**: Same tools, same args, same output shape
- **Graceful degradation**: Weaker models still succeed
### ✅ Exploration & Discovery
- **Resource-driven**: Schema exposed via MCP resources
- **Progressive disclosure**: Start simple, add complexity
- **Documentation**: Examples and usage notes included
## 🎮 Try It Yourself
### Extend the Demo
Add a third tool to show workflow composition:
```typescript
const cityReportTool = createTool({
id: "make_city_report",
description: "Generate a comprehensive city analysis report",
execute: async ({ context }) => {
// Internally runs 2-3 SQL queries
// Returns: { city, userCount, totalSpend, avgOrderValue }[]
},
});
```
### Connect to Your Own Database
Replace the in-memory data with real database connections:
```typescript
// Update src/mcp-server/server.ts
import Database from "better-sqlite3";
const db = new Database("path/to/your/database.db");
// or with PostgreSQL
import postgres from "postgres";
const sql = postgres("postgresql://...");
// Update executeQuery function to use real SQL
```
### Test with More Models
Add models to the compatibility test:
```typescript
// src/workshop-demo.ts
const MODELS_TO_TEST = [
{ name: "GPT-4o Mini", model: openai("gpt-4o-mini") },
{ name: "GPT-4o", model: openai("gpt-4o") },
{ name: "Claude Sonnet", model: anthropic("claude-3-sonnet-20240229") },
// Add your preferred models
];
```
## 🎯 Key Takeaways
1. **Tools are workflows, not APIs** - Design for capabilities
2. **Fewer is better** - 2 general tools > 10 specific ones
3. **Safety first** - Guardrails built into every tool
4. **Test compatibility** - Multiple models, same behavior
5. **Enable exploration** - Resources help discovery
## 📚 Resources
- [Mastra MCP Documentation](https://mastra.ai/en/reference/tools/mcp-server)
- [Model Context Protocol Spec](https://modelcontextprotocol.io/docs/getting-started/intro)
## 🤝 Contributing
Found an issue or want to improve the workshop?
- Report bugs via GitHub issues
- Submit improvements via pull requests
- Share your workshop experiences
---
**Built with ❤️ using [Mastra](https://mastra.ai) - The TypeScript AI Framework**
Connection Info
You Might Also Like
markitdown
MarkItDown-MCP is a lightweight server for converting URIs to Markdown.
servers
Model Context Protocol Servers
Time
A Model Context Protocol server for time and timezone conversions.
Filesystem
Node.js MCP Server for filesystem operations with dynamic access control.
Sequential Thinking
A structured MCP server for dynamic problem-solving and reflective thinking.
git
A Model Context Protocol server for Git automation and interaction.