-
Notifications
You must be signed in to change notification settings - Fork 3.2k
refactor: inject deps via context instead of closures #1640
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
Conversation
This refactor addresses performance issues in per-request server scenarios
where creating ~90 handler closures per request was causing latency.
Changes:
- Add ContextWithDeps, DepsFromContext, MustDepsFromContext to dependencies.go
- Add NewServerToolWithContextHandler, NewServerToolWithRawContextHandler to inventory
- Convert all 89 tool handlers from closure pattern to direct context-based deps
- Update all tests to inject deps into context before calling handlers
- Mark old NewServerTool and NewServerToolFromHandler as deprecated
The new pattern:
- Before: func(deps) handler { return func(ctx, req, args) { use deps } }
- After: func(ctx, deps, req, args) { use deps }
Dependencies are now injected into context once (via ContextWithDeps) and
extracted by NewTool internally before passing to handlers. This eliminates
closure creation on the hot path for remote servers.
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.
Pull request overview
This PR refactors dependency injection from a closure-based pattern to a context-based pattern, improving performance for the remote server by eliminating the need to create closures for each tool on every request. The change reduces allocations from 178 to 89 per request and achieves 2-3.5x throughput improvements.
Key changes:
- Introduces new context-based constructors (
NewServerToolWithContextHandler,NewServerToolWithRawContextHandler) that avoid closure creation at registration time - Changes handler signature from
func(deps) handlertofunc(ctx, deps, req, args) result - Adds context helper functions
ContextWithDeps,DepsFromContext, andMustDepsFromContextinpkg/github/dependencies.go - Converts all 89 tool handlers across 15 files to use the new pattern
- Updates all test files to inject dependencies via context
Reviewed changes
Copilot reviewed 35 out of 35 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
pkg/inventory/server_tool.go |
Adds new context-based constructors; deprecates old closure-based constructors |
pkg/github/security_advisories.go |
Converts 4 tool handlers from closure to context pattern |
pkg/github/security_advisories_test.go |
Updates tests to use ContextWithDeps |
pkg/github/secret_scanning.go |
Converts 2 tool handlers to context pattern |
pkg/github/secret_scanning_test.go |
Updates tests to use ContextWithDeps |
pkg/github/search.go |
Converts 4 search tool handlers to context pattern, including helper function refactoring |
pkg/github/search_test.go |
Updates 5 test cases to use ContextWithDeps |
pkg/github/repositories.go |
Converts 18 repository tool handlers to context pattern |
pkg/github/repositories_test.go |
Updates 17 test cases to use ContextWithDeps |
pkg/github/pullrequests.go |
Converts 10 PR tool handlers to context pattern |
pkg/github/pullrequests_test.go |
Updates 18 test cases to use ContextWithDeps |
pkg/github/projects.go |
Converts 8 project tool handlers to context pattern |
pkg/github/projects_test.go |
Updates 8 test cases to use ContextWithDeps |
pkg/github/notifications_test.go |
Updates 6 test cases to use ContextWithDeps |
pkg/github/labels.go |
Converts 3 label tool handlers to context pattern |
pkg/github/labels_test.go |
Updates 3 test cases to use ContextWithDeps |
The refactoring is comprehensive, consistent, and well-executed. All handler conversions follow the same pattern, tests are properly updated, and the deprecated constructors remain for backward compatibility. No issues were identified during the review.
The issue was that after PR #1640 switched from closure-based deps to context-based deps, the stdio server was missing middleware to inject ToolDependencies into the request context. This caused tools to panic with "ToolDependencies not found in context" when called. Added middleware in NewMCPServer() that wraps all requests with github.ContextWithDeps(), ensuring deps are available to tool handlers via MustDepsFromContext(). Also added tests to verify server creation and toolset resolution logic. Co-authored-by: SamMorrowDrums <4811358+SamMorrowDrums@users.noreply.github.com>
The issue was that after PR #1640 switched from closure-based deps to context-based deps, the stdio server was missing middleware to inject ToolDependencies into the request context. This caused tools to panic with "ToolDependencies not found in context" when called. Added middleware in NewMCPServer() that wraps all requests with github.ContextWithDeps(), ensuring deps are available to tool handlers via MustDepsFromContext(). Also added tests to verify server creation and toolset resolution logic. Co-authored-by: SamMorrowDrums <4811358+SamMorrowDrums@users.noreply.github.com>
Summary
This refactor changes how dependencies are passed to tool handlers from a closure-based pattern to a context-based pattern. This eliminates the need to create new closures for every tool on each request, which is critical for the remote server where one server instance is created per request.
Problem
In the closure-based pattern,
NewToolstored a function that tookdepsand returned a handler:For the remote server with ~89 tools, this meant 178 allocations per request just for tool handler setup.
Solution
The new pattern injects dependencies into the context once, and handlers extract them:
Benchmark Results
Throughput (89 tools, 5s benchmark, 3 runs)
Memory
Scaling (tools from 13 to 1,300)
Changes
New APIs in
pkg/github/dependencies.goContextWithDeps(ctx, deps)- inject dependencies into contextDepsFromContext(ctx)- extract dependencies (returns nil if not present)MustDepsFromContext(ctx)- extract dependencies (panics if not present)Updated in
pkg/inventory/server_tool.goNewServerToolWithContextHandler- new constructor for context-based handlersNewServerToolWithRawContextHandler- new constructor for raw handlersConverted (89 tool handlers across 15 files)
All tool handlers converted from closure pattern to direct context-based pattern.
Test updates (17 test files)
All tests updated to use
ContextWithDeps(context.Background(), deps).Backwards Compatibility
NewServerToolandNewServerToolFromHandlerare deprecated but still workdynamic_tools.gointentionally uses the old pattern (with nolint comment) since it wraps user-provided handlersTesting
script/lintpassesscript/testpasses