GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — fix: accept header accessor function in ServerTransportSecurityValidator by neerajbhatt · Pull Request #982 · modelcontextprotocol/java-sdk

Conversation

@neerajbhatt

@neerajbhatt neerajbhatt commented May 24, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Summary

  • Introduce a HeaderAccessor interface with getHeader(String) and getHeaderNames() methods, replacing the raw Function<String, List<String>> parameter in ServerTransportSecurityValidator.validateHeaders(), as proposed in ServerTransportSecurityValidator accepts an accessor function for header #870 and per reviewer feedback
  • Servlet transports now pass an HttpServletHeaderAccessor wrapping the request directly, leveraging the servlet's native case-insensitive header lookup instead of extracting all headers into a Map upfront
  • Deprecated validateHeaders(Map) is preserved as a default method that bridges to the new validateHeaders(HeaderAccessor), ensuring backward compatibility for existing custom implementations
  • Remove HttpServletRequestUtils utility class (no longer needed)

Changes

  • HeaderAccessor — new public interface with getHeader(String) and getHeaderNames()
  • HttpServletHeaderAccessor — internal implementation wrapping HttpServletRequest
  • ServerTransportSecurityValidator — new validateHeaders(HeaderAccessor) method; deprecated validateHeaders(Map) default bridges to it; both defaults cross-delegate so existing impls work either way
  • DefaultServerTransportSecurityValidator — overrides validateHeaders(HeaderAccessor), queries Origin and Host via accessor directly
  • 3 servlet transports — pass new HttpServletHeaderAccessor(request) instead of extractHeaders(request)
  • Unit tests — updated helpers to return HeaderAccessor; added accessorDefaultBridgesToMapOverride test for the reverse bridge direction

Test plan

  • All 49 DefaultServerTransportSecurityValidatorTests pass
  • CI pipeline passes

Closes #870

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@neerajbhatt
fix: accept header accessor function in ServerTransportSecurityValidator
590c6c3
Replace Map<String, List<String>> parameter with
Function<String, List<String>> in validateHeaders(), allowing callers
to pass a header accessor instead of extracting all headers upfront.

This is more efficient (only requested headers are looked up) and
delegates case-insensitive header matching to the underlying request
implementation (e.g. HttpServletRequest.getHeaders).

- Update DefaultServerTransportSecurityValidator to use the accessor
  directly for Origin and Host headers
- Update all three servlet transport providers to pass
  name -> Collections.list(request.getHeaders(name))
- Remove HttpServletRequestUtils (no longer needed)
- Update unit tests to use accessor-based API

Closes modelcontextprotocol#870
@Kehrlann

Kehrlann commented May 26, 2026

Copy link
Copy Markdown
Contributor

The proposed changed would be breaking. The original ServerTransportSecurityValidator has been shipped in a previous major. If you'd to contribute this, we need a deprecation path.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Kehrlann Kehrlann self-assigned this May 26, 2026
@Kehrlann Kehrlann added area/server waiting for user Waiting for user feedback or more details enhancement New feature or request P2 Moderate issues affecting some users, edge cases, potentially valuable feature labels May 26, 2026
@neerajbhatt
refactor: add deprecation path for validateHeaders API
83c796c
Instead of a breaking change, introduce a deprecation path:
- Keep validateHeaders(Map) as deprecated default method that bridges
  to the new validateHeaders(Function) via case-insensitive lookup
- New implementations override validateHeaders(Function) for efficiency
- Transport providers continue calling validateHeaders(Map) for backward
  compatibility with existing custom implementations
- Restore HttpServletRequestUtils for header extraction in transports
- Add tests for deprecated Map-based API and interface bridge behavior
@neerajbhatt

neerajbhatt commented May 26, 2026

Copy link
Copy Markdown
Contributor Author

Updated with a deprecation path as suggested. The changes:

  • validateHeaders(Map) is now a deprecated default method that bridges to the new validateHeaders(Function) via case-insensitive lookup
  • Transport providers continue calling validateHeaders(Map), so existing custom implementations that override it won't break
  • Added tests for both the deprecated Map-based API and the interface bridge behavior
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@neerajbhatt

neerajbhatt commented May 29, 2026

Copy link
Copy Markdown
Contributor Author

@Kehrlann I've updated the PR with the deprecation path as you suggested. Would you mind taking another look when you get a chance? Thanks!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Kehrlann

Kehrlann commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Hey @neerajbhatt ! Thanks for the update. So this adds a deprecation path but the transport does not use the new API. But that's the whole point in introducing this new API.

Let's try introducing a HeaderAccessor interface, with:

  1. List<String> getHeader(String)
  2. List<String> getHeaderNames()

And then ServerTransportSecurityValidator becomes (more or less):

@FunctionalInterface
public interface ServerTransportSecurityValidator {

	@Deprecated
	void validateHeaders(Map<String, List<String>> headers) throws ServerTransportSecurityException;

	default void validateHeaders(HeaderAccessor accessor) throws ServerTransportSecurityException {
		var collectedHeaders = accessor.getHeaderNames()
			.stream()
			.collect(Collectors.<String, String, List<String>>toUnmodifiableMap(String::toLowerCase,
					accessor::getHeader, (l1, l2) -> {
						var merged = new ArrayList<>(l1);
						merged.addAll(l2);
						return Collections.unmodifiableList(merged);
					}));
		validateHeaders(collectedHeaders);
	}
}
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@neerajbhatt
refactor: introduce HeaderAccessor interface for validateHeaders API
cee7a22
Replace Function<String, List<String>> with a dedicated HeaderAccessor
interface (getHeader + getHeaderNames) as suggested in review. Transports
now pass an HttpServletHeaderAccessor wrapping the request directly,
leveraging the servlet's native case-insensitive header lookup instead of
extracting all headers into a Map upfront.
@neerajbhatt
neerajbhatt force-pushed the fix/870-security-validator-header-accessor branch from b16ccde to cee7a22 Compare June 6, 2026 15:08
@neerajbhatt

neerajbhatt commented Jun 6, 2026

Copy link
Copy Markdown
Contributor Author

@Kehrlann Updated with the HeaderAccessor interface as you suggested. Here's a summary of the changes:

New files:

  • HeaderAccessor — public interface with getHeader(String) and getHeaderNames()
  • HttpServletHeaderAccessor — internal implementation wrapping HttpServletRequest, leveraging its native case-insensitive header lookup

Modified files:

  • ServerTransportSecurityValidator — new validateHeaders(HeaderAccessor) default method; deprecated validateHeaders(Map) bridges to it by wrapping the map as a HeaderAccessor; the new method's default collects headers into a Map and calls the deprecated one — so existing custom implementations that override either method continue to work
  • DefaultServerTransportSecurityValidator — overrides validateHeaders(HeaderAccessor) directly, querying Origin and Host via accessor.getHeader()
  • 3 servlet transports — now call validateHeaders(new HttpServletHeaderAccessor(request)) instead of extractHeaders(request) + Map-based call

Removed:

  • HttpServletRequestUtils — no longer needed

Tests:

  • All 49 DefaultServerTransportSecurityValidatorTests pass
  • Added accessorDefaultBridgesToMapOverride test verifying the reverse bridge (HeaderAccessor → Map) works for existing implementations
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Kehrlann Kehrlann added this to the 2.1.0 Planning milestone Jun 11, 2026
@neerajbhatt

neerajbhatt commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

@Kehrlann Could you remove the "waiting for user" label? I believe all your feedback has been addressed in the latest iteration. Thanks!

Kehrlann reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Kehrlann Kehrlann added breaking-change and removed waiting for user Waiting for user feedback or more details labels Jun 19, 2026
@Kehrlann

Kehrlann commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

By removing @FunctionalInterface, this becomes a breaking (binary-incompatible) change. Maybe we should use your original solution and target 3.0.

Please hold until we discuss this with the team.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Kehrlann Kehrlann removed this from the 2.1.0 Planning milestone Aug 26, 2026
@Kehrlann

Kehrlann commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

@neerajbhatt are you still interested in this?

For context, 2026-07-28 is round the corner and this code will not evolve anymore.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@neerajbhatt

neerajbhatt commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

Hi @Kehrlann — yes, still very much interested, and I'd like to find a way to get this into 2.1 rather than pushing it out to 3.0.

If the blocker is specifically the @FunctionalInterface binary-compatibility break, I can rework the HeaderAccessor addition so it doesn't touch that contract — e.g. keep validateHeaders(Map) as the sole abstract/functional method and add HeaderAccessor support as an additional default method that bridges to it, rather than replacing it. That should keep the change non-breaking and 2.1-compatible. Let me know if that approach works and I'll push an update.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Kehrlann

Kehrlann commented Aug 28, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

So the @FunctionalInterface drop would be a breaking change. Let's keep it.

Let's do this instead:

  1. Deprecate ServerTransportSecurityValidator
  2. Introduce a new interface, ServerHttpHeaderValidator, with a void validate(HeaderAccessor headerAccessor)
  3. Introduce builder methods in the transports with this new validator
  4. Update the default transport validator to also implement ServerHttpHeaderValidator
  5. Add static ServerHttpHeaderValidator toHttpHeaderValidator(ServerTransportSecurityValidator validator)
  6. Change the builder methods in the transports that use a ServerTransportSecurityValidator to wrap the server validator into a ServerHttpHeaderValidator (and deprecate them)
  7. Feed a ServerHttpHeaderValidator into the transports instead of the old validators, and use that.

How does that sound?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@neerajbhatt
Merge remote-tracking branch 'upstream/main' into fix/870-security-va…
c5d0be3
…lidator-header-accessor

# Conflicts:
#	mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletRequestUtils.java
@neerajbhatt

neerajbhatt commented Aug 29, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

Hi @Kehrlann, that sounds good to me. Thanks for laying out the compatibility path clearly. I'll keep ServerTransportSecurityValidator as the deprecated functional, map-based API; introduce ServerHttpHeaderValidator; add the new transport builder methods; and adapt the deprecated builder methods through toHttpHeaderValidator(...). I'll also update the default validator and have the transports use the new validator internally. I'm working through the update now and will push it shortly.

