An MCP (Model Context Protocol) server for querying Vault audit logs through a backend abstraction.
Important backend status:
- Loki is the only currently supported backend.
- The service is intentionally designed to be pluggable via the
audit.Backendinterface (Search,Aggregate,Trace). - The server currently wires
LokiBackendincmd/server/main.go.
- Search Vault audit events with label-based filters
- Aggregate event counts by label dimensions
- Trace all events for a Vault request ID
- Retrieve detailed events for a request ID
- Vault configured with an audit device that ships logs to Loki
- Loki instance receiving Vault audit logs
If your log pipeline enriches Vault audit streams with dedicated labels, the server uses them for fast label-based filtering. The default stream selector is {service="vault", log_kind="audit"}.
For full filtering support, ensure these labels are also present:
vault_namespace(e.g.,root,team-a/)vault_operation(e.g.,create,read,update,delete)vault_mount_type(e.g.,pki,kv,userpass)vault_status(e.g.,ok,error)vault_mount_class(e.g.,auth,secret,system)vault_entity_idvault_policies(comma-separated)vault_token_policies(comma-separated)
If Vault audit logs are collected by the platform log forwarder (e.g., OpenShift Cluster Logging) without custom labels, set LOKI_BASE_LABELS to match your Loki stream selector. The server will automatically fall back to content-based filtering and Go post-processing.
export LOKI_BASE_LABELS='{"kubernetes_namespace_name":"hashicorp-vault"}'In this mode the audit JSON is expected inside a CLF message field (the server handles unwrapping automatically).
go build -o server ./cmd/serverSet Loki URL (defaults to http://localhost:3100):
LOKI_URL=http://localhost:3100 ./serverThe server uses stdio transport and is ready for MCP clients.
LOKI_URL- Loki API endpoint (default:http://localhost:3100)LOKI_BASE_LABELS- JSON object overriding the default stream selector labels. When set, Vault-specific label filters are disabled and the server uses content-based filtering instead. Example:'{"kubernetes_namespace_name":"hashicorp-vault"}'LOKI_BEARER_TOKEN- Bearer token sent in theAuthorizationheader for authenticated Loki endpoints (e.g., OpenShift LokiStack gateway)LOKI_TLS_SKIP_VERIFY- Disable TLS certificate verification for the Loki connection (trueorfalse, defaultfalse)AUDIT_DEBUG_LOG- Enable debug query logging (1ortrue)
internal/audit/model.go defines the storage abstraction:
type Backend interface {
Search(ctx context.Context, filter *SearchFilter) ([]Event, error)
Aggregate(ctx context.Context, filter *AggregateFilter, by string) ([]Bucket, error)
Trace(ctx context.Context, filter *TraceFilter) ([]Event, error)
}Current implementation:
internal/audit/lokibackend.go(LokiBackend)- configured in
cmd/server/main.gousingLOKI_URL
Adding a new backend only requires:
- Implementing the
Backendinterface - Constructing that backend in
cmd/server/main.go - Providing any backend-specific configuration variables
Search Vault audit events. Returns a summarized result (statistics, top dimensions, key insights, and sample events).
Parameters:
start_rfc3339- Start time (RFC3339, defaults to now-15m)end_rfc3339- End time (RFC3339, defaults to now)limit- Max results (1-500, default 100)namespace- Filter by namespaceoperation- Filter by operation (supports special handling forloginand write/update aliasing)mount_type- Filter by mount typemount_class- Filter by mount classstatus- Filter by status (okorerror)policy- Filter by policy name (matches bothvault_policiesandvault_token_policies)entity_id- Filter by entity ID
Count events grouped by a dimension.
Parameters:
start_rfc3339- Start time (RFC3339, defaults to now-15m)end_rfc3339- End time (RFC3339, defaults to now)by- Aggregation dimension- Currently supported at runtime:
vault_namespace,vault_operation,vault_mount_type,vault_status - Note:
vault_mount_classexists in the tool schema but is currently rejected by backend validation
- Currently supported at runtime:
- Optional filters:
namespace,operation,mount_type,mount_class,status
Find events for a specific request ID over a time range. Returns a summarized timeline.
Parameters:
start_rfc3339- Start time (RFC3339, defaults to now-15m)end_rfc3339- End time (RFC3339, defaults to now)limit- Max results (default 100, max 500)request_id- Vault request ID (required)
Retrieve detailed events for a request ID.
Parameters:
request_id- Vault request ID (required)
Notes:
- Looks back over the last 24 hours
- Returns detailed event objects (including redacted
rawaudit payload)
go test ./internal/audit -vIntegration/diagnostic tests:
python3 test_mcp.pyUseful Loki checks when results are empty:
# Default labels mode
curl -s 'http://localhost:3100/loki/api/v1/query?query={service="vault"}' | jq .
curl -s 'http://localhost:3100/loki/api/v1/query?query={service="vault",log_kind="audit"}' | jq .
curl -s 'http://localhost:3100/loki/api/v1/labels' | jq .
# OCP / LokiStack mode (adjust URL and token to match your environment)
curl -sk -H "Authorization: Bearer $LOKI_BEARER_TOKEN" \
"$LOKI_URL/loki/api/v1/labels" | jq .
curl -sk -H "Authorization: Bearer $LOKI_BEARER_TOKEN" \
"$LOKI_URL/loki/api/v1/label/kubernetes_namespace_name/values" | jq .The content previously documented in EVENT_ANALYSIS.md, SUMMARIZATION.md, and TESTING.md is summarized here.
Search results are semantically analyzed (not just counted). The analyzer classifies activity into categories and assigns severity levels (critical/high/medium/low/info), then emits high-signal insights for LLM consumers.
Examples of high-signal conditions:
- policy/audit/system configuration changes
- system namespace operations
- failed authentication/operation spikes
Search summaries include analysis fields such as:
critical_eventshigh_risk_eventsevent_categorieskey_insights
audit.search_events and audit.trace return condensed summaries by default to keep payloads small for LLM contexts.
SearchSummaryincludes:- total events and success/error stats
- top namespaces/operations/mount types
- success rate
- a small sample event set
summarizedflag
TraceSummaryincludes:- request timeline and total events
- first/last event context
- namespace/operation set
- sample events
summarizedflag
audit.aggregate already returns compact bucketed counts and does not require additional summarization.
- No results:
- confirm Loki has Vault audit streams and required labels
- confirm query time window covers data
- confirm namespace/filter values are correct
- Connection issues:
curl http://localhost:3100/ready- verify
LOKI_URLand network routing
- Time parsing errors:
- use RFC3339 timestamps (for example
2026-02-10T14:30:45Z)
- use RFC3339 timestamps (for example
Configure Vault audit output (example):
audit {
file {
path = "stdout"
}
}Then ship logs (Promtail, etc.) to Loki and attach the labels above. An example vector.toml is provided.
Returned events are redacted in code before response. Current redaction includes:
- Top-level
error/errors auth.client_token,auth.accessor,auth.secret_id,auth.metadataresponse.auth.client_token,response.auth.accessor,response.auth.secret_idresponse.secret.dataresponse.wrap_inforequest.data
Other fields (for example path, operation, namespace, mount metadata, and some response fields) may be preserved for analysis.
Vault audit logs should be treated as sensitive data. They can contain security-relevant metadata and potentially sensitive operational context.
Protecting audit log access is critical. Apply strong authentication, authorization, transport security, and storage controls in your logging and observability stack.
This project does not provide any built-in mechanism for securing access to audit logs or backend storage providers. Access control and data protection are the responsibility of your surrounding infrastructure and platform configuration.