Gap-fill: the MCP server supports HTTP/SSE transport (controlled by
MCP_SERVER_PORT) but this was only mentioned in the server's internal
README, not in the public docs. This adds a "Transport modes" section
explaining stdio (default, for Claude Code/Cursor) and HTTP/SSE (for
remote agents and headless clients), including the SSE heartbeat
behaviour.
Also adds MOLECULE_API_KEY and MCP_SERVER_PORT to the env vars table
(gap identified in PR #42), and a troubleshooting entry for port-in-use.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 09:56:05 +00:00
3 changed files with 29 additions and 199 deletions
| `MCP_SERVER_PORT` | `3000` | TCP port for HTTP/SSE transport |
## Transport modes
The MCP server supports two transport modes:
### stdio (default, for local AI clients)
Claude Code, Cursor, and similar local AI clients use stdio transport. The client spawns the MCP server as a child process and communicates via JSON-RPC over stdin/stdout. Configure in your AI client's `mcp.json` as shown in Quick start above.
### HTTP/SSE (for remote agents and headless clients)
For agents running on remote servers or in environments where stdio is not available, the MCP server exposes an HTTP endpoint with Server-Sent Events (SSE) for streaming responses. The agent card and tool responses are available at:
```
http://localhost:3000/agent/card # MCP server info + tool manifest
http://localhost:3000/ # SSE stream for responses
```
Start the server in HTTP/SSE mode:
```bash
MCP_SERVER_PORT=3000 npm start
# Server starts on http://localhost:3000
```
Configure your MCP client to connect over HTTP. The server emits `data: null` heartbeats every 30 seconds on idle connections to keep proxies and load balancers from closing the SSE stream.
## Troubleshooting
@@ -159,4 +187,5 @@ The MCP server exposes tools across these categories:
title: Self-Hosted Workspace Deployment with Docker
---
# Self-Hosted Workspace Deployment with Docker
This guide covers running a Molecule AI workspace agent as a Docker container on a self-hosted server or VM. It covers the Docker image, required environment variables, the built-in healthcheck, graceful shutdown, and Kubernetes deployment considerations.
> **Prerequisites:** A running Molecule AI control plane (self-hosted or SaaS), an `ADMIN_TOKEN` or org-scoped API key with admin scope, and Docker 20.10+ on the host.
## How the workspace container works
The Molecule AI workspace Dockerfile includes:
- A uvicorn server on port 8000 (configurable via `PORT`)
- A healthcheck endpoint at `/.well-known/agent-card.json` (used by Docker and Kubernetes probes)
- Graceful SIGTERM handling via uvicorn — the heartbeat loop and adapter tasks shut down cleanly
Save the returned `WORKSPACE_ID`. The workspace agent obtains its bearer token automatically during its first registration with the platform.
## Step 2: Pull the workspace image
The workspace image is published to the Molecule AI ECR registry. Contact your platform administrator for the registry prefix and credentials, then log in:
| `PLATFORM_URL` | `http://localhost:8080` | Platform API URL. Inside a Docker container, use `http://host.docker.internal:8080` to reach the platform on the host machine. |
| `WORKSPACE_ID` | — | Workspace ID from Step 1 (required; no default) |
| `PORT` | `8000` | Agent server port. Must match `containerPort` in Kubernetes and the port mapped with `-p` in Docker. |
> **Note for Linux hosts:** Docker does not include `host.docker.internal` by default. On Linux, either add `--add-host=host.docker.internal:host-gateway` to the `docker run` command, or use the host machine's IP address directly (e.g. `http://192.168.1.100:8080`).
### Verify the healthcheck
```bash
# Wait for the container to become healthy (up to ~2 minutes)
When the container receives SIGTERM (e.g. from `docker stop` or Kubernetes pod deletion), the workspace's uvicorn server initiates graceful shutdown: the heartbeat loop stops, active A2A tasks are given a grace period to complete, and any snapshotable state is persisted before the process exits.
To integrate the heartbeat loop into custom agent code:
```python
importasyncio
importos,signal
fromheartbeatimportHeartbeatLoop
# SIGTERM is handled by the Docker runtime, which sends the signal to the
# workspace process. The workspace (via uvicorn) initiates graceful shutdown:
# the heartbeat loop is stopped, any active adapter tasks are cancelled, and
# in-flight A2A requests are given a grace period to complete.
#
# For custom integration with the heartbeat loop directly:
asyncdefmain():
heartbeat=HeartbeatLoop(
platform_url=os.environ["PLATFORM_URL"],
workspace_id=os.environ["WORKSPACE_ID"],
)
heartbeat.start()
try:
awaitasyncio.Event().wait()# keep running
finally:
awaitheartbeat.stop()
print("Heartbeat loop stopped.")
```
The Docker `stop` command sends SIGTERM and waits up to 10 seconds by default before sending SIGKILL. The healthcheck ensures orchestrators detect an unhealthy container before the SIGTERM timeout.
## Kubernetes deployment
For Kubernetes deployments, use the native liveness/readiness probe configuration instead of the Docker HEALTHCHECK:
```yaml
ports:
- name:http
containerPort:8000
livenessProbe:
httpGet:
path:/.well-known/agent-card.json
port:http
initialDelaySeconds:30
periodSeconds:30
timeoutSeconds:5
failureThreshold:3
readinessProbe:
httpGet:
path:/.well-known/agent-card.json
port:http
initialDelaySeconds:10
periodSeconds:10
timeoutSeconds:5
failureThreshold:3
terminationGracePeriodSeconds:120
```
> **Note:** The Kubernetes `terminationGracePeriodSeconds` should exceed the liveness probe failure threshold so that the probe can register a failure before the pod is killed. With `periodSeconds: 30` and `failureThreshold: 3`, the probe does not register a failure until approximately 120–150s after the container becomes unhealthy. Set `terminationGracePeriodSeconds: 120` or higher.
## Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Container shows `unhealthy` after startup | Platform unreachable from container | Verify `PLATFORM_URL` uses `host.docker.internal` (Docker) or the correct host IP |
| `curl: (7) Failed to connect` on healthcheck | Container not fully started | Wait up to 30s; increase `start_period` |
| Agent not appearing on canvas | Wrong `WORKSPACE_ID` or expired token | Re-run registration; check platform logs |
| `host.docker.internal` not resolved | Linux host without the Docker flag | Use `--add-host=host.docker.internal:host-gateway` or the host's LAN IP |
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.