Sign inSign up

mekayelanik/serena-mcp

By mekayelanik

•Updated about 2 months ago

Image
0

3.7K

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 24282 — never 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 2 months ago

docker pull mekayelanik/serena-mcp