-
Notifications
You must be signed in to change notification settings - Fork 489
feat(contrib/mcp-go): Simplified way to add tracing #4122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: jb/contrib-mcp-go-init-span
Are you sure you want to change the base?
feat(contrib/mcp-go): Simplified way to add tracing #4122
Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
| // The file contains methods for easily adding tracing to a MCP server. | ||
|
|
||
| type TracingConfig struct { | ||
| Hooks *server.Hooks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the future (next PR in stack) a boolean option to enable intent capture will be added to this config.
BenchmarksBenchmark execution time: 2025-11-14 15:38:38 Comparing candidate commit 58df1a2 in PR branch Found 0 performance improvements and 0 performance regressions! Performance is the same for 6 metrics, 0 unstable metrics. |
|
✅ Tests 🎉 All green!❄️ No new flaky tests detected 🔗 Commit SHA: 58df1a2 | Docs | Datadog PR Page | Was this helpful? Give us feedback! |
contrib/mark3labs/mcp-go/option.go
Outdated
| hooks := options.Hooks | ||
|
|
||
| // Append hooks (hooks is a private field) | ||
| if hooks == nil { | ||
| hooks = &server.Hooks{} | ||
| } | ||
| AddServerHooks(hooks) | ||
|
|
||
| server.WithHooks(hooks)(s) | ||
|
|
||
| server.WithToolHandlerMiddleware(NewToolHandlerMiddleware())(s) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| hooks := options.Hooks | |
| // Append hooks (hooks is a private field) | |
| if hooks == nil { | |
| hooks = &server.Hooks{} | |
| } | |
| AddServerHooks(hooks) | |
| server.WithHooks(hooks)(s) | |
| server.WithToolHandlerMiddleware(NewToolHandlerMiddleware())(s) | |
| if options == nil { | |
| option = new(TracingConfig) | |
| } | |
| // Add tracing hooks to server | |
| AddServerHooks(options.Hooks) | |
| server.WithHooks(options.Hooks)(s) | |
| server.WithToolHandlerMiddleware(NewToolHandlerMiddleware())(s) |
This would be cleaner, you could also rename AddServerHooks to AddTracingHooks so we know what is happening here and handle the hooks == nil in the func too.
This still does not protect us from having our hooks overwritten by another server.WithHooks if the customer has one in their code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should add AddServerHooks be made private?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I cannot move creation of the empty hooks object into AddServerHooks because it must mutate its input in order for that input to later be passed to WithHooks by the caller. Unless I misunderstood something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should add AddServerHooks be made private?
I'd say yes but still provide a way to have hooks added separately in case the user wants to.
My comment #4115 (comment) would still apply. Make it simpler for the user and us to understand. No need to have NewXX funcs, I'd go for clarity and transparent use whenever I could.
WithTracing would become a wrapper around WithHooks and WithToolHandlerMiddleware
func WithHooks(hooks *server.Hooks) server.ServerOption {
addServerHooks(hooks) // handle `nil` hooks inside
return server.WithHooks(hooks)
}
func WithToolHandlerMiddleware() server.ServerOption {
return server.WithToolHandlerMiddleware(func(next server.ToolHandlerFunc) server.ToolHandlerFunc {
return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
toolSpan, ctx := llmobs.StartToolSpan(ctx, request.Params.Name, llmobs.WithIntegration(string(instrumentation.PackageMark3LabsMCPGo)))
result, err := next(ctx, request)
inputJSON, marshalErr := json.Marshal(request)
if marshalErr != nil {
instr.Logger().Warn("mcp-go: failed to marshal tool request: %v", marshalErr)
}
var outputText string
if result != nil {
resultJSON, marshalErr := json.Marshal(result)
if marshalErr != nil {
instr.Logger().Warn("mcp-go: failed to marshal tool result: %v", marshalErr)
}
outputText = string(resultJSON)
}
toolSpan.AnnotateTextIO(string(inputJSON), outputText)
if err != nil {
toolSpan.Finish(llmobs.WithError(err))
} else {
toolSpan.Finish()
}
return result, err
}
})
}
func WithTracing(cfg *TracingConfig) server.ServerOption {
return func(s *server.MCPServer) {
if cfg == nil {
cfg = new(TracingConfig)
}
// Add tracing hooks & middleware to server
WithHooks(cfg.Hooks)(s)
WithToolHandlerMiddleware()(s)
}
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I still like having things organized into separate files with the responsibility of defining the ServerOption in its own file. But I renamed the public factory method to a private variable and renamed the other file to tracing to clarify its purpose.
9f5e898 to
9ae69b2
Compare
335ba2a to
0237e37
Compare
9ae69b2 to
b214025
Compare
0237e37 to
6bacb1a
Compare
b214025 to
58df1a2
Compare

What does this PR do?
Provides a simpler, more extensible, and less leaky way to add tracing to
mcp-go.Motivation
Previously I proposed a version which used reflection, but changed approaches: #4115
Reviewer's Checklist
./scripts/lint.shlocally.Unsure? Have a question? Request a review!