Skip to content

feat(mcp): enhanced audit trail with SQLite backend and rich metadata#43

Merged
Mathews-Tom merged 3 commits into
mainfrom
feat/mcp-audit
Mar 25, 2026
Merged

feat(mcp): enhanced audit trail with SQLite backend and rich metadata#43
Mathews-Tom merged 3 commits into
mainfrom
feat/mcp-audit

Conversation

@Mathews-Tom

Copy link
Copy Markdown
Owner

Summary

Upgrade the MCP audit trail from minimal OK/DENIED/ERROR logging to rich, queryable audit entries with execution duration, tool-specific change details, and output summaries. Adds a SQLite backend alongside existing JSONL for structured queries, inspired by SivaRamSV/paaw's immutable audit trail pattern.

This is the final PR (#43) in the PAAW-inspired enhancement plan.

Before vs After

Before:

{"ts": "2025-03-25T14:32:15", "profile": "planner", "tool": "vault_write", "params": {"path": "02-projects/..."}, "result": "OK"}

After:

{
  "ts": "2025-03-25T14:32:15.123+00:00",
  "profile": "planner",
  "tool": "vault_write",
  "params": {"path": "02-projects/..."},
  "result": "OK",
  "duration_ms": 342,
  "change_detail": {
    "type": "vault_write",
    "note_path": "02-projects/test.md",
    "size_bytes": 2847,
    "was_new": true
  },
  "output_summary": {
    "status": "ok",
    "path": "02-projects/test.md"
  }
}

Tool-Specific Change Details

Tool change_detail fields
vault_write note_path, size_bytes, was_new
capture / capture_note note_path, title
vault_search result_count
vault_read note_path
graph_query / graph_path entity
find_duplicates note_path, matches_found
suggest_links note_path, suggestions_count
Other tools None (no detail)

Queryable SQLite Backend

Every audit entry is dual-written to JSONL (streaming, tail-friendly) and SQLite (indexed, queryable). The SQLite audit_events table has indexes on ts and tool.

# Query recent vault_write events by planner profile
entries = audit_logger.query(days=7, profile="planner", tool="vault_write")

# Purge entries older than 90 days
deleted = audit_logger.purge_old_entries(retention_days=90)

Changes

Modified Files

  • src/vaultmind/mcp/auth.py — Expanded AuditLogger.log() with optional duration_ms, change_detail, output_summary params (backward compatible). Added SQLite backend (audit_events table with WAL mode). Timestamps upgraded to millisecond-precision ISO format. New methods: query() for filtered retrieval and purge_old_entries() for retention management
  • src/vaultmind/mcp/server.py — Added time.perf_counter() timing in call_tool handler. New module-level helpers _build_change_detail() and _build_output_summary() extract tool-specific metadata. Duration, change_detail, and output_summary passed to all three audit log calls (OK, DENIED, ERROR)
  • src/vaultmind/config.py — Added MCPAuditConfig class with enabled, level (minimal/standard/verbose), log_search_queries, retention_days fields. Added audit field to MCPConfig
  • config/default.toml — Added [mcp.audit] section with defaults

New Files

  • tests/test_mcp_audit_enhanced.py (273 lines) — 25 tests across 5 classes

Backward Compatibility

  • AuditLogger.log(profile, tool, params, result) still works — new params all optional with defaults
  • Existing JSONL entries are valid — new fields are additive
  • SQLite DB created alongside JSONL on first use (.db suffix replaces .jsonl)
  • Existing test_mcp_auth.py tests pass unchanged
  • MCPAuditConfig nested inside MCPConfig via audit field — existing MCPConfig callers unaffected

Test plan

  • 25 new tests in test_mcp_audit_enhanced.py across 5 classes:
    • Enhanced logger (7): backward compat, duration_ms, change_detail, output_summary, SQLite write, millisecond timestamps, param truncation
    • Query filtering (7): recent entries, filter by profile/tool/result, limit, descending order, empty result
    • Purge retention (2): removes old entries, keeps recent
    • Change detail builders (7): vault_write, capture, vault_search, unknown tool, output summaries
    • MCPAuditConfig (2): default values, nested in MCPConfig
  • All existing test_mcp_auth.py tests pass unchanged
  • Full suite: 943/943 tests pass, 0 regressions
  • ruff check — clean
  • mypy --ignore-missing-imports — clean
  • Manual: verify dual-write (JSONL + SQLite) on real MCP tool calls
  • Manual: verify query() returns correct filtered results from production audit data

Expand AuditLogger.log() with optional duration_ms, change_detail, and
output_summary parameters (backward compatible). Add SQLite backend
alongside existing JSONL for queryable audit history. Timestamps now
use millisecond-precision ISO format.

New methods: query() for filtered retrieval (by days, profile, tool,
result) and purge_old_entries() for retention management.

Add MCPAuditConfig with enabled, level, log_search_queries, and
retention_days fields.
Track execution time via perf_counter in call_tool handler. Build
tool-specific change_detail (note_path, size_bytes, result_count)
and output_summary for each tool type. Pass duration_ms, change_detail,
and output_summary to all audit log calls (OK, DENIED, ERROR).
25 tests across 5 classes: enhanced logger (7) covering backward
compat, duration, change_detail, SQLite writes, timestamps; query
filtering (7) by profile/tool/result with ordering and limits;
purge retention (2); change detail builders (7) for vault_write,
capture, search, unknown tools; MCPAuditConfig validation (2).
@Mathews-Tom Mathews-Tom merged commit 2ba8f0a into main Mar 25, 2026
3 checks passed
@Mathews-Tom Mathews-Tom deleted the feat/mcp-audit branch March 25, 2026 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant