List security MCP tools only when they are licensed
What does this MR do and why?
Some MCP security tools were listed for everyone, even when the user can't use the feature they wrap. This MR hides them in tools/list when the user doesn't have the license.
The four tools affected:
list_vulnerabilities,get_vulnerability, andsave_vulnerabilityneed the security dashboard (security_dashboard).attach_scan_profileneeds security scan profiles (security_scan_profiles).
Both are Ultimate features.
When the tools are listed:
- GitLab Self-Managed and GitLab Dedicated: when the instance license includes the feature.
- GitLab.com: when the user belongs to at least one top-level group on a plan that includes the feature (Ultimate, including trials). Access to a project in that group also counts.
The rule applies even when the client asks for the code_security toolset in the X-Gitlab-Enabled-Mcp-Server-Toolsets header or uses all. The toolset filter runs first, so the check only runs when code_security is requested.
This MR changes listing only. Calling a tool directly works as before, and each tool still checks permissions. A check at call time is a follow-up on the issue.
Known edge: on GitLab.com, a user whose only access is a public open-source project with a legacy open-source license grant won't see the tools listed. They can still call them. Hiding is the safer side.
No feature flag, because this only narrows what is listed. No database changes. EE only.
Implementation notes:
- New
Mcp::Tools::Security::Availabilitymodule, modeled onMcp::Tools::DuoWorkflows::Availability. The four services include it, andattach_scan_profileoverrides the feature name. - The result is cached per user and feature for the request, not on the shared tool object.
- The test instance license has no Ultimate features, so the annotation test in the
list_toolsrequest spec now stubs them. A new context checks the listed and hidden cases.
References
- Issue: #629267
- Same change for the Duo Agent Platform tools: #631747 (closed)
Screenshots or screen recordings
Not applicable. There is no UI change.
How to set up and validate locally
Before you start:
- Run GDK with the MCP server enabled and an Ultimate license.
- Have an oauth token with the
mcpscope inTOKEN.
Use this command to list the tools:
curl -s -X POST "https://gdk.test:3443/api/v4/mcp" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "X-Gitlab-Enabled-Mcp-Server-Toolsets: code_security" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":"1"}' | jq '.result.tools[].name'You can also use an MCP client or MCP Inspector instead of curl.
-
Run the command. Check that
list_vulnerabilities,get_vulnerability,save_vulnerability, andattach_scan_profileare listed. -
To check GitLab.com behavior, turn on SaaS simulation: add
export GITLAB_SIMULATE_SAAS=1toenv.runit, then rungdk restart. -
In the Rails console, pick a top-level group your user belongs to. Make sure the user is in no other Ultimate group. Put the group on Ultimate:
group = Group.find_by_full_path('your-group') GitlabSubscription.find_or_initialize_by(namespace: group).update!(hosted_plan: Plan.find_by(name: 'ultimate')) -
Run the command again. The four security tools are listed.
-
Put the group on Premium:
group.gitlab_subscription.update!(hosted_plan: Plan.find_by(name: 'premium')) -
Run the command again. The four security tools are gone. No restart is needed between plan changes.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in performance, reliability, security, and maintainability.