mcp-tool-review-board: Update AI development principles from SSOT (20260922)

Summary

This MR updates AI development principles based on recent changes to the development documentation (SSOT). It is one of several team-scoped MRs from this run; the global routing tables (AGENTS.md, CLAUDE.md, SKILL.md) are updated in a separate tooling MR.

Could the authors listed below review the principles distilled from their SSOT changes?

Author SSOT commit(s)
@tgao3701908 8286d0e6
@terrichu 01b53f88, 198fd132
@fdegier 2da325c8
Why you were pinged, and why this needs merging

You were pinged because you changed the SSOT documentation these principles are distilled from.

These MRs need to be reviewed and merged, not just reviewed: the distilled principles only reach the gitlab-coding-principles that AI agents load once this is merged. An unmerged MR is re-distilled and reopened on the next weekly run, so the improvement stays unavailable to agents until someone merges it.

Updated principles and their source-doc changes

mcp-server

Source files (2)
SSOT diff since previous distillation (98a4a3ab → 1fd07530)
diff --git a/doc/development/duo_agent_platform/mcp/_index.md b/doc/development/duo_agent_platform/mcp/_index.md
index d800cdf53a..6abb5d9352 100644
--- a/doc/development/duo_agent_platform/mcp/_index.md
+++ b/doc/development/duo_agent_platform/mcp/_index.md
@@ -164,9 +164,23 @@ A caller provides either:
 
 - `url` - a full GitLab URL that encodes the whole path (for example
   `https://gitlab.com/group/project/-/merge_requests/1`), or
-- The ID group - `project_id` (numeric ID or URL-encoded path such as `gitlab-org%2Fgitlab`) plus
+- The ID group - `project_id` (numeric ID or full path such as `gitlab-org/gitlab`) plus
   the resource's internal ID (`merge_request_iid`, `work_item_iid`, `commit_sha`, and so on).
 
+Both the plain full path (`gitlab-org/gitlab`) and the URL-encoded form (`gitlab-org%2Fgitlab`)
+work as a project or group argument for any tool that resolves it through `ResourceFinder` or
+`ApiTool`. The MCP boundary percent-decodes the value before it reaches lookup logic: in
+`ResourceFinder#find_project!` and `#find_group!` for GraphQL-backed and custom tools, and in
+`ApiTool#execute` for REST-backed tools, mirroring the decoding Rack applies to a path segment in
+a REST request. A tool that passes a path straight to GraphQL (for example `search_labels` or
+`list_vulnerabilities`) skips both decode sites and takes the plain form only.
+
+Use the plain full path in examples and in a tool's own `input_schema` description. The decode
+exists only so that Grape-derived descriptions copied from REST routes, which advertise the
+URL-encoded form, don't become wrong. `Gitlab::ResourceLookup`, which the REST API also uses,
+doesn't decode: its callers already receive a decoded value, and decoding there would let the
+REST API accept a double-encoded path.
+
 Keep the project identifier and the internal ID as separate parameters.
 Do not fold them into a
 single `id`. They are different values (`iid`/`sha` is scoped to a project and is meaningless without
@@ -814,6 +828,36 @@ The default is `false`, so existing tools remain listed until they opt in.
 > Keep `unlisted?` static. Do not drive it from a per-user or credential-dependent check, because
 > the AI Catalog picker cannot evaluate one.
 
+### Declaring a toolset
+
+Every tool must declare exactly one toolset from
+[`Mcp::Tools::Toolsets::ALL`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/mcp/tools/toolsets.rb).
+A tool without a declared toolset becomes unreachable through any toolset:
+`spec/services/mcp/tools/toolset_coverage_spec.rb` asserts every registered tool declares a valid
+toolset, so a missing or invalid declaration fails CI rather than surfacing as a runtime error.
+
+For custom, GraphQL, and aggregated tools, add `toolset: :symbol` to the `register_version`
+metadata:
+
+```ruby
+register_version '0.1.0', {
+  toolset: :work_items,
+  description: 'Example description',
+  # ...
+}
+```
+
+For API tools, add `toolset: :symbol` to the `route_setting :mcp`:
+
+```ruby
+route_setting :mcp, tool_name: :example_tool, toolset: :work_items, params: [:id]
+```
+
+Unknown toolset names in the `X-Gitlab-Enabled-Mcp-Server-Toolsets` header return a `400` error
+because toolsets are a closed enum. Unknown tool names in `X-Gitlab-Enabled-Mcp-Server-Tools` are
+logged as a warning and ignored because tool names are an open, evolving set that varies across
+GitLab editions and versions.
+
 ### Splitting an action out of an aggregated tool
 
 An aggregated tool that folds several operations behind a parameter (for example, an `action` or
diff --git a/doc/development/duo_agent_platform/mcp/graphql_integration.md b/doc/development/duo_agent_platform/mcp/graphql_integration.md
index 7a36694aa2..ee0d95b0e1 100644
--- a/doc/development/duo_agent_platform/mcp/graphql_integration.md
+++ b/doc/development/duo_agent_platform/mcp/graphql_integration.md
@@ -462,8 +462,9 @@ module Mcp
               },
               body: {
                 type: 'string',
-                description: 'Content of the note/comment (max 1,048,576 characters)',
-                maxLength: 1_048_576
+                description: 'Content of the note/comment (max ' \
+                  "#{Concerns::Constants::MARKDOWN_TEXT_MAX_LENGTH} characters)",
+                maxLength: Concerns::Constants::MARKDOWN_TEXT_MAX_LENGTH
               }
             },
             required: ['body']

How this works

A scheduled CI job detects changes to doc/development/ files listed in .ai/principles/manifest.yml, then uses the GitLab Duo Agent Platform Workflow API to distill updated principles for the affected domains.

This MR was generated by https://gitlab.com/gitlab-org/gitlab/-/jobs/16642303636

Please review the checklist changes to ensure they accurately reflect the documentation updates.

Manual review corrections

Manual review corrections restore the plain-path and header-validation rules omitted from distillation. Front matter is unchanged.

Merge request reports

Loading
Loading