Skip to content

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 Product
  • ws://host:3000/mcp/ws — WebSocket MCP transport
  • POST /api/auth/oidc/register — RFC 7591 DCR (opt-in)
  • GET /api/conformance — discovery probe for MCP spec posture

Chart bumps

  • mcp-server 1.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, …).