Docker MCP Gateway vs Claude Desktop Native MCP
Docker MCP Gateway vs Claude Desktop Native MCP
Overview
When building custom MCP (Model Context Protocol) servers, you have two deployment approaches: Docker MCP Gateway (containerized) or Claude Desktop native (direct process). This article explains the tradeoffs between speed, security, isolation, and management complexity.
What Are These Approaches?
Docker MCP Gateway
- Architecture: MCP servers run in Docker containers, managed by Docker's MCP gateway
- Configuration: Servers defined in
~/.docker/mcp/catalogs/*.yamland enabled in~/.docker/mcp/config.yaml - Process model: Gateway runs as single process, spawns containers on-demand
- Tool namespace: Tools prefixed with
MCP_DOCKER_servername_toolname
Claude Desktop Native
- Architecture: MCP servers run as direct child processes of Claude Desktop/OpenCode
- Configuration: Servers defined in
~/Library/Application Support/Claude/claude_desktop_config.json(Claude Desktop) or~/.config/opencode/opencode.json(OpenCode) - Process model: Each server is a separate stdio process
- Tool namespace: Tools prefixed with
servername_toolname
Comparison Matrix
| Dimension | Docker Gateway | Native Approach | Winner |
|---|---|---|---|
| Speed | ~50-100ms overhead for container startup/communication | ~5-10ms process spawn + stdio | Native |
| Security | Full process isolation, network policies, secrets blocking | OS-level process isolation only | Docker |
| Resource limits | Configurable CPU/memory limits per server | No built-in limits | Docker |
| Auto-restart | Gateway manages lifecycle, auto-restarts on crash | Client restarts on next tool call | Docker |
| Tool reload | Requires gateway restart or MCP management tools | Requires client restart | Tie |
| Network isolation | Can block external network access (--block-network) | Full network access | Docker |
| Development iteration | Must rebuild/restart gateway for code changes | Immediate - just edit TypeScript/Python files | Native |
| Multi-client access | Can run gateway on TCP port, serve multiple clients | Single client only (stdio) | Docker |
| Debugging | Container logs + gateway logs (more complex) | Direct stderr logs (simpler) | Native |
| Deployment | Catalog-based, versioned, shareable configs | Manual JSON config per machine | Docker |
Speed Analysis
Docker Gateway Overhead
Sources of latency:
- Container spawn: 30-50ms (if not long-lived)
- Gateway routing: 10-20ms (JSON-RPC over TCP/stdio bridge)
- Network stack: 5-10ms (even localhost)
- Total: ~50-100ms per tool call
Mitigation:
- Use
--long-livedflag to keep containers running - Reduces overhead to ~10-20ms after initial spawn
Native Approach
Sources of latency:
- Process spawn: 5-10ms (first call only, then keeps stdio open)
- JSON-RPC over stdio: 1-2ms
- Total: ~5-10ms first call, <2ms subsequent calls
Real-world impact:
- For interactive workflows (article creation, search), 50-100ms is imperceptible
- For batch operations (100+ tool calls), Docker adds ~5-10 seconds overhead
- For latency-sensitive workflows (real-time code generation), native wins
Security & Isolation
Docker Gateway Advantages
- Process isolation: Each server in separate container namespace
- Network policies: Can block internet access, allow only localhost
- Secrets blocking: Gateway can filter secrets from being sent to tools (
--block-secrets) - Resource limits: Prevent runaway servers from consuming all CPU/memory
- Read-only filesystem: Can mount code as read-only
Use case: Third-party MCP servers from untrusted sources
Native Approach Risks
- Full filesystem access: Server can read/write any file the client can
- Full network access: Can make arbitrary HTTP requests
- No resource limits: Buggy server can freeze your machine
- Process escape: Malicious server could spawn additional processes
Use case: First-party servers you fully control and trust
Development Workflow
Docker Gateway Iteration
bash# Edit server code vim ~/.config/mcp-servers/myserver/index.ts # Rebuild catalog (if structure changed) docker mcp gateway run --dry-run # Validate config # Restart gateway to reload pkill -f "docker.*mcp.*gateway" # OpenCode will auto-restart gateway on next tool call # Or use restart tool (if implemented) myserver_restart_mcp_server()
Pain points:
- Gateway restart required for code changes
- Container builds add complexity for non-Docker users
- Harder to debug with container layers
Native Approach Iteration
bash# Edit server code vim ~/.config/mcp-servers/myserver/index.ts # Changes live immediately (TypeScript interpreted by Bun) # Server restarts automatically on next tool call
Pain points:
- Must manually manage dependencies (npm/bun install)
- No isolation if server crashes or has memory leaks
When to Use Each Approach
Use Docker Gateway When:
- Security matters: Third-party servers or untrusted code
- Resource limits needed: Servers that might consume excessive CPU/memory
- Multi-client access: Want to share MCP server across machines (TCP mode)
- Production deployment: Need versioning, rollback, health checks
- Team environments: Shareable catalog configs across developers
Use Native Approach When:
- Rapid development: Building/testing your own MCP servers
- Performance critical: Batch operations or low-latency requirements
- Simple setup: Single developer, local-only usage
- Full system access needed: Server needs to interact with local dev environment
- Debugging: Need direct stderr logs without container complexity
Hybrid Approach (Recommended)
Development: Native approach for your own servers
json{ "mcp": { "myserver": { "type": "local", "command": ["bun", "run", "/path/to/server/index.ts"] } } }
Production: Docker gateway for all servers
yaml# ~/.docker/mcp/catalogs/personal.yaml registry: myserver: description: My custom MCP server command: ["bun", "run", "/path/to/server/index.ts"] tools: - name: my_tool
yaml# ~/.docker/mcp/config.yaml myserver: {}
Migration path:
- Develop with native approach (fast iteration)
- Add to Docker catalog once stable
- Test with
--long-livedflag to measure overhead - Switch OpenCode config to use gateway only
- Remove native config entry
Real-World Example: The Sly Professor Migration
We recently migrated the theslyprofessor MCP server from native to Docker gateway:
Before (Native):
json{ "mcp": { "theslyprofessor": { "type": "local", "command": ["bun", "run", "/Users/ntiruviluamala/.config/mcp-servers/theslyprofessor/index.ts"], "enabled": true } } }
After (Docker Gateway):
yaml# ~/.docker/mcp/catalogs/personal.yaml name: personal displayName: Personal MCP Servers registry: theslyprofessor: title: The Sly Professor description: Personal productivity MCP server with ARF routing, vault management, chezmoi integration, and email workflow version: "2.0.0" command: - "bun" - "run" - "/Users/ntiruviluamala/.config/mcp-servers/theslyprofessor/index.ts" tools: - name: find_context - name: refresh_arf_catalog # ... 17 total tools
yaml# ~/.docker/mcp/config.yaml theslyprofessor: {}
json// ~/.config/opencode/opencode.json { "mcp": { "MCP_DOCKER": { "type": "local", "command": ["docker", "mcp", "gateway", "run"], "enabled": true } } }
Why we migrated:
- Better lifecycle management: Gateway auto-restarts on crashes
- Centralized catalog: Easier to share configs across machines (chezmoi managed)
- Tool reload mechanism: Added
restart_mcp_servertool for seamless updates - Future-proofing: Easier to migrate to TCP mode for remote access
Tradeoffs accepted:
- ~50ms overhead per tool call (imperceptible for our use cases)
- Slightly more complex debugging (container logs)
- Gateway restart required for code changes (mitigated by
restart_mcp_servertool)
Recommendations
For individual developers building personal tools: โ Start native, migrate to Docker gateway once stable
For teams sharing MCP infrastructure: โ Docker gateway from day one (use catalog versioning)
For AI coding assistants (OpenCode, Claude Desktop): โ Native for first-party tools, Docker gateway for third-party
For latency-sensitive applications (real-time code generation):
โ Native approach, use --long-lived Docker if security needed
For untrusted third-party servers:
โ Docker gateway always, enable --block-network and --block-secrets
Future Considerations
Docker Desktop evolution:
- Docker may improve container startup performance (็ฎๅ ~50ms)
- Gateway may add hot-reload for code changes
- TCP mode may become default for better multi-client support
MCP protocol evolution:
- Protocol may add built-in tool reload notifications
- May support WebSocket transport for lower latency
- May add resource limit hints in protocol itself
Tooling improvements:
- Better debugging tools for containerized MCP servers
- Catalog validation and testing frameworks
- Performance profiling for tool call latency
Conclusion
Neither approach is universally better - the choice depends on your priorities:
- Speed: Native wins (~5-10ms vs ~50-100ms)
- Security: Docker wins (full isolation vs process-only)
- Development: Native wins (live reload vs gateway restart)
- Production: Docker wins (lifecycle management, versioning)
Most developers should start with native for rapid development, then migrate to Docker gateway once their server is stable and they want better lifecycle management and security isolation.
The key insight: Docker's 50ms overhead is negligible for interactive AI workflows, while the security and management benefits are substantial for production deployments.