Add update action to rename pipelines in save_pipeline MCP tool

What does this MR do and why?

This MR adds an update action to the save_pipeline MCP server tool, allowing a pipeline to be renamed. The action accepts pipeline_id plus a new name parameter, which is required when action: "update" is used. The behavior matches the REST endpoint PUT /projects/:id/pipelines/:pipeline_id/metadata.

This closes #619469 (closed), a GA must-have for the tool.

Documentation is updated in doc/user/model_context_protocol/mcp_server_tools.md: the save_pipeline section now lists update in the action enum, documents the new name parameter, includes a rename example, and adds a history bullet. The existing cross-reference to manage_pipeline is narrowed to cover only pipeline deletion, since renaming is now handled directly by save_pipeline.

Design notes

  • No GraphQL mutation exists for pipeline name/metadata (app/graphql/mutations/ci/pipeline/ only has cancel, create, destroy, and retry). Because of this, SavePipelineService#perform_v0_1_0 branches in Ruby for action: "update" and calls Ci::Pipelines::UpdateMetadataService directly — the same service used by the REST metadata endpoint — before the arguments reach SavePipelineTool, whose GraphQL operation map has no update entry. This mirrors the existing precedent in save_merge_request_review, which dispatches its post_duo_review action the same way.
  • Error handling follows the uniform not-found convention introduced in !249875 (merged). A missing pipeline, a pipeline the caller cannot read, and a pipeline the caller can read but not rename (the service's internal :update_pipeline permission check returns reason :forbidden with a nil payload) all resolve to the same message: Pipeline not found or inaccessible. This avoids leaking whether a resource exists when the caller lacks access.
  • A missing pipeline_id or name raises an ArgumentError with a self-correcting message ("Provide pipeline_id to rename a pipeline" / "Provide name to rename the pipeline"), consistent with the tool's existing validation style.
  • The aggregated manage_pipeline tool also exposes rename via the REST route, so there is some overlap. save_pipeline is the GA-canonical tool per the tool consolidation conventions, which is why the update action was added here, with the docs cross-reference narrowed accordingly.

How to validate locally

  1. Check out the branch and restart GDK rails (gdk restart rails-web).
  2. Create a personal access token with api and mcp scopes.
  3. Call tools/list via a JSON-RPC POST to /api/v4/mcp and confirm the save_pipeline tool's action enum is ["retry", "cancel", "update"] and that a name property is present.
  4. Call tools/call with {name: "save_pipeline", arguments: {project_id: "<id>", pipeline_id: <id>, action: "update", name: "New name"}} and confirm the pipeline is renamed (verify in the database or UI). Note that pipeline_id is an integer in the schema, matching the pre-existing convention.
  5. Confirm that omitting name or pipeline_id returns the self-correcting validation errors, and that an unknown pipeline_id returns Pipeline not found or inaccessible.

The following is the transcript of an end-to-end run against GDK exercising the steps above:

1a. action enum: ["retry", "cancel", "update"]
1b. name prop:   {"type"=>"string", "description"=>"New pipeline name. Required for action: \"update\"."}
target pipeline: id=5316 current name=nil
2. rename:        isError=false {"action"=>"update", "id"=>5316, "name"=>"E2E renamed 619469", "status"=>"failed", "ref"=>"duo/fix/24-config-loader-crash-on-malformed-json", "web_url"=>"https://gdk.test:3443/gitlab-duo/mobile-consumer/-/pipelines/5316"}
   db name now:   "E2E renamed 619469"
3. rename again:  isError=false {"action"=>"update", "id"=>5316, "name"=>"E2E renamed twice", ...}
   db name now:   "E2E renamed twice"
4. missing name:  isError=true "Validation error: Provide name to rename the pipeline"
5. no pipeline_id:isError=true "Validation error: Provide pipeline_id to rename a pipeline"
6. bogus id:      isError=true "Pipeline not found or inaccessible."

Merge request reports

Loading
Loading