Production deployment — first-run checklist¶
The quickstart gets a single-user trial running in minutes. This is the other path: a multi-user server real people sign in to, on infrastructure you operate.
Précis-MCP is self-hostable on standard infrastructure, plus a modeling step — it is not a turnkey appliance, and the reason is intrinsic, not a packaging gap. It is a governed semantic layer: someone has to describe your metrics and statements over your chart of accounts and warehouse (step 4 below). The deployment itself is scripted and the dependencies are ordinary (ClickHouse, Postgres, an identity provider); the modeling is the work only you can do, because it encodes your definitions of revenue, margin, and utilisation. Budget for it like a BI semantic-model project, not a software install.
Work the steps in order. Each ends with the failure it guards against — do not move on until the check passes.
1. Provision the host¶
scripts/install-precis-mcp.sh installs Docker, sets the firewall, and creates
/opt/precis-mcp on the target box. You need SSH access as root via a host
alias.
bash scripts/install-precis-mcp.sh --server YOUR_HOST
Guards against: deploying onto a box without Docker / with a closed firewall, which surfaces later as opaque connection failures.
2. Configure deploy/.env¶
Copy deploy/.env.example and set, at minimum, the three deployment axes and
their secrets:
- Identity (
PRECIS_AUTH_MODE):keycloak(bundled, mode B) oroidc(your existing IdP, mode C — setOIDC_ISSUER,OIDC_JWKS_URL,OIDC_AUDIENCE,OIDC_CLIENT_ID/SECRET,PRECIS_IDENTITY_CLAIM/COLUMN). - Data (
PRECIS_DATA_MODE):byo(your ClickHouse — setCHHOSTetc.),bundle-empty, orbundle-sample. - Ingress (
PRECIS_INGRESS_MODE):bundled(Caddy auto-HTTPS on 80/443 forPRECIS_DOMAIN— needs public DNS + ACME reachability) orbyo(front the loopback-published ports with your own proxy; reference vhost indeploy/nginx/). - Excel add-in (optional): set
KC_ENABLE_EXCEL_ADDIN=truein mode B, orEXCEL_ADDIN_CLIENT_IDplus any IdP-specific token-shape vars in mode C. The bundle is baked into the image and served from/excel.
Secrets follow the *_FILE convention (a path to a mounted file) or plain env
vars — see the environment variable reference
and Security model.
Guards against: a silently-disabled audience check (a configured deploy
must resolve an /mcp audience, or any same-issuer token is accepted — the
server refuses to start without it when PRECIS_AUTH_PREFLIGHT is on).
3. Deploy¶
bash scripts/deploy-mcp.sh --server YOUR_HOST \
--data-mode bundle-empty --auth-mode keycloak
This rsyncs the tree, pulls the published release image
(ghcr.io/precis-finance/precis-mcp), and brings up the stack (server + the
bundled dependencies your axes selected). Pulling a pinned release is the
default first-run path; pin a specific version with --tag <version> (it sets
PRECIS_MCP_TAG). Build from source instead — for tracking rolling main, a
fork, or baking warehouse drivers — with --build; --extras bigquery (or
snowflake/mssql/databricks) implies --build, since the published image
ships without warehouse drivers.
Guards against: hand-assembling docker compose invocations and missing a
profile — the script selects the right profiles for your three axes.
4. Describe your model¶
Your model lives in an instance/ directory — catalogue/, semantic/,
integrations/, scenarios.yml. Fork the shipped instance/ template
and replace its contents with your definitions, then provision the schema and
verify it:
# schema only (no demo data)
docker compose -f deploy/docker-compose.yml exec precis-mcp \
python -m precis_mcp.clickhouse_init --scope open
# coherence check — catalogue ↔ ClickHouse ↔ scenario registry
docker compose -f deploy/docker-compose.yml exec precis-mcp \
python -m precis_mcp.clickhouse_init --scope open --check
See Catalogue & semantic model and Ingestion to load your data.
Guards against: serving an incoherent model — --check prints one ok
per check (catalogue, each semantic view, the scenario registry) and exits
non-zero naming exactly what is missing.
5. Seed identity and access¶
No admin exists at install. The simplest path is to let the deploy create it —
pass --admin-id on your deploy-mcp.sh run (or set PRECIS_BOOTSTRAP_ADMIN_ID
in deploy/.env):
bash scripts/deploy-mcp.sh --admin-id you@example.com
The deploy's first-admin step creates that admin — idempotently, so it's safe on
every run — and prints a one-time temporary password (change it on first login)
in the deploy output. If you already deployed with --admin-id, it's done; the
manual path below is only for adding admins later, or if you didn't set it.
To seed it by hand in mode B, note that create-admin provisions a Keycloak
account and so needs the Keycloak bootstrap-admin password — which the
long-running server does not carry. Inject it with run (not exec):
docker compose -f deploy/docker-compose.yml --env-file deploy/.env run --rm \
-e KC_BOOTSTRAP_ADMIN_PASSWORD="$(grep '^KEYCLOAK_ADMIN_PASSWORD=' deploy/.env | cut -d= -f2-)" \
precis-mcp python -m precis_mcp.admin_cli create-admin --id you@example.com
# mode C (external IdP owns the credential): exec the running server is fine
docker compose -f deploy/docker-compose.yml exec precis-mcp \
python -m precis_mcp.admin_cli create-admin --id you@example.com --no-keycloak --external-id <idp-subject>
Then author and assign profiles — see
User profiles & permissions. In mode B,
verify the realm came up and the claim mapping is right with
admin_cli check-auth.
Guards against: a running server nobody can administer, and profiles whose
allow: typos silently lock users out (verify with
admin_cli show-access --user <id>).
6. Smoke-test before announcing¶
curl -fsS https://YOUR_DOMAIN/readyz # 200 + {"status":"ready"} when CH + Postgres are reachable
Then, as a signed-in user: list scenarios, run one metric, and confirm
admin_cli show-access --user <id> resolves the access you intended.
If you enabled the Excel add-in, also fetch
https://YOUR_DOMAIN/excel/manifest.xml and confirm it contains your instance
origin, not localhost; then upload that manifest in Excel and run
=PRECIS.SCENARIOS().
Guards against: announcing a server that returns 503 from /readyz
(a dependency is unreachable) or serves figures the permission model wasn't
meant to expose.
After first run¶
- Wire
/readyzto your load balancer / Kubernetes readiness probe so a degraded instance is drained, not served. - Set up backups and rehearse a restore.
- Point telemetry at your stack — Observability.
- Plan credential rotation.
What is not yet provided¶
A single-host docker compose deployment is the supported shape today. There
is no Helm chart or Terraform module for multi-node / HA orchestration yet
— use the compose manifest (deploy/docker-compose.yml) and deploy-mcp.sh
as the reference for your own IaC if you need HA. ClickHouse cluster sizing for
larger deployments is a review, not a preset.