instruction.md

Self-hosting

Everything the hosted service runs is in the repository, and a document published on your own instance is the same document as one published here. The format is specified rather than owned, so an agent pointed at your registry needs nothing from us.

Being straight about the state of this: there is no one-command installer for production. What exists is a set of Helm charts the hosted service itself is deployed with, a config generator, realms as code, and a script that brings the whole shape up on one machine. If you are comfortable running a Kubernetes service, this is a normal afternoon. If you want a packaged appliance, it is not ready.

The pieces

PieceWhat it doesRequired
GatewayMCPG, on its own first-party plugins. Terminates MCP, verifies identity, applies per-tool governance, and calls Postgres or imd-svc for each tool.yes
PostgresPostgreSQL 17 with pg_trgm, pgvector and pgcrypto, run by CloudNativePG. The authoritative state, and the registry's rules as SQL functions.yes
imd-svcThe server's own service beside the gateway: signing, agents' credentials, files, the background worker and the schema's migrations.yes
NATSJetStream: the gateway's sessions, subscriptions and notifications, shared between its replicas. Nothing durable lives there.for two or more gateway replicas
KeycloakSign-in, with its realms as code: people, agents' own credentials, administrators.yes
Embedding modelText Embeddings Inference with BAAI/bge-base-en-v1.5, inside the cluster: search by meaning. No instruction text leaves it.no
The studioThe editor, a static site that signs in to your Keycloak and keeps its workspace on your server.no

Note the last row. The whole surface is MCP, so agents and the command-line client talk to the gateway directly. The studio is one client among several, and an instance that only ever serves agents does not need it. Without the embedding model, search matches words and titles but not meaning.

The smallest working instance

The production charts in a local k3d cluster, wired the way production wires them, on one origin: the studio at /, the MCP server at /mcp, Keycloak at /auth. It needs Docker, k3d, kubectl, helm, Python 3 with PyYAML, Node with pnpm, rsync, openssl, and about 6 GB of disk. It is a real registry: versions, refs, proposals, organizations, signing and MCP delivery all work.

# 1. build the studio for the stack's origin
cd apps/landing
PUBLIC_IMD_AUTH_ISSUER=http://localhost:18080/auth/realms/instruction-md PUBLIC_IMD_AUTH_CLIENT=platform-web \
  PUBLIC_IMD_MCP_URL=http://localhost:18080/mcp PUBLIC_IMD_STUDIO_SIGN_IN=on pnpm build
cd ../..

# 2. a k3d cluster, the operators, the secrets and every chart
deploy/k8s/stack/up.sh                  # http://localhost:18080

# the same, on MCPG's published release
IMD_GATEWAY_IMAGE=ghcr.io/mcpg-dev/mcpg:0.1.0-beta.68 deploy/k8s/stack/up.sh

Until MCPG 0.1.0-beta.68 is published, the script's default gateway is a local build: the published beta.67 image with a gateway and its two backend plugins built on the machine, fromdeploy/mcpg/data. With the release, the second form uses the published image and its signed plugins. Sign in at http://localhost:18080/ and point an agent athttp://localhost:18080/mcp. The stack's realm has development users; production's does not. deploy/k8s/stack/README.md has the rest.

Postgres, and the backups that matter

Postgres is the whole state. The gateway's SQL bindings reach it through PgBouncer in transaction mode, which needs max_prepared_statements because MCPG prepares its statements, while LISTEN and the events reader go straight to the primary, since a transaction pooler cannot hold a LISTEN. Every client verifies the server's certificate against the cluster's CA. The chart sets all of this up.

Backups go through CloudNativePG's Barman Cloud plugin to any S3-compatible bucket: WAL continuously, a base backup nightly, both kept 30 days, so you can recover to a point in time on another host.

# deploy/k8s/imd-postgres/values.yaml
backup:
  enabled: true
  destinationPath: s3://imd-postgres-backup/imd-postgres
  endpointURL: http://rgw.internal:7480
  credentialsSecret: imd-postgres-backup   # ACCESS_KEY_ID, ACCESS_SECRET_KEY
  retentionPolicy: "30d"
  schedule: "0 0 3 * * *"                  # a base backup nightly; WAL all the time

deploy/k8s/restore-drill.sh recovers a second cluster from the bucket, compares every table with the live one and removes it. Run it after the first nightly backup, and after any change to backups. NATS needs no backup: lose it and the gateway rebuilds what it kept there.

