Skip to content

feat(mcp): add deeper OpenTelemetry integration for MCP tool calls#3493

Merged
Mpdreamz merged 2 commits into
mainfrom
feature/mcp-opentelemetry
Jun 15, 2026
Merged

feat(mcp): add deeper OpenTelemetry integration for MCP tool calls#3493
Mpdreamz merged 2 commits into
mainfrom
feature/mcp-opentelemetry

Conversation

@Mpdreamz

Copy link
Copy Markdown
Member

Why

MCP tool calls were invisible in Elastic Observability — every call collapsed into a single POST /docs/_mcp/ transaction because all tools share one HTTP route. There was also no metrics data for tool calls at all.

What

Each tool call now appears as its own named transaction (e.g. tools/call search_docs) in APM, and tool-level metrics are exported.

Tracing: McpSpanRenameProcessor renames the ASP.NET Core server span in OnEnd — after the route-based name is set — to tools/call {tool}. The standard OTel MCP semconv attributes mcp.method.name and mcp.tool.name are written to the server span by McpToolTelemetry.StartActivity so the processor can read them.

Metrics: A Meter (Elastic.Documentation.Api.McpTools) was added with two instruments — mcp.tool.calls (counter) and mcp.tool.duration (histogram) — both dimensioned by tool name, method, server profile, and outcome. They're recorded in LogCompletion, which all three tool classes already call in their finally blocks, so no per-tool changes were needed.

OTel registration cleanup: Extensions.cs and EuidEnrichmentExtensions.cs were replaced by a unified AddDocumentationOpenTelemetry extension that all three services (docs-api, docs-mcp, docs-builder CLI) now use consistently.

Aspire 13.4.3 bump: Fixes a new required member (ExecuteCommandContext.Arguments) in the integration test bootstrap.

Each MCP tool call now appears as its own transaction in Elastic
Observability instead of collapsing under POST /docs/_mcp/.

- McpSpanRenameProcessor renames the ASP.NET Core server span to
  "tools/call {tool}" in OnEnd, after the route-based name is set
- mcp.method.name and mcp.tool.name semconv tags added to server span
- Meter added with mcp.tool.calls counter and mcp.tool.duration
  histogram, both dimensioned by tool name, method, profile, outcome
- OTel registration consolidated into AddDocumentationOpenTelemetry
  (replaces Extensions.cs and EuidEnrichmentExtensions.cs)
- Aspire bumped to 13.4.3; fixes ExecuteCommandContext.Arguments
  required member in integration test bootstrap

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Mpdreamz Mpdreamz requested a review from a team as a code owner June 12, 2026 10:25
@Mpdreamz Mpdreamz requested a review from cotti June 12, 2026 10:25
@Mpdreamz Mpdreamz temporarily deployed to integration-tests June 12, 2026 10:25 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR consolidates OpenTelemetry configuration across the documentation platform by replacing separate hardcoded extension methods with a parameterizable OtelRegistration-based architecture. A new AddDocumentationOpenTelemetry extension accepts callback delegates for custom tracing, metrics, and logging configuration, enabling each service (docs-api, MCP Remote, docs-builder) to apply service-specific instrumentation without code duplication. The refactor removes the legacy Extensions.cs and EuidEnrichmentExtensions.cs files, adds health check registration to service defaults, simplifies the API-specific telemetry interface, and enhances MCP observability with automatic span renaming and structured metrics recording. Test fixtures are updated to wire logging through the new configuration system.

Possibly related PRs

  • elastic/docs-builder#3333: Earlier telemetry consolidation effort that removed and refactored EUID enrichment patterns; this PR completes that migration by introducing the unified OtelRegistration architecture and removing the legacy extensions.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main focus: adding deeper OpenTelemetry integration specifically for MCP tool calls, which aligns with the core changes around McpSpanRenameProcessor, metrics instrumentation, and telemetry consolidation.
Description check ✅ Passed The description is well-related to the changeset, explaining the motivation (tool calls being invisible), the solution (span renaming via McpSpanRenameProcessor and new metrics), the OTel registration refactoring, and the Aspire version bump—all of which are reflected in the file summaries.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feature/mcp-opentelemetry

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/api/Elastic.Documentation.Api/OpenTelemetry/OpenTelemetryExtensions.cs (1)

25-30: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Health-check traffic is still being traced.

