Skip to content

Audit Logging

Track every tool invocation for compliance, debugging, and operational visibility.

Enabling Audit Logging

Audit logging is disabled by default. Enable it in your policy file:

[audit]
enabled = true

Or for a more complete configuration:

[audit]
enabled = true
level = "all"
include_args = true
redact_fields = ["password", "api_key", "api_secret", "secret"]

Configuration Reference

Field Type Default Description
enabled bool false Master switch for audit logging
level string "all" Which events to log (see levels below)
include_args bool false Include tool arguments in log entries
redact_fields list ["password", "api_key", "api_secret", "secret"] Field names to redact when include_args is true

Audit Levels

Level Logs
all Every tool call (success, error, and denied)
writes Non-read-only calls + errors + denied
destructive Destructive calls + errors + denied
denied Only policy-denied calls and errors

Choose based on your needs:

  • Production monitoring: "denied" -- catch policy violations without log noise
  • Development/debugging: "all" -- see everything
  • Compliance: "writes" or "all" with include_args = true -- full mutation audit trail

Log Format

Audit events are emitted as structured JSON via the tracing crate with target: "audit". When using JSON log output (--log-level with RUST_LOG filtering), each event includes:

{
  "timestamp": "2026-03-05T10:30:00.123Z",
  "level": "INFO",
  "target": "audit",
  "fields": {
    "event": "tool_invocation",
    "tool": "list_enterprise_databases",
    "toolset": "enterprise",
    "result": "success",
    "duration_ms": 45
  }
}

Event Types

Event Description
tool_invocation Tool was called and completed successfully
tool_denied Tool call was blocked by policy (error code -32007)
tool_error Tool call failed with an error

With Arguments

When include_args = true, an arguments field is added containing the tool's input parameters. Sensitive fields listed in redact_fields are replaced with [REDACTED]:

{
  "fields": {
    "event": "tool_invocation",
    "tool": "create_enterprise_database",
    "toolset": "enterprise",
    "result": "success",
    "duration_ms": 1200,
    "arguments": "{\"name\":\"cache-db\",\"memory_size\":104857600,\"password\":\"[REDACTED]\"}"
  }
}

Redaction is recursive -- fields are redacted at any depth in the JSON structure.

Routing Audit Logs

Audit events use target: "audit" so you can route them separately from application logs using RUST_LOG filtering:

# All logs at info, audit at debug
RUST_LOG=info,audit=debug redisctl-mcp --profile my-profile

# Only audit logs
RUST_LOG=warn,audit=info redisctl-mcp --profile my-profile

Log Aggregation

To send audit logs to an external system, pipe JSON output to your collector:

# Datadog
redisctl-mcp --profile my-profile 2>&1 | datadog-agent pipe

# Fluentd
redisctl-mcp --profile my-profile 2>&1 | fluent-cat audit.mcp

# File-based (rotate with logrotate)
redisctl-mcp --profile my-profile 2>> /var/log/redisctl-mcp-audit.jsonl

Since the MCP server uses stderr for logs and stdout for MCP protocol messages, redirect stderr (2>) to capture audit output without interfering with the MCP transport.

Practical Examples

Compliance: Full Mutation Audit

Track all state-changing operations with arguments:

tier = "read-write"

[audit]
enabled = true
level = "writes"
include_args = true
redact_fields = ["password", "api_key", "api_secret", "secret", "token"]

Security: Denied Operations Only

Alert on policy violations:

tier = "read-only"

[audit]
enabled = true
level = "denied"

Debugging: Everything

Full visibility during development:

tier = "full"

[audit]
enabled = true
level = "all"
include_args = true