whisper-money/app/Http/Requests/Settings
Víctor Falcón 5d7b655111
feat(mcp): add write tools (Phase 2) (#690)
## MCP Phase 2 — write tools

> **Stacked on #689** (`mcp-functionality`). Base this PR on
`mcp-functionality`, not `main`, and merge it **after** #689.

Phase 1 shipped a read-only MCP server for Pro accounts. This adds the
**write** surface and re-enables the read/read-write token scope the UI
dropped in PR1.

### Write tools
A new `WriteTool` base extends `McpTool`: on top of the Pro-plan gate it
requires the calling token to carry `mcp:write`, returning a clear error
for read-only tokens. Each concrete tool is annotated `#[IsDestructive]`
(PHP attributes aren't inherited, so the annotation lives on each tool,
not the base — a docblock on `WriteTool` notes this).

- `create_transaction` — manual (non-connected) accounts only; forces
`source = manually_created`.
- `update_transaction` / `delete_transaction` — manually-created
transactions only; bank/imported ones stay locked.
- `categorize_transaction` — sets/clears the category on **any**
transaction (imported included), marking it `category_source = manual`.
- `label_transaction` — add/remove labels on **any** transaction.
- `create_balance` — balance snapshot on manual accounts only.
- `create_category` / `update_category` / `delete_category` — mirrors
the settings controller (parent/depth/cycle rules, cashflow derivation,
child strategies).
- `create_label` / `update_label` / `delete_label`.
- `create_automation_rule` / `update_automation_rule` /
`delete_automation_rule` — JsonLogic conditions + category/label
actions, at least one action required.
- `list_labels` — a small **read** tool added so label ids are
discoverable (label/automation tools are unusable without it).

### Guardrails
Write tools never touch bank-sourced data: the existing
`TransactionSource` enum and `Account::isConnected()` are the barriers,
reused not reinvented. There is no server-side write confirmation
(client-controlled, accepted decision) — hence `#[IsDestructive]`.

### Token scope
`StoreMcpTokenRequest` re-adds `scope` (`read` | `read_write`); the
controller grants `['mcp:read']` or `['mcp:read', 'mcp:write']`. The
settings page gets its scope selector back with honest copy (new strings
added to `lang/es.json`). The `/mcp` route stays gated on
`abilities:mcp:read` — any MCP token can connect and read; the per-tool
`mcp:write` check is what blocks writes.

### Tests
Happy path + guardrail failures for every write tool, the
read-only-token rejection (via a real read-only PAT so the `tokenCan`
gate runs exactly as over HTTP), cross-user isolation, the inherited Pro
gate, and read/read_write scope validation.

### Notes
- `AutomationRule::labels()` gained a generic return annotation (needed
for larastan level 5 on the new label mapping).

### Verification
- `vendor/bin/pint --test` 
- `vendor/bin/phpstan analyse` (larastan level 5) — 0 errors 
- `php artisan test tests/Feature/Mcp
tests/Feature/Settings/McpTokenTest.php
tests/Feature/LocalizationTest.php` 
- `prettier --check` / `eslint` on `settings/mcp.tsx` 
2026-07-17 15:25:03 +00:00
..
Concerns feat: parent/child category tree (#474) 2026-06-03 19:30:12 +02:00
ApplyAutomationRuleRequest.php Apply automation rules to existing transactions (#413) 2026-05-22 08:36:18 +02:00
DeleteCategoryRequest.php feat: parent/child category tree (#474) 2026-06-03 19:30:12 +02:00
ProfileUpdateRequest.php refactor: centralize duplicated provider & locale keys into enums (#543) 2026-06-16 13:43:14 +00:00
StoreAccountRequest.php refactor(requests): share account detail validation rules (#481) 2026-06-03 19:01:03 +02:00
StoreAutomationRuleRequest.php refactor(requests): extract user-owned exists rules to trait (#477) 2026-06-03 17:33:54 +02:00
StoreBankRequest.php Accounts: Allow users to create custom banks with his own logo, and name (#12) 2025-12-04 15:24:04 +01:00
StoreCategoryRequest.php feat: parent/child category tree (#474) 2026-06-03 19:30:12 +02:00
StoreLabelRequest.php feat: add transaction labels feature (#24) 2025-12-13 13:02:19 +01:00
StoreMcpTokenRequest.php feat(mcp): add write tools (Phase 2) (#690) 2026-07-17 15:25:03 +00:00
TwoFactorAuthenticationRequest.php chore: upgrade Laravel 12 to 13 (#242) 2026-03-25 12:56:33 +00:00
UpdateAccountRequest.php refactor(requests): share account detail validation rules (#481) 2026-06-03 19:01:03 +02:00
UpdateAutomationRuleRequest.php refactor(requests): dedupe UpdateAutomationRuleRequest (#476) 2026-06-03 17:29:23 +02:00
UpdateCategoryRequest.php feat: parent/child category tree (#474) 2026-06-03 19:30:12 +02:00
UpdateChartColorSchemeRequest.php chore: upgrade Laravel 12 to 13 (#242) 2026-03-25 12:56:33 +00:00
UpdateLabelRequest.php feat: add transaction labels feature (#24) 2025-12-13 13:02:19 +01:00
UpdateNetWorthChartLoanPreferenceRequest.php chore: upgrade Laravel 12 to 13 (#242) 2026-03-25 12:56:33 +00:00
UpdateNetWorthChartRealEstatePreferenceRequest.php chore: upgrade Laravel 12 to 13 (#242) 2026-03-25 12:56:33 +00:00
UpdateNotificationPreferencesRequest.php feat(settings): let users disable bank transactions email (#472) 2026-06-02 12:24:39 +02:00
UpdateTimezoneRequest.php fix(user): persist detected timezones (#296) 2026-04-16 11:36:57 +01:00