Program.cs maps health checks on /health and /alive, but this filter only excludes /docs/_api/v1. That means probe traffic still lands in tracing and skews the API telemetry. Filter the actual health endpoints instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/Elastic.Documentation.Api/OpenTelemetry/OpenTelemetryExtensions.cs`
around lines 25 - 30, The current aspNetCoreOptions.Filter in
OpenTelemetryExtensions.cs only excludes "/docs/_api/v1" so health-check probes
mapped in Program.cs (endpoints "/health" and "/alive") are still traced; update
the filter lambda (aspNetCoreOptions.Filter) to inspect httpContext.Request.Path
(or use httpContext.Request.Path.StartsWithSegments) and return false for
"/health" and "/alive" in addition to "/docs/_api/v1" so those probe endpoints
are not included in traces (adjust the path comparison to handle nulls and
optional trailing slashes).
🧹 Nitpick comments (1)
src/tooling/docs-builder/Program.cs (1)

23-23: 💤 Low value

Unused variable argh.

The result of GlobalCliOptions.TryParseArgh is captured but never used. If the boolean return value isn't needed, consider discarding it with _ = GlobalCliOptions.TryParseArgh(...).

-var argh = GlobalCliOptions.TryParseArgh(args, out var cliOptions);
+_ = GlobalCliOptions.TryParseArgh(args, out var cliOptions);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tooling/docs-builder/Program.cs` at line 23, The local variable `argh` is
unused after calling GlobalCliOptions.TryParseArgh; replace its capture with a
discard to avoid the unused-variable warning by calling
GlobalCliOptions.TryParseArgh(args, out var cliOptions) and discarding the
boolean result (use `_ = GlobalCliOptions.TryParseArgh(...)`) so only
`cliOptions` remains available for subsequent code.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@aspire/Properties/launchSettings.json`:
- Around line 10-11: In launchSettings.json's https profile update the
mismatched environment variables so they are consistent: change
"ASPNETCORE_ENVIRONMENT": "dev" to "ASPNETCORE_ENVIRONMENT": "Development" (or
alternatively make DOTNET_ENVIRONMENT "dev") so both ASPNETCORE_ENVIRONMENT and
DOTNET_ENVIRONMENT match; ensure the https profile in launchSettings.json is the
one being edited.

In `@src/api/Elastic.Documentation.Api/Program.cs`:
- Around line 17-18: Replace reading
Environment.GetEnvironmentVariable("ENVIRONMENT") with the host builder's
environment name: after calling CreateSlimBuilder(args) read
builder.Environment.EnvironmentName and pass that value into
AddElasticDocsApiServices instead of the raw env var; update the variable used
where AddElasticDocsApiServices(environment) is invoked (and the other
occurrence at the second spot) so they receive
builder.Environment.EnvironmentName to ensure the service sees the actual host
environment.

---

Outside diff comments:
In `@src/api/Elastic.Documentation.Api/OpenTelemetry/OpenTelemetryExtensions.cs`:
- Around line 25-30: The current aspNetCoreOptions.Filter in
OpenTelemetryExtensions.cs only excludes "/docs/_api/v1" so health-check probes
mapped in Program.cs (endpoints "/health" and "/alive") are still traced; update
the filter lambda (aspNetCoreOptions.Filter) to inspect httpContext.Request.Path
(or use httpContext.Request.Path.StartsWithSegments) and return false for
"/health" and "/alive" in addition to "/docs/_api/v1" so those probe endpoints
are not included in traces (adjust the path comparison to handle nulls and
optional trailing slashes).

---

Nitpick comments:
In `@src/tooling/docs-builder/Program.cs`:
- Line 23: The local variable `argh` is unused after calling
GlobalCliOptions.TryParseArgh; replace its capture with a discard to avoid the
unused-variable warning by calling GlobalCliOptions.TryParseArgh(args, out var
cliOptions) and discarding the boolean result (use `_ =
GlobalCliOptions.TryParseArgh(...)`) so only `cliOptions` remains available for
subsequent code.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 70bdb370-6073-44c5-a254-35c6142e172d

📥 Commits

Reviewing files that changed from the base of the PR and between b75b08b and 16493f9.

📒 Files selected for processing (17)
  • Directory.Packages.props
  • aspire/Properties/launchSettings.json
  • src/Elastic.Documentation.ServiceDefaults/AppDefaultsExtensions.cs
  • src/Elastic.Documentation.ServiceDefaults/Extensions.cs
  • src/Elastic.Documentation.ServiceDefaults/Telemetry/EuidEnrichmentExtensions.cs
  • src/Elastic.Documentation.ServiceDefaults/Telemetry/OpenTelemetryRegistrationExtensions.cs
  • src/Elastic.Documentation.ServiceDefaults/Telemetry/VersionHelper.cs
  • src/api/Elastic.Documentation.Api/OpenTelemetry/OpenTelemetryExtensions.cs
  • src/api/Elastic.Documentation.Api/Program.cs
  • src/api/Elastic.Documentation.Mcp.Remote/Program.cs
  • src/api/Elastic.Documentation.Mcp.Remote/SseKeepAliveMiddleware.cs
  • src/api/Elastic.Documentation.Mcp.Remote/Telemetry/McpSpanRenameProcessor.cs
  • src/api/Elastic.Documentation.Mcp.Remote/Telemetry/McpToolTelemetry.cs
  • src/tooling/docs-builder/DocumentationTooling.cs
  • src/tooling/docs-builder/Program.cs
  • tests-integration/Elastic.Documentation.IntegrationTests/Search/SearchBootstrapFixture.cs
  • tests/Elastic.Documentation.Api.Tests/Fixtures/ApiWebApplicationFactory.cs
💤 Files with no reviewable changes (3)
  • src/Elastic.Documentation.ServiceDefaults/Telemetry/EuidEnrichmentExtensions.cs
  • src/Elastic.Documentation.ServiceDefaults/Extensions.cs
  • src/tooling/docs-builder/DocumentationTooling.cs

Comment thread aspire/Properties/launchSettings.json Outdated
Comment thread src/api/Elastic.Documentation.Api/Program.cs
@theletterf

Copy link
Copy Markdown
Member

So necessary, thanks!

@Mpdreamz Mpdreamz temporarily deployed to integration-tests June 15, 2026 08:24 — with GitHub Actions Inactive
@Mpdreamz Mpdreamz enabled auto-merge (squash) June 15, 2026 08:32
@Mpdreamz Mpdreamz merged commit 58acb7e into main Jun 15, 2026
25 checks passed
@Mpdreamz Mpdreamz deleted the feature/mcp-opentelemetry branch June 15, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants