From a1b1e1acb18e50bc9d1ac7f670bf49dd2f4e0416 Mon Sep 17 00:00:00 2001 From: Julian Boilen Date: Tue, 2 Dec 2025 09:31:48 -0500 Subject: [PATCH] Tracing for go-sdk --- contrib/mark3labs/mcp-go/tracing.go | 2 +- contrib/modelcontextprotocol/go-sdk/README.md | 28 ++ .../go-sdk/example_test.go | 21 + contrib/modelcontextprotocol/go-sdk/go.mod | 91 ++++ contrib/modelcontextprotocol/go-sdk/go.sum | 294 ++++++++++++ contrib/modelcontextprotocol/go-sdk/gosdk.go | 16 + .../modelcontextprotocol/go-sdk/tracing.go | 136 ++++++ .../go-sdk/tracing_test.go | 434 ++++++++++++++++++ go.work | 1 + go.work.sum | 1 + instrumentation/packages.go | 65 +-- internal/stacktrace/contribs_generated.go | 3 + 12 files changed, 1065 insertions(+), 27 deletions(-) create mode 100644 contrib/modelcontextprotocol/go-sdk/README.md create mode 100644 contrib/modelcontextprotocol/go-sdk/example_test.go create mode 100644 contrib/modelcontextprotocol/go-sdk/go.mod create mode 100644 contrib/modelcontextprotocol/go-sdk/go.sum create mode 100644 contrib/modelcontextprotocol/go-sdk/gosdk.go create mode 100644 contrib/modelcontextprotocol/go-sdk/tracing.go create mode 100644 contrib/modelcontextprotocol/go-sdk/tracing_test.go diff --git a/contrib/mark3labs/mcp-go/tracing.go b/contrib/mark3labs/mcp-go/tracing.go index 188f74ed66..0e3714ba6a 100644 --- a/contrib/mark3labs/mcp-go/tracing.go +++ b/contrib/mark3labs/mcp-go/tracing.go @@ -79,7 +79,7 @@ func newHooks() *hooks { } func (h *hooks) onBeforeInitialize(ctx context.Context, id any, request *mcp.InitializeRequest) { - taskSpan, _ := llmobs.StartTaskSpan(ctx, "mcp.initialize", llmobs.WithIntegration("mark3labs/mcp-go")) + taskSpan, _ := llmobs.StartTaskSpan(ctx, "mcp.initialize", llmobs.WithIntegration(string(instrumentation.PackageMark3LabsMCPGo))) clientName := request.Params.ClientInfo.Name clientVersion := request.Params.ClientInfo.Version diff --git a/contrib/modelcontextprotocol/go-sdk/README.md b/contrib/modelcontextprotocol/go-sdk/README.md new file mode 100644 index 0000000000..80e6c2a07d --- /dev/null +++ b/contrib/modelcontextprotocol/go-sdk/README.md @@ -0,0 +1,28 @@ +# MCP Go-SDK Integration + +This integration provides Datadog tracing for the [modelcontextprotocol/go-sdk](https://github.com/modelcontextprotocol/go-sdk) library. + +## Usage + +```go +import ( + gosdktrace "github.com/DataDog/dd-trace-go/contrib/modelcontextprotocol/go-sdk/v2" + "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer" + "github.com/modelcontextprotocol/go-sdk/mcp" +) + +func main() { + tracer.Start() + defer tracer.Stop() + + server := mcp.NewServer(&mcp.Implementation{Name: "my-server", Version: "1.0.0"}, nil) + gosdktrace.AddTracingMiddleware(server) +} +``` + +## Features + +The integration automatically traces: +- **Tool calls**: Creates LLMObs tool spans with input/output annotation for all tool invocations +- **Session initialization**: Creates LLMObs task spans for session initialization, including client information + diff --git a/contrib/modelcontextprotocol/go-sdk/example_test.go b/contrib/modelcontextprotocol/go-sdk/example_test.go new file mode 100644 index 0000000000..80dbcdad22 --- /dev/null +++ b/contrib/modelcontextprotocol/go-sdk/example_test.go @@ -0,0 +1,21 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2025 Datadog, Inc. + +package gosdk_test + +import ( + gosdktrace "github.com/DataDog/dd-trace-go/contrib/modelcontextprotocol/go-sdk/v2" + "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer" + "github.com/modelcontextprotocol/go-sdk/mcp" +) + +func Example() { + tracer.Start() + defer tracer.Stop() + + server := mcp.NewServer(&mcp.Implementation{Name: "my-server", Version: "1.0.0"}, nil) + gosdktrace.AddTracingMiddleware(server) + _ = server +} diff --git a/contrib/modelcontextprotocol/go-sdk/go.mod b/contrib/modelcontextprotocol/go-sdk/go.mod new file mode 100644 index 0000000000..938ba4ae93 --- /dev/null +++ b/contrib/modelcontextprotocol/go-sdk/go.mod @@ -0,0 +1,91 @@ +module github.com/DataDog/dd-trace-go/contrib/modelcontextprotocol/go-sdk/v2 + +go 1.24.0 + +require ( + github.com/DataDog/dd-trace-go/v2 v2.5.0-dev + github.com/google/jsonschema-go v0.3.0 + github.com/modelcontextprotocol/go-sdk v1.1.0 + github.com/stretchr/testify v1.11.1 +) + +require ( + github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.71.0 // indirect + github.com/DataDog/datadog-agent/pkg/obfuscate v0.71.0 // indirect + github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.71.0 // indirect + github.com/DataDog/datadog-agent/pkg/proto v0.71.0 // indirect + github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.73.0-rc.1 // indirect + github.com/DataDog/datadog-agent/pkg/trace v0.71.0 // indirect + github.com/DataDog/datadog-agent/pkg/util/log v0.71.0 // indirect + github.com/DataDog/datadog-agent/pkg/util/scrubber v0.71.0 // indirect + github.com/DataDog/datadog-agent/pkg/version v0.71.0 // indirect + github.com/DataDog/datadog-go/v5 v5.6.0 // indirect + github.com/DataDog/go-libddwaf/v4 v4.7.0 // indirect + github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20250721125240-fdf1ef85b633 // indirect + github.com/DataDog/go-sqllexer v0.1.8 // indirect + github.com/DataDog/go-tuf v1.1.1-0.5.2 // indirect + github.com/DataDog/sketches-go v1.4.7 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/ebitengine/purego v0.8.4 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.3 // indirect + github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect + github.com/minio/simdjson-go v0.4.5 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/outcaste-io/ristretto v0.2.3 // indirect + github.com/philhofer/fwd v1.2.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect + github.com/shirou/gopsutil/v4 v4.25.8-0.20250809033336-ffcdc2b7662f // indirect + github.com/theckman/httpforwarded v0.4.0 // indirect + github.com/tinylib/msgp v1.3.0 // indirect + github.com/tklauser/go-sysconf v0.3.15 // indirect + github.com/tklauser/numcpus v0.10.0 // indirect + github.com/yosida95/uritemplate/v3 v3.0.2 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/collector/component v1.39.0 // indirect + go.opentelemetry.io/collector/featuregate v1.46.0 // indirect + go.opentelemetry.io/collector/internal/telemetry v0.133.0 // indirect + go.opentelemetry.io/collector/pdata v1.46.0 // indirect + go.opentelemetry.io/collector/pdata/pprofile v0.140.0 // indirect + go.opentelemetry.io/contrib/bridges/otelzap v0.12.0 // indirect + go.opentelemetry.io/otel v1.37.0 // indirect + go.opentelemetry.io/otel/log v0.13.0 // indirect + go.opentelemetry.io/otel/metric v1.37.0 // indirect + go.opentelemetry.io/otel/sdk v1.37.0 // indirect + go.opentelemetry.io/otel/trace v1.37.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect + golang.org/x/mod v0.29.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/time v0.12.0 // indirect + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b // indirect + google.golang.org/protobuf v1.36.10 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/DataDog/dd-trace-go/v2 => ../../.. diff --git a/contrib/modelcontextprotocol/go-sdk/go.sum b/contrib/modelcontextprotocol/go-sdk/go.sum new file mode 100644 index 0000000000..4f17a236e3 --- /dev/null +++ b/contrib/modelcontextprotocol/go-sdk/go.sum @@ -0,0 +1,294 @@ +github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.71.0 h1:xjmjXOsiLfUF1wWXYXc8Gg6M7Jbz6a7FtqbnvGKfTvA= +github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.71.0/go.mod h1:y05SPqKEtrigKul+JBVM69ehv3lOgyKwrUIwLugoaSI= +github.com/DataDog/datadog-agent/pkg/obfuscate v0.71.0 h1:jX8qS7CkNzL1fdcDptrOkbWpsRFTQ58ICjp/mj02u1k= +github.com/DataDog/datadog-agent/pkg/obfuscate v0.71.0/go.mod h1:B3T0If+WdWAwPMpawjm1lieJyqSI0v04dQZHq15WGxY= +github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.71.0 h1:bowQteds9+7I4Dd+CsBRVXdlMOOGuBm5zdUQdB/6j1M= +github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.71.0/go.mod h1:XeZj0IgsiL3vgeEGTucf61JvJRh1LxWMUbZA/XJsPD0= +github.com/DataDog/datadog-agent/pkg/proto v0.71.0 h1:YTwecwy8kF1zsL2HK6KVa7XLRZYZ0Ypb2anlG0zDLeE= +github.com/DataDog/datadog-agent/pkg/proto v0.71.0/go.mod h1:KSn4jt3CykV6CT1C8Rknn/Nj3E+VYHK/UDWolg/+kzw= +github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.73.0-rc.1 h1:fVqr9ApWmUMEExmgn8iFPfwm9ZrlEfFWgTKp1IcNH18= +github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.73.0-rc.1/go.mod h1:lwkSvCXABHXyqy6mG9WBU6MTK9/E0i0R8JVApUtT+XA= +github.com/DataDog/datadog-agent/pkg/trace v0.71.0 h1:9UrKHDacMlAWfP2wpSxrZOQbtkwLY2AOAjYgGkgM96Y= +github.com/DataDog/datadog-agent/pkg/trace v0.71.0/go.mod h1:wfVwOlKORIB4IB1vdncTuCTx/OrVU69TLBIiBpewe1Q= +github.com/DataDog/datadog-agent/pkg/util/log v0.71.0 h1:VJ+nm5E0+UdLPkg2H7FKapx0syNcKzCFXA2vfcHz0Bc= +github.com/DataDog/datadog-agent/pkg/util/log v0.71.0/go.mod h1:oG6f6Qe23zPTLOVh0nXjlIXohrjUGXeFjh7S3Na/WyU= +github.com/DataDog/datadog-agent/pkg/util/scrubber v0.71.0 h1:lA3CL+2yHU9gulyR/C0VssVzmvCs/jCHzt+CBs9uH4Q= +github.com/DataDog/datadog-agent/pkg/util/scrubber v0.71.0/go.mod h1:/JHi9UFqdFYy/SFmFozY26dNOl/ODVLSQaF1LKDPiBI= +github.com/DataDog/datadog-agent/pkg/version v0.71.0 h1:jqkKmhFrhHSLpiC3twQFDCXU7nyFcC1EnwagDQxFWVs= +github.com/DataDog/datadog-agent/pkg/version v0.71.0/go.mod h1:FYj51C1ib86rpr5tlLEep9jitqvljIJ5Uz2rrimGTeY= +github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEUqFvRDw= +github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/go-libddwaf/v4 v4.7.0 h1:oF2YaaEtu7MYd5TrwceXUsJYdJ6b1kmLZsAj0up/0po= +github.com/DataDog/go-libddwaf/v4 v4.7.0/go.mod h1:/AZqP6zw3qGJK5mLrA0PkfK3UQDk1zCI2fUNCt4xftE= +github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20250721125240-fdf1ef85b633 h1:ZRLR9Lbym748e8RznWzmSoK+OfV+8qW6SdNYA4/IqdA= +github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20250721125240-fdf1ef85b633/go.mod h1:YFoTl1xsMzdSRFIu33oCSPS/3+HZAPGpO3oOM96wXCM= +github.com/DataDog/go-sqllexer v0.1.8 h1:ku9DpghFHeyyviR28W/3R4cCJwzpsuC08YIoltnx5ds= +github.com/DataDog/go-sqllexer v0.1.8/go.mod h1:GGpo1h9/BVSN+6NJKaEcJ9Jn44Hqc63Rakeb+24Mjgo= +github.com/DataDog/go-tuf v1.1.1-0.5.2 h1:YWvghV4ZvrQsPcUw8IOUMSDpqc3W5ruOIC+KJxPknv0= +github.com/DataDog/go-tuf v1.1.1-0.5.2/go.mod h1:zBcq6f654iVqmkk8n2Cx81E1JnNTMOAx1UEO/wZR+P0= +github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/IGQh4= +github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= +github.com/DataDog/sketches-go v1.4.7 h1:eHs5/0i2Sdf20Zkj0udVFWuCrXGRFig2Dcfm5rtcTxc= +github.com/DataDog/sketches-go v1.4.7/go.mod h1:eAmQ/EBmtSO+nQp7IZMZVRPT4BQTmIc5RZQ+deGlTPM= +github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs= +github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= +github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U= +github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/jsonschema-go v0.3.0 h1:6AH2TxVNtk3IlvkkhjrtbUc4S8AvO0Xii0DxIygDg+Q= +github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= +github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= +github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/minio/simdjson-go v0.4.5 h1:r4IQwjRGmWCQ2VeMc7fGiilu1z5du0gJ/I/FsKwgo5A= +github.com/minio/simdjson-go v0.4.5/go.mod h1:eoNz0DcLQRyEDeaPr4Ru6JpjlZPzbA0IodxVJk8lO8E= +github.com/modelcontextprotocol/go-sdk v1.1.0 h1:Qjayg53dnKC4UZ+792W21e4BpwEZBzwgRW6LrjLWSwA= +github.com/modelcontextprotocol/go-sdk v1.1.0/go.mod h1:6fM3LCm3yV7pAs8isnKLn07oKtB0MP9LHd3DfAcKw10= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.133.0 h1:iPei+89a2EK4LuN4HeIRzZNE6XxCyrKfBKG3BkK/ViU= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.133.0/go.mod h1:asV77TgnGfc7A+a9jggdsnlLlW5dnJT8RroVuf5slko= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.133.0 h1:4ca2pM3+xDMB9H3UnhjAiNg7EpIydZ7HdohOexU8xb8= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.133.0/go.mod h1:3N2Saf55l9vrxjbf3KCEcBjbLHDZtbN4nPcxREztpPU= +github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOvUOL9w0= +github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac= +github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= +github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= +github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3 h1:4+LEVOB87y175cLJC/mbsgKmoDOjrBldtXvioEy96WY= +github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3/go.mod h1:vl5+MqJ1nBINuSsUI2mGgH79UweUT/B5Fy8857PqyyI= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc= +github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw= +github.com/shirou/gopsutil/v4 v4.25.8-0.20250809033336-ffcdc2b7662f h1:S+PHRM3lk96X0/cGEGUukqltzkX/ekUx0F9DoCGK1G0= +github.com/shirou/gopsutil/v4 v4.25.8-0.20250809033336-ffcdc2b7662f/go.mod h1:4f4j4w8HLMPWEFs3BO2UBBLigKAaWYwkSkbIt/6Q4Ss= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/theckman/httpforwarded v0.4.0 h1:N55vGJT+6ojTnLY3LQCNliJC4TW0P0Pkeys1G1WpX2w= +github.com/theckman/httpforwarded v0.4.0/go.mod h1:GVkFynv6FJreNbgH/bpOU9ITDZ7a5WuzdNCtIMI1pVI= +github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= +github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= +github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= +github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= +github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= +github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= +github.com/vmihailenco/msgpack/v4 v4.3.13 h1:A2wsiTbvp63ilDaWmsk2wjx6xZdxQOvpiNlKBGKKXKI= +github.com/vmihailenco/msgpack/v4 v4.3.13/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= +github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= +github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/collector/component v1.39.0 h1:GJw80zXURBG4h0sh97bPLEn2Ra+NAWUpskaooA0wru4= +go.opentelemetry.io/collector/component v1.39.0/go.mod h1:NPaMPTLQuxm5QaaWdqkxYKztC0bRdV+86Q9ir7xS/2k= +go.opentelemetry.io/collector/component/componentstatus v0.133.0 h1:fIcFKg+yPhpvOJKeMph9TtSC4DIGdIuNmxvUB0UGcoc= +go.opentelemetry.io/collector/component/componentstatus v0.133.0/go.mod h1:biQWms9cgXSZu3nb92Z0bA9uHh9lEhgmQ8CF4HLmu8Y= +go.opentelemetry.io/collector/component/componenttest v0.133.0 h1:mg54QqXC+GNqLHa9y6Efh3X5Di4XivjgJr6mzvfVQR8= +go.opentelemetry.io/collector/component/componenttest v0.133.0/go.mod h1:E+oqRK03WjG/b1aX1pd0CfTKh12MPTKbEBaBROp4w0M= +go.opentelemetry.io/collector/consumer v1.39.0 h1:Jc6la3uacHbznX5ORmh16Nddh23ZxBzoiNF2L0wD2Ks= +go.opentelemetry.io/collector/consumer v1.39.0/go.mod h1:tW2BXyntjvlKrRc+mwistt1KuC/b4mTfTkc8zWjeeRY= +go.opentelemetry.io/collector/consumer/consumertest v0.133.0 h1:MteqaGpgmHVHFqnB7A2voGleA2j51qJyVfX5x/wm+8I= +go.opentelemetry.io/collector/consumer/consumertest v0.133.0/go.mod h1:vHGknLn/RRUcMQuuBDt+SgrpDN46DBJyqRnWXm3gLwY= +go.opentelemetry.io/collector/consumer/xconsumer v0.133.0 h1:Xx4Yna/We4qDlbAla1nfxgkvujzWRuR8bqqwsLLvYSg= +go.opentelemetry.io/collector/consumer/xconsumer v0.133.0/go.mod h1:he874Md/0uAS2Fs+TDHAy10OBLRSw8233LdREizVvG4= +go.opentelemetry.io/collector/featuregate v1.46.0 h1:z3JlymFdWW6aDo9cYAJ6bCqT+OI2DlurJ9P8HqfuKWQ= +go.opentelemetry.io/collector/featuregate v1.46.0/go.mod h1:d0tiRzVYrytB6LkcYgz2ESFTv7OktRPQe0QEQcPt1L4= +go.opentelemetry.io/collector/internal/telemetry v0.133.0 h1:YxbckZC9HniNOZgnSofTOe0AB/bEsmISNdQeS+3CU3o= +go.opentelemetry.io/collector/internal/telemetry v0.133.0/go.mod h1:akUK7X6ZQ+CbbCjyXLv9y/EHt5jIy+J+nGoLvndZN14= +go.opentelemetry.io/collector/pdata v1.46.0 h1:XzhnIWNtc/gbOyFiewRvybR4s3phKHrWxL3yc/wVLDo= +go.opentelemetry.io/collector/pdata v1.46.0/go.mod h1:D2e3BWCUC/bUg29WNzCDVN7Ab0Gzk7hGXZL2pnrDOn0= +go.opentelemetry.io/collector/pdata/pprofile v0.140.0 h1:b9TZ6UnyzsT/ERQw2VKGi/NYLtKSmjG7cgQuc9wZt5s= +go.opentelemetry.io/collector/pdata/pprofile v0.140.0/go.mod h1:/2s/YBWGbu+r8MuKu5zas08iSqe+3P6xnbRpfE2DWAA= +go.opentelemetry.io/collector/pdata/testdata v0.133.0 h1:K0q47qecWVJf0sWbeWfifbJ72TiqR+A2PCsMkCEKvus= +go.opentelemetry.io/collector/pdata/testdata v0.133.0/go.mod h1:/emFpIox/mi7FucvsSn54KsiMh/iy7BUviqgURNVT6U= +go.opentelemetry.io/collector/pipeline v1.39.0 h1:CcEn30qdoHEzehFxgx0Ma0pWYGhrrIkRkcu218NG4V4= +go.opentelemetry.io/collector/pipeline v1.39.0/go.mod h1:NdM+ZqkPe9KahtOXG28RHTRQu4m/FD1i3Ew4qCRdOr8= +go.opentelemetry.io/collector/processor v1.39.0 h1:QwPJxJnFZwojo09Vfnvph7A27TauxxvA1koO6nr87O8= +go.opentelemetry.io/collector/processor v1.39.0/go.mod h1:WQWZqKmrlJcLjirnQOULxYgWV6h5oxK6FQNiFgw53i8= +go.opentelemetry.io/collector/processor/processorhelper v0.133.0 h1:3w/wvSmzyCvyNXjUQihH/VLQ+Tnzn3MlQNbv1AEoXiU= +go.opentelemetry.io/collector/processor/processorhelper v0.133.0/go.mod h1:lTlC8tGOBqkpdwGXCmaDnWXc2jqIrRUKvV7eK26Thc4= +go.opentelemetry.io/collector/processor/processortest v0.133.0 h1:PAuOr8Pwj/LAuey2LW1fix0vvnE+WwGpSF7bghaxjEE= +go.opentelemetry.io/collector/processor/processortest v0.133.0/go.mod h1:fEhWs9DCe431+iFke1WmlxqjcRDN25GLRXdktKAPyw8= +go.opentelemetry.io/collector/processor/xprocessor v0.133.0 h1:V5YMrXUgClh3awWOdigGXHxvq/Ira2wLDj4DJLqB+Eo= +go.opentelemetry.io/collector/processor/xprocessor v0.133.0/go.mod h1:5gDFI+pGIzoFQeBUM4QZ4E0B+SaU0e+2V7Td+ONoU4M= +go.opentelemetry.io/contrib/bridges/otelzap v0.12.0 h1:FGre0nZh5BSw7G73VpT3xs38HchsfPsa2aZtMp0NPOs= +go.opentelemetry.io/contrib/bridges/otelzap v0.12.0/go.mod h1:X2PYPViI2wTPIMIOBjG17KNybTzsrATnvPJ02kkz7LM= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/log v0.13.0 h1:yoxRoIZcohB6Xf0lNv9QIyCzQvrtGZklVbdCoyb7dls= +go.opentelemetry.io/otel/log v0.13.0/go.mod h1:INKfG4k1O9CL25BaM1qLe0zIedOpvlS5Z7XgSbmN83E= +go.opentelemetry.io/otel/log/logtest v0.13.0 h1:xxaIcgoEEtnwdgj6D6Uo9K/Dynz9jqIxSDu2YObJ69Q= +go.opentelemetry.io/otel/log/logtest v0.13.0/go.mod h1:+OrkmsAH38b+ygyag1tLjSFMYiES5UHggzrtY1IIEA8= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/proto/slim/otlp v1.9.0 h1:fPVMv8tP3TrsqlkH1HWYUpbCY9cAIemx184VGkS6vlE= +go.opentelemetry.io/proto/slim/otlp v1.9.0/go.mod h1:xXdeJJ90Gqyll+orzUkY4bOd2HECo5JofeoLpymVqdI= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0 h1:o13nadWDNkH/quoDomDUClnQBpdQQ2Qqv0lQBjIXjE8= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0/go.mod h1:Gyb6Xe7FTi/6xBHwMmngGoHqL0w29Y4eW8TGFzpefGA= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0 h1:EiUYvtwu6PMrMHVjcPfnsG3v+ajPkbUeH+IL93+QYyk= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0/go.mod h1:mUUHKFiN2SST3AhJ8XhJxEoeVW12oqfXog0Bo8W3Ec4= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= +golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b h1:zPKJod4w6F1+nRGDI9ubnXYhU9NSWoFAijkHkUXeTK8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= +google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= +k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= diff --git a/contrib/modelcontextprotocol/go-sdk/gosdk.go b/contrib/modelcontextprotocol/go-sdk/gosdk.go new file mode 100644 index 0000000000..d3e1cfa5a0 --- /dev/null +++ b/contrib/modelcontextprotocol/go-sdk/gosdk.go @@ -0,0 +1,16 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2025 Datadog, Inc. + +package gosdk // import "github.com/DataDog/dd-trace-go/contrib/modelcontextprotocol/go-sdk" + +import ( + "github.com/DataDog/dd-trace-go/v2/instrumentation" +) + +var instr *instrumentation.Instrumentation + +func init() { + instr = instrumentation.Load(instrumentation.PackageModelContextProtocolGoSDK) +} diff --git a/contrib/modelcontextprotocol/go-sdk/tracing.go b/contrib/modelcontextprotocol/go-sdk/tracing.go new file mode 100644 index 0000000000..f11e8e1aaf --- /dev/null +++ b/contrib/modelcontextprotocol/go-sdk/tracing.go @@ -0,0 +1,136 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2025 Datadog, Inc. + +package gosdk + +import ( + "context" + "encoding/json" + "errors" + + "github.com/DataDog/dd-trace-go/v2/instrumentation" + "github.com/DataDog/dd-trace-go/v2/llmobs" + + "github.com/modelcontextprotocol/go-sdk/mcp" +) + +func AddTracingMiddleware(server *mcp.Server) { + server.AddReceivingMiddleware(tracingMiddleware) +} + +func tracingMiddleware(next mcp.MethodHandler) mcp.MethodHandler { + return func(ctx context.Context, method string, req mcp.Request) (mcp.Result, error) { + switch method { + case "tools/call": + if toolReq, ok := req.(*mcp.CallToolRequest); ok { + return traceToolCallRequest(next, ctx, method, toolReq) + } + case "initialize": + return traceInitializeRequest(next, ctx, method, req) + } + return next(ctx, method, req) + } +} + +func traceToolCallRequest(next mcp.MethodHandler, ctx context.Context, method string, req *mcp.CallToolRequest) (mcp.Result, error) { + toolSpan, ctx := llmobs.StartToolSpan(ctx, req.Params.Name, llmobs.WithIntegration(string(instrumentation.PackageModelContextProtocolGoSDK))) + + var result *mcp.CallToolResult + var err error + + defer func() { + tagWithSessionID(req, toolSpan) + finishSpanWithIO(toolSpan, method, req, result, err) + }() + + res, err := next(ctx, method, req) + result, ok := res.(*mcp.CallToolResult) + if !ok { + instr.Logger().Warn("go-sdk: unexpected result type: %T", res) + } + + return res, err +} + +func traceInitializeRequest(next mcp.MethodHandler, ctx context.Context, method string, req mcp.Request) (mcp.Result, error) { + taskSpan, ctx := llmobs.StartTaskSpan(ctx, "mcp.initialize", llmobs.WithIntegration(string(instrumentation.PackageModelContextProtocolGoSDK))) + + // Extract client info from params if available + if params := req.GetParams(); params != nil { + if initParams, ok := params.(*mcp.InitializeParams); ok { + clientName := initParams.ClientInfo.Name + clientVersion := initParams.ClientInfo.Version + taskSpan.Annotate(llmobs.WithAnnotatedTags(map[string]string{"client_name": clientName, "client_version": clientName + "_" + clientVersion})) + } + } + + var res mcp.Result + var err error + + defer func() { + tagWithSessionID(req, taskSpan) + finishSpanWithIO(taskSpan, method, req, res, err) + }() + + res, err = next(ctx, method, req) + return res, err +} + +func tagWithSessionID(req mcp.Request, span llmobs.Span) { + session := req.GetSession() + if session == nil { + return + } + sessionID := session.ID() + if sessionID == "" { + return + } + span.Annotate(llmobs.WithAnnotatedTags(map[string]string{"mcp_session_id": sessionID})) +} + +type textIOSpan interface { + AnnotateTextIO(input, output string, opts ...llmobs.AnnotateOption) + Finish(opts ...llmobs.FinishSpanOption) +} + +// go-sdk unmarshalls the raw jsonrpc and discards it, so that is not available for logging. +// The mcp.Request object contains extra stuff like auth and internal methods we don't want to log. +// To recreate a MCP request-like object more appropriate for json marshalling and similar to other MCP libraries, we create this struct. +type loggedInput struct { + Method string `json:"method"` + Params mcp.Params `json:"params"` +} + +func finishSpanWithIO[S textIOSpan](span S, method string, req mcp.Request, output mcp.Result, err error) { + loggedInput := loggedInput{ + Method: method, + Params: req.GetParams(), + } + + inputJSON, marshalErr := json.Marshal(loggedInput) + if marshalErr != nil { + instr.Logger().Warn("go-sdk: failed to marshal input: %v", marshalErr) + } + + var outputText string + if output != nil { + outputJSON, marshalErr := json.Marshal(output) + if marshalErr != nil { + instr.Logger().Warn("go-sdk: failed to marshal output: %v", marshalErr) + } + outputText = string(outputJSON) + } + + span.AnnotateTextIO(string(inputJSON), outputText) + + if err != nil { + span.Finish(llmobs.WithError(err)) + } else if toolResult, ok := output.(*mcp.CallToolResult); ok && toolResult.IsError { + // Use generic error message since details are already in the output field + span.Finish(llmobs.WithError(errors.New("tool resulted in an error"))) + } else { + span.Finish() + } +} diff --git a/contrib/modelcontextprotocol/go-sdk/tracing_test.go b/contrib/modelcontextprotocol/go-sdk/tracing_test.go new file mode 100644 index 0000000000..aa9cd58638 --- /dev/null +++ b/contrib/modelcontextprotocol/go-sdk/tracing_test.go @@ -0,0 +1,434 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2025 Datadog, Inc. + +package gosdk + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer" + "github.com/DataDog/dd-trace-go/v2/instrumentation/testutils/testtracer" + "github.com/google/jsonschema-go/jsonschema" + "github.com/modelcontextprotocol/go-sdk/mcp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestIntegrationSessionInitialize(t *testing.T) { + tt := testTracer(t) + defer tt.Stop() + + ctx := context.Background() + + server := mcp.NewServer(&mcp.Implementation{Name: "test-server", Version: "1.0.0"}, nil) + AddTracingMiddleware(server) + + // go-sdk only assigns session ids on streamable transports. + // Using a streamable http transport in this test allows testing session id tagging behavior. + handler := mcp.NewStreamableHTTPHandler(func(req *http.Request) *mcp.Server { return server }, nil) + httpServer := httptest.NewServer(handler) + defer httpServer.Close() + + client := mcp.NewClient(&mcp.Implementation{Name: "test-client", Version: "1.0.0"}, nil) + transport := &mcp.StreamableClientTransport{ + Endpoint: httpServer.URL, + } + + clientSession, err := client.Connect(ctx, transport, nil) + require.NoError(t, err) + defer clientSession.Close() + + sessionID := clientSession.ID() + require.NotEmpty(t, sessionID, "session ID should be set with streamable transport") + + spans := tt.WaitForLLMObsSpans(t, 1) + require.Len(t, spans, 1) + + taskSpan := spans[0] + assert.Equal(t, "mcp.initialize", taskSpan.Name) + assert.Equal(t, "task", taskSpan.Meta["span.kind"]) + + assert.Contains(t, taskSpan.Tags, "client_name:test-client") + assert.Contains(t, taskSpan.Tags, "client_version:test-client_1.0.0") + assert.Contains(t, taskSpan.Tags, "mcp_session_id:"+sessionID) + + assert.Contains(t, taskSpan.Meta, "input") + assert.Contains(t, taskSpan.Meta, "output") + + inputMeta := taskSpan.Meta["input"] + assert.NotNil(t, inputMeta) + inputWrapper := inputMeta.(map[string]any) + inputStr := inputWrapper["value"].(string) + + var inputData map[string]any + err = json.Unmarshal([]byte(inputStr), &inputData) + require.NoError(t, err) + assert.Equal(t, "initialize", inputData["method"]) + params := inputData["params"].(map[string]any) + clientInfo := params["clientInfo"].(map[string]any) + assert.Equal(t, "test-client", clientInfo["name"]) + + outputMeta := taskSpan.Meta["output"] + assert.NotNil(t, outputMeta) + outputJSON, err := json.Marshal(outputMeta) + require.NoError(t, err) + outputStr := string(outputJSON) + assert.Contains(t, outputStr, "serverInfo") +} + +func TestIntegrationToolCallSuccess(t *testing.T) { + tt := testTracer(t) + defer tt.Stop() + + ctx := context.Background() + + server := mcp.NewServer(&mcp.Implementation{Name: "test-server", Version: "1.0.0"}, nil) + AddTracingMiddleware(server) + + type CalcArgs struct { + Operation string `json:"operation"` + X float64 `json:"x"` + Y float64 `json:"y"` + } + + mcp.AddTool(server, + &mcp.Tool{ + Name: "calculator", + Description: "A simple calculator", + InputSchema: &jsonschema.Schema{ + Type: "object", + Properties: map[string]*jsonschema.Schema{ + "operation": {Type: "string", Description: "Operation to perform"}, + "x": {Type: "number", Description: "First operand"}, + "y": {Type: "number", Description: "Second operand"}, + }, + Required: []string{"operation", "x", "y"}, + }, + }, + func(ctx context.Context, req *mcp.CallToolRequest, args CalcArgs) (*mcp.CallToolResult, any, error) { + var result float64 + switch args.Operation { + case "add": + result = args.X + args.Y + case "multiply": + result = args.X * args.Y + default: + return nil, nil, fmt.Errorf("unknown operation: %s", args.Operation) + } + + resultJSON, _ := json.Marshal(map[string]float64{"result": result}) + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: string(resultJSON)}, + }, + }, result, nil + }, + ) + + handler := mcp.NewStreamableHTTPHandler(func(req *http.Request) *mcp.Server { return server }, nil) + httpServer := httptest.NewServer(handler) + defer httpServer.Close() + + client := mcp.NewClient(&mcp.Implementation{Name: "test-client", Version: "1.0.0"}, nil) + transport := &mcp.StreamableClientTransport{ + Endpoint: httpServer.URL, + } + + clientSession, err := client.Connect(ctx, transport, nil) + require.NoError(t, err) + defer clientSession.Close() + + sessionID := clientSession.ID() + require.NotEmpty(t, sessionID) + + result, err := clientSession.CallTool(ctx, &mcp.CallToolParams{ + Name: "calculator", + Arguments: map[string]any{ + "operation": "add", + "x": float64(5), + "y": float64(3), + }, + }) + require.NoError(t, err) + assert.NotNil(t, result) + + spans := tt.WaitForLLMObsSpans(t, 2) + require.Len(t, spans, 2) + + var initSpan, toolSpan *testtracer.LLMObsSpan + for i := range spans { + switch spans[i].Name { + case "mcp.initialize": + initSpan = &spans[i] + case "calculator": + toolSpan = &spans[i] + } + } + + require.NotNil(t, initSpan, "initialize span not found") + require.NotNil(t, toolSpan, "tool span not found") + + // Session id must be the same between spans + assert.Contains(t, initSpan.Tags, "mcp_session_id:"+sessionID) + assert.Contains(t, toolSpan.Tags, "mcp_session_id:"+sessionID) + + assert.Equal(t, "calculator", toolSpan.Name) + assert.Equal(t, "tool", toolSpan.Meta["span.kind"]) + + // Verify the span is NOT marked as an error (success case) + assert.NotContains(t, toolSpan.Meta, "error.message") + assert.NotContains(t, toolSpan.Meta, "error.type") + assert.NotContains(t, toolSpan.Meta, "error.stack") + + assert.Contains(t, toolSpan.Meta, "input") + assert.Contains(t, toolSpan.Meta, "output") + + inputMeta := toolSpan.Meta["input"] + assert.NotNil(t, inputMeta) + inputWrapper := inputMeta.(map[string]any) + inputStr := inputWrapper["value"].(string) + + var inputData map[string]any + err = json.Unmarshal([]byte(inputStr), &inputData) + require.NoError(t, err) + assert.Equal(t, "tools/call", inputData["method"]) + params := inputData["params"].(map[string]any) + assert.Equal(t, "calculator", params["name"]) + arguments := params["arguments"].(map[string]any) + assert.Equal(t, "add", arguments["operation"]) + assert.Equal(t, float64(5), arguments["x"]) + assert.Equal(t, float64(3), arguments["y"]) + + outputMeta := toolSpan.Meta["output"] + assert.NotNil(t, outputMeta) + outputWrapper := outputMeta.(map[string]any) + outputStr := outputWrapper["value"].(string) + + var outputData map[string]any + err = json.Unmarshal([]byte(outputStr), &outputData) + require.NoError(t, err) + + content := outputData["content"].([]any) + require.Len(t, content, 1) + contentItem := content[0].(map[string]any) + assert.Equal(t, "text", contentItem["type"]) + + var resultJSON map[string]any + err = json.Unmarshal([]byte(contentItem["text"].(string)), &resultJSON) + require.NoError(t, err) + const expectedResult = 8.0 + assert.Equal(t, expectedResult, resultJSON["result"]) +} + +func TestIntegrationToolCallError(t *testing.T) { + tt := testTracer(t) + defer tt.Stop() + + ctx := context.Background() + + server := mcp.NewServer(&mcp.Implementation{Name: "test-server", Version: "1.0.0"}, nil) + AddTracingMiddleware(server) + + mcp.AddTool(server, + &mcp.Tool{ + Name: "error_tool", + Description: "A tool that always errors", + InputSchema: &jsonschema.Schema{ + Type: "object", + Properties: map[string]*jsonschema.Schema{}, + }, + }, + func(ctx context.Context, req *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { + return nil, nil, errors.New("intentional test error") + }, + ) + + client := mcp.NewClient(&mcp.Implementation{Name: "test-client", Version: "1.0.0"}, nil) + + clientTransport, serverTransport := mcp.NewInMemoryTransports() + + serverSession, err := server.Connect(ctx, serverTransport, nil) + require.NoError(t, err) + defer serverSession.Close() + + clientSession, err := client.Connect(ctx, clientTransport, nil) + require.NoError(t, err) + defer clientSession.Close() + + result, err := clientSession.CallTool(ctx, &mcp.CallToolParams{ + Name: "error_tool", + Arguments: map[string]any{}, + }) + require.NoError(t, err) + assert.NotNil(t, result) + assert.True(t, result.IsError) + + spans := tt.WaitForLLMObsSpans(t, 2) + require.Len(t, spans, 2) + + var toolSpan *testtracer.LLMObsSpan + for i := range spans { + if spans[i].Name == "error_tool" { + toolSpan = &spans[i] + } + } + + require.NotNil(t, toolSpan, "tool span not found") + + assert.Equal(t, "error_tool", toolSpan.Name) + assert.Equal(t, "tool", toolSpan.Meta["span.kind"]) + + assert.Contains(t, toolSpan.Meta, "error.message") + assert.Contains(t, toolSpan.Meta["error.message"], "tool resulted in an error") + assert.Contains(t, toolSpan.Meta, "error.type") + assert.Contains(t, toolSpan.Meta, "error.stack") + + assert.Contains(t, toolSpan.Meta, "input") + inputMeta := toolSpan.Meta["input"] + assert.NotNil(t, inputMeta) + inputWrapper := inputMeta.(map[string]any) + inputStr := inputWrapper["value"].(string) + + var inputData map[string]any + err = json.Unmarshal([]byte(inputStr), &inputData) + require.NoError(t, err) + assert.Equal(t, "tools/call", inputData["method"]) + params := inputData["params"].(map[string]any) + assert.Equal(t, "error_tool", params["name"]) +} + +func TestIntegrationToolCallStructuredError(t *testing.T) { + tt := testTracer(t) + defer tt.Stop() + + ctx := context.Background() + + server := mcp.NewServer(&mcp.Implementation{Name: "test-server", Version: "1.0.0"}, nil) + AddTracingMiddleware(server) + + type ValidationArgs struct { + Name string `json:"name"` + } + + mcp.AddTool(server, + &mcp.Tool{ + Name: "validation_tool", + Description: "A tool that returns structured error information", + InputSchema: &jsonschema.Schema{ + Type: "object", + Properties: map[string]*jsonschema.Schema{ + "name": {Type: "string", Description: "Name parameter"}, + }, + Required: []string{"name"}, + }, + }, + func(ctx context.Context, req *mcp.CallToolRequest, args ValidationArgs) (*mcp.CallToolResult, any, error) { + if args.Name == "invalid" { + return &mcp.CallToolResult{ + IsError: true, + Content: []mcp.Content{ + &mcp.TextContent{Text: "invalid input: name cannot be 'invalid'"}, + }, + }, nil, nil + } + + return &mcp.CallToolResult{ + Content: []mcp.Content{ + &mcp.TextContent{Text: fmt.Sprintf("Hello, %s!", args.Name)}, + }, + }, nil, nil + }, + ) + + client := mcp.NewClient(&mcp.Implementation{Name: "test-client", Version: "1.0.0"}, nil) + + clientTransport, serverTransport := mcp.NewInMemoryTransports() + + serverSession, err := server.Connect(ctx, serverTransport, nil) + require.NoError(t, err) + defer serverSession.Close() + + clientSession, err := client.Connect(ctx, clientTransport, nil) + require.NoError(t, err) + defer clientSession.Close() + + result, err := clientSession.CallTool(ctx, &mcp.CallToolParams{ + Name: "validation_tool", + Arguments: map[string]any{ + "name": "invalid", + }, + }) + require.NoError(t, err) + assert.NotNil(t, result) + assert.True(t, result.IsError) + + spans := tt.WaitForLLMObsSpans(t, 2) + require.Len(t, spans, 2) + + var toolSpan *testtracer.LLMObsSpan + for i := range spans { + if spans[i].Name == "validation_tool" { + toolSpan = &spans[i] + } + } + + require.NotNil(t, toolSpan, "tool span not found") + + assert.Equal(t, "validation_tool", toolSpan.Name) + assert.Equal(t, "tool", toolSpan.Meta["span.kind"]) + + assert.Contains(t, toolSpan.Meta, "error.message") + assert.Contains(t, toolSpan.Meta["error.message"], "tool resulted in an error") + assert.Contains(t, toolSpan.Meta, "error.type") + assert.Contains(t, toolSpan.Meta, "error.stack") + + assert.Contains(t, toolSpan.Meta, "input") + inputMeta := toolSpan.Meta["input"] + assert.NotNil(t, inputMeta) + inputWrapper := inputMeta.(map[string]any) + inputStr := inputWrapper["value"].(string) + + var inputData map[string]any + err = json.Unmarshal([]byte(inputStr), &inputData) + require.NoError(t, err) + assert.Equal(t, "tools/call", inputData["method"]) + params := inputData["params"].(map[string]any) + assert.Equal(t, "validation_tool", params["name"]) + arguments := params["arguments"].(map[string]any) + assert.Equal(t, "invalid", arguments["name"]) + + assert.Contains(t, toolSpan.Meta, "output") + + outputMeta := toolSpan.Meta["output"] + assert.NotNil(t, outputMeta) + outputJSON, err := json.Marshal(outputMeta) + require.NoError(t, err) + outputStr := string(outputJSON) + assert.Contains(t, outputStr, "invalid input") +} + +// testTracer creates a testtracer with LLMObs enabled for integration tests +func testTracer(t *testing.T, opts ...testtracer.Option) *testtracer.TestTracer { + defaultOpts := []testtracer.Option{ + testtracer.WithTracerStartOpts( + tracer.WithLLMObsEnabled(true), + tracer.WithLLMObsMLApp("test-mcp-app"), + tracer.WithLogStartup(false), + ), + testtracer.WithAgentInfoResponse(testtracer.AgentInfo{ + Endpoints: []string{"/evp_proxy/v2/"}, + }), + } + allOpts := append(defaultOpts, opts...) + tt := testtracer.Start(t, allOpts...) + t.Cleanup(tt.Stop) + return tt +} diff --git a/go.work b/go.work index e266f40e51..06ef7a7c77 100644 --- a/go.work +++ b/go.work @@ -51,6 +51,7 @@ use ( ./contrib/log/slog ./contrib/mark3labs/mcp-go ./contrib/miekg/dns + ./contrib/modelcontextprotocol/go-sdk ./contrib/net/http ./contrib/olivere/elastic.v5 ./contrib/redis/go-redis.v9 diff --git a/go.work.sum b/go.work.sum index a73913bc42..97328788bd 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1652,6 +1652,7 @@ github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQA github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/glog v1.2.3 h1:oDTdz9f5VGVVNGu/Q7UXKWYsD0873HXLHdJUNBsSEKM= github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc= diff --git a/instrumentation/packages.go b/instrumentation/packages.go index 51f6902320..1626978b93 100644 --- a/instrumentation/packages.go +++ b/instrumentation/packages.go @@ -47,32 +47,33 @@ const ( PackageNetHTTP Package = "net/http" PackageIBMSarama Package = "IBM/sarama" - PackageValyalaFastHTTP Package = "valyala/fasthttp" - PackageUrfaveNegroni Package = "urfave/negroni" - PackageTwitchTVTwirp Package = "twitchtv/twirp" - PackageTidwallBuntDB Package = "tidwall/buntdb" - PackageSyndtrGoLevelDB Package = "syndtr/goleveldb" - PackageSirupsenLogrus Package = "sirupsen/logrus" - PackageShopifySarama Package = "Shopify/sarama" - PackageSegmentioKafkaGo Package = "segmentio/kafka-go" - PackageRedisGoRedisV9 Package = "redis/go-redis.v9" - PackageOlivereElasticV5 Package = "olivere/elastic.v5" - PackageMiekgDNS Package = "miekg/dns" - PackageLabstackEchoV4 Package = "labstack/echo.v4" - PackageK8SClientGo Package = "k8s.io/client-go" - PackageK8SGatewayAPI Package = "k8s.io/gateway-api" - PackageJulienschmidtHTTPRouter Package = "julienschmidt/httprouter" - PackageMark3LabsMCPGo Package = "mark3labs/mcp-go" - PackageJmoironSQLx Package = "jmoiron/sqlx" - PackageJackcPGXV5 Package = "jackc/pgx.v5" - PackageHashicorpConsulAPI Package = "hashicorp/consul" - PackageHashicorpVaultAPI Package = "hashicorp/vault" - PackageGraphQLGoGraphQL Package = "graphql-go/graphql" - PackageGraphGophersGraphQLGo Package = "graph-gophers/graphql-go" - PackageGormIOGormV1 Package = "gorm.io/gorm.v1" - PackageGorillaMux Package = "gorilla/mux" - PackageUptraceBun Package = "uptrace/bun" - PackageLogSlog Package = "log/slog" + PackageValyalaFastHTTP Package = "valyala/fasthttp" + PackageUrfaveNegroni Package = "urfave/negroni" + PackageTwitchTVTwirp Package = "twitchtv/twirp" + PackageTidwallBuntDB Package = "tidwall/buntdb" + PackageSyndtrGoLevelDB Package = "syndtr/goleveldb" + PackageSirupsenLogrus Package = "sirupsen/logrus" + PackageShopifySarama Package = "Shopify/sarama" + PackageSegmentioKafkaGo Package = "segmentio/kafka-go" + PackageRedisGoRedisV9 Package = "redis/go-redis.v9" + PackageOlivereElasticV5 Package = "olivere/elastic.v5" + PackageMiekgDNS Package = "miekg/dns" + PackageLabstackEchoV4 Package = "labstack/echo.v4" + PackageK8SClientGo Package = "k8s.io/client-go" + PackageK8SGatewayAPI Package = "k8s.io/gateway-api" + PackageJulienschmidtHTTPRouter Package = "julienschmidt/httprouter" + PackageMark3LabsMCPGo Package = "mark3labs/mcp-go" + PackageJmoironSQLx Package = "jmoiron/sqlx" + PackageJackcPGXV5 Package = "jackc/pgx.v5" + PackageHashicorpConsulAPI Package = "hashicorp/consul" + PackageHashicorpVaultAPI Package = "hashicorp/vault" + PackageGraphQLGoGraphQL Package = "graphql-go/graphql" + PackageGraphGophersGraphQLGo Package = "graph-gophers/graphql-go" + PackageGormIOGormV1 Package = "gorm.io/gorm.v1" + PackageGorillaMux Package = "gorilla/mux" + PackageUptraceBun Package = "uptrace/bun" + PackageLogSlog Package = "log/slog" + PackageModelContextProtocolGoSDK Package = "modelcontextprotocol/go-sdk" PackageValkeyIoValkeyGo Package = "valkey-io/valkey-go" PackageEnvoyProxyGoControlPlane Package = "envoyproxy/go-control-plane" @@ -845,6 +846,18 @@ var packages = map[Package]PackageInfo{ }, }, }, + PackageModelContextProtocolGoSDK: { + TracedPackage: "github.com/modelcontextprotocol/go-sdk", + EnvVarPrefix: "MCP", + naming: map[Component]componentNames{ + ComponentServer: { + useDDServiceV0: true, + buildServiceNameV0: staticName("mcp-server"), + buildOpNameV0: staticName("mcp.server.request"), + buildOpNameV1: staticName("mcp.server.request"), + }, + }, + }, PackageEmickleiGoRestful: { TracedPackage: "github.com/emicklei/go-restful", EnvVarPrefix: "RESTFUL", diff --git a/internal/stacktrace/contribs_generated.go b/internal/stacktrace/contribs_generated.go index f8d24b3164..7fcc2d78bb 100644 --- a/internal/stacktrace/contribs_generated.go +++ b/internal/stacktrace/contribs_generated.go @@ -363,6 +363,7 @@ func generatedThirdPartyLibraries() []string { "github.com/gogo/protobuf", "github.com/golang-jwt/jwt", "github.com/golang-jwt/jwt/v4", + "github.com/golang-jwt/jwt/v5", "github.com/golang-sql/civil", "github.com/golang-sql/sqlexp", "github.com/golang/glog", @@ -378,6 +379,7 @@ func generatedThirdPartyLibraries() []string { "github.com/google/go-cmp", "github.com/google/go-pkcs11", "github.com/google/gofuzz", + "github.com/google/jsonschema-go", "github.com/google/martian/v3", "github.com/google/pprof", "github.com/google/s2a-go", @@ -539,6 +541,7 @@ func generatedThirdPartyLibraries() []string { "github.com/moby/sys/user", "github.com/moby/sys/userns", "github.com/moby/term", + "github.com/modelcontextprotocol/go-sdk", "github.com/modern-go/concurrent", "github.com/modern-go/reflect2", "github.com/modocache/gover",