cd /news/ai-infrastructure/mcp-series-07-enterprise-deployment-… · home topics ai-infrastructure article
[ARTICLE · art-62149] src=dev.to ↗ pub= topic=ai-infrastructure verified=true sentiment=· neutral

MCP Series (07): Enterprise Deployment — Security, Authentication, and Version Management

A developer outlines enterprise deployment strategies for MCP servers, covering API key, OAuth 2.0, and mTLS authentication methods. The post provides Docker and systemd configurations for production-grade security, including network isolation and resource limits. It also addresses version management challenges when multiple agent sessions connect to an MCP server.

read4 min views1 publishedJul 16, 2026

A local MCP Server takes one command: python server.py

. Enterprise production adds three required problems:

Simplest option, suitable for service-to-service calls inside a corporate network:

import os
from mcp.server import Server

EXPECTED_API_KEY = os.environ.get("MCP_API_KEY")
if not EXPECTED_API_KEY:
    raise RuntimeError("MCP_API_KEY environment variable is required")

server = Server("jira-tools")

For HTTP transport (non-stdio), validate in middleware:

from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request

class ApiKeyMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        key = (request.headers.get("X-API-Key")
               or request.headers.get("Authorization", "").removeprefix("Bearer "))
        if key != os.environ["MCP_API_KEY"]:
            return Response("Unauthorized", status_code=401)
        return await call_next(request)

Use when different users need access to different subsets of data (different Jira projects per user):

The MCP spec (2025) defines OAuth integration interfaces. The Host (Claude Desktop / Claude Code) acquires the OAuth token during user login and passes it to the Server on each MCP connection.

For finance, healthcare, or government scenarios with strict data security requirements — mutual certificate verification:

import ssl

ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain("/certs/server.crt", "/certs/server.key")
ssl_context.load_verify_locations("/certs/ca.crt")
ssl_context.verify_mode = ssl.CERT_REQUIRED  # require client cert

Authentication selection:

Internal service calls (same network)         → API Key + network isolation
Cross-org or user-level permission needs      → OAuth 2.0
High security (finance, healthcare, gov)      → mTLS
FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

RUN useradd -r -s /bin/false mcpuser
USER mcpuser

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
    CMD python -c "import sys; print('healthy')" || exit 1

CMD ["python", "jira_server.py"]
version: "3.9"

services:
  jira-mcp:
    build: .
    image: jira-mcp-server:1.3.0
    restart: unless-stopped          # auto-restart on crash
    environment:
      - MCP_API_KEY=${MCP_API_KEY}   # injected from .env or Secrets
      - JIRA_URL=${JIRA_URL}
      - JIRA_TOKEN=${JIRA_TOKEN}
      - LOG_LEVEL=INFO
    volumes:
      - ./logs:/app/logs
    networks:
      - mcp-internal                 # internal only
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: "256M"
        reservations:
          memory: "128M"
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "5"

networks:
  mcp-internal:
    driver: bridge
    internal: true                   # no external network access

Key configuration points:

restart: unless-stopped

: restart on crash; stay stopped only on manual stopinternal: true

: Docker network with no outbound connection — only containers on the same network can reach the Server

[Unit]
Description=Jira MCP Server
After=network.target

[Service]
Type=simple
User=mcpuser
WorkingDirectory=/opt/jira-mcp
ExecStart=/usr/bin/python3 /opt/jira-mcp/jira_server.py
Restart=always
RestartSec=5
Environment="MCP_API_KEY=your-key"
Environment="JIRA_TOKEN=your-token"
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
systemctl enable jira-mcp
systemctl start jira-mcp
journalctl -u jira-mcp -f    # live log stream

Multiple Agent sessions connect to MCP Server v1.2. You need to release v1.3 (adds a new tool) without dropping those connections.

services:
  jira-mcp-stable:
    image: jira-mcp-server:1.2.0     # current stable, serves existing connections
    restart: unless-stopped
    networks: [mcp-internal]

  jira-mcp-canary:
    image: jira-mcp-server:1.3.0     # new version, accepts new connections
    restart: unless-stopped
    networks: [mcp-internal]
    deploy:
      replicas: 1

Point the Host config at the canary version first. Monitor it. Then switch stable:

{
  "mcpServers": {
    "jira": {
      "command": "docker",
      "args": ["exec", "jira-mcp-canary", "python", "jira_server.py"]
    }
  }
}
MAJOR.MINOR.PATCH

MAJOR: breaking changes
  → Remove a tool, rename a tool, change required inputSchema fields
  → Agent code that depends on this tool must update in sync

MINOR: backward-compatible additions
  → Add a tool, add optional parameters, extend return fields
  → Existing Agent code continues working; new features opt-in

PATCH: behavior-preserving fixes
  → Bug fixes, performance improvements, logging changes
  → Transparent upgrade

Declare the version in Server code:

server = Server(
    "jira-tools",
    version="1.3.0"   # returned to Client in initialize response
)
python
@server.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "search_jira":   # old tool name
        logger.warning(
            "Tool 'search_jira' is deprecated. "
            "Use 'search_issues' instead. Removing in v2.0.0."
        )
        return await _search_issues(arguments)  # delegate to new implementation

Keep the old tool name for 90 days, log usage, then remove it in the MAJOR version.

Authentication and authorization

Network isolation

internal: true

— Server has no direct outbound accessTool security

Operations

restart: unless-stopped

or systemd supervision ensures availabilityrestart: unless-stopped

(crash recovery) + internal: true

network (isolation) + resource limits (stability)Check out PrimeSkills — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.

Find more useful knowledge and interesting products on my Homepage

── more in #ai-infrastructure 4 stories · sorted by recency
── more on @mcp 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/mcp-series-07-enterp…] indexed:0 read:4min 2026-07-16 ·