Move MCP toolset from annotations to _meta

What does this MR do and why?

The MCP tools/list response used to merge a custom toolset key into each tool's annotations. The MCP spec defines a fixed set of annotation keys and sets aside _meta for extra metadata, which uses reverse-DNS keys. This MR moves the key:

  • API::Mcp::Handlers::ListTools#build_tool_data now sends _meta: { "com.gitlab/toolset" => "<name>" }. annotations holds only the keys the tool declares, and is left out when it's empty, the same way Base::BaseService#to_h does it.
  • The EE governed_tool_data override now uses deep_merge to add anthropic/requiresUserInteraction, so Ask-tier tools keep the toolset key.
  • The developer docs ("Declaring a toolset") say where the toolset now appears.

Release note for external clients: the toolset moved from annotations.toolset to _meta["com.gitlab/toolset"] in tools/list.

The consumer audit in the issue found nothing that reads annotations.toolset outside in-repo specs, so the key moves without a period where both locations are sent. A repo search also turned up ee/spec/requests/api/mcp/handlers/orbit_tools_spec.rb, which used annotations.toolset to find Orbit tools. That spec is updated here.

get_mcp_server_version keeps annotations: { readOnlyHint: true }, because the service declares it on master. Only tools with empty annotations lose the key. This differs from the issue description, as the workplan points out.

References

Screenshots or screen recordings

Not applicable. This changes the API payload only, with no UI change.

How to set up and validate locally

  1. In GDK or Caproni, call tools/list on /api/v4/mcp with the header X-Gitlab-Enabled-Mcp-Server-Toolsets: all.
  2. Confirm every tool has _meta["com.gitlab/toolset"] and that annotations holds only spec keys (readOnlyHint, destructiveHint, and so on).
  3. Turn on duo_mcp_server_governance_rule_enforcement and duo_workflow_local_tool_governance, then set one tool to Always ask on the group's Tool management tab.
  4. Call tools/list again and confirm that tool's _meta has both com.gitlab/toolset and anthropic/requiresUserInteraction.
  5. Connect Claude Code or MCP Inspector and confirm the tool list loads.

Local verification

Verified against a local Caproni dev environment (gitlab-org/gitlab), following the steps above.

  • Called tools/list on /api/v4/mcp with X-Gitlab-Enabled-Mcp-Server-Toolsets: all. Every tool's annotations no longer carries toolset; it shows up correctly in _meta["com.gitlab/toolset"]. Confirmed directly on get_mcp_server_version:

    "annotations": { "title": "Get MCP server version", "readOnlyHint": true, "idempotentHint": true, "openWorldHint": false },
    "_meta": { "com.gitlab/toolset": "meta" }
  • Ran the four spec files this MR touches (spec/requests/api/mcp/handlers/list_tools_spec.rb, ee/spec/requests/api/mcp/handlers/list_tools_spec.rb, ee/spec/requests/api/mcp/handlers/orbit_tools_spec.rb, ee/spec/lib/ee/api/mcp/handlers/list_tools_spec.rb): 101 examples, 0 failures.

Not yet re-tested: the Ask-tier governance path (steps 3-4 above) via the Tool management UI, and connecting a live MCP client (Claude Code/MCP Inspector) per step 5 — the deep_merge fix for the governance path is covered by the passing ee/spec/lib/ee/api/mcp/handlers/list_tools_spec.rb suite above, but not separately exercised live.

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.

Edited by Amanda Rueda

Merge request reports

Loading
Loading