From 143d1469996887d31db84adfe2c583f2b231ac43 Mon Sep 17 00:00:00 2001 From: Beon de Nood Date: Tue, 12 May 2026 01:07:56 -0400 Subject: [PATCH 1/2] fix: remove bogus /v1/pdp/evaluate default from PDP config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pdp_endpoint was defaulting to {server_url}/v1/pdp/evaluate which does not exist. Policy evaluation is LOCAL: the Go core fetches the OPA bundle via CAPISCIO_BUNDLE_URL and evaluates it with its embedded OPA engine. The pdp_endpoint should only be set when an explicit remote PDP service is deployed. The bogus default caused the guard's Phase 2 org-policy check to silently fail (404), meaning org policy overrides (lockdown, selective) had no effect — only hardcoded @guard min_trust_level values applied. --- capiscio_mcp/connect.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/capiscio_mcp/connect.py b/capiscio_mcp/connect.py index 8c26fb5..7c5e965 100644 --- a/capiscio_mcp/connect.py +++ b/capiscio_mcp/connect.py @@ -306,9 +306,10 @@ async def connect( auto_badge: If ``True``, issue an initial badge and start auto-renewal. renewal_threshold: Renew badge this many seconds before expiry. on_badge_renew: Optional callback ``(badge: str) -> None`` on renewal. - pdp_endpoint: Override PDP URL for org-policy enforcement. Defaults - to ``{server_url}/v1/pdp/evaluate`` (zero-config). Can also be - overridden via ``CAPISCIO_PDP_ENDPOINT`` env var. + pdp_endpoint: Optional remote PDP URL for org-policy enforcement. + Defaults to empty (local OPA bundle evaluation via Go core). + Use ``CAPISCIO_PDP_ENDPOINT`` env var or this param only when + a remote PDP service is explicitly deployed. Returns: :class:`MCPServerIdentity` with ``.did``, ``.badge``, ``.keys_dir``, @@ -540,11 +541,13 @@ async def connect( ) # Step 7: Auto-configure PDP for org-policy enforcement - # Precedence: explicit param > env var > derived from server_url (zero-config). + # Policy evaluation is LOCAL: the Go core fetches the OPA bundle via + # CAPISCIO_BUNDLE_URL and evaluates it with its embedded OPA engine. + # pdp_endpoint is only needed if a remote PDP is explicitly configured. effective_pdp = ( pdp_endpoint or os.environ.get("CAPISCIO_PDP_ENDPOINT") - or f"{server_url}/v1/pdp/evaluate" + or "" ) set_pip_config( PIPConfig( @@ -553,7 +556,10 @@ async def connect( workspace=server_id, ) ) - logger.info("Org-policy enforcement enabled: pdp_endpoint=%s", effective_pdp) + if effective_pdp: + logger.info("Remote PDP configured: pdp_endpoint=%s", effective_pdp) + else: + logger.debug("Using local OPA bundle for policy evaluation") return cls( server_id=server_id, From de48dcd3d785d0c8a436e5d8390cf01de2936d55 Mon Sep 17 00:00:00 2001 From: Beon de Nood Date: Tue, 12 May 2026 01:15:34 -0400 Subject: [PATCH 2/2] fix: default enforcement mode to EM-GUARD when bundle URL is configured EM-OBSERVE (Go core default) is shadow-mode: logs DENY but allows through. When connect() sets CAPISCIO_BUNDLE_URL for local OPA evaluation, it now also defaults CAPISCIO_ENFORCEMENT_MODE to EM-GUARD so policy decisions are actually enforced. Callers can still override via the env var for explicit shadow-mode. --- capiscio_mcp/connect.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/capiscio_mcp/connect.py b/capiscio_mcp/connect.py index 7c5e965..279f7db 100644 --- a/capiscio_mcp/connect.py +++ b/capiscio_mcp/connect.py @@ -361,6 +361,12 @@ async def connect( if cached_org_id: os.environ["CAPISCIO_BUNDLE_URL"] = f"{server_url}/v1/bundles/{cached_org_id}" + # Default enforcement mode to EM-GUARD when a bundle URL is configured. + # EM-OBSERVE (the Go core default) is shadow-mode: logs DENY but allows + # through, which silently breaks policy enforcement for callers. + if os.environ.get("CAPISCIO_BUNDLE_URL") and not os.environ.get("CAPISCIO_ENFORCEMENT_MODE"): + os.environ["CAPISCIO_ENFORCEMENT_MODE"] = "EM-GUARD" + did: Optional[str] = None private_key_pem: Optional[str] = None pub_pem: Optional[str] = None @@ -507,6 +513,8 @@ async def connect( # the Go core could be started.) if org_id and org_id != cached_org_id: os.environ["CAPISCIO_BUNDLE_URL"] = f"{server_url}/v1/bundles/{org_id}" + if not os.environ.get("CAPISCIO_ENFORCEMENT_MODE"): + os.environ["CAPISCIO_ENFORCEMENT_MODE"] = "EM-GUARD" # Step 5: Issue initial badge and start keeper badge: Optional[str] = None