feat(mcp): add mcp_writes audit log table (draft, RFC #2536)#2553
feat(mcp): add mcp_writes audit log table (draft, RFC #2536)#2553justinhsu1477 wants to merge 1 commit into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ad06465 to
cf34296
Compare
|
Review/CI triage note for the draft: the current failures are not CI infrastructure. The branch is still using the pre-consolidation memory tree path ( |
…inyhumansai#2269 Q4) Closes the audit gap left by Phase 3 tinyhumansai#2269 Q4: the three MCP write tools (memory.store, memory.note, tree.tag) currently only emit tracing::info! lines, which evaporate when the process restarts. This adds a persistent SQLite audit table colocated in the existing memory-tree DB. V1 scope (draft — gated on RFC tinyhumansai#2536 lazy-consensus answers): * schema: new mcp_writes table + 3 indexes added to tree::store::SCHEMA (additive CREATE TABLE IF NOT EXISTS, applied on first connection through the existing with_connection cache — no separate migration step). * types: McpWriteRecord with the columns the RFC enumerates (timestamp, client_source_type, tool_name, args_summary, resulting_chunk_id, success, error_message). * insert: memory::mcp_audit::record_write — async wrapper around spawn_blocking, caps error_message at 1 KiB on a UTF-8 char boundary so a runaway error stack can't bloat the table (and so multibyte chars never get sliced). * wiring: dispatch_write_tool now records audit rows for both success and failure dispatches; failed audit inserts are logged but never propagate (RFC Q2=A — write availability not degraded by audit subsystem). The new call_tool_with_session entry point threads McpSession::source_type() through so per-client provenance ("mcp:claude-desktop" / "mcp:cursor" / fallback "mcp") makes it into the row. * args_summary: slim per-tool extractor stores identifying metadata only (title ≤128 chars + namespace + tag_count for memory.store; chunk_id + note_text_length for memory.note; chunk_id + applied_tags for tree.tag). Document content stays in the memory tree — never duplicated here. Out of scope (deferred to follow-up PRs once RFC tinyhumansai#2536 questions ratify, per the lazy-consensus comment thread): * Q4 query RPC (openhuman.mcp_audit_list) — recent() helper is in place but #[cfg(test)] only; graduates to pub once Q4 lands. * MCP-client-side exposure of the audit log. * UI surface for browsing audit history. * Retention / pruning policy. Tests: 6 new tests in memory::mcp_audit::tests cover round-trip, failure-row preservation, DESC ordering, limit honouring, oversize truncation, and multibyte-safe truncation. All 78 existing mcp_server tests still pass.
cf34296 to
1203aac
Compare
|
Thanks for the triage @YOMXXX — both points were dead-on. The path issue: you're right — my force-push yesterday landed an interim version of the commit where the Just amended + force-pushed the fix (
Local verification on the rebased tree:
CI should reflect the same shortly. On #2566 / consolidation: agreed reviewer load is the bigger concern here than authorship. My #2553 was intentionally a V1 scaffold (Q4 query RPC explicitly deferred per the lazy-consensus comment on #2536), so the two PRs aren't quite the same merge candidate — #2566 takes Q4=A as a unilateral call and ships the Either way I'll defer to his call rather than ask reviewers to compare the two cold. Will post a coordination note on #2566 with the RFC design context so it doesn't get lost if that one becomes canonical. |
Draft scaffold for the MCP write audit log proposed in #2536 (Phase 3 RFC #2269 Q4).
Opening as draft now to claim territory and give @senamakel a concrete artifact to react to alongside the four design questions on #2536. The implementation here follows the lazy-consensus defaults I posted in that thread — happy to revise if the consensus lands elsewhere.
What this PR does
Replaces the
tracing::info!-only audit trail for the three MCP write tools (memory.store,memory.note,tree.tag) with a persistent SQLite-backedmcp_writestable colocated in the existing memory-tree DB.Schema (in
tree::store::SCHEMA)Additive
CREATE TABLE IF NOT EXISTS— applied on first connection through the existingwith_connectioncache. No separate migration step.Wiring
dispatch_write_toolnow records both successful and failed dispatches. Failed audit inserts are logged atwarn!and never propagate — write availability is not degraded by the audit subsystem.tools::call_tool_with_session(name, args, client_source_type)threadsMcpSession::source_type()(from @YOMXXX-G's feat(mcp): capture client provenance in stdio sessions #2332) through so per-client provenance (mcp:claude-desktop/mcp:cursor/ fallbackmcp) lands in the row.tools::call_toolkept as a back-compat wrapper for any callers we haven't threaded yet.args_summary— identifying metadata onlyPer-tool slim extractor stores just enough to recognise a write — never the document content itself (that already lives in the memory tree via
doc_put, so duplicating here would bloat the table for no information gain):memory.store{title (≤128 chars), namespace, tag_count}memory.note{chunk_id, note_text_length}tree.tag{chunk_id, tags}Safety details
error_messagecapped at 1 KiB, truncated on a UTF-8 char boundary (so multibyte chars never get sliced mid-codepoint).tokio::task::spawn_blocking— no blocking the async runtime.Mapping to RFC #2536 design questions
Following the lazy-consensus defaults I posted in #2536:
recent()helper is#[cfg(test)]only; graduates topub+ anopenhuman.mcp_audit_listRPC in a follow-up PR once the surface is ratifiedIf any of these need to flip, I'd rather revise here in this draft than ship a v1 we have to walk back.
Out of scope (follow-ups)
openhuman.mcp_audit_listquery RPCTest plan
memory::mcp_audit::tests:error_messagelimithonourederror_messagetruncated to 1 KiBcargo test --lib mcp_server— all 78 existing tests still passcargo fmt --checkcleancargo check --libclean (only pre-existing warnings, none in new code)Why draft
I want @senamakel to see the shape of the wiring before I close out the lazy-consensus window on #2536. If a different consensus emerges on Q1/Q2/Q3, the changes here are localised enough to pivot without rewriting everything. Marking ready-for-review once the RFC questions settle (or after enough silent agreement on the issue thread).
Closes part of #2269 (Phase 3 Q4). Refs #2536.