Skip to content

Conversation

@apetti1920
Copy link

Overview

This PR adds HTTP/SSE transport support to the MCP server and consolidates Docker image builds into a unified multi-mode image. The server now supports both local stdio clients (Claude Desktop) and remote HTTP/SSE clients (web applications, APIs, distributed systems).

Why HTTP/SSE Server?

Problem: stdio Transport Limitations

The existing MCP server uses stdio transport (JSON-RPC over stdin/stdout), which works well for local clients but has significant limitations:

stdio transport challenges:

  • Local only - Cannot be accessed remotely over a network
  • Single client - One process per client, no connection sharing
  • No web support - Browser-based MCP clients cannot use stdin/stdout
  • Deployment complexity - Requires process spawning for each connection
  • No load balancing - Cannot distribute load across instances

Solution: HTTP/SSE Transport

The HTTP/SSE server enables remote MCP use cases:

  • Network-accessible - Deploy centrally, access from anywhere
  • Multi-client - Multiple clients can connect to the same server
  • Web-compatible - Browser-based MCP clients can connect via HTTP/SSE
  • Scalable - Deploy behind load balancers, horizontal scaling
  • Cloud-native - Kubernetes, ECS, Docker Swarm ready
  • Session management - Built-in session lifecycle and cleanup

Architecture

HTTP/SSE Proxy Design

The implementation uses a proxy architecture to maintain code reuse:

┌─────────────────┐         ┌──────────────────┐         ┌─────────────────┐
│   MCP Client    │◄───────►│  mcp-http-proxy  │◄───────►│   mcp-server    │
│  (Web/Remote)   │  HTTP   │   (Translator)   │  stdio  │  (Core Logic)   │
└─────────────────┘   SSE   └──────────────────┘         └─────────────────┘

Benefits:

  • Core MCP logic (mcp-server) remains transport-agnostic
  • Same business logic serves both stdio and HTTP clients
  • HTTP proxy handles protocol translation and session management
  • Zero code duplication between transport modes

Key Components

  1. mcp-server - Existing stdio MCP server (unchanged)

    • incident.io API integration
    • Tool implementations
    • JSON-RPC protocol handling
  2. mcp-http-proxy (new) - HTTP/SSE transport layer

    • Spawns mcp-server process per session
    • Translates HTTP/SSE ↔ stdio communication
    • Session lifecycle management with reference counting
    • Protocol state machine enforcement
  3. entrypoint.sh - Mode selector

    • MCP_TRANSPORT_MODE=stdio → Direct mcp-server execution
    • MCP_TRANSPORT_MODE=http → Proxy wrapper with HTTP server

Changes Made

1. New Files

mcp-http-proxy.go - HTTP/SSE server implementation (~600 lines)

  • HTTP endpoints: /sse, /message, /health
  • Session management with concurrent client support
  • Protocol state tracking (uninitialized → initializing → ready)
  • Graceful cleanup with goroutine synchronization

entrypoint.sh - Transport mode selector

  • Detects MCP_TRANSPORT_MODE environment variable
  • Routes to appropriate binary (mcp-server or mcp-http-proxy)

docs/HTTP_SERVER.md - Comprehensive server documentation (~400 lines)

  • Architecture explanation
  • Multi-architecture builds with docker buildx
  • Runtime configuration and deployment examples
  • Security best practices

docs/QUICK_REFERENCE.md - Quick command reference (~200 lines)

  • Common build and run commands
  • Environment variable reference
  • Troubleshooting guide

2. Unified Dockerfile

Before: Basic stdio-only image
After: Unified image supporting both stdio and HTTP modes

Structure:

# Builder stage - compiles both binaries
FROM golang:1.21-alpine AS builder
RUN go build -o mcp-server ./cmd/mcp-server
RUN go build -o mcp-http-proxy ./mcp-http-proxy.go

# Final stage - includes both binaries
FROM alpine:latest
COPY --from=builder /app/mcp-server .
COPY --from=builder /app/mcp-http-proxy .
COPY entrypoint.sh .

Features:

  • Both binaries included in single image
  • Non-root user (incidentio:1001) for security
  • Runtime mode switching via environment variable
  • Multi-architecture support (amd64, arm64)

3. Documentation Updates

README.md:

  • Added HTTP server guide and quick reference links
  • Added direct Docker usage example for Claude Desktop
  • Organized documentation into "Getting Started" and "Advanced Topics"

Building and Running

Build

Simple build:

docker build -t incidentio-mcp:latest .

Multi-architecture build:

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --tag your-registry/incidentio-mcp:v1.0.0 \
  --push \
  .

Run - HTTP Server Mode (Default)

Basic:

docker run --rm -p 8080:8080 \
  -e INCIDENT_IO_API_KEY="your-api-key" \
  incidentio-mcp:latest

HTTP endpoints:

  • GET /sse?session=<id> - Establish SSE stream for receiving messages
  • POST /message?session=<id> - Send JSON-RPC messages
  • GET /health - Health check endpoint

Run - stdio Mode

For Claude Desktop:

docker run --rm -i \
  -e MCP_TRANSPORT_MODE=stdio \
  -e INCIDENT_IO_API_KEY="your-api-key" \
  incidentio-mcp:latest

Environment Variables

Variable Values Default Description
MCP_TRANSPORT_MODE stdio, http, sse http Transport mode selection
MCP_HTTP_PORT 1-65535 8080 HTTP server port
INCIDENT_IO_API_KEY string (required) API authentication
MCP_DEBUG 0, 1 0 Enable MCP protocol debug logs
INCIDENT_IO_DEBUG 0, 1 0 Enable API debug logs

Testing

stdio Mode Verification

# Test MCP protocol over stdio
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | \
  docker run --rm -i -e MCP_TRANSPORT_MODE=stdio -e INCIDENT_IO_API_KEY=test incidentio-mcp:latest

Expected output:

{"jsonrpc":"2.0","result":{"capabilities":{"tools":{"listChanged":false}},"protocolVersion":"2024-11-05","serverInfo":{"name":"incidentio-mcp-server","version":"1.0.0"}},"id":1}

HTTP Mode Verification

# Start server
docker run --rm -p 8080:8080 -e INCIDENT_IO_API_KEY=test incidentio-mcp:latest &

# Health check
curl http://localhost:8080/health
# Expected: {"status":"healthy","sessions":0}

# Establish SSE stream (terminal 1)
curl -N http://localhost:8080/sse?session=test

# Send initialize (terminal 2)
curl -X POST http://localhost:8080/message?session=test \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}'

Security Considerations

Non-Root User

  • Image includes user incidentio:1001
  • Run with -u 1001:1001 flag for security
  • Prevents privilege escalation

API Key Management

# Load from file (recommended)
-e INCIDENT_IO_API_KEY="$(cat /path/to/secret.txt)"

## Backward Compatibility

### ✅ Zero Breaking Changes

- Existing stdio usage continues to work unchanged
- Environment variable defaults to HTTP mode
- Docker image can be used as drop-in replacement
- All existing tools and functionality preserved

### Migration Path

**For stdio users (Claude Desktop, local clients):**
- No changes required if running natively
- If using Docker, add `-e MCP_TRANSPORT_MODE=stdio`

**For new HTTP users:**
- Use default settings (HTTP mode enabled)
- Configure network port and API key
- Connect clients to HTTP/SSE endpoints

## Testing Checklist

- [x] stdio mode works with Claude Desktop
- [x] HTTP mode serves multiple concurrent clients
- [x] Multi-architecture builds (amd64, arm64)
- [x] Docker Compose deployment
- [x] Environment variable configuration
- [x] Health check endpoint
- [x] Session lifecycle management
- [x] Protocol state machine enforcement
- [x] Graceful shutdown and cleanup
- [x] Non-root user security
- [x] Documentation accuracy

## Related Files

**Core implementation:**
- `mcp-http-proxy.go` - HTTP/SSE server
- `entrypoint.sh` - Mode selector
- `Dockerfile` - Unified image build

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant