Sign inSign up

mekayelanik/serena-mcp

By mekayelanik

Updated about 1 month ago

Image
0

3.3K

mekayelanik/serena-mcp repository overview

Serena Logo

Serena MCP Server

Docker Pulls Docker Stars GHCR License: GPL-3.0 Platforms GitHub Stars GitHub Forks GitHub Issues

Unofficial Multi-Architecture Docker Image for LSP-Backed Semantic Code Intelligence

Note: This is an unofficial community-maintained Docker image. It is not affiliated with or endorsed by Oraios GmbH, the creators of Serena.

Table of Contents


Buy Me A Coffee


Overview

Serena is an LSP-backed MCP providing 30+ tools for semantic code intelligence across 30+ languages (Python, TypeScript, Rust, Go, Java, C/C++, Ruby, PHP, Kotlin, and more). Fills gaps where tree-sitter-based code-graph tools (like CodeGraphContext, GitNexus, Narsil) stop: real symbol resolution via language servers, cross-file rename, symbol-aware edits.

This Docker image wraps serena-agent with mcp-proxy for HTTP/SSE transport and HAProxy for TLS, rate limiting, API key auth, and CORS — matching the transport layer used by all other MCP images in this ecosystem.

Stdio bridge update (May 2026): the image now ships with mcp-proxy instead of supergateway. The new bridge is stateful by default and multiplexes all client sessions through a single backend stdio child via JSON-RPC IDs (no spawn-per-session), which eliminates a class of memory leaks observed with supergateway in stateless streamableHttp mode (supercorp-ai/supergateway#108). Empirical measurement on this image: 4.6× lower total RSS (1247 MiB → 268 MiB) under bursty unreused-session traffic. The Node.js runtime is no longer required.

Key Features
  • Multi-Architecture Support - Native support for x86-64 and ARM64
  • LSP-Backed Intelligence - Real symbol resolution via pyright, gopls, rust-analyzer, and more
  • Modern Transport Layer - SHTTP (Streamable HTTP), SSE, and pure stdio via mcp-proxy (stateful by default, single-backend multiplex)
  • Secure by Design - HAProxy with TLS, API key auth, rate limiting, IP allowlist/blocklist, CORS
  • Context-Aware - Adapts tool exposure per MCP client (Claude Code, Cursor, VS Code, ChatGPT, etc.)
  • Production Ready - Stable releases with comprehensive CI/CD and multi-registry publishing
  • Easy Configuration - Simple environment variable setup
  • Predictable Memory - prlimit per-child cap and HAProxy concurrency caps prevent runaway LSPs from OOM-ing the host

Supported Architectures

ArchitectureTag
x86-64amd64
ARM64arm64

Multi-architecture images are available — Docker automatically selects the correct platform.


Available Tags

TagDescription
stableProduction-ready, tested release
latestMost recent release
1.1.2Specific version
betaPre-release testing

System Requirements
ResourceMinimumRecommended
CPU1 core2+ cores
RAM512 MB1 GB+
Disk500 MB1 GB+
Docker23.0+Latest
Docker Compose2.0+Latest

Quick Start

services:
  serena-mcp:
    image: mekayelanik/serena-mcp:latest
    container_name: serena-mcp
    restart: unless-stopped
    ports:
      - "9121:9121"
    volumes:
      - /path/to/your/project:/data:rw
      - serena-config:/config
      - serena-cache:/home/serena/.cache
    environment:
      - PORT=9121
      - PUID=1000
      - PGID=1000
      - TZ=UTC
      - PROTOCOL=SHTTP
      - SERENA_PROJECT=/data
      - SERENA_CONTEXT=desktop-app
      - SERENA_TRANSPORT=stdio
      - SERENA_LOG_LEVEL=INFO
      - ENABLE_HTTPS=false
      - HTTP_VERSION_MODE=auto
      # mcp-proxy session model. Stateful by default — one stdio child shared
      # across all sessions (multiplexed via JSON-RPC ids). Set to "true" only
      # if full per-request isolation is required (memory-hostile).
      - MCP_PROXY_STATELESS=false
      # Cap virtual memory of the Serena stdio child (MiB; 0 disables)
      - SERENA_MAX_MEM_MB=4096
      # HAProxy concurrency caps (0 disables) — bound burst-spawn risk
      - HAPROXY_FRONTEND_MAXCONN=64
      - HAPROXY_SERVER_MAXCONN=16
      # Optional: require Bearer token auth at HAProxy layer
      # - API_KEY=replace-with-strong-secret
      # Optional: CORS origins
      # - CORS=*

volumes:
  serena-config:
    driver: local
  serena-cache:
    driver: local
Docker CLI
docker run --rm -i \
  -v /path/to/your/project:/data:rw \
  -e SERENA_PROJECT=/data \
  mekayelanik/serena-mcp:latest
Access Endpoints
ProtocolEndpointDescription
SHTTPhttp://host-ip:9121/mcpStreamable HTTP (default; exposed simultaneously)
SSEhttp://host-ip:9121/sseServer-Sent Events (exposed simultaneously)
Healthhttp://host-ip:9121/healthzHealth check (answered by HAProxy, sub-millisecond)

WebSocket transport was dropped in the migration to mcp-proxy. Setting PROTOCOL=WS will now fail at startup with a clear message. Use SHTTP or SSE instead.


Configuration

Environment Variables
VariableDefaultDescription
PORT9121External server port
PUID1000User ID for file permissions
PGID1000Group ID for file permissions
TZUTCContainer timezone (TZ database)
PROTOCOLSHTTPTransport protocol (SHTTP, SSE, STDIO). WS is no longer supported.
SERENA_PROJECT/dataProject path (mapped to --project)
SERENA_CONTEXTdesktop-appClient context (see Context Selection)
SERENA_TRANSPORTstdioSerena transport (stdio, sse, streamable-http)
SERENA_PORT9121Serena port when transport != stdio
SERENA_MODES(empty)Comma-separated modes fed to --mode
SERENA_LOG_LEVELINFOLog level (DEBUG, INFO, WARNING, ERROR)
MCP_PROXY_STATELESSfalseWhen true, mcp-proxy disables Mcp-Session-Id issuance on /mcp (per-request isolation). Default false = stateful, single backend reused across all sessions.
SERENA_MAX_MEM_MB0Cap virtual memory of the Serena stdio child via prlimit --as (MiB). 0 disables the cap.
HAPROXY_FRONTEND_MAXCONN0HAProxy frontend maxconn. Bounds total concurrent connections accepted. 0 disables.
HAPROXY_SERVER_MAXCONN0HAProxy backend server maxconn. Bounds in-flight requests to the upstream mcp-proxy. 0 disables.
API_KEY(empty)Enables Bearer token auth (Authorization: Bearer <API_KEY>)
CORS(empty)Comma-separated CORS origins, supports *
ENABLE_HTTPSfalseEnables TLS termination in HAProxy
TLS_CERT_PATH/etc/haproxy/certs/server.crtTLS cert path
TLS_KEY_PATH/etc/haproxy/certs/server.keyTLS private key path
TLS_PEM_PATH/etc/haproxy/certs/server.pemCombined PEM file used by HAProxy
TLS_CNlocalhostCN for auto-generated certificate
TLS_SANDNS:<TLS_CN>SAN for auto-generated certificate
TLS_DAYS365Auto-generated cert validity period
TLS_MIN_VERSIONTLSv1.3Minimum TLS protocol (TLSv1.2 or TLSv1.3)
HTTP_VERSION_MODEautoauto, all, h1, h2, h3, h1+h2
RATE_LIMIT0Max requests per RATE_LIMIT_PERIOD per IP (0 = disabled)
RATE_LIMIT_PERIOD10sSliding window for rate limiting (e.g., 10s, 1m, 1h)
MAX_CONNECTIONS_PER_IP0Max concurrent connections per IP (0 = disabled)
IP_ALLOWLIST(empty)Comma-separated IPs/CIDRs to allow (all others blocked)
IP_BLOCKLIST(empty)Comma-separated IPs/CIDRs to block
HTTPS and HTTP Version Notes
  • When ENABLE_HTTPS=false, only HTTP/1.1 is available regardless of HTTP_VERSION_MODE
  • When ENABLE_HTTPS=true, the server auto-generates a self-signed certificate if none is provided
  • HTTP/3 (QUIC) requires ENABLE_HTTPS=true and a HAProxy build with QUIC support
  • Valid HTTP_VERSION_MODE values: auto (h1+h2+h3 if available), h1, h2, h3, h1+h2, all
API Key Authentication Notes
  • API key auth protects access but does not encrypt traffic — use with ENABLE_HTTPS=true for security
  • Health endpoint (/healthz) always bypasses authentication
  • Key length must be 5-256 characters
Rate Limiting and IP Access Control
  • RATE_LIMIT and RATE_LIMIT_PERIOD work together (e.g., RATE_LIMIT=100 + RATE_LIMIT_PERIOD=1m = 100 req/min)
  • MAX_CONNECTIONS_PER_IP limits concurrent connections (useful for preventing abuse)
  • IP_ALLOWLIST and IP_BLOCKLIST accept comma-separated IPs or CIDR ranges
  • When both are set, blocklist is checked first, then allowlist

Serena indexes and caches per-project. Default LSP settings can grow the cache to GiB scale on TypeScript / JavaScript projects, because the bundled language server indexes node_modules (see upstream oraios/serena#944). Drop the following file at <project>/.serena/config.toml before first run to keep the footprint bounded:

[indexing]
exclude_patterns = [
  "**/node_modules/**",
  "**/.next/**",
  "**/dist/**",
  "**/build/**",
  "**/.venv/**",
  "**/__pycache__/**",
  "**/.git/**",
  "**/coverage/**",
  "**/target/**",
  "**/.serena/cache/**",
]

[cache]
max_size_mb = 200
prune_after_days = 3
max_memory_mb = 1024

[language_servers.typescript]
# Single most impactful line for TS/JS repos — keeps the TS LSP from
# indexing node_modules and library declaration files.
exclude_libs = true
max_memory_mb = 1024
worker_threads = 2

[language_servers.python]
max_memory_mb = 512

This file is per-project (lives inside the mounted volume), so it cannot be baked into the image. Clear any stale cache with rm -rf <project>/.serena/cache/ after changing the limits.

Memory & Concurrency Tuning

mcp-proxy runs the Serena backend as a single long-lived stdio child and multiplexes all client sessions through it via JSON-RPC ids. This caps the expected memory footprint; the knobs below cap the worst case:

  • MCP_PROXY_STATELESS=false (default) — share one backend child across all sessions. Recommended for almost every deployment. Flip to true only when you genuinely need per-request isolation (and accept the per-request transport-instance cost).
  • SERENA_MAX_MEM_MB=4096 — caps the virtual-memory size of the Serena child via prlimit --as. A runaway LSP gets OOM-killed by the kernel before it exhausts the host. The recommended starting value is 4 GiB; raise if you index very large monorepos.
  • HAPROXY_FRONTEND_MAXCONN=64 + HAPROXY_SERVER_MAXCONN=16 — bound concurrent connections at the HAProxy layer so a burst cannot saturate the upstream stdio bridge.
  • /healthz is answered directly by HAProxy with a local 200 — Docker's container healthcheck no longer depends on upstream MCP readiness, so a slow LSP startup will not mark the container unhealthy.
User & Group IDs

Set PUID and PGID to match your host user to avoid permission issues with mounted volumes:

-e PUID=$(id -u) -e PGID=$(id -g)
Timezone Examples
-e TZ=America/New_York
-e TZ=Europe/London
-e TZ=Asia/Dhaka

Context Selection

The SERENA_CONTEXT environment variable controls which tools Serena exposes, tailored per MCP client:

ClientSERENA_CONTEXTWhat it does
Claude Code CLIclaude-codeExcludes tools Claude Code already has (create/read file, shell) — tighter token budget
Cursor / WindsurfideGeneric IDE; excludes create_text_file
VS Code + CopilotvscodeAssumes internal file/shell tools; activate_project required
ChatGPT Desktopchatgpt30-tool cap + short descriptions
CodexcodexExcludes shell + non-symbolic edits
GitHub Copilot CLIcopilot-cliTuned for gh copilot
JetBrains AI Assistantjb-ai-assistantJetBrains-specific tool set
JetBrains Copilot pluginjb-copilot-pluginJetBrains Copilot variant
JuniejunieJetBrains Junie context
OpenAI-compat agentoaicompat-agentGeneric OpenAI-compatible agent
Google AntigravityantigravityGoogle Antigravity context
Any desktop / chat appdesktop-app (default)Full toolset — broadest compatibility

MCP Client Configuration

Transport Support
TransportProtocolUse Case
SHTTPStreamable HTTPBest for remote/multi-client setups (default). /mcp endpoint.
SSEServer-Sent EventsCompatible with older MCP clients. /sse endpoint exposed simultaneously when PROTOCOL=SHTTP or SSE.
STDIOStandard I/OSingle local client, lightest mode. Bypasses HAProxy and mcp-proxy.

WebSocket transport was removed when the image migrated from supergateway to mcp-proxy. mcp-proxy does not expose a WS output transport.

Claude Code

Configure in ~/.config/claude-code/mcp.json or project-level .mcp.json:

{
  "mcpServers": {
    "serena": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i",
        "-v", "${PWD}:/data:rw",
        "-e", "SERENA_PROJECT=/data",
        "-e", "SERENA_CONTEXT=claude-code",
        "-e", "PROTOCOL=STDIO",
        "mekayelanik/serena-mcp:latest"
      ]
    }
  }
}
VS Code (Cline/Roo-Cline)
{
  "mcpServers": {
    "serena": {
      "transport": "http",
      "url": "http://host-ip:9121/mcp"
    }
  }
}
Claude Desktop App
{
  "mcpServers": {
    "serena": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i",
        "-v", "/path/to/project:/data:rw",
        "-e", "SERENA_PROJECT=/data",
        "-e", "PROTOCOL=STDIO",
        "mekayelanik/serena-mcp:latest"
      ]
    }
  }
}
Codex CLI

Configure in ~/.codex/config.json:

{
  "mcpServers": {
    "serena": {
      "transport": "http",
      "url": "http://host-ip:9121/mcp"
    }
  }
}
Codeium (Windsurf)
{
  "mcpServers": {
    "serena": {
      "transport": "http",
      "url": "http://host-ip:9121/mcp"
    }
  }
}
Cursor
{
  "mcpServers": {
    "serena": {
      "transport": "http",
      "url": "http://host-ip:9121/mcp"
    }
  }
}
Testing Configuration

Test the SHTTP endpoint:

curl -s http://localhost:9121/healthz

Transport Selection

TransportSERENA_TRANSPORTPROTOCOLPort exposedUse when
Pure stdio (lightest)stdioSTDIOnoneSingle local client; docker run -i only. Bypasses HAProxy and mcp-proxy.
SHTTP + SSE via HAProxy + mcp-proxystdioSHTTP (default)$PORT (9121)Multiple remote clients, need TLS/QUIC. /mcp and /sse are exposed simultaneously.
Direct SSE from serena (no HAProxy, no mcp-proxy)sse(any)$SERENA_PORT (9121)Dev / debug, single client
Direct Streamable HTTP from serena (MCP spec)streamable-http(any)$SERENA_PORTMCP spec-compliant HTTP client direct, no bridging
Common Env Overrides
# Minimal (most common)
-e SERENA_PROJECT=/data

# Tight for Claude Code
-e SERENA_PROJECT=/data -e SERENA_CONTEXT=claude-code

# SSE mode exposed
-e SERENA_TRANSPORT=sse -e SERENA_PORT=9121 -p 9121:9121

# Enable dashboard (bind 127.0.0.1 inside; publish only if you want remote access)
-p 127.0.0.1:24282:24282

# With API key auth at HAProxy layer
-e API_KEY=your-strong-secret -p 9121:9121

# Non-root UID matching host
-e PUID=$(id -u) -e PGID=$(id -g)

Single-Project vs Multi-Project Deploys

services:
  serena-hfe:
    image: mekayelanik/serena-mcp:latest
    environment: [SERENA_PROJECT=/data]
    volumes: [/host/hfe:/data:rw]
  serena-loinc:
    image: mekayelanik/serena-mcp:latest
    environment: [SERENA_PROJECT=/data]
    volumes: [/host/loinc:/data:rw]

Pros: crash isolation, per-project resource limits, matches other MCP containers.

Pattern 2 — One container, many projects, switch at runtime
services:
  serena:
    image: mekayelanik/serena-mcp:latest
    environment:
      - SERENA_PROJECT=/projects/hfe
    volumes:
      - /host/hfe:/projects/hfe:rw
      - /host/loinc:/projects/loinc:rw
      - serena-config:/config
