refactor(mcp_server): extract write-dispatch and audit pipeline into write_dispatch.rs#2650
Conversation
📝 WalkthroughWalkthroughExtracts the write-tool dispatch, policy enforcement, and audit pipeline from ChangesWrite dispatch module extraction and integration
Sequence DiagramsequenceDiagram
participant call_tool
participant write_dispatch
participant RPC
participant AuditStore
call_tool->>write_dispatch: is_write_tool(name)
write_dispatch-->>call_tool: true/false
call_tool->>write_dispatch: load_write_config(tool_name)
write_dispatch->>RPC: load config (with timeout)
RPC-->>write_dispatch: Config or error
write_dispatch-->>call_tool: Result<Config>
call_tool->>write_dispatch: enforce_write_policy_for_config(tool_name, config)
write_dispatch-->>call_tool: Ok() or InvalidParams
call_tool->>write_dispatch: dispatch_write_tool(tool_name, params, args, client_info, config)
write_dispatch->>RPC: invoke mapped RPC method
RPC-->>write_dispatch: result or handler error
write_dispatch->>AuditStore: record audit row (success/failure)
AuditStore-->>write_dispatch: row inserted
write_dispatch-->>call_tool: Ok(tool response)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning Review ran into problems🔥 ProblemsStopped waiting for pipeline failures after 30000ms. One of your pipelines takes longer than our 30000ms fetch window to run, so review may not consider pipeline-failure results for inline comments if any failures occurred after the fetch window. Increase the timeout if you want to wait longer or run a Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/openhuman/mcp_server/write_dispatch.rs (1)
508-546: ⚡ Quick winMake this test prove the handler-error branch.
This still passes if
openhuman.memory_doc_putis unregistered anddispatch_write_tooltakes theNonepath, so it doesn't actually pin theSome(Err(_))branch named in the test. Assert on the exact returned/audited error or set up a deterministic failing handler here.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/openhuman/mcp_server/write_dispatch.rs` around lines 508 - 546, The test currently can pass via the None-path; make it exercise the Some(Err(_)) branch by explicitly registering a deterministic failing write handler for the "memory.store" tool before calling dispatch_write_tool (or otherwise ensure openhuman.memory_doc_put is registered to return Err). Specifically, install a handler that returns Some(Err("deterministic failure")) for "memory.store", then call dispatch_write_tool and assert the returned tool_error content and the audit row from list_writes (via McpWriteListQuery) contains the same deterministic error message and success=false to prove the handler-error branch executed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/openhuman/mcp_server/write_dispatch.rs`:
- Around line 508-546: The test currently can pass via the None-path; make it
exercise the Some(Err(_)) branch by explicitly registering a deterministic
failing write handler for the "memory.store" tool before calling
dispatch_write_tool (or otherwise ensure openhuman.memory_doc_put is registered
to return Err). Specifically, install a handler that returns
Some(Err("deterministic failure")) for "memory.store", then call
dispatch_write_tool and assert the returned tool_error content and the audit row
from list_writes (via McpWriteListQuery) contains the same deterministic error
message and success=false to prove the handler-error branch executed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: bf02c5ce-1c3f-4e9c-8c1b-523f50586620
📒 Files selected for processing (3)
src/openhuman/mcp_server/mod.rssrc/openhuman/mcp_server/tools.rssrc/openhuman/mcp_server/write_dispatch.rs
…write_dispatch.rs Moves dispatch_write_tool, audit_write, audit_write_rejection, audit_write_rejection_without_config, is_write_tool, summarize_write_args, and related helpers out of the 2758-line tools.rs into a focused sibling module (mcp_server/write_dispatch.rs). tools.rs delegates to write_dispatch:: at call sites; public behavior is unchanged. Adds 8 new targeted tests covering the dispatch error path, is_write_tool, now_ms, first_chars, and summarize_write_args edge cases. Closes tinyhumansai#2604
1c1a2ef to
949df25
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/openhuman/mcp_server/write_dispatch.rs (2)
203-218: 💤 Low valueTimestamp captured inside async task may drift from rejection time.
The
now_ms()call on line 210 occurs inside the spawned async task afterload_config_with_timeout()completes. In contrast,audit_write_rejection(line 175) captures the timestamp immediately. If config loading is slow, the audit timestamp will reflect when the config loaded rather than when the rejection occurred.Consider capturing the timestamp before spawning:
⏱️ Suggested fix
let tool_name = tool_name.to_string(); let client_info = client_info.to_string(); let error_message = error_message.to_string(); let args_summary = summarize_write_args(&tool_name, audit_arguments); + let timestamp_ms = now_ms(); match tokio::runtime::Handle::try_current() { Ok(handle) => { let _ = handle.spawn(async move { match config_rpc::load_config_with_timeout().await { Ok(config) => audit_write( &config, NewMcpWriteRecord { - timestamp_ms: now_ms(), + timestamp_ms, client_info,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/openhuman/mcp_server/write_dispatch.rs` around lines 203 - 218, The timestamp for the rejection should be captured before spawning the async task so it reflects the actual rejection time rather than when config loading completes; modify the block around tokio::runtime::Handle::try_current() to call now_ms() into a local variable (e.g., captured_ts) before creating the closure, then move that captured_ts into the async move and use it when constructing NewMcpWriteRecord in audit_write (similar to how audit_write_rejection captures time); update references to now_ms() inside the spawned task to use the captured variable and keep use of load_config_with_timeout() and audit_write unchanged otherwise.
58-58: Confirm write tool → RPC routing is consistent
memory.store,memory.note, andtree.tagare all registered insrc/openhuman/mcp_server/tools.rswithrpc_method: Some("openhuman.memory_doc_put"), so the hardcoded value insrc/openhuman/mcp_server/write_dispatch.rsmatches the existing routing.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/openhuman/mcp_server/write_dispatch.rs` at line 58, The code hardcodes let rpc_method = "openhuman.memory_doc_put" in write_dispatch.rs which must match the rpc_method used when registering tools (memory.store, memory.note, tree.tag) in tools.rs; update the code to remove the literal by introducing and using a single shared identifier (e.g., a pub const OPENHUMAN_MEMORY_DOC_PUT) and replace the literal in write_dispatch.rs with that constant (and use it in tools.rs registrations), or if a constant already exists, reference that constant from write_dispatch.rs instead so routing is guaranteed consistent across memory.store, memory.note, tree.tag and write_dispatch::rpc_method.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/openhuman/mcp_server/write_dispatch.rs`:
- Around line 203-218: The timestamp for the rejection should be captured before
spawning the async task so it reflects the actual rejection time rather than
when config loading completes; modify the block around
tokio::runtime::Handle::try_current() to call now_ms() into a local variable
(e.g., captured_ts) before creating the closure, then move that captured_ts into
the async move and use it when constructing NewMcpWriteRecord in audit_write
(similar to how audit_write_rejection captures time); update references to
now_ms() inside the spawned task to use the captured variable and keep use of
load_config_with_timeout() and audit_write unchanged otherwise.
- Line 58: The code hardcodes let rpc_method = "openhuman.memory_doc_put" in
write_dispatch.rs which must match the rpc_method used when registering tools
(memory.store, memory.note, tree.tag) in tools.rs; update the code to remove the
literal by introducing and using a single shared identifier (e.g., a pub const
OPENHUMAN_MEMORY_DOC_PUT) and replace the literal in write_dispatch.rs with that
constant (and use it in tools.rs registrations), or if a constant already
exists, reference that constant from write_dispatch.rs instead so routing is
guaranteed consistent across memory.store, memory.note, tree.tag and
write_dispatch::rpc_method.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: dc9df06a-24af-4086-b084-90e1507a7432
📒 Files selected for processing (3)
src/openhuman/mcp_server/mod.rssrc/openhuman/mcp_server/tools.rssrc/openhuman/mcp_server/write_dispatch.rs
✅ Files skipped from review due to trivial changes (1)
- src/openhuman/mcp_server/mod.rs
Summary
dispatch_write_tool,audit_write,audit_write_rejection,audit_write_rejection_without_config,is_write_tool,summarize_write_args, and 6 private helpers out of the 2,758-linetools.rsinto a new focused sibling modulemcp_server/write_dispatch.rstools.rsdelegates towrite_dispatch::at all call sites; public behavior (tool responses, audit records, logging, correlation fields) is unchangedis_write_tool,now_ms,first_chars, andsummarize_write_argsedge cases; 7 existing tests moved into the new moduleResult
tools.rswrite_dispatch.rsAll 9,490 Rust lib tests pass. TypeScript, ESLint, Prettier, and
cargo fmtall clean.Test plan
cargo test -p openhuman --lib -- openhuman::mcp_server— 94 passed, 0 failedcargo fmt --all -- --check— cleancargo clippy -p openhuman— no errorspnpm --filter openhuman-app compile— cleanpnpm --filter openhuman-app lint— 0 errors (pre-existing warnings only)pnpm --filter openhuman-app format:check— cleanCloses #2604
Summary by CodeRabbit