Running sysml-mcp as a service¶
sysml-mcp is usually started by an agent host over stdin and
stdout, one process per client. With -http it is a service instead: started once, serving every
client that connects, and run the way other services are — probed, configured from the
environment, in a container or under systemd, with a token (and later other credentials) in front.
This page is the operator's view; the reference has the
transport's rules and the security policy the hardening advice that applies to
every served binary.
sysml-mcp -http 0.0.0.0:8765 -token-file /etc/sysml-mcp/mcp.token -root /srv/models \
-tls-cert server.crt -tls-key server.key
Health and readiness¶
The server answers two open endpoints on the -http address, outside the bearer token and the
origin check that guard /mcp, in the same stance sysml-grpc takes on its main
port: a probe carries no token, and what it learns is
the build and no more.
| Endpoint | Answer | Meaning |
|---|---|---|
GET /health |
200 {"service":"sysml-mcp","status":"ok","version":"0.2.1"} |
Liveness. The process is up and its listener bound; answers from the first moment the address is bound. |
GET /ready |
503 {"service":"sysml-mcp","status":"starting"} |
Not yet ready. The workspace is being indexed or the runtime's standard library prepared; a Retry-After: 1 header says to come back. |
200 {"service":"sysml-mcp","status":"ready"} |
Ready. A client's first call is answered in full. | |
503 {"service":"sysml-mcp","status":"draining"} |
Stopping. The server has been told to stop and is closing sessions; a balancer should route elsewhere. |
A protocol request that arrives at /mcp before the server is ready is answered 503 with
Retry-After rather than served partially. Point a Kubernetes livenessProbe at /health and
its readinessProbe at /ready; a Compose or systemd deployment polls /ready once after
starting, as the examples below do.
$ curl -s http://localhost:8765/health
{"service":"sysml-mcp","status":"ok","version":"0.2.1"}
$ curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8765/ready
200
Stopping — SIGINT or SIGTERM — turns /ready to 503, closes the clients' sessions so their event
streams end, and waits up to -shutdown-timeout (default 30s) for the connections to drain
before closing what is left. A request body larger than -max-body-bytes (default 4 MiB) is
refused with 413 before it is read in full.
Configuring from the environment¶
Every flag is read from the environment as OPENSYSML_MCP_<FLAG>: the flag's name in upper case
with each dash an underscore, so -log-level is OPENSYSML_MCP_LOG_LEVEL and -http is
OPENSYSML_MCP_HTTP. The rules:
- A flag on the command line wins over its variable.
- A variable set to nothing is as good as unset, so a unit file may leave
OPENSYSML_MCP_STRICT=in place. - A value the flag would refuse —
OPENSYSML_MCP_CACHE_SIZE=many— is refused with the flag's own message and exit status 2, before anything is served. OPENSYSML_MCP_TOKENholds the token itself, as it always has, andOPENSYSML_MCP_TOKEN_FILEnames the file holding it; each is read only when neither-tokennor-token-fileis given, and the file wins over the token, as-token-filewins over-token.
OPENSYSML_MCP_HTTP=0.0.0.0:8765 OPENSYSML_MCP_TOKEN_FILE=/run/secrets/mcp.token \
OPENSYSML_MCP_PUBLIC_URL=https://models.example.org/mcp sysml-mcp
The run bounds every OpenSysML binary reads (OPENSYSML_MAX_STEPS, OPENSYSML_LIBRARY_PATH, …)
are listed in the environment reference.
The container image¶
sysml-mcp ships in the openmbee/opensysml
image beside sysml-grpc (the entrypoint), sysml and sysml-lsp; --entrypoint sysml-mcp
selects it. The image is distroless and runs as nonroot; the one path it writes is the standard
library's record cache under /home/nonroot/.cache, so a read-only root filesystem needs a volume
there (or the server runs uncached and says so once).
docker run --rm -p 8765:8765 -v "$PWD:/models:ro" -e OPENSYSML_MCP_TOKEN="$(cat mcp.token)" \
--read-only --tmpfs /tmp -v opensysml-cache:/home/nonroot/.cache \
--entrypoint sysml-mcp openmbee/opensysml -http 0.0.0.0:8765 -root /models
deploy/sysml-mcp/compose.yaml in the repository is the complete example: sysml-mcp on a
private network behind Caddy, which terminates TLS and forwards
/mcp, /health and /ready; the token as a Docker secret read from secrets/mcp.token through
OPENSYSML_MCP_TOKEN_FILE; the models mounted read-only from ./models; the root filesystem
read-only with every capability dropped; OPENSYSML_MCP_PUBLIC_URL naming the proxied URL, host
and published port, so the bearer challenge and the resource metadata name what clients see.
cd deploy/sysml-mcp
openssl rand -base64 32 > secrets/mcp.token
docker compose up -d
curl -sk https://localhost:8443/health
curl -sk https://localhost:8443/ready
The -public-url is what a client's -print-config block names; a browser-based host needs its
origin in -allow-origin as well.
The systemd unit¶
deploy/sysml-mcp/systemd/sysml-mcp.service runs the binary under its own user with the
hardening systemd offers (ProtectSystem=strict, no new privileges, an empty capability set, a
private /tmp), reading every setting from /etc/sysml-mcp/sysml-mcp.env — the example
sysml-mcp.env beside it names the address, the token file, the public URL, the workspace and
the log level and the bounds, and leaves the TLS pair and the origins empty, which is unset. The unit's
TimeoutStopSec allows for -shutdown-timeout before SIGKILL; raise both together. The record
cache lands in the unit's own /var/cache/sysml-mcp. deploy/sysml-mcp/README.md lists the
installation steps.
Authentication¶
The server is an OAuth 2.1 resource server in its minimal form: one bearer token, issued by
whoever runs it, that every request to /mcp must carry; the reference
has the rules. The sections below are reserved for the credentials a later release adds.
Static bearer token¶
Today's -token-file, -token and OPENSYSML_MCP_TOKEN, as the reference describes.
API keys¶
Reserved: per-client keys issued and revoked by the operator.
Single sign-on (OIDC)¶
Reserved: tokens issued by an identity provider, validated against its discovery document.
Scopes and per-principal sessions¶
Reserved: which tool groups a credential reaches, and REPL sessions owned by the principal that opened them.
Audit log¶
Reserved: a record of who called what.