volumes:
  serena-config:

Then in your MCP client, call Serena's activate_project tool with /projects/loinc to switch — no container restart. Shared LSP cache, fast switching, but crashes kill all project sessions.


Network Configuration

Comparison
ModeUse CaseIsolationComplexity
Bridge (default)Most deploymentsContainer-levelLow
HostMaximum performanceNoneMedium
MACVLANDedicated IPNetwork-levelHigh
Bridge Network (Default)
services:
  serena-mcp:
    ports:
      - "9121:9121"
Host Network (Linux Only)
services:
  serena-mcp:
    network_mode: host
    environment:
      - PORT=9121
MACVLAN Network (Advanced)
networks:
  mcp_net:
    driver: macvlan
    driver_opts:
      parent: eth0
    ipam:
      config:
        - subnet: 192.168.1.0/24
          gateway: 192.168.1.1
services:
  serena-mcp:
    networks:
      mcp_net:
        ipv4_address: 192.168.1.100

Updating

Docker Compose
docker compose pull
docker compose down && docker compose up -d
Docker CLI
docker pull mekayelanik/serena-mcp:latest
docker stop serena-mcp && docker rm serena-mcp
# Re-run your docker run command
One-Time Update with Watchtower
docker run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower --run-once serena-mcp

Check tag stable for production; <version> (e.g. 1.1.2) for pinning.


