Releases: chris-dare/pontifex
Release list
v0.5.1
Fixes a migration failure on databases provisioned before ec55a7a renamed the core schema from core to pontifex_mcp_core. Those databases never had a migration to perform the rename, so core_0005 (introduced in 0.5.0) would fail with schema "pontifex_mcp_core" does not exist on upgrade.
Fix
Adds migration core_0004b that conditionally renames the schema on existing installs and is a no-op on fresh ones.
Upgrading from 0.5.0
Run pontifex-mcp db upgrade — core_0004b and core_0005 will apply in sequence.
Upgrading from < 0.5.0
Follow the 0.5.0 upgrade notes, then run pontifex-mcp db upgrade.
Full Changelog: v0.5.0...v0.5.1
v0.5.0
Pontifex 0.5.0 is the namespace release.
Adds agent skill for pontifex-map
Terminology cleanup: Renames domain to namespace. That changes the words we use in docs, configs, and internal names, but it does not change your scope values, API keys, CLI commands, or auth flow.
Features
- Bundle the
pontifex-mcpskill in the wheel so the coding-agent guidance ships with the package (#103). - Add the Upgrading guide and the coding-agent guide (#103).
Refactors
- Rename the core concept from
domaintonamespaceacross the public docs and codebase (#102). - Move the worked example from
domains/toexamples/(#102).
Upgrading
- Run
pontifex-mcp db upgrade. - Rename
domain:tonamespace:in connector YAML. - If you import the internal registry model, switch
DomainRegistryModeltoNamespaceRegistryModel.
What stays the same
- Existing scope values such as
payments:balance:readandgse:*:*. ApiKeyAuthandJwtAuth.- The
pontifex-mcpCLI commands and flags.
v0.4.2
Zero-infra quickstart + key management CLI
ApiKeyAuth no longer requires Postgres or Redis to get started. Pass a SQLite URL and the schema is created on first request — no migration step, no containers:
auth = ApiKeyAuth(database_url="sqlite+aiosqlite:///./keys.db")Redis is optional in all configurations. When REDIS_URL is absent, rate limiting is disabled and logged; everything else works normally.
New: pontifex-mcp CLI
Migrations and key provisioning are now first-class CLI operations — no custom scripts needed.
pontifex-mcp db upgrade — runs the packaged Alembic migrations against Postgres. Safe to call on every deploy; concurrent runs are serialized via a Postgres advisory lock so a multi-replica rollout never races.
$ pontifex-mcp db upgrade
INFO [alembic.runtime.migration] Running upgrade -> 001, core schema
INFO [alembic.runtime.migration] Running upgrade 001 -> 002, audit log indexes
pontifex-mcp keys create/list/revoke — full key lifecycle. The plaintext token is shown once at creation and never stored; only the SHA-256 hash is written to the database. Revocation invalidates the Redis cache immediately.
$ pontifex-mcp keys create --owner usr_01 --label "prod" --scopes payments:invoices:read
sk_live_... ← copy now, shown once
Breaking change
The Postgres schema was renamed from core to pontifex_mcp_core (#92). Existing Postgres deployments need a one-time migration:
ALTER SCHEMA core RENAME TO pontifex_mcp_core;Then run pontifex-mcp db upgrade to apply the remaining migrations.
Fixes
ApiKeyAuthwithDATABASE_URLset no longer activates API-key auth on aJwtAuthserver (#93)keys revokenow invalidates the Redis cache immediately — revocation is not TTL-delayed (#97)- Scope validation requires 3-part
domain:resource:actionformat; 2-part scopes are rejected at creation (#97) keys createon Postgres withoutdb upgradegives a clean "schema not set up" error instead of raw SQL (#97)- Model
server_defaultvalues aligned with migrations so rawINSERTs behave identically on SQLite and Postgres (#98)
Full Changelog: v0.4.1...v0.4.2
v0.4.1
Replaces the deprecated authlib.jose JWT backend with PyJWT and patches a pydantic-settings vulnerability.
What changed
- JWT validation migrated to PyJWT —
authlib.joseis deprecated upstream and would break at authlib 2.0. The new implementation usesPyJWKSet+jwt.decodewith explicitkid-based key lookup. All security properties are preserved: asymmetric-only algorithm allowlist,alg: nonerejection, fullexp/iss/aud/subenforcement, generic error messages. Key rotation resilience is also restored for providers that reuse akidacross rotations. pydantic-settingsbumped to 2.14.2 — fixes GHSA-4xgf-cpjx-pc3j (symlink traversal inNestedSecretsSettingsSource).authlibremoved from the published dependency list;pyjwt>=2.8.0added.cryptography>=42.0(already declared) provides RSA/EC key support.
Full Changelog: v0.4.0...v0.4.1
v0.4.0 — the PontifexMCP facade
PontifexMCP — a governed MCP server in a few lines
PontifexMCP is a drop-in subclass of the MCP SDK's FastMCP: swap the import and your tools keep working. The difference is what you can turn on. The floor needs no database, no Redis, and no auth — an anonymous caller, audit to stdout, stdio or localhost HTTP. You graduate to the full governance stack one keyword at a time.
from pontifex_mcp import PontifexMCP, ApiKeyAuth
mcp = PontifexMCP("payments", auth=ApiKeyAuth(), audit="audit.db")
@mcp.tool(scope="refunds:execute")
async def issue_refund(charge_id: str, amount: int, idempotency_key: str) -> dict:
...
mcp.run(http=True)What's new
- Zero-infra floor → opt-in ceiling.
auth=ApiKeyAuth()/JwtAuth()turn on Bearer auth and scope enforcement;@tool(scope="resource:action")is advisory until then. No auth → HTTP binds127.0.0.1; exposing it publicly is an explicitauth="none". - Pluggable audit sinks.
audit=is stdout by default, a path/URL for durable rows, or a list to tee. Audit needs no infrastructure to stay visible. - SQLite alongside Postgres. Point any datastore at a
sqlite:///file path for local dev or apostgresql+asyncpg://…URL for production; the dialect is detected from the connection string. Postgres keeps its schema-per-domain isolation. mcp.cacheandmcp.add_openapi(...). A Redis cache exposed to your tools, and one-line generation of governed tools from an OpenAPI spec — each authenticated, scope-checked, and audited like a hand-written one.
Compatibility
Backward-compatible. create_mcp_http_app, run_mcp_stdio, and tool_runtime are unchanged — now the lower-level path beneath the facade.
Full Changelog: v0.3.0...v0.4.0
v0.3.0
Highlights
Per-user downstream auth via OAuth token exchange (RFC 8693)
Connectors can now authenticate to a downstream API as the calling user rather than with a single shared service credential. Pontifex exchanges the caller's token at your IdP for one scoped to the downstream's audience — the inbound token is never forwarded (no passthrough). Works with any RFC 8693 provider (Keycloak, Auth0, Microsoft Entra, Okta). Enable with type: token_exchange in a connector's config, or TokenExchange(...) in code. API-key callers (no user token to exchange) are cleanly rejected. (#44)
Shared encrypted token cache
Exchanged tokens can be cached in Redis across workers (PONTIFEX_TOKEN_CACHE=redis), encrypted at rest with a Fernet key held in the environment — a Redis dump yields only ciphertext. The default remains in-process memory. External-KMS key management is tracked in #52. (#47)
Enhancements
- Audit log records per-user delegations via a new
delegated_audiencecolumn — auditors can see which downstream a user's credential was delegated to (never the token). (#45) - Metrics for the token-exchange path (exchange latency, outcomes, cache hit/miss/coalesced) when Logfire is configured. (#48)
Fixes
- Isolate the IdP circuit breaker from the downstream connector breaker, so an IdP outage no longer trips the downstream breaker or locks out callers whose delegated token is still cached. (#46)
Docs
- New token-exchange and token-cache guidance on the Connectors page, including the two-persona (service credential vs. user identity) framing.
Full Changelog: v0.2.0...v0.3.0
v0.2.0 — OpenAPI connectors
Auto-generate governed tools from an OpenAPI spec — onboarding a system goes from code to config.
Highlights
- OpenAPI connectors —
register_openapi_tools(...)reads an OpenAPI 3.x spec (URL, file, or dict; JSON or YAML) and registers one MCP tool per allowlisted operation. Each tool is wrapped in the sametool_runtimeas a hand-written one — same scope check, audit row, and error envelope. Auto-generated does not mean ungoverned. - Config-only onboarding — point
PONTIFEX_CONNECTORS_CONFIGat a connectors YAML file and the server registers the tools at startup. No domain code required. - Derived scopes — each generated tool enforces a
domain:resource:actionscope derived from the operation (resource from the path, action from the verb), slotting straight into the existing scope model. - Opt-in by design — operations are exposed via an explicit
includeallowlist; mutating verbs requireallow_mutations. Typos and unapproved writes fail at startup, never silently. - Backend auth —
BearerFromEnv/HeaderFromEnvauthenticate the generated adapter to the downstream API; secrets are read from the environment (presence checked at boot). - Resilient — downstream calls run through a generated
DataAdapterunderDataSourceManager, so circuit breaking applies; connector health appears in/health/ready.
New docs: the Connectors guide. Full detail in #38 / #40.
Compatibility
Additive and backward compatible with 0.1.0. New dependency: pyyaml.
v0.1.0
First release of pontifex-mcp — enterprise-grade capabilities for MCP servers, built on the official MCP Python SDK.
Highlights
- Auth, two ways — OAuth 2.1 JWTs (any OIDC provider) and
sk_…API keys, resolving to oneCallerIdentity. - Least-privilege scopes —
domain:resource:action, enforced before every tool call. - Audit trail — every call recorded (who, what, when, source, cache hit, latency).
- Standards-based discovery — RFC 9728 protected-resource metadata +
WWW-Authenticate. - Resilient — per-caller rate limiting, adapter failover, circuit breaking.
- Observable — Logfire / OpenTelemetry wired in.