- name
- mcp-complete-guide
- description
- Complete 11-phase guide for building production-ready MCP (Model Context Protocol) servers with semantic layer integration. Covers foundation to deployment, including agent-centric design, tool development, testing, error handling, performance optimization, monitoring, security, governance, and semantic layer integration for business metrics. Use when building enterprise-grade MCP servers that integrate with dbt, Tableau, or other semantic layers for Finance SSC, business analytics, or data governance use cases.
# Complete MCP Development Guide: Phases 1-11
**A Comprehensive Guide to Building Production-Ready MCP Servers**
*From Foundation to Semantic Layer Integration*
---
## Table of Contents
1. [Phase 1: Foundation & Planning](#phase-1-foundation--planning)
2. [Phase 2: Core Implementation](#phase-2-core-implementation)
3. [Phase 3: Tool Development](#phase-3-tool-development)
4. [Phase 4: Testing & Validation](#phase-4-testing--validation)
5. [Phase 5: Error Handling & Resilience](#phase-5-error-handling--resilience)
6. [Phase 6: Performance Optimization](#phase-6-performance-optimization)
7. [Phase 7: Monitoring & Observability](#phase-7-monitoring--observability)
8. [Phase 8: Documentation & Examples](#phase-8-documentation--examples)
9. [Phase 9: Security & Governance](#phase-9-security--governance)
10. [Phase 10: Production Deployment](#phase-10-production-deployment)
11. [Phase 11: Semantic Layer Integration](#phase-11-semantic-layer-integration)
---
# Phase 1: Foundation & Planning
## Overview
Before writing any code, invest time in deep research and strategic planning. This phase sets the foundation for a high-quality MCP server.
## 1.1 Understand Agent-Centric Design
**Build for Workflows, Not Just API Endpoints:**
- Don't simply wrap existing API endpoints - build thoughtful, high-impact workflow tools
- Consolidate related operations (e.g., `schedule_event` that both checks availability and creates event)
- Focus on tools that enable complete tasks, not just individual API calls
- Consider what workflows agents actually need to accomplish
**Optimize for Limited Context:**
- Agents have constrained context windows - make every token count
- Return high-signal information, not exhaustive data dumps
- Provide "concise" vs "detailed" response format options
- Default to human-readable identifiers over technical codes (names over IDs)
**Design Actionable Error Messages:**
- Error messages should guide agents toward correct usage patterns
- Suggest specific next steps: "Try using filter='active_only' to reduce results"
- Make errors educational, not just diagnostic
**Follow Natural Task Subdivisions:**
- Tool names should reflect how humans think about tasks
- Group related tools with consistent prefixes for discoverability
- Design tools around natural workflows, not just API structure
## 1.2 Study MCP Protocol Documentation
**Load the complete MCP specification:**
```bash
# Fetch the latest MCP protocol documentation
https://modelcontextprotocol.io/llms-full.txt
```
This comprehensive document contains the complete MCP specification and guidelines.
## 1.3 Study Framework Documentation
**For Python implementations:**
- Python SDK Documentation: `https://raw.githubusercontent.com/modelcontextprotocol/python-sdk/main/README.md`
- Review FastMCP patterns and best practices
**For Node/TypeScript implementations:**
- TypeScript SDK Documentation: `https://raw.githubusercontent.com/modelcontextprotocol/typescript-sdk/main/README.md`
- Review MCP SDK patterns
## 1.4 Exhaustive API Research
To integrate a service, read through **ALL** available API documentation:
- Official API reference documentation
- Authentication and authorization requirements
- Rate limiting and pagination patterns
- Error responses and status codes
- Available endpoints and their parameters
- Data models and schemas
## 1.5 Create Implementation Plan
Based on your research, create a detailed plan:
**Tool Selection:**
- List the most valuable endpoints/operations to implement
- Prioritize tools that enable the most common and important use cases
- Consider which tools work together to enable complex workflows
**Shared Utilities:**
- Identify common API request patterns
- Plan pagination helpers
- Design filtering and formatting utilities
- Plan error handling strategies
**Input/Output Design:**
- Define input validation models (Pydantic for Python, Zod for TypeScript)
- Design consistent response formats (JSON or Markdown)
- Plan for large-scale usage (thousands of users/resources)
- Implement character limits and truncation strategies (e.g., 25,000 tokens)
**Error Handling Strategy:**
- Plan graceful failure modes
- Design clear, actionable, LLM-friendly error messages
- Consider rate limiting and timeout scenarios
- Handle authentication and authorization errors
---
# Phase 2: Core Implementation
## Overview
With a comprehensive plan in place, begin systematic implementation following language-specific best practices.
## 2.1 Project Structure Setup
### Python Structure
```python
project/
├── src/
│ ├── __init__.py
│ ├── server.py # Main MCP server
│ ├── tools.py # Tool implementations
│ ├── utils.py # Shared utilities
│ └── models.py # Pydantic models
├── tests/
│ ├── test_tools.py
│ └── test_utils.py
├── requirements.txt
├── pyproject.toml
└── README.md
```
### TypeScript Structure
```typescript
project/
├── src/
│ ├── index.ts # Main MCP server
│ ├── tools/ # Tool implementations
│ ├── utils/ # Shared utilities
│ └── types.ts # Type definitions
├── tests/
├── package.json
├── tsconfig.json
└── README.md
```
## 2.2 Server Initialization
### Python (FastMCP)
```python
from mcp import FastMCP
from pydantic import BaseModel, Field
import httpx
import asyncio
# Initialize server
mcp = FastMCP("your-service-name")
# Module-level constants
CHARACTER_LIMIT = 25000
API_BASE_URL = "https://api.example.com"
```
### TypeScript (MCP SDK)
```typescript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server({
name: "your-service-name",
version: "1.0.0"
}, {
capabilities: {
tools: {}
}
});
```
## 2.3 Implement Core Infrastructure
**Create shared utilities before implementing tools:**
### API Request Helper
```python
async def make_api_request(
endpoint: str,
method: str = "GET",
params: dict | None = None,
data: dict | None = None
) -> dict:
"""Make authenticated API request with error handling."""
async with httpx.AsyncClient() as client:
response = await client.request(
method=method,
url=f"{API_BASE_URL}/{endpoint}",
params=params,
json=data,
headers={"Authorization": f"Bearer {API_TOKEN}"},
timeout=30.0
)
response.raise_for_status()
return response.json()
```
### Response Formatting
```python
def format_response(data: dict, format_type: str = "json") -> str:
"""Format response as JSON or Markdown."""
if format_type == "json":
return json.dumps(data, indent=2)
else:
# Convert to markdown format
return convert_to_markdown(data)
def truncate_content(content: str, max_chars: int = CHARACTER_LIMIT) -> str:
"""Truncate content with ellipsis if exceeds limit."""
if len(content) <= max_chars:
return content
return content[:max_chars] + "\n\n[Content truncated...]"
```
### Pagination Helper
```python
async def paginate_results(
endpoint: str,
max_results: int = 100,
page_size: int = 50
) -> list:
"""Fetch paginated results up to max_results."""
results = []
page = 1
while len(results) < max_results:
data = await make_api_request(
endpoint,
params={"page": page, "per_page": page_size}
)
if not data.get("items"):
break
results.extend(data["items"])
if not data.get("has_more"):
break
page += 1
return results[:max_results]
```
---
# Phase 3: Tool Development
## Overview
Implement tools systematically, following consistent patterns and best practices.
## 3.1 Tool Implementation Pattern
### Complete Tool Example (Python)
```python
from pydantic import BaseModel, Field
from typing import Literal
class SearchInput(BaseModel):
"""Input schema for search tool."""
query: str = Field(
description="Search query string",
min_length=1,
max_length=200
)
filter_type: Literal["all", "active", "archived"] = Field(
default="active",
description="Filter results by status"
)
max_results: int = Field(
default=50,
ge=1,
le=100,
description="Maximum number of results to return"
)
response_format: Literal["json", "markdown"] = Field(
default="markdown",
description="Format for response data"
)
@mcp.tool()
async def search_items(
query: str,
filter_type: str = "active",
max_results: int = 50,
response_format: str = "markdown"
) -> str:
"""
Search for items matching the query.
This tool searches across all items in the system and returns
matching results with their key attributes.
Args:
query: Search query string
filter_type: Filter by status (all/active/archived)
max_results: Maximum number of results (1-100)
response_format: Response format (json/markdown)
Returns:
Formatted search results with item details
Example:
>>> search_items("project alpha", filter_type="active", max_results=10)
Returns active items matching "project alpha"
Hints:
readOnly: true
destructive: false
idempotent: true
openWorld: true
"""
try:
# Validate and prepare request
params = {
"q": query,
"status": filter_type,
"limit": min(max_results, 100)
}
# Fetch results
results = await make_api_request("search", params=params)
# Format response
formatted = format_response(results, response_format)
# Apply character limit
return truncate_content(formatted)
except httpx.HTTPStatusError as e:
if e.response.status_code == 404:
return "No items found matching your query. Try broader search terms."
elif e.response.status_code == 401:
return "Authentication failed. Please check API credentials."
else:
return f"Search failed: {str(e)}. Please try again."
except Exception as e:
return f"Unexpected error during search: {str(e)}"
```
## 3.2 Tool Design Checklist
For each tool, ensure:
- ✅ **Clear Purpose**: One-line description of what the tool does
- ✅ **Input Validation**: Pydantic/Zod schema with constraints
- ✅ **Error Handling**: Graceful handling of all error cases
- ✅ **Actionable Errors**: Error messages guide next steps
- ✅ **Response Formats**: Support JSON and Markdown
- ✅ **Character Limits**: Truncate long responses
- ✅ **Tool Hints**: Add readOnly, destructive, idempotent, openWorld
- ✅ **Type Safety**: Full type hints/types
- ✅ **Documentation**: Comprehensive docstrings/descriptions
- ✅ **Examples**: Usage examples in documentation
## 3.3 Common Tool Patterns
### Read-Only List Tool
```python
@mcp.tool()
async def list_resources(
limit: int = 50,
offset: int = 0,
response_format: str = "markdown"
) -> str:
"""
List all available resources.
Hints:
readOnly: true
destructive: false
idempotent: true
"""
results = await paginate_results("resources", max_results=limit)
return format_response(results, response_format)
```
### Create/Update Tool (Destructive)
```python
@mcp.tool()
async def create_resource(
name: str,
description: str,
metadata: dict | None = None
) -> str:
"""
Create a new resource.
Hints:
readOnly: false
destructive: true
idempotent: false
"""
data = {
"name": name,
"description": description,
"metadata": metadata or {}
}
result = await make_api_request("resources", method="POST", data=data)
return f"✅ Resource created successfully: {result['id']}"
```
---
# Phase 4: Testing & Validation
## Overview
Comprehensive testing ensures your MCP server works reliably in production scenarios.
## 4.1 Unit Testing
### Python (pytest)
```python
import pytest
from unittest.mock import AsyncMock, patch
from your_server import search_items, make_api_request
@pytest.mark.asyncio
async def test_search_items_success():
"""Test successful search returns formatted results."""
mock_response = {
"items": [
{"id": "1", "name": "Item 1"},
{"id": "2", "name": "Item 2"}
]
}
with patch('your_server.make_api_request', return_value=mock_response):
result = await search_items("test query")
assert "Item 1" in result
assert "Item 2" in result
assert len(result) < CHARACTER_LIMIT
@pytest.mark.asyncio
async def test_search_items_not_found():
"""Test 404 returns helpful error message."""
with patch('your_server.make_api_request', side_effect=httpx.HTTPStatusError(
"Not Found", request=None, response=AsyncMock(status_code=404)
)):
result = await search_items("nonexistent")
assert "No items found" in result
assert "broader search terms" in result
```
## 4.2 Evaluation Creation
Create 10 realistic evaluation questions to test agent effectiveness:
```xml
<evaluation>
<qa_pair>
<question>What are the top 5 most active projects in the last 30 days, and who are their lead contributors?</question>
<answer>Project Alpha (John Doe), Project Beta (Jane Smith), Project Gamma (Bob Johnson), Project Delta (Alice Williams), Project Epsilon (Charlie Brown)</answer>
</qa_pair>
<!-- More qa_pairs... -->
</evaluation>
```
### Evaluation Requirements
Each question must be:
- **Independent**: Not dependent on other questions
- **Read-only**: Only non-destructive operations required
Auf GitHub ansehen