whisper-money/tests/Feature/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
..
AccountPageTest.php Reorganize setting items 2025-11-07 18:23:08 +00:00
AccountTest.php feat(currencies): add Colombian and Dominican peso (#471) 2026-06-01 18:14:16 +02:00
BankTest.php refactor(api): standardize serialization via model $hidden (#492) 2026-06-05 13:57:34 +02:00
BudgetSettingsRedirectTest.php fix(settings): restore budgets settings redirect (#228) 2026-03-16 13:04:08 +01:00
CategoryTest.php refactor(api): standardize serialization via model $hidden (#492) 2026-06-05 13:57:34 +02:00
CategoryTreeTest.php feat: parent/child category tree (#474) 2026-06-03 19:30:12 +02:00
ChartColorSchemeTest.php fix: route new PWA guests to signup (#313) 2026-04-21 10:53:05 +01:00
DeleteAccountPageTest.php fix(account): block deletion while subscription or trial is active (#531) 2026-06-14 20:46:44 +02:00
McpTokenTest.php feat(mcp): add write tools (Phase 2) (#690) 2026-07-17 15:25:03 +00:00
NetWorthChartLoanPreferenceTest.php fix: route new PWA guests to signup (#313) 2026-04-21 10:53:05 +01:00
NetWorthChartRealEstatePreferenceTest.php fix: route new PWA guests to signup (#313) 2026-04-21 10:53:05 +01:00
NotificationPreferenceTest.php feat(settings): let users disable bank transactions email (#472) 2026-06-02 12:24:39 +02:00
PasswordUpdateTest.php E2E Encryption 2025-11-07 14:21:25 +00:00
ProfileUpdateTest.php fix(account): block deletion while subscription or trial is active (#531) 2026-06-14 20:46:44 +02:00
TimezoneTest.php fix: route new PWA guests to signup (#313) 2026-04-21 10:53:05 +01:00
TwoFactorAuthenticationTest.php E2E Encryption 2025-11-07 14:21:25 +00:00