diff --git a/src/cmcp_runtime/__init__.py b/src/cmcp_runtime/__init__.py index 2d96b9f..e9a7dc5 100644 --- a/src/cmcp_runtime/__init__.py +++ b/src/cmcp_runtime/__init__.py @@ -1,3 +1,3 @@ """cMCP Runtime: hardware-attested MCP runtime.""" -__version__ = "0.1.0" +__version__ = "0.3.0" diff --git a/src/cmcp_runtime/config.py b/src/cmcp_runtime/config.py index 778dcd7..16c848c 100644 --- a/src/cmcp_runtime/config.py +++ b/src/cmcp_runtime/config.py @@ -67,7 +67,7 @@ class Config: attestation: AttestationConfig = field(default_factory=AttestationConfig) agent_manifest: AgentManifestConfig = field(default_factory=AgentManifestConfig) kill_switch: KillSwitchConfig = field(default_factory=KillSwitchConfig) - policy_bundle_path: str = "policy/" + policy_bundle_path: str = "policies/" catalog_path: str = "catalog.json" listen_addr: str = "0.0.0.0:8443" max_response_size_bytes: int = 2 * 1024 * 1024 # 2MB diff --git a/src/cmcp_runtime/mcp/proxy.py b/src/cmcp_runtime/mcp/proxy.py index 6cefbda..e5610ee 100644 --- a/src/cmcp_runtime/mcp/proxy.py +++ b/src/cmcp_runtime/mcp/proxy.py @@ -375,6 +375,12 @@ def _build_cedar_context( entry = self._catalog.lookup(tool_name) ctx: dict[str, Any] = { "tool_name": tool_name, + # Cedar resource entity: the backend builds Resource::"" from + # this, so policies can match a tool by name, e.g. + # forbid(principal, action, resource == Resource::"salesforce.contacts"); + # Without it the resource defaults to Resource::"default" and no + # resource-scoped policy can ever match. + "resource": tool_name, "arguments": _cedar_safe(arguments), "server_identity": entry.server.url if entry else "", "compliance_domain": entry.compliance_domain if entry else "external", diff --git a/src/cmcp_runtime/mcp/server.py b/src/cmcp_runtime/mcp/server.py index 3035f83..cfea7ee 100644 --- a/src/cmcp_runtime/mcp/server.py +++ b/src/cmcp_runtime/mcp/server.py @@ -528,7 +528,15 @@ async def _session_close(self, request: Request) -> Response: session_id: str = request.path_params["session_id"] if session_id != self._session.session_id: return JSONResponse( - {"error": f"unknown or already closed session_id={session_id}"}, + { + "error": "session_not_found", + "message": ( + f"No open session with id '{session_id}'. It may already be " + "closed, or you passed the _cmcp.session_id label instead of " + "the internal session id. Look up the internal id via " + "GET /audit/export?session_id=