Skip to content

feat: use cors configuration also on mcp server#2363

Merged
alepane21 merged 17 commits intomainfrom
ale/eng-8562-mcp-server-connection-failure-on-ecs-deployment
Dec 1, 2025
Merged

feat: use cors configuration also on mcp server#2363
alepane21 merged 17 commits intomainfrom
ale/eng-8562-mcp-server-connection-failure-on-ecs-deployment

Conversation

@alepane21
Copy link
Copy Markdown
Contributor

@alepane21 alepane21 commented Nov 25, 2025

We allow the MCP server to forward almost all the headers that it receives, but the CORS middleware of the MCP server was not changed to allow any header other than the basic ones.
I've used the CORS framework that we already have in place and used its configuration also in the MCP server.

The HTTP Methods and some headers are forced based on the needs of the MCP protocol.

I've used directly the cors middleware from the cors pkg, some tests were failing so I had to fix them:

  • Last-Event-ID got converted to Last-Event-Id
  • the tests were also expecting all the CORS headers to be set also on all HTTP methods, but there should be only Access-Control-Allow-Origin (as set from th cors middleware)

Summary by CodeRabbit

  • New Features

    • Per-instance CORS support for MCP endpoints; CORS is applied when a server is configured with CORS options.
  • Refactor

    • CORS handling moved to a config-driven middleware that derives headers and behavior from provided settings.
  • Tests

    • Added/updated tests verifying custom CORS headers, methods, preflight and GET/POST behavior are honored by the MCP server.

✏️ Tip: You can customize this high-level summary in your review settings.

Checklist

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 25, 2025

Walkthrough

Threads a cors.Config from router options into MCP server initialization, adds a config-driven CORS middleware path in the MCP server implementation, exposes WithCORS(...), and updates tests to assert custom Allow-Headers propagate to MCP responses.

Changes

Cohort / File(s) Summary
MCP init wiring
router/core/router.go
Conditionally appends mcpserver.WithCORS(*r.corsOptions) to MCP server options when router CORS options are present.
MCP server CORS support & API
router/pkg/mcpserver/server.go
Adds CorsConfig cors.Config to Options and GraphQLSchemaServer, exposes WithCORS(cors.Config), removes legacy header helpers, and replaces inlined CORS handling with middleware via cors.New(s.corsConfig) in Serve. Imports updated.
Tests
router-tests/mcp_test.go
Imports cors, adds a subtest asserting router-configured custom Allow-Headers propagate to MCP responses, updates CORS header expectations and header-casing assertions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Verify public API/signature changes: WithCORS(cors.Config) and Options.CorsConfig.
  • Confirm plumbing: OptionsNewGraphQLSchemaServerServe and middleware construction/use.
  • Inspect removal of legacy header helpers and ensure middleware produces expected headers.
  • Review updated tests for accurate expectations and casing.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: applying CORS configuration to the MCP server, which aligns with the primary modifications across all three files.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings

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

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Nov 25, 2025

Router image scan passed

✅ No security vulnerabilities found in image:

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

@codecov
Copy link
Copy Markdown

codecov Bot commented Nov 25, 2025

Codecov Report

❌ Patch coverage is 0% with 13 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@a496530). Learn more about missing BASE report.

Files with missing lines Patch % Lines
router/pkg/mcpserver/server.go 0.00% 11 Missing ⚠️
router/core/router.go 0.00% 2 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2363   +/-   ##
=======================================
  Coverage        ?   32.33%           
=======================================
  Files           ?      213           
  Lines           ?    22779           
  Branches        ?        0           
=======================================
  Hits            ?     7366           
  Misses          ?    14499           
  Partials        ?      914           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@alepane21 alepane21 marked this pull request as ready for review November 26, 2025 12:04
Copy link
Copy Markdown
Contributor

@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/pkg/mcpserver/server.go (1)

804-839: Update the function comment to reflect the new signature.

The function comment (lines 804-816) describes accepting methods as parameters and mentions a usage example that no longer matches the current signature. The function now accepts a corsConfig *cors.Config parameter instead.

Apply this diff to update the comment:

