Skip to content

Conversation

@asoorm
Copy link
Contributor

@asoorm asoorm commented Oct 27, 2025

Adds header forwarding from MCP client through MCP server to the GraphQL router, enabling authentication tokens, tracing headers, and custom metadata to be propagated through the request chain.

Summary by CodeRabbit

  • New Features

    • MCP server now forwards all incoming HTTP request headers to downstream GraphQL services, preserving tracing, custom and non-standard headers while ensuring required GraphQL headers are set.
    • Added ability to configure a custom HTTP client for GraphQL requests.
  • Tests

    • End-to-end tests validate header forwarding in stateless mode, confirming custom/non-standard headers reach subgraphs and return 200 OK.

- Modified MCP server to capture and forward all HTTP headers to router
- Router's existing header propagation rules apply transparently
- Added test to validate header forwarding behavior
@coderabbitai
Copy link

coderabbitai bot commented Oct 27, 2025

Walkthrough

Replaces auth-centric MCP context with request-header-centric APIs, forwards all incoming HTTP headers to downstream GraphQL requests, adds GraphQLSchemaServer.SetHTTPClient, and adds a duplicated "Header Forwarding" test asserting end-to-end header propagation in stateless mode.

Changes

Cohort / File(s) Summary
Header forwarding tests
router-tests/mcp_test.go
Added a "Header Forwarding" test block (appears twice) that configures stateless MCP sessions and router header rules to propagate all headers; injects a GlobalMiddleware on subgraphs to capture requests; sends direct HTTP POSTs with custom headers (foo, X-Custom-Header, X-Trace-Id, Authorization) and asserts those headers arrive at the subgraph and that responses are 200 OK.
MCP server header/context refactor
router/pkg/mcpserver/server.go
Replaced auth-focused context identifiers and helpers with request-header equivalents (authKeyrequestHeadersKey, withAuthKeywithRequestHeaders, authFromRequestrequestHeadersFromRequest, tokenFromContextheadersFromContext). executeGraphQLQuery now clones and forwards all headers from context to downstream GraphQL requests (while enforcing Accept/Content-Type). Added GraphQLSchemaServer.SetHTTPClient(*http.Client) to inject a custom HTTP client. Error messages updated from "missing auth" to "missing request headers".

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Verify all call sites were updated to use the new context helpers and no auth-only usages remain.
  • Review header cloning/merge logic and ordering to prevent header loss or unintended overrides (ensure Accept/Content-Type enforcement is explicit).
  • Confirm SetHTTPClient is concurrency-safe and doesn't introduce races.
  • Inspect the duplicated test block in router-tests/mcp_test.go to confirm intent (duplicate vs. accidental).

Possibly related issues

Possibly related PRs

  • feat(mcp): streamable HTTP support #2157 — Makes related changes to router/pkg/mcpserver/server.go (stateless HTTP transport and header/context handling); likely overlaps with this PR's refactor and forwarding behavior.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "feat(mcp): 8245 header forwarding" directly and clearly summarizes the main change across the modified files. The changes implement header forwarding functionality in the MCP server to propagate request headers from the MCP client through to the GraphQL router, which is exactly what the title indicates. The title is concise, uses proper semantic commit formatting, includes an issue reference for tracking, and avoids vague or generic language. A teammate scanning the commit history would immediately understand that this PR adds header forwarding capability to the MCP system.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@asoorm asoorm changed the title Ahmet/eng 8245 mcp header forwarding v2 feat:(mcp) 8245 header forwarding Oct 27, 2025
@asoorm asoorm changed the title feat:(mcp) 8245 header forwarding feat(mcp): 8245 header forwarding Oct 27, 2025
@github-actions
Copy link

github-actions bot commented Oct 27, 2025

Router image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-d174d9bb5da936604eb0ad41cee1a296de866b9e

@asoorm asoorm force-pushed the ahmet/eng-8245-mcp-header-forwarding-v2 branch 2 times, most recently from 7faa9f5 to 1f58783 Compare October 27, 2025 11:12
Add test proving custom headers like "foo: bar" are forwarded through
MCP server to GraphQL subgraphs using stateless mode and direct HTTP requests.
@asoorm asoorm force-pushed the ahmet/eng-8245-mcp-header-forwarding-v2 branch from 1f58783 to eec2900 Compare October 27, 2025 11:14
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
router-tests/mcp_test.go (1)

572-573: Consider removing mutex for single-request test.

The sync.Mutex protects capturedSubgraphRequest, but this test only makes a single HTTP request that will be processed serially. The GlobalMiddleware will be invoked once during that request, so concurrent access is unlikely. The mutex adds complexity without clear benefit.

If you want to keep the mutex for defensive coding or future-proofing, this is fine. Otherwise, consider simplifying:

-var capturedSubgraphRequest *http.Request
-var subgraphMutex sync.Mutex
+var capturedSubgraphRequest *http.Request

 ...

 GlobalMiddleware: func(handler http.Handler) http.Handler {
     return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-        subgraphMutex.Lock()
         capturedSubgraphRequest = r.Clone(r.Context())
-        subgraphMutex.Unlock()
         handler.ServeHTTP(w, r)
     })
 },

And remove the mutex lock/unlock at lines 647-648.

Also applies to: 596-600

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7faa9f5 and eec2900.

📒 Files selected for processing (2)
  • router-tests/mcp_test.go (2 hunks)
  • router/pkg/mcpserver/server.go (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
router/pkg/mcpserver/server.go (1)
router/internal/expr/expr.go (2)
  • Context (35-39)
  • Request (66-75)
router-tests/mcp_test.go (3)
router-tests/testenv/testenv.go (4)
  • Run (105-122)
  • Config (284-341)
  • SubgraphsConfig (366-379)
  • Environment (1729-1765)
router/pkg/config/config.go (7)
  • Config (1000-1074)
  • MCPConfiguration (965-975)
  • MCPSessionConfig (977-979)
  • HeaderRules (253-258)
  • GlobalHeaderRule (260-264)
  • RequestHeaderRule (278-301)
  • HeaderRuleOperationPropagate (269-269)
router/core/router.go (2)
  • Option (172-172)
  • WithHeaderRules (1720-1724)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: build-router
  • GitHub Check: build_push_image
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: build_test
  • GitHub Check: image_scan
  • GitHub Check: Analyze (go)
🔇 Additional comments (4)
router-tests/mcp_test.go (1)

558-674: Excellent test coverage for end-to-end header propagation.

This test effectively validates that headers flow through the complete chain (MCP Client → MCP Server → Router → Subgraphs). The comprehensive comments explaining why direct HTTP requests are used instead of the mcp-go client library are particularly valuable for future maintainers.

The test verifies multiple header types (custom, tracing, auth) and correctly uses stateless mode to avoid session complexity.

router/pkg/mcpserver/server.go (3)

25-47: Well-structured refactor from auth-only to all-headers context.

The replacement of auth-centric context functions with request-header-centric APIs is clean and follows Go context best practices. Key improvements:

  • r.Header.Clone() at line 36 prevents mutation issues
  • Error message updated from "missing auth" to "missing request headers" appropriately reflects the new semantics
  • Functions maintain consistent naming and patterns

227-230: LGTM - useful test helper.

The public SetHTTPClient method enables injecting custom HTTP clients for testing, which aligns with the test's use of GlobalMiddleware to capture subgraph requests.


681-697: Header forwarding implementation is correct.

The approach of forwarding all headers from the MCP request context and then overriding GraphQL-specific headers (Accept, Content-Type) is sound. Key aspects:

  • Line 683: Headers retrieved from context
  • Lines 688-692: All headers are forwarded, as intended per PR objectives
  • Lines 696-697: GraphQL-required headers are enforced after copying
  • Line 685: Debug-level logging for missing headers is appropriate

The security model correctly relies on the router's header rules (configured via config.HeaderRules) to filter what ultimately reaches subgraphs, as validated by the test at lines 584-593 in mcp_test.go.

Copy link
Contributor

@StarpTech StarpTech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@asoorm asoorm merged commit db4c780 into main Oct 27, 2025
29 checks passed
@asoorm asoorm deleted the ahmet/eng-8245-mcp-header-forwarding-v2 branch October 27, 2025 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants