Skip to content

feat(elicitation): support configurable headers for URL elicitation tokens#1058

Closed
Aryanburnwal05 wants to merge 1 commit into
Kuadrant:mainfrom
Aryanburnwal05:feat/url-elicitation-custom-header-970
Closed

feat(elicitation): support configurable headers for URL elicitation tokens#1058
Aryanburnwal05 wants to merge 1 commit into
Kuadrant:mainfrom
Aryanburnwal05:feat/url-elicitation-custom-header-970

Conversation

@Aryanburnwal05

@Aryanburnwal05 Aryanburnwal05 commented May 28, 2026

Copy link
Copy Markdown
Contributor

This updates URL elicitation token injection to support configurable header names and value formats instead of always injecting :-

Authorization: Bearer <token>

The default behavior remains unchanged, but MCP server registrations can now customize both the header name and token formatting when forwarding elicited credentials upstream.

Examples :-

tokenURLElicitation:
  headerName: Authorization
  headerValueFormat: "Bearer {token}"
tokenURLElicitation:
  headerName: X-API-Key
  headerValueFormat: "{token}"

What changed

  1. Added headerName and headerValueFormat to token URL elicitation configuration
  2. Propagated the new fields through controller/internal config handling
  3. Replaced the hardcoded auth injection path with configurable header injection
  4. Centralized default resolution logic for token headers/formats
  5. Added safe handling for missing {token} placeholders
  6. Updated router and hairpin passthrough flows to use the same logic

Backward compatibility

Existing behavior is preserved when the new fields are omitted :-

Authorization: Bearer <token>

remains the default behavior for both standard injection and pass through/hairpin flows.

Tests

Extended the existing router test coverage with cases for :-

  1. default Authorization bearer behavior
  2. custom header names
  3. custom token formats
  4. bare token formats
  5. multiple {token} placeholders
  6. missing placeholder handling
  7. nil/empty config handling
  8. passthrough compatibility

Notes

I was not able to regenerate CRD/deepcopy artifacts locally due to a controller-gen / golang.org/x/tools issue on my Windows environment (invalid array length -delta * delta).

The implementation and tests are complete and passing locally, but generated artifacts may still need to be refreshed from a Linux/WSL environment if CI requires it.

Fixes #970

Summary by CodeRabbit

  • New Features
    • Configurable header injection for URL-elicitation tokens with customizable header name and value format (defaults to Authorization / Bearer {token}).
    • Router respects configured header settings and formats injected values accordingly, avoiding overwriting an existing non-empty Authorization header.
  • Tests
    • Added tests covering default and custom header names, value formatting (including multiple/placeholders), and missing-placeholder behavior.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0f3c8306-e636-487f-8ade-688d14d77500

📥 Commits

Reviewing files that changed from the base of the PR and between 4c1386d and 7e056ed.

⛔ Files ignored due to path filters (2)
  • charts/mcp-gateway/crds/mcp.kuadrant.io_mcpserverregistrations.yaml is excluded by !charts/mcp-gateway/crds/**
  • config/crd/mcp.kuadrant.io_mcpserverregistrations.yaml is excluded by !config/crd/mcp.kuadrant.io_*.yaml
📒 Files selected for processing (5)
  • api/v1alpha1/types.go
  • internal/config/types.go
  • internal/controller/mcpserverregistration_controller.go
  • internal/mcp-router/request_handlers.go
  • internal/mcp-router/request_handlers_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • internal/config/types.go
  • internal/controller/mcpserverregistration_controller.go
  • internal/mcp-router/request_handlers.go
  • internal/mcp-router/request_handlers_test.go

📝 Walkthrough

Walkthrough

This PR adds HeaderName and HeaderValueFormat to token URL elicitation config, wires them into controller/config diff logic, implements resolving and formatting of the injected header in the router (skipping overwrite of existing Authorization when appropriate), and adds tests for default and custom formats.

Changes

Configurable URL-elicitation token headers

Layer / File(s) Summary
API schema and config contract
api/v1alpha1/types.go, internal/config/types.go
TokenURLElicitationConfig adds optional HeaderName (default "Authorization") and HeaderValueFormat (default "Bearer {token}") fields to API schema and internal config types.
Config wiring and change detection
internal/controller/mcpserverregistration_controller.go, internal/config/types.go
Controller populates the new header fields when building MCP server config; tokenURLElicitationChanged now compares URL, HeaderName, and HeaderValueFormat with explicit nil handling.
Token header injection implementation
internal/mcp-router/request_handlers.go
Adds helpers to resolve header name and value format and to substitute {token} (logs a warning if missing). Replaces hardcoded authorization injection in hairpin initialize and upstream token resolution with configurable header-name/value-format injection and avoids overwriting an existing non-empty Authorization header.
Header injection test coverage
internal/mcp-router/request_handlers_test.go
Updates existing cached-token injection assertion to expect Bearer <token> and adds table-driven tests verifying defaults, custom header names, multiple {token} substitutions, and static fallback when {token} is absent.

🎯 4 (Complex) | ⏱️ ~45 minutes


suggested labels: review-effort/large``

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly and concisely summarizes the main feature: configurable headers for URL elicitation tokens, directly matching the changeset's primary purpose.
Linked Issues check ✅ Passed PR implements all requirements from #970: adds headerName and headerValueFormat fields with correct defaults, enables {token} substitution, and preserves backward compatibility.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing configurable header injection for URL elicitation tokens as specified in #970; no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot added review-effort/medium Medium review effort (3): few files, moderate logic high-risk Touches concurrency, auth, sessions, CRDs, ext_proc, or routing labels May 28, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/mcp-router/request_handlers_test.go`:
- Around line 1627-1634: Remove the conversational draft comments in
internal/mcp-router/request_handlers_test.go and replace them with a single
terse comment (or none) describing the assertion; then update the failing
assertion that checks the injected Authorization header to expect the new
default format "Bearer ghp_cached_token" instead of "ghp_cached_token" (locate
the header assertion in the test that inspects the Authorization value and the
token variable used for injection). Ensure only a concise comment remains and
the assertion compares to "Bearer "+token.

In `@internal/mcp-router/request_handlers.go`:
- Around line 760-766: This file fails gofmt; run "gofmt -s -w" on
internal/mcp-router/request_handlers.go to apply the standardized formatting
(including the changed hunks around resolveTokenHeaderConfig,
formatTokenHeaderValue and the passThroughHeaders assignment) and re-check the
other modified region around the code referenced at lines roughly 927-930 so the
file is gofmt-clean for CI.
- Around line 760-765: The code unconditionally overwrites the configured header
(notably Authorization) when injecting the token; update the injection logic in
the places that call resolveTokenHeaderConfig and formatTokenHeaderValue so you
only set passThroughHeaders[strings.ToLower(headerName)] = formattedValue if
that key is not already present (i.e., check presence with
strings.ToLower(headerName) before assignment), ensuring Authorization is
injected only when no existing Authorization header exists; apply this same
guard to both token-injection paths where these functions are used.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fa2d37ed-6c81-468a-ab87-1051341291a8

📥 Commits

Reviewing files that changed from the base of the PR and between a2a1cf2 and 4c1386d.

📒 Files selected for processing (5)
  • api/v1alpha1/types.go
  • internal/config/types.go
  • internal/controller/mcpserverregistration_controller.go
  • internal/mcp-router/request_handlers.go
  • internal/mcp-router/request_handlers_test.go

Comment thread internal/mcp-router/request_handlers_test.go Outdated
Comment thread internal/mcp-router/request_handlers.go Outdated
Comment thread internal/mcp-router/request_handlers.go
…kens

Resolves Kuadrant#970 by adding HeaderName and HeaderValueFormat to the
TokenURLElicitation struct in the MCPServerRegistration API.
This enables connecting to MCP servers that expect the URL elicitation
token in a different header (e.g., X-API-Key) or format (e.g., bare token
instead of Bearer {token}).

Signed-off-by: Aryan Burnwal <burnwalaryan922@gmail.com>
@Aryanburnwal05 Aryanburnwal05 force-pushed the feat/url-elicitation-custom-header-970 branch from 4c1386d to 7e056ed Compare May 29, 2026 15:05
@coderabbitai coderabbitai Bot added review-effort/large High review effort (4-5): many files, complex, cross-cutting and removed review-effort/medium Medium review effort (3): few files, moderate logic high-risk Touches concurrency, auth, sessions, CRDs, ext_proc, or routing labels May 29, 2026

@jasonmadigan jasonmadigan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

changes requested. ci is red (lint, crd sync, e2e).

blockers

  • buildRouterConfigKey is dead code, never called. remove it (this is why lint fails)
  • default token format changed from raw to Bearer {token}. WithAuth injected the raw value before. any deployment using elicitation with raw PATs (e.g. github) will break silently. either default to {token} for backwards compat or document the migration
  • crd manifests out of sync -- run make generate-all

should fix

  • no validation on headerName in the crd. a cluster admin could set it to :authority or mcp-session-id and overwrite routing-critical headers. add a kubebuilder enum/pattern or runtime blocklist
  • the "already has authorization" guard in both code paths only checks for Authorization specifically. if headerName is X-API-Key and the client already sends one, gateway overwrites it silently. generalise the guard to check the configured header name

nits

  • guard block between initializeMCPSeverSession and resolveUpstreamToken is repeated. shared helpers help but the guard itself could be extracted
  • hairpin path lowercases header name, tool/call path preserves case. http/2 normalises anyway but the inconsistency is odd

@david-martin

Copy link
Copy Markdown
Member

Thanks for the contributions! One thing to flag: our contributing guidelines ask contributors to limit themselves to one open PR at a time. The idea is that getting one change reviewed and merged is more valuable than having several open in parallel.

You currently have three open PRs. Could you pick whichever one you think is most ready for review, and we'll focus on getting that one through first?

@david-martin david-martin added the triage/has-issue PR links to an existing issue label Jun 22, 2026
@Patryk-Stefanski

Copy link
Copy Markdown
Contributor

Closing due to inactivity

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

Labels

review-effort/large High review effort (4-5): many files, complex, cross-cutting triage/has-issue PR links to an existing issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow configuring header name and format for URL elicitation tokens

4 participants