MCP tool call log does not mark alias calls

Background

Handlers::CallTool logs tool_name as the caller sent it (tool_name = params[:name], before Manager#resolve_alias runs), so mcp.log already carries alias usage. What it does not carry is any indication that a name was an alias, or what it resolved to. Reading the logs requires knowing the alias map from memory.

#629245 is the concrete cost: that analysis had to know semantic_code_search is an alias of semantic_search in order to see that both rows were the same defect. Came up while reviewing !255111 (merged), which makes tools/list resolve aliases the same way tools/call already did.

Current behavior

event_name: tool_call records tool_name: "semantic_code_search" with nothing to distinguish it from a canonical tool name. Finding all legacy-name traffic means querying each alias individually, from a list held outside the logs.

Expected behavior

When the name the caller sent resolves to a different tool, the log line also carries the canonical name. The field is absent for canonical calls, so its presence alone identifies alias traffic and _exists_: json.canonical_tool_name becomes the "all legacy alias calls" filter.

Implementation plan

  1. In CallTool#log_tool_call, resolve the name and add the field only when it differs:

    canonical_name = manager.resolve_alias(tool_name)
    
    logger.conditional_info(
      current_user,
      ...
      tool_name: tool_name,
      **(canonical_name == tool_name ? {} : { canonical_tool_name: canonical_name }),
      ...
    )

    alias_map is memoized on the manager and tools is already built by the time we log, on both the success and the ToolNotFoundError path, so this is a hash lookup. Canonical calls get no extra field, so log volume is unchanged for almost all traffic.

  2. Do not repurpose tool_name to hold the canonical name instead. It is an indexed field with existing queries against it (including #629245's tables), and changing its meaning would silently empty them. A new field breaks nothing.

  3. Specs for both cases: an alias call logs canonical_tool_name, a canonical call does not.

Out of scope: the EE internal events (track_start_event / track_finish_event) if we also want this dimension in Snowflake, and the tools/list header side, where alias names arrive by design from preapproved_tools_from_annotations and are not logged at all.

Relevant code

  • lib/api/mcp/handlers/call_tool.rb — tool_name capture (line 24) and log_tool_call
  • app/services/mcp/tools/manager.rb — resolve_alias, alias_map
  • lib/gitlab/mcp/logger.rb — conditional_info, which fields are always indexed
Edited by 🤖 GitLab Bot 🤖