Send ask tools in the tool access policies claim
What does this MR do and why?
Ai::ToolRules::ResolutionService already computes an ask_tools list, but Rails never included it in the tool_access_policies JWT claim sent to the Duo Workflow Service, so ask had to be inferred from a tool being absent from both allow and deny.
Send it explicitly, matching the field the Duo Workflow Service already parses and already uses to decide whether governance is active for a workflow.
Also excludes asked tools from the MCP-derived allow additions in the /ws claim builder: MCP server tool names get gitlab_-prefixed, and a few catalog tools already carry that prefix natively (for example gitlab_api_get), so an asked tool could otherwise end up listed in both allow and ask on the wire. The Duo Workflow Service's own ask-wins precedence already made this a no-op in practice, but the claim should not carry a contradictory pair of lists, so it's fixed at the source too.
Measured claim size impact: the default (no rules configured) case grows by 9 bytes; a fully-configured namespace (every governed tool asked) adds about 1.8 KB of JSON, well within request header limits.
This change is safe to ship on its own — the Duo Workflow Service already treats a non-empty ask list as active governance, independent of the companion MR below.
References
Resolves https://gitlab.com/gitlab-org/gitlab/-/issues/600612
Companion Duo Workflow Service MR, which acts on the ask list's contents (not required for this MR to be safe or complete on its own): gitlab-org/modelops/applied-ml/code-suggestions/ai-assist!6594 (merged)
Screenshots or screen recordings
Not applicable. No UI in this repo changes.
How to set up and validate locally
- In a Rails console, pick (or create) a governed group and set an explicit "Ask" rule on a tool, for example:
group = Group.find_by_full_path('<group>') Ai::ToolRule.find_or_initialize_for_namespace(namespace_id: group.id, tool_name: 'list_issues') .tap { |r| r.web_access = :ask }.save! - Confirm the resolver actually reports it:
payload = Ai::ToolRules::ResolutionService.new(namespace: group, surface: :web).execute.payload payload[:ask_tools] # => ["list_issues"] payload[:pre_approved_tools] # => does not include "list_issues" - Mint a real token and decode it to see the actual claim. On a local GDK (not SaaS-licensed), self-signed token minting needs an env override:
# CLOUD_CONNECTOR_SELF_SIGN_TOKENS=true bin/rails console user = User.find_by(username: 'root') client = Ai::DuoWorkflow::DuoWorkflowService::Client.new( duo_workflow_service_url: 'localhost:50052', current_user: user, secure: false, pre_approved_tools: payload[:pre_approved_tools], denied_tools: payload[:denied_tools], ask_tools: payload[:ask_tools] ) token = client.send(:cloud_connector_token) JSON.parse(JWT.decode(token, nil, false)[0]['tool_access_policies']) # => {"allow"=>[...], "ask"=>["list_issues"], "deny"=>[]} - Regression check for the bug this fixes: set every tool in
read_only_gitlab(the only default-Allow group) to:ask, and re-run step 3. Before this change,allowanddenyboth come back empty andaskis silently dropped from the claim — indistinguishable, on the wire, from a namespace with no governance configured at all. After this change,askcarries all of them, so the Duo Workflow Service's existinggovernance_activecheck (already onmain) correctly stays active instead of going false. - Regression check for the MCP collision: enable an MCP server whose tools include one that's also asked (or reuse a catalog tool with a native
gitlab_prefix, likegitlab_api_get, and set it to Ask), and confirm the claim'sallowlist does not also contain that tool name. - Run the updated specs:
bundle exec rspec ee/spec/services/ai/tool_rules/resolution_service_spec.rb \ ee/spec/lib/ai/duo_workflow/duo_workflow_service/client_spec.rb \ ee/spec/services/ai/duo_workflows/workflow_context_generation_service_spec.rb \ ee/spec/requests/api/ai/duo_workflows/workflows_spec.rb
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.