feat: emit policy_enforced events on guard deny (RFC-008 B9)#22
Merged
Conversation
- Add GuardEventEmitter with fire-and-forget POST to /v1/events - Add module-level singleton via get_event_emitter/set_event_emitter - Wire _emit_deny_event into both async and sync guard deny paths - Auto-configure emitter in MCPServerIdentity.connect() - Export GuardEventEmitter, get_event_emitter, set_event_emitter
|
✅ Integration tests passed! capiscio-core gRPC tests working. |
There was a problem hiding this comment.
Pull request overview
Adds RFC-008 (B9) policy enforcement telemetry by emitting capiscio.policy_enforced events when @guard denies tool access, enabling real-time observability via the registry /v1/events endpoint.
Changes:
- Introduces
capiscio_mcp/events.pywith a module-level singletonGuardEventEmitterthat POSTs events (fire-and-forget). - Updates
capiscio_mcp/guard.pyto emit a DENY event from both async and sync deny paths. - Updates
MCPServerIdentity.connect()to auto-configure the event emitter, and exports the new APIs fromcapiscio_mcp/__init__.py.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| capiscio_mcp/guard.py | Emits capiscio.policy_enforced on deny via the configured emitter singleton. |
| capiscio_mcp/events.py | Adds GuardEventEmitter + singleton accessors; sends events via background thread + requests. |
| capiscio_mcp/connect.py | Auto-configures the global emitter during MCPServerIdentity.connect(). |
| capiscio_mcp/init.py | Exposes event emitter types/helpers as part of the public API. |
Comment on lines
+143
to
+145
| # Fire-and-forget in a daemon thread | ||
| t = threading.Thread(target=self._send, args=(event,), daemon=True) | ||
| t.start() |
Comment on lines
505
to
511
| result.requested_capability, | ||
| result.presented_capability, | ||
| result.evidence_id, | ||
| ) | ||
| _emit_deny_event(result, effective_tool_name, capability_class) | ||
| raise GuardError( | ||
| reason=result.deny_reason or DenyReason.INTERNAL_ERROR, |
Comment on lines
476
to
+485
| logger.info("MCPServerIdentity ready for server %s: %s", server_id, did) | ||
|
|
||
| # Step 6: Auto-configure guard event emitter | ||
| set_event_emitter( | ||
| GuardEventEmitter( | ||
| server_url=server_url, | ||
| api_key=api_key, | ||
| agent_id=server_id, | ||
| ) | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds event emission to the
@guarddecorator so that DENY decisions are POSTed to the registry's/v1/eventsendpoint ascapiscio.policy_enforcedevents. This enables real-time observability of policy enforcement in the CapiscIO dashboard.Changes
New module:
capiscio_mcp/events.pyGuardEventEmitter— lightweight event emitter usingrequests(existing dependency)get_event_emitter()/set_event_emitter()Guard (
guard.py)_emit_deny_event()helper called from both async and sync deny pathsConnect (
connect.py)MCPServerIdentity.connect()now auto-configures the event emitter singleton using the sameserver_urlandapi_keyExports (
__init__.py)GuardEventEmitter,get_event_emitter,set_event_emitteradded to public APITesting
RFC-008 Sub-task
This implements B9 from the RFC-008 implementation plan.