MCP Guards [TAN-7975] and destroy_resource tool [TAN-7979]#14164
Merged
Conversation
destroy_resource tool [TAN-7979]
Collaborator
|
Contributor
Author
|
@kogre @amanda-anderson, if that's okay with you, I've assigned Amanda to the review so we don't pile too much onto Koen's plate. But your input is of course still welcome if you'd like to chime in, Koen. |
Wires every MCP write tool through Pundit's `authorize(record, :verb)` so the core app policies decide what the current user can do. `BaseTool.for` now rescues `Pundit::NotAuthorizedError` and converts it to an LLM-friendly `isError` MCP response via `unauthorized_message`. Today the MCP channel is super_admin-only (gated at the controller via `McpServer::McpPolicy`), so core policies already return true for every action. The wiring becomes load-bearing when moderators/admins are added to the channel.
Adds an MCP-channel guard on top of the user-level Pundit authorization: write tools that mutate a project (or anything inside one) call `authorize_project!(project)` at the top of `#run`, which refuses any operation whose target project isn't in `draft`. The check raises a `Pundit::NotAuthorizedErrorWithReason` and `BaseTool.for`'s existing rescue converts it to an LLM-friendly `isError` MCP response. Channel-level rules live on the runner (project state), user-level rules live in Pundit policies (who can do what). Two layers, two call sites, no extra abstraction.
Generic destroy tool that dispatches on `resource_type`. It uses the same two-layer authorization as the create tools: channel-level `authorize_project!(...)` (the target project has to be in draft) plus user-level Pundit `authorize(record, :destroy?)`. Each branch mirrors the corresponding core controller's `#destroy` action through the shared `destroy_with_sidefx!` helper, except where the MCP channel needs to be stricter: - You can't destroy a project if any of its phases contain inputs. - You can't destroy a phase if its participation method cascades to inputs (`native_survey`, `community_monitor_survey`, `common_ground`, `proposals`) and the phase has any. Other participation methods keep their ideas via the `IdeasPhase` join, so destroying the phase is safe.
406f976 to
5641805
Compare
| input_count = Idea.where(project: project).count | ||
| return if input_count.zero? | ||
|
|
||
| message = 'Cannot destroy project: it has inputs that would be deleted along with it.' |
Contributor
There was a problem hiding this comment.
Maybe not so relevant now, but I wonder about other phase types too - e.g. If there are votes submitted (baskets)?
Contributor
Author
There was a problem hiding this comment.
Good point! I logged it here: https://app.notion.com/p/MCP-Small-improvements-3849663b7b26803b8cebcb76da0f8081?v=0ab9663b7b2682f9bbdb08ab8edc5e05
amanda-anderson
approved these changes
Jun 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changelog
For translators