# BEST_PRACTICES.md
> π **Objective**: Production-oriented FastMCP (SDK v1.10.1) server best practices. Clarify project structure, deployment recommendations, and bypass capabilities all at once to avoid the embarrassment of "runs but hard to maintain".
------
## 1 Β· Transport Layer Selection
| Scenario | Recommended Transport | Description |
| ----------------------- | -------------------------- | ----------------------------------------------- |
| Local CLI / IDE Debug | **stdio** | No ports, shortest path |
| Web / Multi-client | **Streamable HTTP** | Best compatibility with LB / auth middleware; supports HTTP2 / H2C |
| Long connection push | Streamable HTTP + built-in SSE | Auto-upgrade on same path, no additional /sse endpoint needed |
β οΈ Public deployments must implement Host validation or bind to `127.0.0.1` at gateway to prevent DNS rebinding attacks.
------
## 2 Β· Directory Structure (Production Template)
```text
mcp-server/
βββ server/ # Core Python package (import server)
β βββ __init__.py
β βββ main.py # FastMCP instance & app entry
β βββ config.py # Pydantic Settings / env parsing
β βββ resources/ # All @mcp.resource
β β βββ __init__.py
β β βββ greeting.py
β βββ tools/ # All @mcp.tool
β β βββ __init__.py
β β βββ math.py
β βββ prompts/ # All @mcp.prompt
β β βββ __init__.py
β β βββ echo.py
β βββ routes/ # Bypass REST API
β β βββ __init__.py
β β βββ health.py
β β βββ convert.py
β βββ static/ # Static resources directory (mounted by StaticFiles)
βββ tests/ # pytest & httpx
β βββ __init__.py
β βββ conftest.py
β βββ unit/ # Pure function unit tests
β βββ integration/ # End-to-end Streamable HTTP calls
βββ scripts/ # Local ops scripts
β βββ start.sh # Unified entry (uvicorn / gunicorn)
β βββ migrate.sh # DB / KV migration example
βββ configs/ # Config & secret templates (not in Git)
β βββ logging.yml # Structured logging config
β βββ .env.example # Environment variable example (.env β docker-compose)
βββ deploy/ # Unified deployment directory
β βββ docker/ # Containerization
β β βββ Dockerfile
β β βββ entrypoint.sh
β βββ kubernetes/ # K8s / Helm Chart
β β βββ deployment.yaml
β β βββ service.yaml
β βββ ci/ # CI/CD (GitHub Actions / GitLab CI)
β βββ github/
β βββ workflows/
β βββ ci.yml
βββ pyproject.toml # Poetry / PEP 621; lock mcp==1.10.1
βββ README.md # Project overview & quick start
βββ docs/ # Development / ops documentation
βββ QUICKSTART.md
βββ BEST_PRACTICES.md
βββ API_REFERENCE.md
```
### Directory Key Points
| Directory | Purpose / Highlights |
| ------------------- | ------------------------------------------------------------ |
| **server/** | Code as protocol, layered by resources / tools / prompts; `config.py` unified loading of `os.environ`, avoiding scattered `os.getenv()` |
| **routes/** | Only bypass HTTP; keep MCP core clean. |
| **tests/** | Unit tests cover pure functions; integration tests directly connect `/mcp` Streamable HTTP, ensuring tools & resources are callable under real protocol. |
| **deploy/** | Unified deployment directory containing all deployment configs including containerization, K8s, CI/CD. |
| **deploy/docker/** | Independent of source code, any container changes don't pollute server package; `entrypoint.sh` reads `/configs/.env` β generates final config. |
| **deploy/kubernetes/** | Decoupled from CI, avoiding Helm over-coupling early projects; add Helm Chart in production. |
| **docs/** | Separate **running (QUICKSTART)**, **development (BEST_PRACTICES)**, **interface (API_REFERENCE)** for targeted reading by newcomers. |
> β
**Practice Rule**: All variable content unrelated to business logic (config / logs / secrets) should always be placed in `configs/` or external volumes.
------
## 3 Β· Bypass Capabilities
| Need | Approach |
| --------------- | ------------------------------------------------------------ |
| Health Check | `GET /health`: Return 200 & build sha |
| RESTful API | `POST /api/v1/xxx`: Other RESTful API entries |
| Static Files | `StaticFiles(directory=server.static)` β `/static` |
| Sub-microservice | `from fastapi import APIRouter`; mount `router` to `app.mount("/sub", sub_app)` |
> **Naming Suggestion**: Unified bypass routes in `routes/`, filename consistent with HTTP path, search cost = 0.
------
## 4 Β· Deployment Tips
1. **Process Model**: Uvicorn Worker β₯ *4* (`--worker-class=uvicorn.workers.UvicornWorker`); IO-intensive suggests 2 Γ CPU Core.
2. **Logging**: Unified structured JSON β ELK / Loki; no `print()`.
3. **Version Locking**: `mcp==1.10.1`, `fastapi~=0.115.14`; regression test in staging before upgrade.
4. **Security**:
- Streamable HTTP endpoint at `/mcp`, other custom APIs don't share cookies;
- Public only expose 443 TLS, internal `ClusterIP` communication via H2C.
5. **Monitoring**: Prometheus + OpenTelemetry Exporter; key metrics: `tool_invocation_total`, `tool_latency_seconds`, `resource_hit_total`.