Send the workflow ID header on internal MCP calls

What does this MR do and why?

When a Duo Agent Platform flow runs through Workhorse, Workhorse acts as the MCP client for the flow and calls MCP servers. It already sends X-Duo-Workflow-Session-Id on those calls.

This MR makes Workhorse also send X-Gitlab-Duo-Workflow-Id on MCP tool calls. It sends it only to GitLab's own MCP servers:

  • gitlab (/api/v4/mcp)
  • orbit (/api/v4/orbit/mcp)

Third-party MCP servers do not get this header.

Why

Rails reads the calling workflow from X-Gitlab-Duo-Workflow-Id. Gitlab::Middleware::DuoWorkflowId stores it in Gitlab::ApplicationContext[:duo_workflow_id]. RunHTTPRequest actions in Workhorse already send this header (workhorse/internal/ai_assist/duoworkflow/actions.go). MCP tool calls did not. So Rails could not tell which workflow made an MCP tool call.

A follow-up Rails change will use this header. Sessions started with the start_duo_session MCP tool (which calls POST /api/v4/ai/duo_workflows/workflows) will record the workflow that started them as their parent. POST /api/v4/ai/duo_workflows/agent_workflows already does this since !259111 (merged).

How

  • In workhorse/internal/ai_assist/duoworkflow/mcp.go, roundTripper gets a new internal field.
  • buildSession sets this field from the same internalPaths lookup that picks the Rails endpoint. So the header goes only to requests that are routed to Rails.
  • TestBuildSession_sendsWorkflowIDOnlyToInternalServers checks the gitlab server, the orbit server, and an external server.

Behavior changes

MCP tool calls from a flow to GitLab now get the same workflow attribution that RunHTTPRequest calls already get. This means:

  1. Issues, merge requests, and notes created through MCP tools are linked to the workflow that created them.
  2. Flows set up to reuse an open merge request can reuse it when they create a merge request through MCP.
  3. API log lines for /api/v4/mcp and /api/v4/orbit/mcp include duo_workflow_id.
  4. With the ai_flow_trigger_chaining feature flag enabled (off by default), assignments made by a flow through MCP tools can start the assigned flow with the calling workflow as its parent. See !259133 (merged).

No changelog. This is an internal attribution change with no user-facing change on its own.

References

Screenshots or screen recordings

Not applicable. Backend only.

How to set up and validate locally

  1. In GDK, run a flow that has the GitLab MCP server enabled, for example the Developer flow.
  2. Let the flow call a GitLab MCP tool.
  3. In log/api_json.log, find the request to /api/v4/mcp. Check that duo_workflow_id is set to the workflow ID of the session.
  4. Optional: check that requests to a third-party MCP server configured for the flow do not carry the X-Gitlab-Duo-Workflow-Id header.

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.

Merge request reports

Loading
Loading