Add granular token directives to MCP-reached Duo types

What does this MR do and why?

With a fine-grained PAT, any GraphQL object type without an authorize_granular_token directive resolves to null. Once the token is passed into the MCP GraphQL tools (#631630 (closed)), list_duo_agents_and_flows and get_duo_session return no data. I found the affected types by loading each MCP query against the schema and listing every object type it reaches.

This is the second MR for #631631. The first, !261599 (merged), covers the core MCP tools (work items, notes, merge requests, commits, repository tree, wiki). This one covers the EE Duo tools. The two are independent and can merge in either order. They only both touch config/authz/graphql/authorization_todo.txt and the generated doc/auth/tokens/fine_grained_access_tokens_graphql.md.

Changes (see the implementation guide):

  1. AiCatalogAgent, AiCatalogFlow, AiCatalogThirdPartyFlow: read_ai_catalog_item at the user boundary.
  2. AiFoundationalChatAgent: read_ai_catalog_item at the user boundary. It is not a database record and has no project or group.
  3. AiCatalogItemConsumer: a new raw and assignable permission, read_ai_catalog_item_consumer, at the project and group boundaries (the consumer's project or group). This matches the existing update_ai_catalog_item_consumer and delete_ai_catalog_item_consumer permissions. It is removed from config/authz/permissions/definitions_todo.txt.
  4. AiCatalogItemConsumerPermissions: skip_reason: :parent_authorizes, because it is only reachable through the consumer.
  5. DuoWorkflowEvent: read_duo_workflow at the user boundary, matching DuoWorkflow. The type directive covers every path to events: DuoWorkflow.latestCheckpoint, Project.duoWorkflowEvents, the root Query.duoWorkflowEvents and the workflowEventsUpdated subscription.
  6. The todo lines for all seven types are removed and the docs are regenerated.
  7. Specs: request specs for each directive and boundary, including the MCP combination. A consumer in one project for an item owned by another project returns the item when the token has read_ai_catalog_item_consumer on the project plus user-level read_ai_catalog_item. Without the user-level permission it returns the consumer with item null.

Design decision for reviewers

The catalog item types use the user boundary instead of the owning project. list_duo_agents_and_flows reads each consumer's item, and that item usually belongs to a different catalog project than the one the token is scoped to. Foundational items have no project at all. A project boundary would return the consumers with item: null.

AiCatalogSkill, added recently, uses a project boundary. This MR leaves it unchanged, and the two could be aligned in a follow-up. I would like feedback on whether the user boundary is the right choice here.

Out of scope

The SecurityScanProfileAttach and vulnerabilitiesSeverityOverride mutations, used by the MCP security tools, are not covered. Both take arrays of IDs (projectIds/groupIds, vulnerabilityIds), and Gitlab::Graphql::Authz::BoundaryExtractor cannot yet resolve a boundary from an array argument. They need framework support first and will be handled separately.

References

Screenshots or screen recordings

Not applicable, this MR only changes the backend.

How to set up and validate locally

  1. Create a fine-grained personal access token in the UI (User settings > Personal access tokens > Fine-grained) with "AI catalog item consumer: Read" for a project that has enabled AI catalog agents or flows, plus "AI catalog item: Read" at the user level.

  2. Run this query against /api/graphql with the token:

    query { aiCatalogConfiguredItems(projectId: "gid://gitlab/Project/1") { nodes { id enabled item { id name } } } }

    On master the nodes list is empty. With this MR it returns each consumer with its item.

  3. Remove the user-level "AI catalog item: Read" permission from the token and run the query again. The consumers are returned with item: null.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

🤖 Generated with Claude Code

Edited by Alex Buijs

Merge request reports

Loading
Loading