This repository serves two important audiences:
For developers adding MCP (Model Context Protocol) to existing REST APIs without separate server binaries. This demonstrates how to make institutional knowledge accessible to AI assistants and development tools.
For developers adopting AI-assisted workflows with Claude Code and other AI tools. A complete project structure optimized for parallel task execution and AI orchestration.
π¬ Watch the workflow: Compound Engineering with Claude Code (15 min)
π Fork the scaffold: See SCAFFOLD_NOTES.md for reusing this structure
This scaffold implements the workflow from Kieran Klaassen's Claude Code: How Two Engineers Ship Like a Team of 15.
Two engineers at Every shipped 6 features, 5 bug fixes, and 3 infrastructure updates in one week. How? By using GitHub issues + AI in a way that makes every line of code traceable to requirements.
- You already use GitHub/GitLab issues
- Everything is transparent: Issues β Kanban β PRs
- No "vibe coding" - every change has a clear purpose
- No new tools to learn, just better use of existing ones
Built entirely with this approach. Check our public Kanban board to see how PRs #7 and #9 went from issue to deployment.
- πΊ Original video (74K+ views)
- π Detailed article by Every
- π¬ 15-min demo
- π My story: SCAFFOLD_NOTES.md
A glossary service exposing Siemens-specific terminology via REST API and MCP endpoints. This working example demonstrates modern MCP best practices that solve real integration problems.
One MCP server, multiple AI assistants: The same Siemens glossary seamlessly integrates with Claude Code's CLI for automation and VS Code's GitHub Copilot for interactive development.
- Dual Protocol Support: REST API for web apps, MCP for AI assistants
- Fuzzy Search: Handles typos like "Temcenter" β "Teamcenter"
- Multi-Client: Works with Claude Code CLI, VS Code, and any MCP client
- Hot Reload: Update glossary without restarting server
- Secure: API key authentication for MCP endpoints
- Fast: <50ms response time with 1000+ terms
This server demonstrates modern MCP best practices that solve real integration problems:
- Simple API key management via
MCP_API_KEYSenvironment variable - Perfect for small teams who want to manage their own authentication
- No complex OAuth flows or enterprise identity provider setup required
- Uses HTTP 403 Forbidden (not 401 Unauthorized) for invalid API keys
- Prevents "Dynamic Client Registration not supported" OAuth popup errors
- Maintains security without breaking the VS Code user experience
- Actually uses
@mcp.tool()decorators (doesn't define them and ignore them) - Clean 150-line implementation vs typical 300+ line manual JSON-RPC handling
- FastMCP handles all protocol details automatically
- Middleware-based authentication with proper error handling
- Separate service modules for clean code organization
- Comprehensive test coverage with modern pytest patterns
- Python 3.10 or higher
- pip package manager
- FastMCP 2.11.2 or higher (older versions have different API)
Prerequisites Check:
# Verify Python version (should be 3.10+)
python --version
# Verify pip is installed and recent
python -m pip --version
# Upgrade pip to latest (recommended)
python -m pip install --upgrade pip-
Clone the repository
git clone <repository-url> cd siemens-acronyms-mcp
-
Set up Python virtual environment
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate python -m pip install --upgrade pip pip install -r requirements.txt
Verify Installation:
# Check FastMCP version (should be 2.11.2+) python -c "import fastmcp; print(f'FastMCP version: {fastmcp.__version__}')" # Verify all dependencies installed python -c "import fastapi, starlette, rapidfuzz; print('All dependencies OK')"
-
Configure environment variables
cp env.example .env
Edit
.envand add your API keys:# Comma-separated list of API keys for MCP endpoint authentication MCP_API_KEYS=sk-team-A,sk-team-B,sk-Daniel # Optional: Custom path to acronyms file # GLOSSARY_FILE_PATH=siemens_acronyms.json # Optional: Log level (DEBUG, INFO, WARNING, ERROR) LOG_LEVEL=INFO
-
Run the server
uvicorn src.main:app --reload --port 8000
The server will be available at http://localhost:8000
After starting the server, verify everything is working:
# Test 1: Health endpoint
curl http://localhost:8000/health
# Expected: {"status":"healthy","hostname":"...","uptime":...,"version":"1.0.0"}
# Test 2: REST API search
curl "http://localhost:8000/api/v1/search?q=EDA"
# Expected: Results with EDA definition
# Test 3: Fuzzy search (handles typos)
curl "http://localhost:8000/api/v1/search?q=Temcenter"
# Expected: Returns "Teamcenter" with ~94% match score
# Test 4: MCP via Claude Code (if configured)
claude -p "Using the siemens-acronyms MCP server, what does DISW mean?"
# Expected: Returns DISW definitionsiemens-acronyms-mcp/
βββ src/
β βββ main.py # FastAPI server (150 lines, clean & focused)
β βββ mcp_service.py # MCP tools using proper @mcp.tool() decorators
β βββ auth_middleware.py # HTTP 403 auth (VS Code compatible)
β βββ acronyms_service.py # Fuzzy search engine with file watching
βββ tests/
β βββ test_acronyms_server.py # Modern pytest test suite
βββ siemens_acronyms.json # Glossary data (3 samples, add more!)
βββ .vscode/
β βββ mcp.json # VS Code MCP configuration (works without OAuth!)
β βββ settings.json # Development settings
βββ .claude/
β βββ settings.json # Claude Code permissions
βββ assets/
βββ MCP_demo_*.png # Working demo screenshot
Search Endpoint: GET /api/v1/search?q={query}
No authentication required. Returns acronym definitions with fuzzy matching.
# Search for an exact acronym
curl "http://localhost:8000/api/v1/search?q=EDA"
# Fuzzy search handles typos
curl "http://localhost:8000/api/v1/search?q=Temcenter"
# Returns: Teamcenter (with similarity score)
# Partial matches
curl "http://localhost:8000/api/v1/search?q=Sim"
# Returns: Simcenter and related termsResponse format:
{
"results": [
{
"term": "EDA",
"full_name": "Electronic Design Automation",
"description": "IC design, PCB design, and electronic systems design tools",
"score": 100.0
}
],
"query": "EDA",
"count": 1
}Endpoint: POST /mcp
Requires X-API-Key header with valid API key from MCP_API_KEYS (except for the get_health tool).
π Important: This implementation uses HTTP 403 Forbidden for authentication failures (not 401 Unauthorized). This prevents VS Code from triggering OAuth popups and maintains a smooth user experience while keeping your data secure.
Available MCP Tools:
-
search_acronyms- Search for Siemens acronyms and terminology- Parameters:
query(string, required) - Returns: Matching terms with fuzzy search support
- Authentication: Required
- Parameters:
-
get_health- Get server health status- Parameters: None
- Returns: Server status, hostname, uptime, and version
- Authentication: Not required (public tool)
# List available tools
curl -X POST http://localhost:8000/mcp \
-H "X-API-Key: sk-team-A" \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "method": "tools/list", "id": 1}'
# Search using MCP protocol (requires authentication)
curl -X POST http://localhost:8000/mcp \
-H "X-API-Key: sk-team-A" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "search_acronyms",
"arguments": {"query": "DISW"}
},
"id": 2
}'
# Get health status via MCP (no authentication required)
curl -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "get_health",
"arguments": {}
},
"id": 3
}'Endpoint: GET /health
curl http://localhost:8000/healthReturns:
{
"status": "healthy",
"hostname": "your-hostname",
"uptime": 123.45,
"version": "1.0.0"
}Interactive Swagger UI available at: http://localhost:8000/docs
After starting the server, you can connect Claude Code to use the acronyms service:
-
Add the MCP Server
# Add the MCP server with HTTP transport and API key claude mcp add --transport http siemens-acronyms http://localhost:8000/mcp \ --header "X-API-Key: sk-team-A" # Verify connection status (should show β Connected) claude mcp list
-
Option 2: Manual Configuration in
.mcp.jsonCreate or edit
.mcp.jsonin your project directory (for local scope) or useclaude mcp add --scope projectfor team sharing:{ "mcpServers": { "siemens-acronyms": { "type": "http", "url": "http://localhost:8000/mcp", "headers": { "X-API-Key": "sk-team-A" } } } }Or with environment variable expansion:
{ "mcpServers": { "siemens-acronyms": { "type": "http", "url": "http://localhost:8000/mcp", "headers": { "X-API-Key": "${MCP_API_KEY:-sk-team-A}" } } } } -
Verify Configuration
# List configured MCP servers claude mcp list # Get details of the siemens-acronyms server claude mcp get siemens-acronyms
-
Interactive Usage in Claude Code
After configuration, in Claude Code you can ask:
- "What does DISW mean in Siemens context?"
- "Search for EDA in the Siemens glossary"
- "Find information about Teamcenter"
- "Use the siemens-acronyms MCP server to search for TeamCenter"
-
Non-Interactive Usage (Command Line)
Use the
-pflag for non-interactive queries, perfect for scripts and automation:# Basic search claude -p "Using the siemens-acronyms MCP server, what does DISW mean?" # Test fuzzy matching (handles typos) claude -p "Using the siemens-acronyms MCP server, search for 'Temcenter'" # Returns: Teamcenter with 94.74% match score # Search by partial terms claude -p "Using the siemens-acronyms MCP server, search for 'software'" # Returns: DISW and other software-related acronyms # With permission bypass for automation claude -p --dangerously-skip-permissions \ "Using the siemens-acronyms MCP server, what does EDA stand for?"
Note: Claude Code will request permission to use MCP tools on first use. Either:
- Grant permission interactively when prompted
- Use
--dangerously-skip-permissionsflag for scripts (use cautiously) - Configure allowed tools in
.claude/settings.json
VS Code v1.103+ includes native MCP support integrated with GitHub Copilot Chat.
Important for WSL2/Windows Users: If running VS Code on Windows with the server in WSL2, use the User Configuration method below.
-
Start the MCP server (in WSL2 or terminal):
uvicorn src.main:app --reload --port 8000
-
Configure in VS Code (choose one method):
Method A: Via UI (Easier)
- Open Command Palette (
Ctrl+Shift+P) - Run:
MCP: Add Server - Enter server details when prompted
- VS Code will save configuration automatically
Method B: Edit Configuration File
- Open Command Palette (
Ctrl+Shift+P) - Run:
MCP: Open User Configuration - Add this configuration:
{ "servers": { "siemens-acronyms": { "type": "http", "url": "http://localhost:8000/mcp", "headers": { "X-API-Key": "sk-team-A" } } } }- Save and reload VS Code
- Open Command Palette (
-
Start and verify the server:
- Command Palette β
MCP: Show Installed Servers - Click "Start" next to
siemens-acronyms - Should show "Running" status
- Command Palette β
-
Use in GitHub Copilot Chat:
- Open Copilot Chat (
Ctrl+I) - Switch to "Agent" mode (click agent icon)
- Test: "What does DISW mean?"
- The tool
search_acronymswill be invoked automatically
- Open Copilot Chat (
Note: Windows automatically forwards localhost:8000 from WSL2, so no special network configuration is needed.
Based on practical testing with Claude Code, here are actual results:
# Typo in "Teamcenter" β Still finds the right result
$ claude -p --dangerously-skip-permissions \
"Using siemens-acronyms MCP, search for 'Temcenter'"
> Found "Teamcenter" with 94.74% confidence score
# Partial word matching
$ claude -p --dangerously-skip-permissions \
"Using siemens-acronyms MCP, search for 'software'"
> Found DISW (Digital Industries Software) with 84% matchEach Claude Code query triggers multiple MCP calls:
initialize- Protocol handshaketools/list- Discover available toolstools/call- Execute the search- Server logs show all requests with 200 OK status
- Response time: <50ms for typical queries
- Connection overhead: ~100ms for initial handshake
- Fuzzy matching adds negligible latency (<10ms)
Run the comprehensive test suite:
# Run all tests
pytest -v
# Run with coverage report
pytest -v --cov=src
# Run specific test categories
pytest -v tests/test_acronyms_server.py::TestRESTEndpoint
pytest -v tests/test_acronyms_server.py::TestMCPEndpointCurrent test coverage: 83% with 31 tests passing.
siemens-acronyms-mcp/
βββ src/
β βββ main.py # FastAPI application
β βββ acronyms_service.py # Core search service
βββ tests/
β βββ test_acronyms_server.py # Test suite
βββ siemens_acronyms.json # Glossary data (hot-reloadable)
βββ requirements.txt # Python dependencies
βββ env.example # Environment template
βββ README.md # This file
Edit siemens_acronyms.json:
{
"acronyms": [
{
"term": "NEW_TERM",
"full_name": "Full Name of Term",
"category": "Acronym",
"description": "Description of what this term means",
"division": "DISW",
"url": "https://optional-url.com",
"related": ["RELATED1", "RELATED2"]
}
]
}The service automatically reloads the file without restart.
Before committing:
# Format code
ruff format .
# Check linting
ruff check . --fix
# Run tests
pytest -v-
Using Uvicorn directly
uvicorn src.main:app --host 0.0.0.0 --port 8000 --workers 4
-
Using Gunicorn with Uvicorn workers
gunicorn src.main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
-
Docker deployment
FROM python:3.10-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]
| Variable | Description | Default | Required |
|---|---|---|---|
MCP_API_KEYS |
Comma-separated API keys for MCP auth | - | Yes (for MCP) |
GLOSSARY_FILE_PATH |
Path to acronyms JSON file | siemens_acronyms.json |
No |
LOG_LEVEL |
Logging level | INFO |
No |
- Search response time: <100ms for 1000+ terms
- Concurrent requests: Handles 50+ simultaneous connections
- Memory usage: ~50MB with 1000 terms loaded
- Hot reload: File changes detected within 1 second
- Check existing issues or create new ones
- Follow TDD approach - write tests first
- Ensure all tests pass and code is formatted
- Update
siemens_acronyms.jsonfor new terms - Create pull request with clear description
- Source Code: MIT License - Use freely in your projects
- Glossary Data: CC BY 4.0 - Share and adapt with attribution
This dual licensing allows both internal Siemens use and external contributions.
- Check Python version:
python --version(needs 3.10+) - Verify all dependencies:
pip install -r requirements.txt - Check port availability:
lsof -i :8000
- Verify API key in
.envfile - Check
X-API-Keyheader in request - Ensure no spaces in
MCP_API_KEYSvalue
- Check
siemens_acronyms.jsonfile exists and is valid JSON - Verify file permissions allow reading
- Check logs for file loading errors
- Ensure server is running:
curl http://localhost:8000/health - Verify API key matches between server and client config
- Check firewall/network settings allow localhost connections
- Protocol version issue: Claude Code uses
"2025-06-18"protocol version- The server automatically echoes back the client's requested version
- If you see connection success but tools don't work, restart Claude Code
For issues or questions:
- Check the Issues page
- Review test files for usage examples
- Consult
CLAUDE.mdfor AI assistant guidance
Note: This is a demonstration project showing how to MCP-enable internal glossaries and APIs. The patterns shown here can be applied to other internal services to make them accessible to AI assistants while maintaining security through API key authentication.
