refactor(api): route HabitTools via AgentOperationExecutor + add 6 IAiTools (#79) - #175
Conversation
…iTools (#79) MCP habit mutations now run through McpExecutorBridge -> IAgentOperationExecutor (Surface=Mcp), so they share the agent policy layer (read-only-credential denial, ownership pre-check, confirmation gating) and the AgentAuditLogs trail instead of calling IMediator.Send directly. Reads stay on MediatR. bulk_log_habits and bulk_skip_habits stay on MediatR because their MCP contract supports an explicit per-instance date the chat IAiTools do not model. Authors the 6 previously-unbacked habit IAiTools (update_checklist, reorder_habits, move_habit_parent, link_goals_to_habit, bulk_create_habits, bulk_delete_habits), registers them in DI, and maps them in AgentCatalogService so the build-time catalog enforcement passes. Overlaps wave-2 #89, which needs the same 6 tools. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Routes 13 MCP habit mutations through McpExecutorBridge → IAgentOperationExecutor with Surface=Mcp, giving them the same policy layer (read-only-credential denial, ownership pre-check, confirmation gating) and AgentAuditLogs trail as every other agent surface. The 6 new IAiTool implementations are correctly validated via JsonArgumentParser, registered in DI, and mapped into the catalog so the build-time enforcement tests pass. The bridge correctly extracts all four credential fields from ClaimsPrincipal, and the executor passes the Payload object reference through in-process (no JSON round-trip), so the BulkCreate/BulkDelete typed casts work as expected. BulkLogHabits and BulkSkipHabits are intentionally left on MediatR with clear documentation. Unit tests were rewritten to assert routing through the executor with Surface=Mcp and the correct OperationId, and integration tests cover the audit-row write and read-only-credential denial across three capability groups.
There was a problem hiding this comment.
Solid execution of a real security improvement. The McpExecutorBridge is a clean, well-scoped adapter: it extracts the four claim-derived credential fields correctly, sets Surface = Mcp, and maps the three executor outcomes (Succeeded / PendingConfirmation / Failed|Denied) back to the MCP string contract without leaking domain types. All 13 in-scope mutations route through the policy evaluator, the intentional exceptions (bulk_log/skip staying on MediatR) are documented with a clear rationale, and the catalog is updated consistently with the new IAiTool registrations. The 6 new IAiTool implementations follow the existing parsing/validation pattern (JsonArgumentParser, TryGetProperty-before-use, ToolResult on failure). Unit tests verify the routing surface (Surface=Mcp, correct operationId) and all three outcome paths; integration tests cover the audit-row write and the read-only-credential denial ordering. Build is clean with 0 new warnings.
Refs thomasluizon/orbit-ui-mobile#79
What
Routes MCP
HabitToolswrite methods through the shared agent executor instead of callingIMediator.Senddirectly, so MCP habit mutations now pass the same policy evaluation (read-only-credential denial, ownership pre-check, confirmation gating) andAgentAuditLogsaudit trail as every other agent surface.McpExecutorBridge(Api/Mcp) builds anAgentExecuteOperationRequestwithSurface = AgentExecutionSurface.Mcpand the four claim-derived credential fields (mirrorsAgentTools.ExecuteAgentOperation), callsIAgentOperationExecutor.ExecuteAsync, and maps the outcome back to the legacy MCP string contract (success → caller-formatted message; denied/failed →Error: …; pending → aconfirm_agent_operation_v2prompt).create_habit,update_habit,delete_habit,log_habit,skip_habit,create_sub_habit,duplicate_habit,update_checklist,reorder_habits,move_habit_parent,link_goals_to_habit,bulk_create_habits,bulk_delete_habits.bulk_log_habits/bulk_skip_habitsalso stay on MediatR — their MCP contract accepts an explicit per-instance date that the current chatIAiTools do not model, so routing them would silently drop that capability. Documented at the top ofHabitTools.cs.Authors the 6 missing
IAiTools (broad approach B)update_checklist,reorder_habits,move_habit_parent,link_goals_to_habit,bulk_create_habits,bulk_delete_habits— each wraps its existing MediatR command, is DI-registered inServiceCollectionExtensions, and is mapped into the matching capability'schatTools[]inAgentCatalogServiceso the build-time catalog enforcement tests (EveryChatTool_IsMappedToTheCatalog/EveryMcpTool_IsMappedToTheCatalog) pass.Tests
HabitToolsTestsrewritten for the new constructor: write-path tests assert the method routes through the executor withSurface=Mcpand the correctoperationId, and mapSucceeded→ success string /Failed/Denied→Error: …/PendingConfirmation→ confirmation prompt. Read-path and the two still-MediatR bulk methods are unchanged.McpHabitExecutorRoutingTestsintegration tests: (a) a successful MCPcreate_habitwrites anAgentAuditLogsrow (CapabilityId="habits.write",Surface=Mcp,OutcomeStatus=Succeeded); (b) a read-only API-key credential is denied withread_only_credentialacross three distinct toolsets (habits.write,goals.write, and the Destructivehabits.bulk.delete, proving read-only wins before the confirmation gate).dotnet build Orbit.slnxclean (0 errors, no new warnings). Unit projects green: Domain 344, Application 1679, Infrastructure 917. Integration tests authored but not executable locally — the entire integration suite currently fails at app startup with a pre-existingJwt:SecretKey must be at least 32 bytesconfig error unrelated to this change (CI runs unit projects only).🤖 Generated with Claude Code