Migrating from 1.x to 2.0¶
v2.0 is a major version bump but the migration is additive — every new capability is opt-in via env or Helm value. A vanilla single-replica anonymous-auth Docker-compose demo runs identically on 1.8.x and 2.0.0. The two behaviour flips operators must know about are documented inline below.
tl;dr¶
| You are running… | Required action |
|---|---|
docker compose --profile demo up (the OSS demo) |
None — defaults are unchanged. |
helm install obs ./helm/observability-mcp (defaults) |
None — the chart still ships single-replica + no Redis. |
helm install with plugins.image pointing at a private mirror without a configured trust root |
Action: either configure plugins.verify.trustRootPem / existingSecret, OR opt out via plugins.verify.enabled=false. See Plugin signature default below. |
A connector source pointing at a private-IP backend (e.g. http://10.0.0.5:9090) |
Action: set OMCP_ALLOW_PRIVATE_BACKENDS=true. See SSRF strict-mode below. |
| A managed deployment running >1 replica without sticky-session ingress | Action: either set redis.enabled=true (the F8 shared store) OR ingressSticky.enabled=true. See Multi-replica HA below. |
Behaviour flips that need operator action¶
Plugin signature default¶
VERIFY_PLUGINS defaults to true since 2.0. Filesystem plugins
without a valid trust root will not load — the gateway falls
back to builtin connectors only. Builtins (Prometheus, Loki,
Kubernetes) are part of the trusted image and remain available
regardless, so the OSS demo and any deployment without /app/plugins
is unaffected.
If you intentionally run unsigned plugins (development / internal catalog), opt out:
```bash export VERIFY_PLUGINS=false
or in Helm:¶
plugins.verify.enabled: false ```
SSRF strict-mode on connector URLs¶
Adding a source via /api/sources (or sources.yaml) now rejects
URLs whose hostname resolves to a private/loopback IP, cloud-metadata
IP, or non-http(s) scheme. In-cluster Prometheus / Loki / Tempo at
e.g. http://10.0.0.5:9090 is the most common legitimate case — opt
out:
```bash export OMCP_ALLOW_PRIVATE_BACKENDS=true
or in Helm via extraEnv¶
```
Cloud-metadata IPs (AWS / GCE / Azure / Oracle 169.254.169.254 and
the AWS IMDS IPv6 fd00:ec2::254) remain blocked even with the
opt-out — there's no operational case for the gateway to hit them.
New opt-in capabilities¶
These do nothing until you turn them on; existing 1.x deployments see no behaviour change.
| Env / Value | What it does | Docs |
|---|---|---|
OMCP_FEDERATION_UPSTREAMS=name=url,… + OMCP_FEDERATION_TOKEN_<NAME> |
Federates upstream MCP servers' tools under <name>.<tool> namespace |
federation.md |
redis.enabled=true + redis.url |
Multi-replica HA via shared session store | horizontal-scaling.md |
OMCP_OTEL_ENABLED=true + OMCP_OTEL_ENDPOINT |
OpenTelemetry self-tracing | self-observability.md |
OMCP_AUDIT_WEBHOOK_URL + OMCP_AUDIT_WEBHOOK_TOKEN |
External SIEM webhook sink | audit-sinks.md |
OMCP_OIDC_PROFILE=<vendor> |
SSO vendor presets | auth-oidc.md |
OMCP_OIDC_DCR_ENABLED=true |
RFC 7591 Dynamic Client Registration | – |
ingressSticky.enabled=true |
Sticky-session ingress fallback | horizontal-scaling.md |
OMCP_CSRF_BYPASS_BEARER=false |
Force CSRF on every mutating call regardless of auth method | hardening.md |
New endpoints¶
POST /mcp/v/<product-id>— virtual MCP server bound to a Productws://host:3000/mcp/ws— WebSocket MCP transportPOST /api/auth/oidc/register— RFC 7591 DCR (opt-in)GET /api/conformance— discovery probe for MCP spec posture
Chart bumps¶
mcp-server1.8.1 → 2.0.0- Helm chart 0.12.1 → 1.0.0 (appVersion
2.0.0)
Verifying the upgrade¶
```bash
Boot the demo. Watch the startup logs for the line¶
"VERIFY_PLUGINS is on but PLUGIN_TRUST_ROOT is unset — refusing¶
to load any filesystem plugins (fail-closed). Builtins remain¶
available." — that confirms the default-on signature path.¶
docker compose --profile demo up
Confirm conformance harness still green:¶
make conformance
Confirm /api/conformance reports the supported revision (no auth /¶
CSRF required — discovery endpoint is intentionally open):¶
curl -s http://localhost:3000/api/conformance | jq '.revisions'
Smoke a virtual server. CSRF defaults bypass any Authorization:¶
Bearer request, so a token is enough — even in anonymous-auth mode¶
(the demo accepts any non-empty bearer when no OMCP_API_KEYS is¶
configured). Alternatively seed the Product via products.yaml.¶
TOKEN=smoke-token-not-required-in-anonymous-mode curl -sS -X PUT http://localhost:3000/api/products/test \ -H "Authorization: Bearer $TOKEN" \ -H 'content-type: application/json' \ --data '{"id":"test","name":"Test","status":"published","tools":["list_services"]}' curl -sS -X POST http://localhost:3000/mcp/v/test \ -H "Authorization: Bearer $TOKEN" \ -H 'content-type: application/json' \ -H 'accept: application/json, text/event-stream' \ --data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}' ```
Rollback¶
Downgrade to 1.8.1 is supported in-place — the on-disk schemas
(audit JSONL, products.yaml, sources.yaml, DCR store) are the same
shape. The only one-way changes are env-var defaults; flip them
back explicitly (VERIFY_PLUGINS=false) when rolling back if you
relied on them.
After the upgrade¶
The capabilities scoped to v2.x incremental follow-ups are listed in CHANGELOG.md under the "Deferred to v2.x (incremental)" section. Track ROADMAP.md for the v3.0 work (multi-cloud topology, anomaly history, SCIM provisioning, MkDocs site, …).