-// WithCORS creates a reusable CORS middleware that can be used with any HTTP handler.
-// It handles preflight OPTIONS requests and sets appropriate CORS headers.
-//
-// Example usage:
-//
-//	corsMiddleware := WithCORS("GET", "POST", "PUT", "DELETE")
-//	http.Handle("/api/", corsMiddleware(apiHandler))
-//
-// The middleware sets the following CORS headers:
-//   - Access-Control-Allow-Origin: *
-//   - Access-Control-Allow-Methods: specified methods + OPTIONS
-//   - Access-Control-Allow-Headers: Content-Type, Authorization
-//   - Access-Control-Max-Age: 86400 (24 hours)
+// WithCORS creates a reusable CORS middleware that can be used with any HTTP handler.
+// It handles preflight OPTIONS requests and sets appropriate CORS headers based on the provided configuration.
+// If corsConfig is nil or disabled, the middleware passes requests through without modification.
+//
+// Example usage:
+//
+//	corsMiddleware := WithCORS(corsConfig)
+//	http.Handle("/api/", corsMiddleware(apiHandler))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce57515 and 32e4977.

📒 Files selected for processing (2)
  • router/core/router.go (1 hunks)
  • router/pkg/mcpserver/server.go (8 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: StarpTech
Repo: wundergraph/cosmo PR: 2157
File: router-tests/go.mod:16-16
Timestamp: 2025-08-20T22:13:25.222Z
Learning: github.com/mark3labs/mcp-go v0.38.0 has regressions and should not be used in the wundergraph/cosmo project. v0.36.0 is the stable version that should be used across router-tests and other modules.
📚 Learning: 2025-08-20T22:13:25.222Z
Learnt from: StarpTech
Repo: wundergraph/cosmo PR: 2157
File: router-tests/go.mod:16-16
Timestamp: 2025-08-20T22:13:25.222Z
Learning: github.com/mark3labs/mcp-go v0.38.0 has regressions and should not be used in the wundergraph/cosmo project. v0.36.0 is the stable version that should be used across router-tests and other modules.

Applied to files:

  • router/core/router.go
  • router/pkg/mcpserver/server.go
📚 Learning: 2025-09-24T12:54:00.765Z
Learnt from: endigma
Repo: wundergraph/cosmo PR: 2222
File: router-tests/websocket_test.go:2238-2302
Timestamp: 2025-09-24T12:54:00.765Z
Learning: The wundergraph/cosmo project uses Go 1.25 (Go 1.23+ minimum), so fmt.Appendf and other newer Go standard library functions are available and can be used without compatibility concerns.

Applied to files:

  • router/pkg/mcpserver/server.go
📚 Learning: 2025-09-24T12:54:00.765Z
Learnt from: endigma
Repo: wundergraph/cosmo PR: 2222
File: router-tests/websocket_test.go:2238-2302
Timestamp: 2025-09-24T12:54:00.765Z
Learning: The wundergraph/cosmo project uses Go 1.25 (Go 1.25 minimum), so fmt.Appendf and other newer Go standard library functions are available and can be used without compatibility concerns.

Applied to files:

  • router/pkg/mcpserver/server.go
📚 Learning: 2025-11-19T15:13:57.821Z
Learnt from: dkorittki
Repo: wundergraph/cosmo PR: 2273
File: router/core/graphql_handler.go:0-0
Timestamp: 2025-11-19T15:13:57.821Z
Learning: In the Cosmo router (wundergraph/cosmo), error handling follows a two-phase pattern: (1) Prehandler phase handles request parsing, validation, and setup errors using `httpGraphqlError` and `writeOperationError` (in files like graphql_prehandler.go, operation_processor.go, parse_multipart.go, batch.go); (2) Execution phase handles resolver execution errors using `WriteError` in GraphQLHandler.ServeHTTP. Because all `httpGraphqlError` instances are caught in the prehandler before ServeHTTP is invoked, any error type checks for `httpGraphqlError` in the execution-phase WriteError method are unreachable code.

Applied to files:

  • router/pkg/mcpserver/server.go
🧬 Code graph analysis (1)
router/core/router.go (1)
router/pkg/mcpserver/server.go (1)
  • WithCORSConfig (311-315)
🔇 Additional comments (8)
router/pkg/mcpserver/server.go (7)

19-20: LGTM!

The new imports support the CORS configuration functionality correctly.


96-97: LGTM!

The CorsConfig field is well-documented and follows the established pattern for other option fields.


118-118: LGTM!

The corsConfig field properly stores the CORS configuration at the server level.


245-245: LGTM!

The initialization correctly copies the CORS configuration from options to the server struct.


311-315: LGTM!

The WithCORSConfig function follows the established functional options pattern and provides a clean API for configuring CORS.


335-335: LGTM!

The CORS configuration is correctly wired into the middleware.


843-853: The design decision to hardcode CORS origin and methods appears intentional but inconsistently documented and implemented.

The function includes a comment stating it "Only used for web browsers, not for API clients," which suggests protocol-specific requirements. However, this creates an inconsistency:

  • config.AllowHeaders is merged with MCP-specific headers (line 846)
  • config.MaxAge is used with a fallback default (lines 848-852)
  • config.AllowOrigins is hardcoded to "*" (line 844)
  • config.AllowMethods is hardcoded to "GET, PUT, POST, DELETE, OPTIONS" (line 845)

The comment above the WithCORS function (lines 814-816) claims "Access-Control-Allow-Methods: specified methods + OPTIONS," which contradicts the actual hardcoded implementation. This inconsistency between documented intent and actual behavior means developers who pass a cors.Config will be surprised that AllowOrigins and AllowMethods are silently ignored while AllowHeaders and MaxAge are respected.

Consider either:

  1. Documenting this behavior more clearly (e.g., "MCP requires these specific methods and wildcard origin; only custom headers and max-age from config are applied")
  2. Merging config.AllowMethods with the required MCP methods (like is done for headers)
  3. Respecting config.AllowOrigins when a non-wildcard origin is configured
router/core/router.go (1)

917-917: LGTM!

The CORS configuration is correctly propagated from the router to the MCP server. The router's CORS options (r.corsOptions) are guaranteed to be initialized by lines 264-266, so this is safe.

Copy link
Copy Markdown
Contributor

@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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32e4977 and a836c15.

📒 Files selected for processing (3)
  • router-tests/mcp_test.go (2 hunks)
  • router/core/router.go (1 hunks)
  • router/pkg/mcpserver/server.go (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • router/core/router.go
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: StarpTech
Repo: wundergraph/cosmo PR: 2157
File: router-tests/go.mod:16-16
Timestamp: 2025-08-20T22:13:25.222Z
Learning: github.com/mark3labs/mcp-go v0.38.0 has regressions and should not be used in the wundergraph/cosmo project. v0.36.0 is the stable version that should be used across router-tests and other modules.
📚 Learning: 2025-08-20T22:13:25.222Z
Learnt from: StarpTech
Repo: wundergraph/cosmo PR: 2157
File: router-tests/go.mod:16-16
Timestamp: 2025-08-20T22:13:25.222Z
Learning: github.com/mark3labs/mcp-go v0.38.0 has regressions and should not be used in the wundergraph/cosmo project. v0.36.0 is the stable version that should be used across router-tests and other modules.

Applied to files:

  • router-tests/mcp_test.go
  • router/pkg/mcpserver/server.go
📚 Learning: 2025-10-01T20:39:16.113Z
Learnt from: SkArchon
Repo: wundergraph/cosmo PR: 2252
File: router-tests/telemetry/telemetry_test.go:9684-9693
Timestamp: 2025-10-01T20:39:16.113Z
Learning: Repo preference: In router-tests/telemetry/telemetry_test.go, keep strict > 0 assertions for request.operation.*Time (parsingTime, normalizationTime, validationTime, planningTime) in telemetry-related tests; do not relax to >= 0 unless CI flakiness is observed.

Applied to files:

  • router-tests/mcp_test.go
📚 Learning: 2025-11-19T15:13:57.821Z
Learnt from: dkorittki
Repo: wundergraph/cosmo PR: 2273
File: router/core/graphql_handler.go:0-0
Timestamp: 2025-11-19T15:13:57.821Z
Learning: In the Cosmo router (wundergraph/cosmo), error handling follows a two-phase pattern: (1) Prehandler phase handles request parsing, validation, and setup errors using `httpGraphqlError` and `writeOperationError` (in files like graphql_prehandler.go, operation_processor.go, parse_multipart.go, batch.go); (2) Execution phase handles resolver execution errors using `WriteError` in GraphQLHandler.ServeHTTP. Because all `httpGraphqlError` instances are caught in the prehandler before ServeHTTP is invoked, any error type checks for `httpGraphqlError` in the execution-phase WriteError method are unreachable code.

Applied to files:

  • router-tests/mcp_test.go
  • router/pkg/mcpserver/server.go
📚 Learning: 2025-09-24T12:54:00.765Z
Learnt from: endigma
Repo: wundergraph/cosmo PR: 2222
File: router-tests/websocket_test.go:2238-2302
Timestamp: 2025-09-24T12:54:00.765Z
Learning: The wundergraph/cosmo project uses Go 1.25 (Go 1.23+ minimum), so fmt.Appendf and other newer Go standard library functions are available and can be used without compatibility concerns.

Applied to files:

  • router/pkg/mcpserver/server.go
📚 Learning: 2025-09-24T12:54:00.765Z
Learnt from: endigma
Repo: wundergraph/cosmo PR: 2222
File: router-tests/websocket_test.go:2238-2302
Timestamp: 2025-09-24T12:54:00.765Z
Learning: The wundergraph/cosmo project uses Go 1.25 (Go 1.25 minimum), so fmt.Appendf and other newer Go standard library functions are available and can be used without compatibility concerns.

Applied to files:

  • router/pkg/mcpserver/server.go
🧬 Code graph analysis (1)
router-tests/mcp_test.go (3)
router-tests/testenv/testenv.go (3)
  • Run (105-122)
  • Config (285-342)
  • Environment (1733-1769)
router/pkg/config/config.go (2)
  • Config (1020-1095)
  • MCPConfiguration (980-990)
router/core/router.go (2)
  • Option (172-172)
  • WithCors (1590-1594)
⏰ 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). (12)
  • GitHub Check: build-router
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: integration_test (./events)
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: build_push_image
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: build_test
  • GitHub Check: image_scan
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: build_test
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
🔇 Additional comments (7)
router-tests/mcp_test.go (2)

19-19: LGTM!

Import added correctly to support the new CORS configuration test.


564-612: LGTM!

The test properly validates that custom CORS headers from router configuration are propagated to the MCP server. It follows the existing test patterns and verifies both custom headers (Test, X-Custom-Auth) and default MCP headers are present in the response.

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

19-20: LGTM!

Necessary imports for the new CORS functionality.


96-98: LGTM!

The CorsConfig field is appropriately added to the Options struct, following the existing pattern for configuration options.


311-315: LGTM!

The WithCORS option function follows the established pattern for option setters in this codebase.


335-342: LGTM!

The middleware is properly constructed and applied to the MCP endpoint handler.


804-833: LGTM!

The middleware correctly handles preflight OPTIONS requests and sets CORS headers for all responses.

Comment thread router/pkg/mcpserver/server.go Outdated
…on-failure-on-ecs-deployment' into ale/eng-8562-mcp-server-connection-failure-on-ecs-deployment
Copy link
Copy Markdown
Contributor

@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

♻️ Duplicate comments (1)
router/pkg/mcpserver/server.go (1)

837-847: CORS header construction is correct and resolves prior duplicate-header issue

The union of config.AllowHeaders with the MCP-required defaults and deduplication via stringsx.RemoveDuplicates now treats each default header as its own slice element, so duplicates are correctly removed when callers pre-include any of these headers. The max-age defaulting to 24h when unset is also reasonable.

🧹 Nitpick comments (2)
router/pkg/mcpserver/server.go (2)

311-315: Add GoDoc for exported WithCORS helper

WithCORS is exported and follows the same pattern as the other With* option setters, but it lacks a GoDoc comment. Adding one will keep linters happy and improve discoverability, e.g.:

// WithCORS sets the CORS configuration for the MCP server.
func WithCORS(corsCfg cors.Config) func(*Options) {
    return func(o *Options) {
        o.CorsConfig = corsCfg
    }
}

804-817: Update corsMiddleware documentation to match current API and behavior

The doc block still shows corsMiddleware("GET", "POST", ...) and describes method/header behavior that no longer matches the implementation (which now takes a cors.Config and delegates specifics to setCORSHeaders). It would be good to refresh:

  • The example to use corsMiddleware(corsCfg).
  • The described headers/methods to align with setCORSHeaders (hard-coded methods, union of config.AllowHeaders with the MCP-required defaults, max-age behavior).

This avoids future confusion for readers reusing this middleware elsewhere.

Also applies to: 817-833

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a836c15 and 6ecc2fb.

📒 Files selected for processing (1)
  • router/pkg/mcpserver/server.go (8 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: StarpTech
Repo: wundergraph/cosmo PR: 2157
File: router-tests/go.mod:16-16
Timestamp: 2025-08-20T22:13:25.222Z
Learning: github.com/mark3labs/mcp-go v0.38.0 has regressions and should not be used in the wundergraph/cosmo project. v0.36.0 is the stable version that should be used across router-tests and other modules.
📚 Learning: 2025-08-20T22:13:25.222Z
Learnt from: StarpTech
Repo: wundergraph/cosmo PR: 2157
File: router-tests/go.mod:16-16
Timestamp: 2025-08-20T22:13:25.222Z
Learning: github.com/mark3labs/mcp-go v0.38.0 has regressions and should not be used in the wundergraph/cosmo project. v0.36.0 is the stable version that should be used across router-tests and other modules.

Applied to files:

  • router/pkg/mcpserver/server.go
📚 Learning: 2025-09-24T12:54:00.765Z
Learnt from: endigma
Repo: wundergraph/cosmo PR: 2222
File: router-tests/websocket_test.go:2238-2302
Timestamp: 2025-09-24T12:54:00.765Z
Learning: The wundergraph/cosmo project uses Go 1.25 (Go 1.23+ minimum), so fmt.Appendf and other newer Go standard library functions are available and can be used without compatibility concerns.

Applied to files:

  • router/pkg/mcpserver/server.go
📚 Learning: 2025-09-24T12:54:00.765Z
Learnt from: endigma
Repo: wundergraph/cosmo PR: 2222
File: router-tests/websocket_test.go:2238-2302
Timestamp: 2025-09-24T12:54:00.765Z
Learning: The wundergraph/cosmo project uses Go 1.25 (Go 1.25 minimum), so fmt.Appendf and other newer Go standard library functions are available and can be used without compatibility concerns.

Applied to files:

  • router/pkg/mcpserver/server.go
📚 Learning: 2025-11-19T15:13:57.821Z
Learnt from: dkorittki
Repo: wundergraph/cosmo PR: 2273
File: router/core/graphql_handler.go:0-0
Timestamp: 2025-11-19T15:13:57.821Z
Learning: In the Cosmo router (wundergraph/cosmo), error handling follows a two-phase pattern: (1) Prehandler phase handles request parsing, validation, and setup errors using `httpGraphqlError` and `writeOperationError` (in files like graphql_prehandler.go, operation_processor.go, parse_multipart.go, batch.go); (2) Execution phase handles resolver execution errors using `WriteError` in GraphQLHandler.ServeHTTP. Because all `httpGraphqlError` instances are caught in the prehandler before ServeHTTP is invoked, any error type checks for `httpGraphqlError` in the execution-phase WriteError method are unreachable code.

Applied to files:

  • router/pkg/mcpserver/server.go
⏰ 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). (10)
  • GitHub Check: build-router
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: image_scan
  • GitHub Check: build_push_image
  • GitHub Check: build_test
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
🔇 Additional comments (3)
router/pkg/mcpserver/server.go (3)

19-20: New imports correctly scoped for CORS utilities

The additions of stringsx and cors are used only in the new CORS helpers and keep dependencies localized; no issues here.


96-98: CORS config threading through Options and GraphQLSchemaServer looks sound

Storing cors.Config on Options and copying it into GraphQLSchemaServer.corsConfig provides a clear, immutable snapshot of the CORS settings at server construction time and is consistent with the other option fields.

Also applies to: 118-119, 245-245


335-341: CORS middleware wrapping of /mcp route is straightforward

Using middleware := corsMiddleware(s.corsConfig) and applying it only to the /mcp handler keeps CORS concerns localized and preserves the existing behavior while letting you inject config-driven headers.

Copy link
Copy Markdown
Contributor

@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/pkg/mcpserver/server.go (1)

310-321: WithCORS cleanly enforces MCP‑specific CORS defaults while reusing router config

Taking cors.Config by value here and overriding AllowOrigins / AllowMethods while augmenting AllowHeaders and normalizing MaxAge gives the MCP server its own policy without mutating the router’s shared config, which is a good separation. If you want slightly tidier responses, you could optionally de‑duplicate corsCfg.AllowHeaders (e.g. using the same helper the router CORS code uses) in case upstream config already includes some of the MCP headers, and consider spelling the MCP header as "Last-Event-Id" to match the canonical form asserted in tests—but both are polish rather than correctness issues.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ecc2fb and d331637.

📒 Files selected for processing (2)
  • router-tests/mcp_test.go (5 hunks)
  • router/pkg/mcpserver/server.go (6 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: StarpTech
Repo: wundergraph/cosmo PR: 2157
File: router-tests/go.mod:16-16
Timestamp: 2025-08-20T22:13:25.222Z
Learning: github.com/mark3labs/mcp-go v0.38.0 has regressions and should not be used in the wundergraph/cosmo project. v0.36.0 is the stable version that should be used across router-tests and other modules.
📚 Learning: 2025-08-20T22:13:25.222Z
Learnt from: StarpTech
Repo: wundergraph/cosmo PR: 2157
File: router-tests/go.mod:16-16
Timestamp: 2025-08-20T22:13:25.222Z
Learning: github.com/mark3labs/mcp-go v0.38.0 has regressions and should not be used in the wundergraph/cosmo project. v0.36.0 is the stable version that should be used across router-tests and other modules.

Applied to files:

  • router-tests/mcp_test.go
  • router/pkg/mcpserver/server.go
📚 Learning: 2025-10-01T20:39:16.113Z
Learnt from: SkArchon
Repo: wundergraph/cosmo PR: 2252
File: router-tests/telemetry/telemetry_test.go:9684-9693
Timestamp: 2025-10-01T20:39:16.113Z
Learning: Repo preference: In router-tests/telemetry/telemetry_test.go, keep strict > 0 assertions for request.operation.*Time (parsingTime, normalizationTime, validationTime, planningTime) in telemetry-related tests; do not relax to >= 0 unless CI flakiness is observed.

Applied to files:

  • router-tests/mcp_test.go
📚 Learning: 2025-11-19T15:13:57.821Z
Learnt from: dkorittki
Repo: wundergraph/cosmo PR: 2273
File: router/core/graphql_handler.go:0-0
Timestamp: 2025-11-19T15:13:57.821Z
Learning: In the Cosmo router (wundergraph/cosmo), error handling follows a two-phase pattern: (1) Prehandler phase handles request parsing, validation, and setup errors using `httpGraphqlError` and `writeOperationError` (in files like graphql_prehandler.go, operation_processor.go, parse_multipart.go, batch.go); (2) Execution phase handles resolver execution errors using `WriteError` in GraphQLHandler.ServeHTTP. Because all `httpGraphqlError` instances are caught in the prehandler before ServeHTTP is invoked, any error type checks for `httpGraphqlError` in the execution-phase WriteError method are unreachable code.

Applied to files:

  • router-tests/mcp_test.go
  • router/pkg/mcpserver/server.go
📚 Learning: 2025-09-24T12:54:00.765Z
Learnt from: endigma
Repo: wundergraph/cosmo PR: 2222
File: router-tests/websocket_test.go:2238-2302
Timestamp: 2025-09-24T12:54:00.765Z
Learning: The wundergraph/cosmo project uses Go 1.25 (Go 1.23+ minimum), so fmt.Appendf and other newer Go standard library functions are available and can be used without compatibility concerns.

Applied to files:

  • router/pkg/mcpserver/server.go
📚 Learning: 2025-09-24T12:54:00.765Z
Learnt from: endigma
Repo: wundergraph/cosmo PR: 2222
File: router-tests/websocket_test.go:2238-2302
Timestamp: 2025-09-24T12:54:00.765Z
Learning: The wundergraph/cosmo project uses Go 1.25 (Go 1.25 minimum), so fmt.Appendf and other newer Go standard library functions are available and can be used without compatibility concerns.

Applied to files:

  • router/pkg/mcpserver/server.go
🧬 Code graph analysis (1)
router-tests/mcp_test.go (1)
router/core/router.go (2)
  • Option (172-172)
  • WithCors (1590-1594)
⏰ 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). (11)
  • GitHub Check: build-router
  • GitHub Check: build_push_image
  • GitHub Check: image_scan
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: build_test
  • GitHub Check: integration_test (./events)
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
router/pkg/mcpserver/server.go (2)

73-97: CORS config plumbing into GraphQLSchemaServer looks consistent; just confirm it’s always set when needed

Adding CorsConfig cors.Config to Options, mirroring it as corsConfig on GraphQLSchemaServer, and wiring it through NewGraphQLSchemaServer into Serve is coherent and keeps CORS concerns encapsulated in the server instance. Please just double‑check that all call sites which expect MCP CORS behavior actually apply WithCORS(...) (or otherwise populate Options.CorsConfig), so cors.New is never called with an unintended zero‑value config that might silently disable CORS on /mcp.

Also applies to: 99-118, 188-247


19-24: CORS middleware wrapping only the /mcp route is scoped and straightforward

Importing router/pkg/cors and applying middleware := cors.New(s.corsConfig) only around the /mcp handler keeps CORS behavior localized to the MCP endpoint while reusing the shared configuration; the integration with server.NewStreamableHTTPServer looks correct and doesn’t introduce any obvious side effects.

Also applies to: 323-348

router-tests/mcp_test.go (3)

376-423: Updated Last-Event-Id expectation matches middleware’s header casing

Switching the assertion to "Last-Event-Id" in the preflight test aligns the expectation with the header value emitted by the CORS middleware while keeping the rest of the CORS checks (methods, headers, max‑age) intact; this looks correct.


425-542: Non‑OPTIONS CORS tests now match typical middleware behavior

For POST, GET, PUT, and DELETE you now assert only Access-Control-Allow-Origin and explicitly expect empty Access-Control-Allow-Methods, Access-Control-Allow-Headers, and Access-Control-Max-Age. That matches how a focused CORS implementation usually behaves on actual requests (vs preflight) and lines up with the new middleware’s semantics.


19-24: Custom‑header test nicely validates reuse of router CORS config in MCP

Importing router/pkg/cors and wiring RouterOptions: []core.Option{ core.WithCors(&cors.Config{ AllowHeaders: []string{"Test", "X-Custom-Auth"}, }) } into the new test is a good way to assert that router CORS headers flow through to the MCP server. Verifying that the MCP OPTIONS response includes both the MCP‑specific headers and the router‑configured "Test" / "X-Custom-Auth" entries gives solid coverage that the shared config is actually being reused. If you want this to mimic a real browser preflight even more closely, you could also set Access-Control-Request-Method and Access-Control-Request-Headers on the OPTIONS request, but it’s not required for this assertion to be meaningful.

Also applies to: 544-592

Copy link
Copy Markdown
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

@alepane21 alepane21 merged commit 4ccbfb8 into main Dec 1, 2025
39 of 40 checks passed
@alepane21 alepane21 deleted the ale/eng-8562-mcp-server-connection-failure-on-ecs-deployment branch December 1, 2025 08:52
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.

2 participants