Building & Deploying MCP Best Practices
Ten practices for building, securing, and versioning production MCP servers.
Use this list when you scaffold a new server, promote stdio to HTTP, or review an MCP change before merge.
How to Use This Checklist
- Work top to bottom; protocol hygiene before fancy primitives.
- Tick items that are true in production or the target host, not only in a demo.
- Re-run after transport changes, new tools, or auth provider swaps.
- Pair each unchecked item with an owner and a date.
A - Protocol and Design
- 1. Keep tools small, typed, and well described. One capability per tool; JSON Schema / Zod bounds; descriptions that tell the model when not to call.
- 2. Separate tools, resources, and prompts on purpose. Actions vs read-only context vs templates; do not hide mutations inside "read" resources.
- 3. Never pollute stdio stdout. Logs and banners go to stderr (or a file). CI should fail on framing-breaking output for stdio builds.
B - Quality and Change Control
- 4. Smoke-test with a real MCP client in CI. Initialize, list tools, golden-path
call_tool, plus invalid args. Manual host checks alone are not enough. - 5. Version server identity and freeze schemas. Bump
versionon breaking tool renames; snapshot tool names/required fields so agents do not silently break. - 6. Spill large outputs. Threshold + sandbox/object storage + windowed reader (or resource URI). Do not paste multi-megabyte blobs into the model.
C - Remote and Security
- 7. Treat remote HTTP as a public API surface. Streamable HTTP behind TLS; validate
Origin; forward session and protocol version headers correctly. - 8. Require OAuth-grade auth for non-local HTTP. Resource metadata, bearer validation, audience checks, least-privilege scopes per tool class. Session id is not auth.
- 9. Least privilege for side effects. Scoped credentials inside handlers; no blanket shell; rate-limit by identity; audit tool name, subject, and latency.
D - Operations
- 10. Operate with health, limits, and rollback. Dedicated
/healthz, timeouts on outbound calls, retention for spills, one-command rollback image, and dual-mode (stdio + HTTP) only when both are tested.
Applying the Habits in Order
| Stage | Habits | Exit criterion |
|---|---|---|
| Design | 1-3 | Tool map + primitive roles + stdio logging rules written |
| Build | 4-6 | CI smoke green; schema freeze; spill path documented |
| Expose | 7-9 | HTTPS + auth + scopes reviewed |
| Run | 10 | Health, timeouts, rollback rehearsed |
Quick Anti-Patterns
- Mega-tool that wraps your entire product API in one call.
console.log/printon stdio "just for debug" left enabled.- Public
/mcpwithout bearer validation. - Tokens accepted without audience/resource checks.
- Returning entire database exports as tool text.
- Different absolute paths in CI vs host config.
- Sticky-session blindness when scaling HTTP replicas.
- Auth logging that prints
Authorizationheaders.
Minimal Reference Snippet
# Patterns: stderr logs, small tool, spill hook, transport switch
import logging, os, sys
from mcp.server.fastmcp import FastMCP
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
mcp = FastMCP("prod-server")
@mcp.tool()
def ping() -> str:
"""Liveness check; no side effects."""
return "pong"
if __name__ == "__main__":
transport = os.environ.get("MCP_TRANSPORT", "stdio")
mcp.run(transport=transport) # verify allowed values at buildFAQs
Which three habits are non-negotiable?
Safe stdio logging (3), automated MCP smoke tests (4), and auth for remote HTTP (8). Without those, the rest is decoration.
Do local stdio servers need OAuth?
Usually no. Use env-injected secrets and OS user isolation. Add OAuth when the server is remote HTTP.
How often should schema freezes update?
On every intentional tool rename or required-field change. Unexpected freezes failures should block merge.
Is Streamable HTTP mandatory?
For new remote deploys it is the current standard. Support legacy HTTP+SSE only with a documented compatibility path (verify at build).
Where do resources fit if we only have tools today?
Add resources when agents repeatedly need the same large read-only context. Until then, habits 1 and 6 keep tools usable.
How do we review MCP PRs quickly?
Check tool descriptions, schemas, tests, auth impact, and whether outputs can grow without bound.
Should every server offer prompts?
Only when hosts will surface them. Empty prompt support is optional noise.
What metrics matter in production?
Tool success rate, latency p95, auth failure rate, spill rate, and session error rate (bad session / version mismatch).
Related
Related: Building & Deploying MCP Basics
Related: Anatomy of an MCP Server: Handlers, Schemas, and Transport
Related: Auth and Access Control for Remote MCP Servers
Related: Handling Large Tool Outputs: Auto-Spill to Sandbox Files
Stack versions: Pins from the category manifest (verify at build): OpenRouter (~315+ models, July 2026 pricing/fees); LangGraph 1.0+; CrewAI 1.14+; Microsoft Agent Framework 1.0; Vercel AI SDK 6; Pydantic AI (latest); LlamaIndex (latest); OpenAI Agents SDK (latest + MCP); MCP (Linux Foundation governance); A2A (HTTP+SSE+JSON-RPC 2.0); Solana
@solana/web3.js+@solana/spl-token.