Kehrlann reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Kehrlann Kehrlann added the waiting for user Waiting for user feedback or more details label Aug 31, 2026
@neerajbhatt
fix: introduce ServerHttpHeaderValidator with non-breaking deprecatio…
5436fc1
…n path

Deprecates ServerTransportSecurityValidator (kept as-is, still
@FunctionalInterface, fully binary-compatible) and introduces
ServerHttpHeaderValidator, which validates via the new HeaderAccessor
abstraction instead of a pre-built Map. DefaultServerTransportSecurityValidator
now implements both. A static toHttpHeaderValidator(ServerTransportSecurityValidator)
adapter lets old validators plug into the new API. The three servlet
transports (SSE, stateless, streamable) now use ServerHttpHeaderValidator
internally; their deprecated securityValidator(...) builder methods wrap
existing validators through the adapter so no existing caller breaks.

Addresses review feedback from modelcontextprotocol#982.
@neerajbhatt

neerajbhatt commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

@Kehrlann Pushed the update following the plan above:

  • ServerTransportSecurityValidator — deprecated, unchanged contract (still @FunctionalInterface, still validateHeaders(Map))
  • ServerHttpHeaderValidator — new interface with void validate(HeaderAccessor), plus a NOOP constant
  • DefaultServerTransportSecurityValidator — now implements both interfaces; the real logic lives in validate(HeaderAccessor), and the deprecated validateHeaders(Map) bridges into it
  • ServerTransportSecurityValidator.toHttpHeaderValidator(ServerTransportSecurityValidator) — static adapter wrapping an old validator as a ServerHttpHeaderValidator
  • 3 servlet transports (SSE, stateless, streamable) — added httpHeaderValidator(ServerHttpHeaderValidator) builder methods; the existing securityValidator(ServerTransportSecurityValidator) methods are deprecated and now wrap via the adapter; internally the transports store and call only ServerHttpHeaderValidator

Tests: all 48 DefaultServerTransportSecurityValidatorTests and all 19 ServerTransportSecurityIntegrationTests pass. No existing caller (old or new API) should observe a behavior change.

Let me know if you'd like anything adjusted.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Kehrlann
Kehrlann requested changes Sep 4, 2026
View reviewed changes
Comment thread ...n/java/io/modelcontextprotocol/server/transport/DefaultServerTransportSecurityValidator.java Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HeaderAccessor.java Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread mcp-core/src/main/java/io/modelcontextprotocol/server/transport/HttpServletHeaderAccessor.java Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread ...src/main/java/io/modelcontextprotocol/server/transport/ServerTransportSecurityValidator.java Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread ...n/java/io/modelcontextprotocol/server/transport/DefaultServerTransportSecurityValidator.java Resolved

Uh oh!

There was an error while loading. Please reload this page.

@neerajbhatt @claude
fix: address review feedback on HeaderAccessor bridging
da5ef38
- Hoist the anonymous HeaderAccessor in
  DefaultServerTransportSecurityValidator#validateHeaders(Map) into a
  named MapHeaderAccessor nested class
- Filter out null header values before findFirst() in MapHeaderAccessor
- Preserve original header name casing in
  ServerTransportSecurityValidator#toHttpHeaderValidator, matching the
  case-sensitive behavior of the removed HttpServletRequestUtils#extractHeaders
- Correct @SInCE tags on HeaderAccessor and HttpServletHeaderAccessor to 2.1.0

Co-Authored-By: Claude Sonnet 5 <[email protected]>
@neerajbhatt

neerajbhatt commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

@Kehrlann Pushed a follow-up commit addressing all 5 review comments:

  • Hoisted the anonymous HeaderAccessor in validateHeaders(Map) into a named MapHeaderAccessor nested class
  • Added .filter(Objects::nonNull) before .findFirst() in that accessor
  • Fixed toHttpHeaderValidator to preserve original header-name casing (was lowercasing via String::toLowerCase, which changed behavior vs. the old case-sensitive HttpServletRequestUtils#extractHeaders)
  • Corrected @since tags on HeaderAccessor and HttpServletHeaderAccessor to 2.1.0

All existing tests pass (updated one test that relied on the old lowercase-key behavior). Would appreciate another look when you have a chance. Thanks!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Kehrlann

Kehrlann commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Thanks for your contribution. Merged through 5be2fa0.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Kehrlann Kehrlann closed this Sep 7, 2026
@neerajbhatt neerajbhatt mentioned this pull request Nov 8, 2026
Forward Mcp-Name header in the McpTransportContext for extension validation #1317
Open
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@Kehrlann Kehrlann Kehrlann requested changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@Kehrlann Kehrlann

Labels

area/server breaking-change enhancement New feature or request P2 Moderate issues affecting some users, edge cases, potentially valuable feature waiting for user Waiting for user feedback or more details

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

ServerTransportSecurityValidator accepts an accessor function for header

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@neerajbhatt @Kehrlann
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.