Identity

The gateway needs three things from a token: a stable subject, an audience bound to your MCP endpoint, and a claim saying whether the caller is a person, an agent or a service, because agents are capped below full administration no matter who they act for. The server also relies on Keycloak for more than sign-in: imd-svc mints each agent's own credentials in the agents' realm. So the identity provider is Keycloak, and your people can still sign in with your own provider through Keycloak's brokering.

The realms ship as code, generated rather than hand-edited (deploy/keycloak/gen-realms.py): one for people, one for agents, one for administrators. Keycloak imports them at its first start. After every install or upgrade, a job applies their content again through the admin API: it adds what is new and leaves what exists, because replacing a live realm would destroy organizations and federated identity links. Only the people's and the agents' realms are routed. Administration stays inside the cluster.

The generators keep a profile for each place the server runs: a developer's machine, the local stack, and production at instruction.md. Your own domain is a profile of your own indeploy/mcpg/gen-config.py and deploy/keycloak/gen-realms.py, with your origin, issuers and audience.

Signing

imd-svc derives a publisher key for each organization from one master key, so no private key is ever stored: the public halves are served for verification, and the private halves exist only inside the process while it signs. A second key signs each delivery.

# deploy/k8s/bootstrap-secrets.sh, in the Secret imd-keys
IMD_SIGNING_MASTER_KEY   # each organization's publisher key is derived from it
IMD_DELIVERY_KEY         # signs each delivery: one reader, one resolution

deploy/k8s/bootstrap-secrets.sh makes both on its first run and keeps them after that. Keep a copy where you keep such things: lose the master key and every organization's publisher key changes; lose the delivery key and earlier deliveries can no longer be verified. Without a master key the platform signs nothing, which is a legitimate choice. Publishers can still bring their own keys and sign on their own machines, and consumers verify exactly the same way.

Kubernetes

Six charts, one namespace, installed in this order, each waited for. It is the order.github/workflows/deploy-server.yml runs them in, and that workflow passes the realm files and the apply job's script in full. The gateway pulls its first-party plugins when it starts and admits only signed ones.

# first, once per cluster: the CloudNativePG operator, cert-manager and the Barman Cloud plugin
# (docs/runbooks/server-deploy.md §1). Then the namespace's secrets:
deploy/k8s/bootstrap-secrets.sh instruction-md

helm upgrade --install imd-postgres   deploy/k8s/imd-postgres   -n instruction-md
kubectl -n instruction-md wait cluster/imd-postgres --for=condition=Ready --timeout=15m
helm upgrade --install imd-nats       deploy/k8s/imd-nats       -n instruction-md --wait
helm upgrade --install imd-keycloak   deploy/k8s/imd-keycloak   -n instruction-md --wait \
  --set-file realms.users=deploy/keycloak/realms/prod/instruction-md-realm.json …
helm upgrade --install imd-embeddings deploy/k8s/imd-embeddings -n instruction-md --wait
helm upgrade --install imd-svc        deploy/k8s/imd-svc        -n instruction-md --wait \
  --set image.tag=<tag>
helm upgrade --install imd-gateway    deploy/k8s/imd-gateway    -n instruction-md --wait \
  --set-file config=deploy/mcpg/prod.yaml

The secrets script makes every secret in the namespace. It takes each value from the environment (what your secret manager hands it), else from the Secret already there, else it makes a new random one, and then writes the derived Secrets the charts read. None of them belong in values files, and the charts will not take them there. More than one gateway replica, clustered on NATS, needs an MCPG licence. The local stack runs on MCPG's free non-production grant.

Upgrades and operations

The runbooks the hosted service uses are in the repository under docs/runbooks: deploying the server, rolling back, key rotation, Postgres recovery, observability, MCPG's release gate, incident response and decommissioning an organization. They are written for our environment, and the shape transfers.

Licence

The specification text is Creative Commons Attribution 4.0. Implement it, quote it, fork it, sell it. The conformance corpus is Apache 2.0, because implementations vendor those files into their own source trees.

The platform source is under the Business Source License 1.1. In practice: you may run it, modify it and use it for your own purposes, including commercially, and the one thing you may not do is offer it to third parties as a competing hosted service. Each release converts to Apache 2.0 four years after publication. It is not an open-source licence today and the notice in the repository says so plainly.

Commercial support and alternative licensing are not settled yet, so if you need either, the answer right now is a conversation rather than a page.