Troubleshooting

Pre-Flight Checklist
  1. Docker version 23.0+ installed
  2. Sufficient disk space (500MB minimum)
  3. Port not already in use
  4. Project volume mounted correctly
Common Issues
Container Won't Start
  • Check logs: docker logs serena-mcp
  • Verify image pulled: docker images | grep serena-mcp
  • Check port conflicts: ss -tlnp | grep 9121
Permission Errors
  • Set correct PUID/PGID: -e PUID=$(id -u) -e PGID=$(id -g)
  • Verify volume permissions: ls -la /path/to/your/project
Client Cannot Connect
  • Verify container is running: docker ps | grep serena-mcp
  • Test health endpoint: curl http://localhost:9121/healthz
  • Check firewall rules for port 9121
  • If using HTTPS, ensure certificates are valid
find_references Returns No Results
  • Ensure SERENA_PROJECT points at repo root
  • Check logs for LSP startup errors
  • First invocation may be slow while pyright indexes
LSP Startup Slow (>30s first call)
  • Normal on first run; LSP cache persists in /home/serena/.cache volume
  • Mount cache volume to speed up subsequent starts
Context name 'ide-assistant' is deprecated Warning
  • Change SERENA_CONTEXT to claude-code
Dashboard Port 24282 Not Reachable
  • Bound to 0.0.0.0 inside container but not published
  • Add -p 127.0.0.1:24282:24282 (keep dashboard local; never expose publicly)
Slow ARM Performance
  • Serena uses QEMU emulation on ARM for some workloads
  • Native ARM64 images are provided for best performance
  • Consider allocating more memory: --memory=2g
Debug Information
# Container logs
docker logs serena-mcp

# Enter container shell
docker exec -it serena-mcp bash

# Check serena version
docker exec serena-mcp serena --version

# Check running processes
docker exec serena-mcp ps aux

Security

  • Dashboard port 24282never expose publicly. Default compose does NOT publish it. If needed, bind to 127.0.0.1 on host.
  • API_KEY env — optional HAProxy Bearer auth. Generate with openssl rand -base64 32. Rotate quarterly.
  • Project volumes — mount :ro when Serena only needs read access; :rw is required for symbol-editing tools.
  • No network egress required for stdio mode; SHTTP mode respects ENABLE_HTTPS + API_KEY.

Additional Resources

Documentation
Docker Resources
Monitoring
  • Health check: GET /healthz
  • Container logs: docker logs serena-mcp

Buy Me A Coffee


Support & License

Getting Help
Contributing

Pull requests are welcome! Please open an issue first to discuss proposed changes.

License

This Docker image packaging is licensed under GPL-3.0.

The upstream serena-agent is licensed under the MIT License by Oraios GmbH.

Tag summary

Content type

Image

Digest

sha256:277bc1d11

Size

257.7 MB

Last updated

about 1 month ago

docker pull mekayelanik/serena-mcp