Interceptor Framework
MCP Hangar implements the SEP-1763 interceptor framework with hook-based event delivery and priority-ordered mutator pipelines. See ADR-005 for design rationale.
Architecture
Tool invocation
|
v
DigestValidator (ADR-004: schema integrity check)
| emits DigestMismatchEvent on mismatch
v
MutatorPipeline (ADR-005: sequential transformation)
| ResponseTruncator, future: PII redaction, schema enforcement
v
EventBus.publish()
|
+---> flat subscribers (backward-compatible)
+---> hook subscribers (phase-wrapped Hook objects)
+---> wildcard filters (EventPattern matching)
Components
Digest Pinning (ADR-004)
| Type | Location | Purpose |
|---|
ToolDigest | domain/value_objects/tool_digest.py | SHA-256 fingerprint of a tool's canonical schema |
DigestPolicy | domain/value_objects/tool_digest.py | Enforcement level + unknown-tool handling + allowlist |
DigestEnforcement | domain/value_objects/tool_digest.py | Enum: audit, warn, block |
compute_tool_digest() | domain/services/digest_computation.py | Deterministic SHA-256 over canonical JSON |
DigestValidator | domain/services/digest_validator.py | Validates tools against policy, emits DigestMismatchEvent |
Hook-Based Event Model (ADR-005)
| Type | Location | Purpose |
|---|
HookPhase | domain/value_objects/hook.py | Enum: BEFORE, AROUND, AFTER, ON_ERROR, OBSERVE |
Hook | domain/value_objects/hook.py | Wraps (event, phase, sequence_number) |
IHookSubscriber | domain/contracts/hook_subscriber.py | Protocol for phase-aware event delivery |
EventBus | infrastructure/event_bus.py | Fan-out to both flat subscribers and hook subscribers |
Mutator Pipeline (ADR-005)
| Type | Location | Purpose |
|---|
IMutator | domain/contracts/mutator.py | Protocol: priority_hint, applies_to, mutate() |
MutationContext | domain/contracts/mutator.py | Input: method, direction, payload, correlation_id |
MutationResult | domain/contracts/mutator.py | Output: payload, changed flag, audit_only flag |
MutatorPipeline | application/services/mutator_pipeline.py | Sorts by (priority_hint, registration_index), executes sequentially |
ResponseTruncator | application/mutators/response_truncator.py | Truncates oversized tools/call responses, emits ResponseTruncated |
Wildcard Subscriptions (ADR-005)
| Type | Location | Purpose |
|---|
EventPattern | domain/value_objects/event_pattern.py | Segment-wise wildcard matching (*, tools/*, */response) |
compile_event_patterns() | server/api/ws/filters.py | Compiles raw strings into EventPattern objects |
matches_filters() | server/api/ws/filters.py | Tests events against wildcard-aware subscription filters |
Interceptor Discoverability
GET /interceptors/list returns mcp-hangar's capabilities as a SEP-1763 interceptor.
This is exposed as a plain HTTP endpoint (not a JSON-RPC method) because the MCP
Python SDK does not yet support custom JSON-RPC method registration for non-standard
methods. Once SDK support lands, this should migrate to a JSON-RPC interceptors/list
method per SEP-1763 (PR #2624).
{
"interceptors": [
{
"name": "mcp-hangar",
"version": "<package version>",
"type": "validator",
"supportedEvents": ["tools/call", "tools/list"],
"modes": ["audit", "enforce"],
"trustBoundary": "host"
},
{
"name": "mcp-hangar",
"version": "<package version>",
"type": "mutator",
"supportedEvents": ["tools/call"],
"modes": ["enforce"],
"trustBoundary": "host"
}
]
}
Mutator Ordering
Mutators execute in ascending priority_hint order. Ties are broken by registration order (stable sort).
| Mutator | priority_hint | Rationale |
|---|
| (future: PII redactor) | 100 | Runs early to redact before other transforms |
| (future: schema enforcer) | 500 | Validates structure after redaction |
| ResponseTruncator | 1000 | Runs last to truncate after all other transforms |
Event Flow
DigestValidator.validate_tool() produces DigestValidationResult with optional DigestMismatchEvent.
- Caller publishes events via
EventBus.publish().
- EventBus delivers to flat subscribers (type-matched), hook subscribers (phase-wrapped), and wildcard-filtered WebSocket streams.
MutatorPipeline.execute() runs registered mutators sequentially.
- Mutators collect domain events (e.g.,
ResponseTruncated) via event_collector list pattern.
- Caller publishes mutator events to EventBus for audit trail.
P2 Items (Not Yet Implemented)
interceptor/invoke JSON-RPC method (explicit invocation mode)
- Shadow mutations (audit mode on mutators)
- Per-interceptor
failOpen granularity
- Extended lifecycle events (
resources/*, prompts/*, sampling/*, elicitation/*, roots/*)