FlowFuse / FlowFuse/flowfuse

5.9-b Write tools, non-destructive (phase 2)

Open
#7,703 0 comments 0 reactions 0 assignees View on GitHub
story
Dominant language
JavaScript
Stars
400
Forks
89
Avg merge
1d 20h
Merged PRs (30d)
149

Description

**Parent:** #7678 (5.9 Notifications and user self-service)
**Tool file:** new `forge/ee/lib/mcp/tools/user.js`

`readOnlyHint: false`, `destructiveHint: false`. Sibling actions that share one concept are consolidated into a single tool with an `action` enum (or an optional target id), following 5.1's pattern.

| Tool | Endpoint(s) | Scope | Annotation |
|---|---|---|---|
| `platform_set_notification_read_state` (single via `notificationId`, or bulk via `ids`) | `PUT /user/notifications/:notificationId`, `PUT /user/notifications` | `user:edit` (self) | write |
| `platform_respond_to_team_invitation` (`action`: `accept`\|`reject`) | `PATCH /user/invitations/:invitationId` (accept), `DELETE /user/invitations/:invitationId` (reject) | `user:edit` (self) | write |
| `platform_update_profile` | `PUT /user` | `user:edit` (self) | write |

**Design notes:**
- `set_notification_read_state` consolidates the single (`PUT /user/notifications/:notificationId`) and bulk (`PUT /user/notifications`) variants: with a `notificationId` the tool calls the single-notification route with `{ read }`; otherwise it calls the bulk route with `{ ids, read }`. There is **no bare "mark all" route** - the bulk route takes an explicit array of notification ids. `read` may be `true` or `false` (mark read or unread), so the tool is named for setting the state, not just marking read.
- `respond_to_team_invitation` consolidates accept and reject on the same invitation resource. `accept` maps to `PATCH`, `reject` maps to `DELETE`; both self-scope via `byId(id, request.session.User)` (404 if not owner). Reject only declines an invitation the caller received, so it is modelled as a non-destructive response rather than a destructive delete.
- `platform_update_profile` should expose only safe profile fields (name, defaultTeam), not privileged fields that only `/users/:userId` (admin) can set. (The route itself also accepts `username`/`email`/`tcs_accepted` as self-service, but v1 exposes only name/defaultTeam.)

**Tool definitions (description + zod inputSchema):**
```js
platform_set_notification_read_state: {
description: 'Set the read state of the caller notifications. Provide notificationId to set the state of one notification, or ids to set several; there is no bare "mark all" shortcut, so bulk changes require an explicit list of ids. read=true marks read, read=false marks unread.',
inputSchema: z.object({
notificationId: z.string().optional().describe('Notification hashid'),
ids: z.array(z.string()).optional().describe('Array of notification hashids'),
read: z.boolean().describe('Read state to set: true marks read, false marks unread')
})
}
platform_respond_to_team_invitation: {
description: 'Respond to a team invitation the caller received. accept joins the team; reject declines the invitation. Neither action requires any additional input.',
inputSchema: z.object({
invitationId: z.string().describe('Invitation hashid the caller received'),
action: z.enum(['accept', 'reject']).describe('Whether to accept or reject the invitation')
})
}
platform_update_profile: {
description: 'Update the current user profile. Only name and defaultTeam are exposed; other self-service profile fields are not editable through this tool.',
inputSchema: z.object({
name: z.string().optional().describe('New display name for the current user'),
defaultTeam: z.string().optional().describe('Team hashid to set as the current user default team')
})
}
```

**Tests:**
- Write tools rejected for read-only PAT.
- `set_notification_read_state` routes to the single-notification endpoint when `notificationId` is supplied, and to the bulk endpoint with `ids` otherwise; both accept `read: true` and `read: false`.
- `respond_to_team_invitation` routes `accept` to `PATCH` and `reject` to `DELETE`.
- Notification/invitation tools operate only on the caller's own records.

---

Contributor guide

Open the contributing guide

Research direction

Implement the tools in forge/ee/lib/mcp/tools/user.js, first comparing the sibling 5.1 pattern and the listed user endpoints. Run the write-tool tests, including read-only PAT rejection, routing for notification and invitation actions, both read states, and self-record restrictions. Done means all three tools expose the specified inputs and annotations and pass those behaviors.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
api, backend, tooling
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.