Skip to content

Fixes #20018: prevent duplicate Accept headers in MCP Streamable HTTP transport#20553

Closed
ravencore06 wants to merge 4 commits intogoogle-gemini:mainfrom
ravencore06:fix-20089-clean
Closed

Fixes #20018: prevent duplicate Accept headers in MCP Streamable HTTP transport#20553
ravencore06 wants to merge 4 commits intogoogle-gemini:mainfrom
ravencore06:fix-20089-clean

Conversation

@ravencore06
Copy link
Copy Markdown

Fixes an issue where the httpUrl transport failed to connect to strict MCP servers (like Firecrawl) due to a duplicated Accept header. The Gemini CLI and the @modelcontextprotocol/sdk were both injecting this header with different casings (Accept vs accept), which caused fetch to concatenate them into an invalid duplicate string.

Changes Made:
- Modified [createTransportRequestInit](cci:1://file:///c:/Users/srini/OneDrive/Documents/GSoC%2026/gemini- cli/packages/core/src/tools/mcp-client.ts:775:0-823:1) to lowercase all CLI-provided and configured headers before merging, enabling the SDK's headers to safely override without creating duplicates.
- Updated unit tests to assert against the new lowercased header keys.

@ravencore06 ravencore06 requested review from a team as code owners February 27, 2026 13:16
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a critical bug in the MCP Streamable HTTP transport that prevented connections to strict MCP servers. The problem stemmed from the fetch API concatenating Accept headers with different casings (Accept vs. accept) into an invalid duplicate string. The solution normalizes all header keys to lowercase during the merging process, ensuring that only a single, correctly cased accept header is sent, and that any user-defined Accept header is respected.

Highlights

  • Duplicate Accept Headers Resolved: Fixed an issue where Accept headers were duplicated due to conflicting casing from the CLI and SDK, which caused connection failures with strict MCP servers like Firecrawl.
  • Header Normalization Implemented: Modified the createTransportRequestInit function to lowercase all CLI-provided and configured HTTP header keys before merging, ensuring proper header handling and preventing unintended duplicates.
  • Enhanced Test Coverage: Updated unit tests to assert against lowercased header keys and added new test cases to verify that user-provided Accept headers are correctly preserved, regardless of their initial casing.
Changelog
  • docs/CONTRIBUTING.md
    • Removed trailing whitespace from the file.
  • packages/core/src/tools/mcp-client.test.ts
    • Updated test expectations for _requestInit.headers to use lowercase keys (e.g., accept, authorization, x-goog-user-project).
    • Added new test cases to verify that user-provided accept headers are correctly preserved, regardless of their initial casing.
  • packages/core/src/tools/mcp-client.ts
    • Modified createTransportRequestInit to iterate through all provided headers, convert their keys to lowercase, and merge them into a new mergedHeaders object.
    • Added logic to conditionally set the accept header if not already provided by the user, based on the transport type.
    • Changed the Accept header to accept in the connectToMcpServer function's fetch call to ensure consistent casing.
  • run-tests-json.cjs
    • Added a new script to run specific Vitest tests for mcp-client.test.ts and output results in JSON format.
  • test-fetch-headers.mjs
    • Added a new script to demonstrate how fetch handles duplicate Accept headers with different casings.
  • test-fetch-identical-headers.mjs
    • Added a new script to demonstrate how fetch handles duplicate Accept headers with identical values but different casings.
  • test-mcp-headers.mjs
    • Added a new script to test MCP client header handling with a local HTTP server.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request effectively resolves the issue of duplicate Accept headers in the MCP Streamable HTTP transport, which was causing connection failures with strict MCP servers. The core change involves consistently lowercasing all header keys before merging, ensuring proper header management. The updated unit tests accurately reflect this new behavior. The changes are well-implemented and directly address the problem described.

I am having trouble creating individual review comments. Click here to see my feedback.

packages/core/src/tools/mcp-client.ts (786-815)

critical

The new logic to lowercase all header keys before merging is a robust solution to prevent duplicate headers with different casings. This directly addresses the issue of strict MCP servers failing due to concatenated Accept headers. The subsequent check for an existing accept header and the conditional assignment of a default value based on mcpServerConfig.type ensures that the correct Accept header is always present without overwriting user-defined values.

packages/core/src/tools/mcp-client.test.ts (1573)

high

The test confirms that the accept header is correctly added for Streamable HTTP transport when not explicitly provided.

packages/core/src/tools/mcp-client.test.ts (1512)

high

This change correctly updates the test expectation to reflect the lowercased authorization header and the automatically added accept header, aligning with the intended behavior.

packages/core/src/tools/mcp-client.test.ts (1534)

high

The test correctly verifies that the accept header is automatically added when not explicitly provided, which is crucial for the fix.

packages/core/src/tools/mcp-client.test.ts (1554)

high

This test ensures that the accept header is correctly set for SSE transport types, which is important for the createTransportRequestInit logic.

packages/core/src/tools/mcp-client.test.ts (1466)

high

The change from Authorization to authorization in the test expectation correctly reflects the new header processing logic, ensuring consistency with the lowercasing of header keys.

packages/core/src/tools/mcp-client.test.ts (1595)

high

This test correctly asserts the lowercasing of the authorization header and the inclusion of the default accept header.

packages/core/src/tools/mcp-client.test.ts (1618)

high

The test correctly verifies that custom headers like x-api-key are lowercased and the default accept header is added.

packages/core/src/tools/mcp-client.test.ts (1639)

high

This test correctly asserts the default accept header for HTTP transport.

packages/core/src/tools/mcp-client.test.ts (1802)

high

The change to x-goog-user-project ensures that the header key is consistently lowercased in the test expectation, matching the new behavior.

packages/core/src/tools/mcp-client.test.ts (1644-1661)

high

These new tests are crucial for verifying that user-provided Accept headers (regardless of casing) are preserved and not overwritten by the default logic. This ensures flexibility for users while fixing the duplicate header issue.

packages/core/src/tools/mcp-client.test.ts (2070)

high

The change to authorization in the test expectation is correct, reflecting the lowercasing of header keys.

packages/core/src/tools/mcp-client.test.ts (1864)

high

The test now correctly expects the x-goog-user-project header to be lowercased, which is consistent with the overall header normalization strategy.

packages/core/src/tools/mcp-client.test.ts (1833)

high

This update correctly reflects the lowercasing of the X-Goog-User-Project header in the test assertion.

packages/core/src/tools/mcp-client.test.ts (2117)

high

This update correctly asserts the lowercased authorization header in the test.

packages/core/src/tools/mcp-client.test.ts (1490)

high

The test now correctly asserts the presence of the accept header with its default value, which is a good validation of the new logic in createTransportRequestInit.

packages/core/src/tools/mcp-client.ts (1722)

high

Changing Accept to accept here is consistent with the new header normalization strategy implemented in createTransportRequestInit. This ensures that all outgoing headers are consistently lowercased, preventing potential issues with servers that are sensitive to header casing.

run-tests-json.cjs (1-16)

high

New source code files (.cjs, .mjs) should include the Apache-2.0 license header with the current year, as specified in the Repository Style Guide (lines 76-79).

References
  1. For all new source code files (.ts, .tsx, .js), include the Apache-2.0 license header with the current year. (e.g., Copyright 2026 Google LLC). This is enforced by ESLint. (link)

test-fetch-headers.mjs (1-20)

high

New source code files (.cjs, .mjs) should include the Apache-2.0 license header with the current year, as specified in the Repository Style Guide (lines 76-79).

References
  1. For all new source code files (.ts, .tsx, .js), include the Apache-2.0 license header with the current year. (e.g., Copyright 2026 Google LLC). This is enforced by ESLint. (link)

test-fetch-identical-headers.mjs (1-20)

high

New source code files (.cjs, .mjs) should include the Apache-2.0 license header with the current year, as specified in the Repository Style Guide (lines 76-79).

References
  1. For all new source code files (.ts, .tsx, .js), include the Apache-2.0 license header with the current year. (e.g., Copyright 2026 Google LLC). This is enforced by ESLint. (link)

test-mcp-headers.mjs (1-47)

high

New source code files (.cjs, .mjs) should include the Apache-2.0 license header with the current year, as specified in the Repository Style Guide (lines 76-79).

References
  1. For all new source code files (.ts, .tsx, .js), include the Apache-2.0 license header with the current year. (e.g., Copyright 2026 Google LLC). This is enforced by ESLint. (link)

@gemini-cli gemini-cli bot added the status/need-issue Pull requests that need to have an associated issue. label Feb 27, 2026
@gemini-cli
Copy link
Copy Markdown
Contributor

gemini-cli bot commented Feb 28, 2026

Hi there! Thank you for your contribution to Gemini CLI.

To improve our contribution process and better track changes, we now require all pull requests to be associated with an existing issue, as announced in our recent discussion and as detailed in our CONTRIBUTING.md.

This pull request is being closed because it is not currently linked to an issue. Once you have updated the description of this PR to link an issue (e.g., by adding Fixes #123 or Related to #123), it will be automatically reopened.

How to link an issue:
Add a keyword followed by the issue number (e.g., Fixes #123) in the description of your pull request. For more details on supported keywords and how linking works, please refer to the GitHub Documentation on linking pull requests to issues.

Thank you for your understanding and for being a part of our community!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status/need-issue Pull requests that need to have an associated issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant