Skip to content

Commit b4c51b9

Browse files
authored
Add telemetry for initialize (docker#132)
* Add telemetry for initialize. * Add intitialize span test.
1 parent 4cc1f55 commit b4c51b9

File tree

5 files changed

+90
-2
lines changed

5 files changed

+90
-2
lines changed

cmd/docker-mcp/internal/interceptors/telemetry.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ func TelemetryMiddleware() mcp.Middleware {
2626
var tracked bool
2727

2828
switch method {
29+
case "initialize":
30+
params := req.GetParams().(*mcp.InitializeParams)
31+
ctx, span = telemetry.StartInitializeSpan(ctx)
32+
telemetry.RecordInitialize(ctx, params)
33+
tracked = true
2934
case "tools/list":
3035
ctx, span = telemetry.StartListSpan(ctx, "tools")
3136
telemetry.RecordListTools(ctx)

cmd/docker-mcp/internal/telemetry/telemetry.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77

8+
"github.com/modelcontextprotocol/go-sdk/mcp"
89
"go.opentelemetry.io/otel"
910
"go.opentelemetry.io/otel/attribute"
1011
"go.opentelemetry.io/otel/metric"
@@ -41,6 +42,9 @@ var (
4142
// GatewayStartCounter tracks gateway starts
4243
GatewayStartCounter metric.Int64Counter
4344

45+
// InitializeCounter tracks initialize calls
46+
InitializeCounter metric.Int64Counter
47+
4448
// ListToolsCounter tracks list tools calls
4549
ListToolsCounter metric.Int64Counter
4650

@@ -134,6 +138,16 @@ func Init() {
134138
}
135139
}
136140

141+
InitializeCounter, err = meter.Int64Counter("mcp.initialize",
142+
metric.WithDescription("Number of initialize calls"),
143+
metric.WithUnit("1"))
144+
if err != nil {
145+
// Log error but don't fail
146+
if os.Getenv("DOCKER_MCP_TELEMETRY_DEBUG") != "" {
147+
fmt.Fprintf(os.Stderr, "[MCP-TELEMETRY] Error creating initialize counter: %v\n", err)
148+
}
149+
}
150+
137151
ListToolsCounter, err = meter.Int64Counter("mcp.list.tools",
138152
metric.WithDescription("Number of list tools calls"),
139153
metric.WithUnit("1"))
@@ -401,6 +415,13 @@ func StartPromptSpan(ctx context.Context, promptName string, attrs ...attribute.
401415
trace.WithSpanKind(trace.SpanKindClient))
402416
}
403417

418+
// StartListSpan starts a new span for a list operation (tools, prompts, resources)
419+
func StartInitializeSpan(ctx context.Context, attrs ...attribute.KeyValue) (context.Context, trace.Span) {
420+
return tracer.Start(ctx, "mcp.initialize",
421+
trace.WithAttributes(attrs...),
422+
trace.WithSpanKind(trace.SpanKindServer))
423+
}
424+
404425
// StartListSpan starts a new span for a list operation (tools, prompts, resources)
405426
func StartListSpan(ctx context.Context, listType string, attrs ...attribute.KeyValue) (context.Context, trace.Span) {
406427
allAttrs := append([]attribute.KeyValue{
@@ -466,6 +487,25 @@ func RecordGatewayStart(ctx context.Context, transportMode string) {
466487
))
467488
}
468489

490+
func RecordInitialize(ctx context.Context, params *mcp.InitializeParams) {
491+
if InitializeCounter == nil {
492+
if os.Getenv("DOCKER_MCP_TELEMETRY_DEBUG") != "" {
493+
fmt.Fprintf(os.Stderr, "[MCP-TELEMETRY] WARNING: InitializeCounter is nil - metrics not initialized\n")
494+
}
495+
return // Telemetry not initialized
496+
}
497+
498+
if os.Getenv("DOCKER_MCP_TELEMETRY_DEBUG") != "" {
499+
fmt.Fprintf(os.Stderr, "[MCP-TELEMETRY] Initialize called - adding to counter\n")
500+
}
501+
502+
InitializeCounter.Add(ctx, 1,
503+
metric.WithAttributes(
504+
attribute.String("mcp.client.name", params.ClientInfo.Name),
505+
attribute.String("mcp.client.version", params.ClientInfo.Version),
506+
))
507+
}
508+
469509
// RecordListTools records a list tools call
470510
func RecordListTools(ctx context.Context) {
471511
if ListToolsCounter == nil {

cmd/docker-mcp/internal/telemetry/telemetry_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,44 @@ func TestInitialization(t *testing.T) {
174174
})
175175
}
176176

177+
func TestStartInitializeSpan(t *testing.T) {
178+
spanRecorder, _ := setupTestTelemetry(t)
179+
Init()
180+
181+
ctx := context.Background()
182+
clientName := "claude-ai"
183+
clientVersion := "1.0.0"
184+
185+
// Start a tool call span
186+
newCtx, span := StartInitializeSpan(ctx,
187+
attribute.String("mcp.client.name", clientName),
188+
attribute.String("mcp.client.version", clientVersion),
189+
)
190+
191+
// Verify context was updated
192+
assert.NotEqual(t, ctx, newCtx, "should return new context with span")
193+
194+
// End the span
195+
span.End()
196+
197+
// Verify span attributes
198+
spans := spanRecorder.Ended()
199+
require.Len(t, spans, 1)
200+
201+
recordedSpan := spans[0]
202+
assert.Equal(t, "mcp.initialize", recordedSpan.Name())
203+
204+
// Check attributes
205+
attrs := recordedSpan.Attributes()
206+
attrMap := make(map[string]string)
207+
for _, attr := range attrs {
208+
attrMap[string(attr.Key)] = attr.Value.AsString()
209+
}
210+
211+
assert.Equal(t, clientName, attrMap["mcp.client.name"])
212+
assert.Equal(t, clientVersion, attrMap["mcp.client.version"])
213+
}
214+
177215
func TestStartToolCallSpan(t *testing.T) {
178216
spanRecorder, _ := setupTestTelemetry(t)
179217
Init()

docs/telemetry/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ AI Client (e.g., Claude Code)
5454

5555
#### Startup and Lifecycle
5656
- **`mcp.gateway.starts`** - Records when the gateway starts, including transport mode (stdio/sse/streaming)
57+
- **`mcp.initialize`** - Records when the host initializes a connection with the gateway
5758

5859
#### Discovery Operations
5960
When the gateway connects to MCP servers, it discovers their capabilities:
@@ -115,6 +116,10 @@ All metrics include contextual attributes for filtering and aggregation:
115116
- **`mcp.server.name`** - Name of the MCP server handling the operation
116117
- **`mcp.server.type`** - Type of server (docker, stdio, sse, unknown)
117118

119+
### Initialize Attributes
120+
- **`mcp.client.name`** - Name of the connecting client (e.g. `claude-ai`)
121+
- **`mcp.client.version`** - Version of the connecting client (e.g. `0.1.0`)
122+
118123
### Operation-Specific Attributes
119124
- **`mcp.tool.name`** - Name of the tool being called
120125
- **`mcp.prompt.name`** - Name of the prompt being retrieved

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ require (
1717
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
1818
github.com/mikefarah/yq/v4 v4.45.4
1919
github.com/modelcontextprotocol/go-sdk v0.2.0
20+
github.com/opencontainers/go-digest v1.0.0
21+
github.com/opencontainers/image-spec v1.1.1
2022
github.com/pkg/errors v0.9.1
2123
github.com/sigstore/cosign/v2 v2.5.0
2224
github.com/sigstore/sigstore v1.9.5
@@ -113,8 +115,6 @@ require (
113115
github.com/oklog/ulid v1.3.1 // indirect
114116
github.com/onsi/gomega v1.37.0 // indirect
115117
github.com/open-policy-agent/opa v1.5.1 // indirect
116-
github.com/opencontainers/go-digest v1.0.0 // indirect
117-
github.com/opencontainers/image-spec v1.1.1 // indirect
118118
github.com/opentracing/opentracing-go v1.2.0 // indirect
119119
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
120120
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect

0 commit comments

Comments
 (0)