DocsNew Run

Deployment

Principle

The repo is fully platform-agnostic. The Dockerfile is the only deployment contract — no platform-specific config files in the repo. All platform settings live in each platform's dashboard.

Required sidecars

The service boots with a single requirement: MACP_AUTH_SERVICE_URL must be set. AppConfigService.onModuleInit rejects an empty value with INVALID_CONFIG, but nothing contacts the URL at boot, so any syntactically valid value starts the process (CI's container boot probe uses http://127.0.0.1:9/unused). Note the failure happens after every route is mapped, so a missing URL looks like a startup that hangs rather than a config error.

To actually run sessions (POST /examples/run with agent bootstrap), two more services must be reachable:

ServiceWhy it's requiredHow to run it
MACP runtime (gRPC)Agents open their own gRPC channels to the runtime (RFC-MACP-0004 §4). MACP_RUNTIME_ADDRESS must point to a live runtime.See macp-runtime/docs/deployment.md.
auth-service (HTTP)Every agent spawn mints a short-lived JWT via POST /tokens (wire format); PolicyRegistrarService mints an admin JWT at startup to register scenario policies.docker-compose.dev.yml runs it as a sidecar; in production, deploy the auth-service image alongside the runtime (macp-auth-service/docs/deployment.md).

The control-plane is optional: set MACP_CONTROL_PLANE_URL to register each run with it (CP-1 POST /runs, best-effort and non-fatal — see direct-agent-auth.md § CP-1 run registration).

The runtime must be configured to accept JWTs minted by the same auth-service (MACP_AUTH_ISSUER, MACP_AUTH_AUDIENCE, MACP_AUTH_JWKS_URL=<auth-service>/.well-known/jwks.json). For the full setup see macp-runtime/docs/getting-started.md § JWT mode and macp-auth-service/docs/integration.md.

Pinned versions. docker-compose.fullstack.yml pins the runtime to ghcr.io/multiagentcoordinationprotocol/macp-runtime:0.8.6 and uses locally built :0.8.0 images for the control-plane, auth-service and playground. Runtime behaviour that matters to operators (MACP_ALLOW_INSECURE, JWT algorithm allowlist, MACP_METRICS_ADDR, image tags) is documented in macp-runtime/docs/deployment.md and § Published image tags.

Node. The service needs Node 22.22.3+ (the NestJS 12 toolchain's floor; NestJS 12 is ESM-only and loaded via require(esm)). CI and the shipped image both use Node 26.

Startup order

auth-service up ↓ runtime up (with MACP_AUTH_JWKS_URL pointing at auth-service) ↓ macp-playground boots ├─ validates MACP_AUTH_SERVICE_URL is set (fails fast otherwise; never contacted here) └─ PolicyRegistrarService.onApplicationBootstrap() ├─ skips with a warning if MACP_RUNTIME_ADDRESS is unset ├─ mints admin JWT from auth-service (can_manage_mode_registry) ├─ opens gRPC channel to runtime └─ registers each non-default policy (idempotent) ↓ ready to accept POST /examples/run

Neither the runtime nor the auth-service being down stops the process from booting — PolicyRegistrarService logs and carries on. The failures surface at request time instead: an unreachable auth-service makes uncached agent mints fail with AUTH_MINT_FAILED (502), and a policy that never got registered makes launches that reference it fail with UNKNOWN_POLICY_VERSION. Watch the startup logs for PolicyRegistrarService warnings.

Pipeline

push to main / any PR / manual dispatch (superseded runs on the same ref are cancelled) │ ├─ lint ESLint + Prettier check + typecheck (src/ + scripts/ + test/) │ + npm audit (non-blocking) + schemas:sync drift check (non-blocking) ├─ build TypeScript compile ├─ test unit (coverage thresholds, report artifact) + e2e + integration (mock) ├─ python pip --dry-run resolution of agents/requirements.txt + constraints.txt │ + ruff + pytest (fallback scorers only) + compileall │ └─ docker needs all four jobs above PR → build + load image locally (no registry push) non-PR → build + push to GHCR (:sha-<commit>, plus :latest on main) then, on EVERY event, against that exact image: framework import smoke (HAS_LANGCHAIN / HAS_LANGGRAPH / HAS_CREWAI) framework construct smoke (dummy key, no LLM call) framework-installed pytest container boot probe (runs dist/main.js, GET /healthz) Trivy vulnerability scan (non-blocking)

agents/tests outside the image installs no frameworks, so it exercises only the deterministic fallback scorers; the image gates are what fail the build when a framework import or constructor breaks.

Dependency updates are automated by Dependabot (.github/dependabot.yml): monthly, grouped PRs for four ecosystems — npm (root), pip (agents/), GitHub Actions, and the Dockerfile base image. A few packages are deliberately capped by ignore rules (enforced by src/dependabot-policy.spec.ts).

Image Tags

TriggerTagPurpose
Push to main:latestRolling production tag
Push to main / manual dispatch:sha-<commit>Immutable, for rollback

Pull requests build the image for validation but do not push it. A manual workflow_dispatch run pushes a :sha-<commit> tag only; :latest moves only on main.

Images live at:

ghcr.io/multiagentcoordinationprotocol/macp-playground

Deploying

Point any container platform at the GHCR image. No platform config files needed — configure everything in the platform's dashboard. Remember to deploy the auth-service sidecar in the same network.

Railway

  1. Create a service → set source to Docker Image
  2. Image: ghcr.io/multiagentcoordinationprotocol/macp-playground:latest
  3. Set env vars in the Variables tab

Render

  1. Create a Web Service → type Docker Image
  2. Image: ghcr.io/multiagentcoordinationprotocol/macp-playground:latest
  3. Set env vars in the Environment tab

Fly.io

flyctl apps create macp-playground
flyctl deploy --image ghcr.io/multiagentcoordinationprotocol/macp-playground:latest
flyctl secrets set \
  MACP_RUNTIME_ADDRESS=runtime.internal:50051 \
  MACP_AUTH_SERVICE_URL=http://auth-service.internal:3200

AWS ECS

  1. Create an ECR repo, push the GHCR image (or point the task definition at GHCR directly)
  2. Create ECS cluster + service + task definition referencing the image
  3. Deploy the auth-service alongside as a second task definition in the same cluster + VPC

Any other platform

Any platform that can run a Docker image works. Point it at:

ghcr.io/multiagentcoordinationprotocol/macp-playground:latest

Environment Variables

This is the canonical env-var reference. The tables mirror AppConfigService (src/config/app-config.service.ts) and .env.example. The service does not load .env itself — export the variables, or pass them with docker run --env-file. "Required" means the service either refuses to boot or the relevant feature will fail at request time.

Core HTTP / logging

VariableRequiredDefaultDescription
PORTNo3000HTTP listen port
HOSTNo0.0.0.0HTTP listen host
CORS_ORIGINNohttp://localhost:3000Comma-separated origins (supports * and wildcards)
NODE_ENVNodevelopmentdevelopment enables Swagger at /docs
LOG_LEVELNoinfo
AUTH_API_KEYSNo—Comma-separated keys guarding the macp-playground HTTP surface

Scenario registry

VariableRequiredDefaultDescription
PACKS_DIRNo./packs (/app/packs in the image)Path to scenario pack YAML files. The code default is ./packs; the Dockerfile sets ENV PACKS_DIR=/app/packs.
REGISTRY_CACHE_TTL_MSNo0Cache TTL; 0 reloads on every request

MACP runtime (gRPC)

VariableRequiredDefaultDescription
MACP_RUNTIME_ADDRESSYes (for runs)(empty)gRPC endpoint every agent dials. Not needed to boot, but required for /examples/run to drive a session; if unset, every bootstrap carries an empty runtime_url and PolicyRegistrarService logs a warning and skips registration.
MACP_RUNTIME_TLSNotrueTLS flag written into each agent's bootstrap. RFC-MACP-0004 §2 requires true in production.
MACP_RUNTIME_ALLOW_INSECURENofalseMust be true when MACP_RUNTIME_TLS=false — local dev only.

auth-service (AUTH-2)

VariableRequiredDefaultDescription
MACP_AUTH_SERVICE_URLYes(empty)Base URL of the auth-service. Startup fails with INVALID_CONFIG if unset. Not contacted at boot, so any valid URL lets the process start.
MACP_AUTH_SERVICE_TIMEOUT_MSNo5000HTTP timeout for POST /tokens.
MACP_AUTH_TOKEN_TTL_SECONDSNo3600TTL requested from auth-service on every mint. Must exceed the agent's gRPC stream lifetime (SDKs bind auth once at stream open). Capped by auth-service MACP_AUTH_MAX_TTL_SECONDS.
MACP_AUTH_SCOPES_JSONNo(empty)Per-sender scope overrides, JSON {"sender":{"can_start_sessions":true,...}}. Deep-merged onto role-derived defaults; explicit null clears a key.

Control-plane (CP-1, optional)

VariableRequiredDefaultDescription
MACP_CONTROL_PLANE_URLNo(empty)Control-plane base URL. When set, /examples/run registers each run via POST /runs concurrently with agent bootstrap; best-effort, failure is logged and never fails the run. Empty disables submission. Wire format: macp-control-plane/docs/API.md § POST /runs.
MACP_CONTROL_PLANE_TIMEOUT_MSNo5000HTTP timeout for POST /runs. An invalid value (non-integer, <= 0, too large) falls back to 5000 with a startup warning.
MACP_CONTROL_PLANE_API_KEYNo(empty)Sent as Authorization: Bearer <key>. The control-plane's AuthGuard requires an Authorization header on every route, so set this to match its AUTH_API_KEYS (the fullstack compose uses demo-key).

Policy registration

VariableRequiredDefaultDescription
REGISTER_POLICIES_ON_LAUNCHNotrueWhen true, PolicyRegistrarService registers every non-default policy with the runtime at bootstrap. Set to false only in tests or when policies are pre-registered out-of-band.

Read-only registry (prod-style alternative). Mount ./policies into a runtime started with MACP_POLICIES_DIR and set REGISTER_POLICIES_ON_LAUNCH=false. Registrar behaviour in that shape (verification instead of mutation) is documented in policy-authoring.md. The fullstack compose ships a commented-out variant of this setup.

Agent workers

VariableRequiredDefaultDescription
AUTO_BOOTSTRAP_EXAMPLE_AGENTSNotrueAuto-resolve agent bindings on /examples/run.
EXAMPLE_AGENT_PYTHON_PATHNopython3Python interpreter for Python workers. A manifest's own host.python overrides it; the shipped manifests set none.
EXAMPLE_AGENT_NODE_PATHNo(process.execPath)Node interpreter for Node workers. A manifest's own host.node overrides it.
MACP_CANCEL_CALLBACK_HOSTNo127.0.0.1Host each agent binds for the cancel-callback HTTP server. Empty disables.
MACP_CANCEL_CALLBACK_PORT_BASENo0Port base for deterministic per-agent ports; 0 = ephemeral.
MACP_CANCEL_CALLBACK_PATHNo/agent/cancelHTTP path for the cancel-callback server.

Worker processes inherit the service's environment, so these are set on the playground itself and read by the spawned agents:

VariableRequiredDefaultDescription
OPENAI_API_KEYNo(empty)Used by the LangGraph / LangChain / CrewAI workers (gpt-4o-mini). Empty → each worker uses its deterministic fallback scorer and makes no LLM call.
RISK_DECIDER_WAIT_ALL_TIMEOUT_MSNo60000How long the risk-decider coordinator waits for every specialist before committing with the votes it has.
RISK_DECIDER_SUSPEND_HOLD_MSNo8000How long the suspend/resume demo (suspend customerId sentinel) holds the session suspended before resuming.

GHCR Visibility

If the package is private, set it to Public (simplest), or configure pull credentials in your platform's dashboard.

Rollback

# Use an older immutable tag
ghcr.io/multiagentcoordinationprotocol/macp-playground:sha-abc1234

Or re-tag:

docker pull ghcr.io/…/macp-playground:sha-<old>
docker tag  ghcr.io/…/macp-playground:sha-<old> ghcr.io/…/macp-playground:latest
docker push ghcr.io/…/macp-playground:latest

Local Testing

docker-compose.dev.yml wires the macp-playground to an auth-service sidecar built from the sibling ../auth-service checkout. Start the runtime separately, then:

docker compose -f docker-compose.dev.yml up
curl http://localhost:3000/healthz

Both compose files forward NODE_AUTH_TOKEN (default empty) as a build arg, because @multiagentcoordinationprotocol/proto installs from GitHub Packages: NODE_AUTH_TOKEN=<token with read:packages> docker compose -f docker-compose.dev.yml up --build.

The plain docker-compose.yml defaults MACP_AUTH_SERVICE_URL to http://host.docker.internal:3200 (override via the environment) and passes MACP_RUNTIME_ADDRESS through, so it boots on its own. To actually run sessions, start the auth-service (and a runtime) first:

# In auth-service/
npm install && npm run build && npm start  # listens on :3200

# In macp-playground/
# A transitive dependency (@multiagentcoordinationprotocol/proto) comes from GitHub Packages,
# so the build needs a token with read:packages.
docker build --build-arg NODE_AUTH_TOKEN="$GITHUB_TOKEN" -t macp-playground .
docker run -p 3000:3000 \
  -e MACP_AUTH_SERVICE_URL=http://host.docker.internal:3200 \
  -e MACP_RUNTIME_ADDRESS=host.docker.internal:50051 \
  -e MACP_RUNTIME_TLS=false -e MACP_RUNTIME_ALLOW_INSECURE=true \
  macp-playground