Skip to content

Operations

Deployment artifact

A single Docker image. Released builds are published to Docker Hub at docker.io/miragon/miragon-ai-server — pull a tagged version (or :latest) and run it:

bash
docker run --rm -p 8400:8400 \
  -e CAMUNDA_BASE_URL=... \
  -e PROMETHEUS_URL=... \
  docker.io/miragon/miragon-ai-server:latest

Releases are cut by release-please: merging the Release PR tags v<version>, and after manual approval publish-to-docker.yml builds the root Dockerfile and pushes :<version> and :latest. docker build -t miragon-ai-server . builds it locally (all dependencies are public — no registry credential). The image's HEALTHCHECK polls /health/ready (see Observability).

playground/docker/docker-compose.yml is the fully wired local demo (--profile full adds the server); playground/README.md covers deploying the same stack to Fly.io (deploy-playground.yml, manual).

Security

By default the MCP endpoint is unauthenticated — any client that reaches port 8400 gets full tool access. Protect it with an authenticating reverse proxy, or set MCP_OAUTH to make the server an OAuth resource server: bearer tokens on /mcp are validated against your IdP (Keycloak or Auth0), unauthenticated requests get 401, and the .well-known discovery metadata is served. Token audience is validated against the server's canonical MCP URL (RFC 8707) — the IdP must issue tokens whose aud includes it (e.g. a Keycloak audience mapper; the playground realm seeds one as the mcp-resource client scope). Set MCP_URL so advertised URLs are right.

The 1.x providers oidc and oidc-proxy are gone with mcp-use 2 (no generic JWKS verifier, no OAuth proxy broker) — both fail the boot with an actionable error. For an IdP without Dynamic Client Registration, front the server with an OAuth-terminating gateway.

CAMUNDA_AUTH_TYPE=passthrough forwards each caller's bearer token to the engine per request (never to Prometheus). With MCP_OAUTH the server validates the token and the engine enforces the caller's permissions — which needs an engine with REST auth enabled; a default engine accepts anonymous requests and ignores the token.

With auth active, user profiles and saved dashboards scope to the authenticated user (sub) and never expire. Without auth there is no caller identity — mcp-use 2 issues no MCP session ids — so settings reads return defaults and saves, incl. the default engine, refuse (boot warning).

To try it locally, docker compose --profile auth up -d adds a Keycloak on localhost:8480 (realm miragon, user demo/demo; the matching MCP_OAUTH block is commented out in .env.example). The Fly playground runs unauthenticated.

Environment variables

VariableDefaultNotes
PORT8400HTTP port the MCP server listens on
MCP_URLPublic base URL the server advertises (resource URIs, OAuth callbacks)
MCP_OAUTHJSON OAuth resource-server config; providers keycloak, auth0 (oidc/oidc-proxy were removed with mcp-use 2 and fail the boot) — full field lists in .env.example
MCP_ACTIVE_MODULESallComma-separated module or module:toolset entries; e.g. camunda7:read-only,analytics
DATABASE_URLPostgres for saved dashboards + user profiles (both stores; migrations run at boot). Beats MCP_*_DIR; the Compose stack ships an instance on host port 8440
MCP_DASHBOARD_DIR / MCP_PROFILE_DIRin-memoryDirectories persisting saved dashboards / user profiles across restarts — the file-based alternative when no DATABASE_URL is set
MCP_PROFILE_SESSION_TTL_DAYS30Expiry for session-keyed profiles (gateway-stamped Mcp-Session-Id or 1.x leftovers), checked at boot + daily. User-bound profiles and the shared stdio anonymous record never expire. 0 disables
REDIS_URLIgnored since mcp-use 2 (the pluggable session-backend seam was removed upstream; the server warns at boot). Sessions are instance-local — scale out only with sticky routing
CAMUNDA_ENGINES_FILEPath to a JSON file with the engine list [{id, baseUrl, cockpitUrl?, environment?, flavor?, auth?}, ...] or the environment map {"<environment>": [engines…]}; highest precedence
CAMUNDA_ENGINES_JSONSame engine JSON inline; ignored when CAMUNDA_ENGINES_FILE is set. environment (field or map key) groups engines for the two-stage environment → engine pickers; flavor (cibseven | operaton | camunda7, default cibseven) selects the vendor's cockpit-link routes — mixed fleets are fine
CAMUNDA_BASE_URLhttp://localhost:8410/engine-restLegacy single-engine REST endpoint (registered as id CAMUNDA_ENGINE_ID); ignored when CAMUNDA_ENGINES_* is set. The server warns at boot when no engine source is set at all — the silent fallback works against the Compose engine but breaks the engine_id join
CAMUNDA_ENGINE_IDdefaultEngine id for the CAMUNDA_BASE_URL shorthand. Must match the engine container's ENGINE_ID (= the engine_id metric label) or every engine-scoped analytics query — BPMN heatmap, engine compare — comes back empty
CAMUNDA_COCKPIT_URLderivedUsed for jump-out links to Cockpit; multi-engine setups use per-engine cockpitUrl instead
CAMUNDA_AUTH_TYPEnonebasic, bearer, passthrough, or none — fallback for engines without a per-engine auth
CAMUNDA_USERNAME / CAMUNDA_PASSWORDRequired for basic (enforced at boot)
CAMUNDA_TOKENRequired for bearer (enforced at boot)
CAMUNDA_INCIDENT_ISSUE_REPODefault owner/repo for the GitHub-issue tool
CAMUNDA_HEALTH_CRITICAL_*50 / 25…_INCIDENTS / …_CLUSTER_SIZE — thresholds for the engine-health critical verdict
PROMETHEUS_URLhttp://localhost:9090Prometheus HTTP API — the analytics data source (the repo's Compose stack publishes :8460; the server warns at boot when unset)

Unknown CAMUNDA_*/MCP_* variables are reported at boot (typos aren't silently ignored); mcp-use telemetry is off by default (MCP_USE_ANONYMIZED_TELEMETRY=true opts in). The engine container takes METRICS_ENABLED, ENGINE_ID (must match CAMUNDA_ENGINE_ID or the id in CAMUNDA_ENGINES_*, or that engine's analytics come back empty — the heatmap then says so instead of rendering an uncolored diagram), plus OTEL_EXPORTER_OTLP_ENDPOINT/OTEL_SERVICE_NAME for the OTLP push.

Module activation

Disable a module by listing only the ones you want, e.g. MCP_ACTIVE_MODULES=camunda7. The camunda7 module also takes a toolset suffix to narrow the tool surface: camunda7:read-only (queries + engine discovery), camunda7:operations (adds start/complete/claim/variables/retries/messages), camunda7:admin (everything, incl. delete/modify/suspension, deployments, migrations). No suffix exposes all tools; unknown toolsets warn and degrade to read-only.

Observability

Three operational routes sit next to /mcp, outside the OAuth gate (which is scoped to the MCP path) — probes and scrapers need no token:

RoutePurpose
/health/liveLiveness — 200 as soon as the process serves HTTP
/health/readyReadiness — 200 once the server's own dependencies respond (the Postgres store under DATABASE_URL), else 503 naming the failing check. Never probes engines or Prometheus: their outages surface as tool errors, not as an unroutable server. /health aliases it (Docker HEALTHCHECK, Compose, Fly)
/metricsPrometheus text — mcp_tool_calls_total{tool,outcome}, mcp_tool_call_duration_seconds{tool}, mcp_http_requests_total{method,route,status}, mcp_http_request_duration_seconds{method,route} plus the standard process_*/nodejs_* collectors. Labels are bounded by construction (tool catalogue, known routes) — never users, sessions or arguments

HTTP transport logs structured JSON to stdout. The server expects Camunda 7 / CIB Seven and Prometheus; Grafana is optional (:8470, playground/docker/grafana/).

Engine metrics take the push path: the Kotlin plugin (engine-plugins/cibseven-history-metrics) records history-event metrics (no sampling) into Micrometer's global registry inside the CIB Seven runtime — any Micrometer export works: OTLP push to the OTEL Collector (micrometer-registry-otlp, the playground default), an Actuator Prometheus scrape, or the OTEL agent's Micrometer bridge. Prometheus scrapes the Collector; the analytics module queries it over PromQL — event-driven counters/histograms (throughput, durations) plus point-in-time gauges (running WIP, open incidents, job/task backlog). Per-instance drill-down is not metric-backed — use the camunda7_query_historic_* tools. Alert rules ship in playground/docker/prometheus/alerts.yml (wire an Alertmanager under alerting: to route them); the analytics_engine_health tool surfaces the same gauges + firing alerts in one call.

CI/CD

.github/workflows/ci.yml runs parallel jobs on every push — TypeScript (build, test, lint, format), Kotlin engine plugins, and the CIB Seven example. All npm dependencies are public, so no registry credential is involved. This docs site deploys to Netlify (root netlify.toml; docs-only pnpm install).