Skip to content

feat(mcp): create, update, log progress, and delete goals via MCP#8480

Open
ZachL111 wants to merge 15 commits into
BasedHardware:mainfrom
ZachL111:zach/mcp-goals-write
Open

feat(mcp): create, update, log progress, and delete goals via MCP#8480
ZachL111 wants to merge 15 commits into
BasedHardware:mainfrom
ZachL111:zach/mcp-goals-write

Conversation

@ZachL111

@ZachL111 ZachL111 commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

The MCP server already lets ChatGPT/Claude read the user's goals (get_goals), but not change them. This adds the write half, so an assistant connected to Omi can manage goals instead of only observing them: create a goal the user mentions, log progress against it, adjust its target, review its history, and remove a stale one.

Goals are measurable, long-horizon objectives (a target_value the user works toward, with current_value progress and recorded history), which is what makes this more than a tasks clone. Memories (create/edit/delete) and action items are already writable on the MCP surface, so this rounds out goals to give the agent the full two-way memory bank from #4862.

New MCP tools (with matching REST endpoints under /v1/mcp/goals):

  • create_goal(title, target_value, goal_type?, current_value?, min_value?, max_value?, unit?)
  • update_goal(goal_id, title? / target_value? / current_value? / unit? / min_value? / max_value?)
  • update_goal_progress(goal_id, current_value) (logs the value and records it to the goal's history)
  • delete_goal(goal_id)
  • get_goal_history(goal_id, days?) (read, complements progress logging)

Refs #4862.

Design

  • Shared orchestration. All goal writes run through a single utils/mcp_goals.py helper that both transports (REST mcp.py and SSE mcp_sse.py) call, so the two MCP paths cannot drift, the same approach used for memories and action items. utils sits below routers, so neither router imports the other.
  • Wraps the existing, tested goals data layer (create_goal, update_goal, update_goal_progress, delete_goal, get_goal_history) and the app's own /v1/goals semantics, including the small active-goal cap the database already enforces (it retires the oldest goal when full).
  • Ownership. Every write is uid-scoped through the goals database, so a goal id from another user can never resolve; a missing goal returns 404 / MCP -32001.
  • New goals.write OAuth scope, advertised in the server metadata. Writes are annotated (delete as destructive); get_goal_history stays under goals.read.
  • Input is bounded and validated: title length, numeric coercion for the value fields, the goal_type enum, and history days clamped to 1-365.

Testing

  • New tests/unit/test_mcp_goals.py (26 tests), registered in test.sh. Covers the shared util (create/update/progress/delete/history, validation, not-found mapping) and both transports (REST status codes, MCP error codes, tool registration and scope).
  • The existing MCP suites (data endpoints, search, profile) still pass. black --line-length 120 --skip-string-normalization is clean and scripts/lint_async_blockers.py reports no violations.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 6 files

Confidence score: 3/5

  • In backend/utils/mcp_goals.py, get_goal_history not translating missing goals to GoalNotFound can return a different error shape/status than other goal endpoints, which risks client-side handling regressions and inconsistent 404 vs -32001 behavior in production—align this path with the shared GoalNotFound mapping before merging.

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread backend/utils/mcp_goals.py
@ZachL111

Copy link
Copy Markdown
Contributor Author

Thanks cubic. Fixed: get_goal_history now raises GoalNotFound when the goal does not exist (it checks the user's goals first), so it returns the same 404 / MCP -32001 as the other goal operations instead of an empty list. Added tests for the missing-goal case across the util, REST, and SSE.

@kodjima33 kodjima33 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MCP feature: manage goals via MCP — approve only.

@Git-on-my-level Git-on-my-level added needs-maintainer-review Needs a human maintainer to review/approve (e.g. stacked, product, or architecture judgment) feature-fit-review PR touches feature direction; qualitative fit assessed labels Jun 29, 2026

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ZachL111 — the core MCP goals-write implementation still looks directionally good (shared utils/mcp_goals.py, uid-scoped database calls, and coverage across REST + SSE are all the right shape).

I’m requesting changes on this revision before it should merge for two concrete reasons:

  1. CI is currently red: Lint & Format Check fails because dart format --line-length 120 --set-exit-if-changed would reformat 8 changed Dart files (app/lib/backend/http/api/conversations.dart, app/lib/backend/schema/bt_device/bt_device.dart, app/lib/pages/chat/page.dart, app/lib/providers/capture_provider.dart, app/lib/providers/conversation_provider.dart, app/lib/services/auth_service.dart, app/lib/services/devices/device_connection.dart, and app/lib/utils/analytics/analytics_manager.dart). Please run the repo formatter and push the resulting formatting.
  2. The latest revision broadened the diff from the MCP goals work into unrelated app and firmware formatting-only changes. For reviewability and safe rollback, please trim this PR back to the MCP goals surface (backend router/helper/tests) unless those app/firmware formatting changes are intentionally part of this feature.

Separately, this still opens a new user-data write capability through MCP (goals.write), so I’d keep it under human maintainer/product review even after the mechanical issues are fixed. No formal approval from me under the automation policy.

@ZachL111

Copy link
Copy Markdown
Contributor Author

Thanks David. I reverted the unrelated app and firmware formatting churn, so the diff is now limited to the backend MCP goals surface:

  • backend/routers/mcp.py
  • backend/routers/mcp_sse.py
  • backend/utils/mcp_data.py
  • backend/utils/mcp_goals.py
  • backend/tests/unit/test_mcp_goals.py
  • backend/test.sh

The app Dart and firmware C files are back to their main versions, which also clears the Lint and Format Check failure (the 8 dart format files were all in that reverted set). The goals tests still pass locally (29 passed), and the shared utils/mcp_goals.py orchestration plus the REST and SSE coverage are unchanged.

@ZachL111

Copy link
Copy Markdown
Contributor Author

Preemptively added the consent permission text for goals.write to SCOPE_PERMISSION_TEXT (commit 41b6c9d), plus a unit test asserting every supported scope has a permission string. Same OAuth authorize KeyError gap David flagged on the folders PR #8489, so the consent page now renders for this scope too.

@Git-on-my-level Git-on-my-level added the security-review Needs security/privacy review label Jun 29, 2026

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ZachL111 — the latest revision does address the earlier mechanical blockers: the unrelated app/firmware formatting churn is gone, the lint/backend checks are green, and the goals.write consent text gap is now covered.

I’m still requesting one small change before this should merge: please put the new REST POST /v1/mcp/goals create endpoint behind the same style of write rate limit used for comparable MCP create surfaces (/v1/mcp/memories and /v1/mcp/action-items). Right now create_goal uses a plain get_uid_from_mcp_api_key dependency, while creating a goal writes Firestore and can retire/deactivate the oldest active goal when the user is at the active-goal cap. That makes repeated calls both a write-amplification path and a user-data churn path. The SSE transport has the general mcp:sse request limiter, but the REST create endpoint should not be easier to spam than memories/action-items.

Suggested shape: use Depends(with_rate_limit(get_uid_from_mcp_api_key, "goals:write")) (or the repo’s preferred equivalent policy name) on create_goal. I don’t think this needs to block the overall feature direction, but because this adds a new MCP user-data write/delete surface, I’d keep the PR under human maintainer/security review rather than formal automation approval.

@ZachL111

Copy link
Copy Markdown
Contributor Author

Done in 40e443f. Added a goals:write rate-limit policy and put create_goal behind Depends(with_rate_limit(get_uid_from_mcp_api_key, "goals:write")), matching how create_memory and create_action_item are limited.

I kept it at the memories:create tier (60 per hour) so it is not easier to spam than the other MCP writes, and following the action_items:write convention I left update/progress/delete to ride the shared mcp:sse and per-request auth limits, since they operate on an existing goal rather than the create-and-retire path that causes the write amplification and churn you flagged. Added a unit test asserting the policy exists and is not looser than memories:create.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature-fit-review PR touches feature direction; qualitative fit assessed needs-maintainer-review Needs a human maintainer to review/approve (e.g. stacked, product, or architecture judgment) security-review Needs security/privacy review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants