feat(mcp): create, complete, update, and search action items via MCP#8439
Conversation
There was a problem hiding this comment.
6 issues found across 6 files
Confidence score: 2/5
- In
backend/routers/mcp_sse.py, SSE write tools (includingcreate_action_item) appear to enforcesecuritySchemesonly in metadata and do not verifyaction_items.writescope at execution time, so clients with a valid key but insufficient scope could still perform writes — add explicit scope checks in the SSE executor before merging. - In
backend/routers/mcp_sse.py(create_action_item), theaction_items:writelimiter is not enforced, which lets clients bypass the intended 120/hour throttle and could lead to abuse or noisy write amplification — wire the same rate-limit guard into the SSE path before merge. - In
backend/utils/mcp_action_items.py,delete_action_itemignores the DB delete boolean and can report success on no-op/race-condition deletes, which can mislead clients and mask missing-record behavior — raiseActionItemNotFoundwhen the delete result is false. - In
backend/utils/mcp_action_items.py,update_action_itemallows empty-stringdue_atto clear the field despite the documented contract, andset_completedcan throw an unhandledAttributeErrorif the post-write fetch returnsNone; with missing REST coverage for some write/search handlers inbackend/tests/unit/test_mcp_action_item_writes.py, these regressions are easier to ship — tighten input validation/None checks and add the missing handler tests before merging.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="backend/routers/mcp_sse.py">
<violation number="1" location="backend/routers/mcp_sse.py:929">
P1: `create_action_item` in the SSE tool executor does not enforce the `action_items:write` rate limit, allowing clients to bypass the intended 120/hr creation throttle.</violation>
<violation number="2" location="backend/routers/mcp_sse.py:929">
P1: New write tools rely on metadata-only `securitySchemes` declarations (`ACTION_ITEMS_WRITE_SECURITY`) without enforcing `action_items.write` scope during SSE tool execution. `authenticate_api_key` returns only a `user_id`; the `handle_mcp_message` → `execute_tool` path never validates scopes before allowing creates, updates, completions, or deletes.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| raise ToolExecutionError(str(e), code=-32602) | ||
| return {"action_items": items} | ||
|
|
||
| elif tool_name == "create_action_item": |
There was a problem hiding this comment.
P1: New write tools rely on metadata-only securitySchemes declarations (ACTION_ITEMS_WRITE_SECURITY) without enforcing action_items.write scope during SSE tool execution. authenticate_api_key returns only a user_id; the handle_mcp_message → execute_tool path never validates scopes before allowing creates, updates, completions, or deletes.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/routers/mcp_sse.py, line 929:
<comment>New write tools rely on metadata-only `securitySchemes` declarations (`ACTION_ITEMS_WRITE_SECURITY`) without enforcing `action_items.write` scope during SSE tool execution. `authenticate_api_key` returns only a `user_id`; the `handle_mcp_message` → `execute_tool` path never validates scopes before allowing creates, updates, completions, or deletes.</comment>
<file context>
@@ -820,6 +917,74 @@ def execute_tool(user_id: str, tool_name: str, arguments: dict) -> dict:
+ raise ToolExecutionError(str(e), code=-32602)
+ return {"action_items": items}
+
+ elif tool_name == "create_action_item":
+ try:
+ completed = parse_mcp_bool(arguments.get("completed"), "completed", default=False)
</file context>
| raise ToolExecutionError(str(e), code=-32602) | ||
| return {"action_items": items} | ||
|
|
||
| elif tool_name == "create_action_item": |
There was a problem hiding this comment.
P1: create_action_item in the SSE tool executor does not enforce the action_items:write rate limit, allowing clients to bypass the intended 120/hr creation throttle.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/routers/mcp_sse.py, line 929:
<comment>`create_action_item` in the SSE tool executor does not enforce the `action_items:write` rate limit, allowing clients to bypass the intended 120/hr creation throttle.</comment>
<file context>
@@ -820,6 +917,74 @@ def execute_tool(user_id: str, tool_name: str, arguments: dict) -> dict:
+ raise ToolExecutionError(str(e), code=-32602)
+ return {"action_items": items}
+
+ elif tool_name == "create_action_item":
+ try:
+ completed = parse_mcp_bool(arguments.get("completed"), "completed", default=False)
</file context>
… and do not clear a due date on blank input
…pdate/search handlers
|
Thanks cubic. Addressed the four in-scope issues and pushed a fix:
On the two SSE findings (scope enforcement and the per-tool rate limit): both are accurate, but they describe the existing MCP server design rather than anything specific to these tools. The SSE executor authenticates with a full-access per-user MCP API key and has never enforced per-tool OAuth scopes for any write tool, including the existing |
|
Thanks @ZachL111 — this is a well-built feature. The centralized orchestration in Two notes for a human maintainer, not blockers from me:
Labeling for product/maintainer review before merge — this rounds out action items as a two-way MCP surface, which is a direction call (whether Omi should let assistants mutate tasks, not just read them), so I'm not approving from here. The implementation itself is in good shape. |
kodjima33
left a comment
There was a problem hiding this comment.
MCP write tools for action items (create/complete/update/delete/search). New capability; approving as feature.
Summary
The MCP server already lets ChatGPT/Claude read the user's tasks (
get_action_items), but not change them. This adds the write half, so an assistant connected to Omi can manage tasks instead of only observing them: create a follow-up it identified, mark one done, reschedule one, delete a stale one, and find a specific task by what it is about before acting on it.New MCP tools (with matching REST endpoints under
/v1/mcp/action-items):create_action_item(description, due_at?, completed?)complete_action_item(action_item_id, completed?)(reopen by passingcompleted=false)update_action_item(action_item_id, description?, due_at?)delete_action_item(action_item_id)search_action_items(query, limit?)(semantic, completes thesearch_memories/search_conversationspattern)This rounds out action items as a two-way surface, the same way memories already have create/edit/delete tools, and moves in the direction of #4862 (make Omi an easily integratable memory bank for ChatGPT/Claude).
Design notes
utils/mcp_action_items.pyhelper that both transports (RESTmcp.pyand SSEmcp_sse.py) call thinly. The two MCP paths have drifted before (for example the SSE/REST search alignment in Align SSE MCP memory search filtering with REST #7788), so the write logic lives in one place rather than being duplicated.utilssits belowrouters, so neither router imports the other.create_action_itemidempotency key, so a model client that retries a tool call after a transport hiccup updates the same task instead of creating duplicates. This mirrors the app's own create endpoint.search_action_items(and the app's task search) the same way app-created tasks do. Vector writes are best-effort: the task is already persisted, so a vector failure only degrades ranking and never loses the task.action_items.writeOAuth scope, advertised in the server metadata. Writes are annotated (delete as destructive). Creation is rate limited (action_items:write, 120/hr) since it is the only unbounded-growth operation; the others act on existing tasks.Out of scope
MCP-created tasks are not pushed to external task managers (Apple Reminders / Todoist) or armed with a reminder notification. Those are client-side concerns today, and silently syncing AI-created tasks into a user's external tools felt like the wrong default for a first cut. Straightforward to add later if maintainers want it.
Testing
tests/unit/test_mcp_action_item_writes.py(33 tests), registered intest.sh. Covers the shared util (idempotency key, due-date parsing, create/complete/update/delete/search, vector-failure resilience, paywall and not-found guards) and both transports (REST status codes, MCP error codes, tool registration and scope).backend/test.shpasses locally, and the existing MCP suites (data endpoints, search, profile) still pass.black --line-length 120 --skip-string-normalizationclean.scripts/lint_async_blockers.pyreports no violations.