Repository navigation
Docker Deployment
Use the Docker image when the MCP server must be reachable over the network (remote MCP clients, a shared server). Local clients on your own machine don't need it. They launch the server via npx over stdio (see Getting Started).
| Docker Hub | jokeks2023/huly-mcp |
| GHCR |
ghcr.io/jokeks2023/huly-mcp (same image) |
| Tags |
latest (built from main), <version> for v* git tags, short commit SHA |
| Built by |
.github/workflows/docker-publish.yml on push to main, v* tags, manual dispatch |
latestis built frommainand includes the multi-workspace release, soHULY_WORKSPACEis no longer needed. Images built before PR #1 (e.g. tag55fd94f) still use the old single-workspace model. After pulling a new image, removeHULY_WORKSPACEfrom.envif you want all workspaces. If yourdocker-compose.ymlstill references${HULY_WORKSPACE}, remove that line too; otherwise Compose warns "The HULY_WORKSPACE variable is not set". The warning is harmless, because an empty value means no restriction.
Dockerfile, two stages:
-
Builder (
node:22-slim):npm ci,npm run build→dist/. -
Runtime (
node:22-slim):- installs Python + pinned
mcp==1.29.0andmcp-proxy==0.12.0 - installs production npm dependencies
- runs as user
node - exposes port 8000
- installs Python + pinned
The entrypoint:
mcp-proxy --host=0.0.0.0 --port=8000 --stateless --pass-environment -- node dist/index.js
-
mcp-proxy starts
node dist/index.jsonce as a stdio child process at startup and bridges it to HTTP. All HTTP clients share this one process. They share the account session and the per-workspace connection cache. Every tool call still names its own workspace, so clients don't influence each other's workspace choice. -
--pass-environmentpasses the container environment (yourHULY_*variables) to the child. -
--stateless: streamable-HTTP requests don't need a server-side MCP session.
Endpoints served by mcp-proxy 0.12:
| Path | Transport |
|---|---|
/mcp |
Streamable HTTP (current MCP transport) |
/sse (+ /messages/) |
Server-Sent Events (older MCP transport) |
/status |
Health/status |
Copy .env.example to .env next to docker-compose.yml and fill in credentials. No workspace is needed:
HULY_EMAIL=you@example.com
HULY_PASSWORD=your-password
# self-hosted:
HULY_ACCOUNTS_URL=https://huly.example.com/_accounts
HULY_FRONT_URL=https://huly.example.com
# optional, legacy: restrict to one workspace
# HULY_WORKSPACE=acmeteam.env is excluded from the image build (.dockerignore) and from git (.gitignore). It is only read at runtime via env_file.
docker-compose.yml: image jokeks2023/huly-mcp:latest, restart: unless-stopped, env_file: .env, port 8000:8000, no-new-privileges.
docker compose up -d
docker compose logs -f
docker compose pull && docker compose up -d # update to the latest image
docker compose build --no-cache && docker compose up -d --force-recreate # with `build: .` enableddocker run -d --name huly-mcp -p 8000:8000 --env-file .env jokeks2023/huly-mcp:latestPoint a client that supports remote MCP servers at:
https://mcp.example.com/mcp # streamable HTTP
https://mcp.example.com/sse # SSE, for clients that only support SSE
Example for Claude Code:
claude mcp add --transport http huly https://mcp.example.com/mcpThe container does not authenticate MCP clients. Anyone who can reach port 8000 can use every tool as the configured Huly account, in all of its workspaces. Therefore:
- Don't publish port 8000 on a public interface. Bind it to localhost or a private network (e.g.
127.0.0.1:8000:8000). - Expose it only through a reverse proxy with authentication (OAuth2 proxy, basic auth, mTLS), a VPN, or a private network.
- For SSE through a reverse proxy, disable response buffering (e.g. nginx
proxy_buffering off;). - Consider a dedicated Huly account, and
HULY_WORKSPACEto limit the server to one workspace.
See Security.
The container must reach:
HULY_ACCOUNTS_URL- the transactor WebSocket endpoint returned by
selectWorkspace(…, 'external') HULY_FRONT_URL
For a self-hosted Huly on the same host, localhost inside the container is the container itself. Use the public hostname, a shared Docker network, or host.docker.internal. See Self-Hosted Huly.