Integrations

SigNoz

SigNoz is not a place Kams sends data to look at later. It is part of the runtime: it evaluates the condition that causes containment.

Provision as code

bash
uv run python scripts/provision.py

Both artifacts are versioned JSON applied through SigNoz's own MCP server, so re-running updates in place rather than duplicating.

The dashboard

provisioning/dashboards/kams-mcp.json defines nine panels:

#PanelSource
1Call rate by servermcp.tool.call.count
2p95 operation durationmcp.client.operation.duration
3Error ratemcp.tool.error.count
4Integrity driftmcp.integrity.drift.count
5Context tokens by server and estimated/measuredmcp.context.cost.tokens
6Sensitive-egress classesmcp.egress.classified.count
7Enforcement by action, rule, and originkams.enforcement.count
8All Kams findingskams.finding.count
9Correlated integrity logsOTLP logs

Panel 7 is the one that proves the loop: it separates origin=reflex from origin=signoz, so you can see at a glance whether a containment was decided locally or by the backend.

A saved dashboard is not proof

A dashboard JSON proves a query was written, not that it returns anything. Every panel here has been checked against live data from a real run — which is a different claim, and the only one worth making.

The critical alert

provisioning/alerts/integrity-critical.json is intentionally narrow. All four conditions must hold:

text
severity = 'CRITICAL'
AND kind = 'INTEGRITY_DRIFT'
AND detector = 'integrity.definition_drift'
AND baseline_state = 'pinned'

Why the detector clause

INTEGRITY_DRIFT could in principle come from elsewhere. Naming the detector pins the alert to exactly one code path.

Why the pinned clause

Drift on a baseline nobody vouched for is not an assertion violation. Only a pinned definition can quarantine a server.

This is also why mcp.integrity.drift.count exists as a separate metric from kams.finding.count — so the alert cannot fire on a result injection or an egress finding that happens to be critical.

The webhook daemon

bash
kams daemon --port 8787 --state kams-state.json --ttl 1h

Point a SigNoz webhook notification channel at http://<host>:8787/. The daemon serves three things:

RouteMethodPurpose
/POSTSigNoz alert notifications, firing and resolved
/findingsPOSTFindings from shims, queued for LLM enrichment. Always 202
/GETHealth plus the current restriction list

What a firing alert does

1

Identify the server

Payload shapes vary, so Kams checks alerts, groupedAlerts, and the payload root, in each looking at labels, commonLabels, and annotations for server, mcp.server.name, or mcp_server_name.

2

Refuse to guess

No identifiable server means the payload is logged and the alert ignored with a 202. Quarantining the wrong server is worse than quarantining nothing.

3

Write shared state

A quarantine_server restriction with origin=signoz, the rule prefixed signoz:, and a bounded TTL.

4

Emit control-plane telemetry

Its own span, a kams.enforcement.count increment, and a structured OTLP log — all carrying kams.enforce.origin=signoz.

A resolved notification calls lift(server) instead, removing matching restrictions.

The demo webhook does not verify signatures

It assumes loopback or a trusted network. Do not expose it to the internet as-is.

Verify the loop end to end

bash
kams-demo-alert

Runs the scenario with --no-reflex, waits for Foundry's evaluator, and fails unless the saved restriction carries origin=signoz and the next MCP call is blocked. Allow up to five minutes for ingestion and evaluation.

Correlated logs

Python logging is exported through OTLP. Finding and enforcement records emitted inside an active span arrive with non-empty trace_id and span_id, so panel 9 links straight back to the trace waterfall.

Attributes carry safe structured dimensions only — sensitive values are excluded at finding construction time, not filtered out later.