Conversation
- Extracted `wrapWithMiddleware` helper function in http_helpers.go - This consolidates the middleware chain pattern that was duplicated between transport.go and routed.go (SDK logging → shutdown check → auth) - Updated both CreateHTTPServerForMCP and CreateHTTPServerForRoutedMode to use the new helper function - Added comprehensive tests for the helper function covering: - Auth scenarios (valid key, invalid key, missing key, no auth) - Shutdown scenarios (with and without auth) - Middleware ordering (verifies shutdown takes precedence per spec 5.1.3) - Log tag variations (unified and routed modes) - All tests pass successfully - No functionality changes, purely refactoring to reduce duplication Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Refactors the HTTP server factories to eliminate duplicated middleware chaining by centralizing the standard middleware stack into a single helper, with accompanying unit tests to validate behavior.
Changes:
- Added
wrapWithMiddleware()helper to encapsulate SDK logging, shutdown rejection, and optional auth. - Updated unified and routed HTTP server creation to use the helper instead of inline chaining.
- Added tests for
wrapWithMiddleware()across auth/shutdown scenarios and log tag variants.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| internal/server/http_helpers.go | Introduces wrapWithMiddleware() helper to consolidate standard middleware wrapping. |
| internal/server/transport.go | Uses wrapWithMiddleware() for unified-mode handler wiring. |
| internal/server/routed.go | Uses wrapWithMiddleware() for per-backend routed-mode handler wiring. |
| internal/server/http_helpers_test.go | Adds new unit tests targeting wrapWithMiddleware() behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Test that shutdown check happens before auth | ||
| // This is important per spec 5.1.3 | ||
| us.InitiateShutdown() | ||
|
|
There was a problem hiding this comment.
This test claims to validate that “shutdown check happens before auth”, but it sets a valid Authorization header, so auth will always pass and the observed 503 only demonstrates shutdown rejection after successful auth. If the intended precedence is “shutdown takes precedence even when auth is missing/invalid”, add a case with apiKey configured + missing/invalid Authorization while shutdown=true and assert 503 (or rename/adjust comments if 401 is intended).
| // Apply shutdown check middleware (spec 5.1.3) | ||
| // This must come before auth to ensure shutdown takes precedence | ||
| shutdownHandler := rejectIfShutdown(unifiedServer, loggedHandler, "server:"+logTag) | ||
|
|
||
| // Apply auth middleware if API key is configured (spec 7.1) | ||
| finalHandler := applyAuthIfConfigured(apiKey, shutdownHandler.ServeHTTP) | ||
|
|
There was a problem hiding this comment.
wrapWithMiddleware’s docstring and inline comment say the shutdown check “must come before auth to ensure shutdown takes precedence”, but the composed handler currently wraps auth outside rejectIfShutdown (applyAuthIfConfigured(apiKey, shutdownHandler.ServeHTTP)). Given authMiddleware returns 401 before calling next when Authorization is missing/invalid, shutdown will not take precedence in those cases (you’ll return 401 instead of 503 during shutdown when auth is enabled). Either change the wrapping so rejectIfShutdown is the outermost middleware (or make authMiddleware aware of shutdown), or update the comments/spec claims if 401 is the intended precedence.
| // This must come before auth to ensure shutdown takes precedence | ||
| shutdownHandler := rejectIfShutdown(unifiedServer, loggedHandler, "server:"+logTag) | ||
|
|
There was a problem hiding this comment.
rejectIfShutdown’s logNamespace used to be a stable call-site identifier (e.g. "server:transport" / "server:routed"), but wrapWithMiddleware now derives it from logTag ("server:"+logTag). In routed mode this creates per-backend logger namespaces like "server:routed:", which can explode cardinality and makes log filtering/alerting less predictable. Consider passing an explicit shutdown log namespace into wrapWithMiddleware (or keep the previous constants) instead of deriving it from logTag.
Summary
Consolidates duplicate middleware chaining pattern (
WithSDKLogging→rejectIfShutdown→applyAuthIfConfigured) that was replicated verbatim betweenCreateHTTPServerForMCP(transport.go) andCreateHTTPServerForRoutedMode(routed.go).Changes
wrapWithMiddlewarehelper inhttp_helpers.gothat encapsulates the standard middleware stack with documented spec references (5.1.3 for shutdown, 7.1 for auth)Example
Before:
After:
Impact
Adding new middleware layers (rate limiting, tracing, etc.) now requires changes in one location instead of two, with spec references and ordering documented centrally.
Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
example.com/tmp/go-build1187723181/b275/launcher.test /tmp/go-build1187723181/b275/launcher.test -test.testlogfile=/tmp/go-build1187723181/b275/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true go HEAD ndor/bin/as --global r = get && echo "--abbrev-ref ortcfg rev-�� 64/src/runtime/c-c=4 64/src/os/dir_un-nolocalimports x_amd64/vet get --global ache/Python/3.12/home/REDACTED/go/pkg/mod/github.com/google/jsonschema-go@v0.4.2/jsonschema/annotations.go x_amd64/vet(dns block)invalid-host-that-does-not-exist-12345.com/tmp/go-build1187723181/b260/config.test /tmp/go-build1187723181/b260/config.test -test.testlogfile=/tmp/go-build1187723181/b260/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true ortcfg s.go 64/pkg/tool/linu-o --global go x86-64.so.2 64/pkg/tool/linu-buildtags ortc�� 1554416/b013/_pk-errorsas 64/src/crypto/in-ifaceassert 64/pkg/tool/linu-nilfunc unset --global 86_64/uname 64/pkg/tool/linu-tests(dns block)nonexistent.local/tmp/go-build1187723181/b275/launcher.test /tmp/go-build1187723181/b275/launcher.test -test.testlogfile=/tmp/go-build1187723181/b275/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true go HEAD ndor/bin/as --global r = get && echo "--abbrev-ref ortcfg rev-�� 64/src/runtime/c-c=4 64/src/os/dir_un-nolocalimports x_amd64/vet get --global ache/Python/3.12/home/REDACTED/go/pkg/mod/github.com/google/jsonschema-go@v0.4.2/jsonschema/annotations.go x_amd64/vet(dns block)slow.example.com/tmp/go-build1187723181/b275/launcher.test /tmp/go-build1187723181/b275/launcher.test -test.testlogfile=/tmp/go-build1187723181/b275/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true go HEAD ndor/bin/as --global r = get && echo "--abbrev-ref ortcfg rev-�� 64/src/runtime/c-c=4 64/src/os/dir_un-nolocalimports x_amd64/vet get --global ache/Python/3.12/home/REDACTED/go/pkg/mod/github.com/google/jsonschema-go@v0.4.2/jsonschema/annotations.go x_amd64/vet(dns block)this-host-does-not-exist-12345.com/tmp/go-build1187723181/b284/mcp.test /tmp/go-build1187723181/b284/mcp.test -test.testlogfile=/tmp/go-build1187723181/b284/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true go 64/src/internal/-ifaceassert 64/pkg/tool/linu-nilfunc --global user.email ache/Python/3.12--abbrev-ref 64/pkg/tool/linuHEAD abis�� toml@v1.6.0/decode.go toml@v1.6.0/deprecated.go u/13/cc1 -tree /cgroup k/_temp/ghcca-node/node/bin/node/opt/hostedtoolcache/go/1.25.6/x64/src/net u/13/cc1(dns block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt