feat(integrations): support custom request headers on MCP shttp/sse transports#364
Conversation
…ransports Add `headerFields?: MarketplaceField[]` to the `shttp` and `sse` variants of `IntegrationTransport`, mirroring the `envFields`/`argFields` pattern already used by `stdio`. This lets a hosted MCP integration declare a list of named request headers the user must supply — the generalized mechanism behind header-based auth flows like Datadog's `DD-API-KEY` / `DD-APPLICATION-KEY` (https://docs.datadoghq.com/mcp_server/setup/?tab=other#authentication), which were previously expressible only via OAuth. `headerFields` composes with the existing `api_key`/`bearer`/`basic`/`oauth2` auth strategies rather than replacing them: the Bearer token from `api_key` is folded into `Authorization` separately, and each `headerFields` entry renders one input in the install modal (with `type: "password"` secret-saved by default and `required` validation). No new auth-strategy enum value is needed. Add a non-OAuth `api` connection option to the Datadog catalog entry that declares `DD-API-KEY` and `DD-APPLICATION-KEY` as `headerFields`, so the marketplace install flow can surface the header-based path without Datadog Technology Partner Program enrollment. Tests: - `test_remote_no_auth_mcp_entries_are_intentionally_public` tightened to exclude options with `headerFields` (they still require credentials, just via named headers, so they are not "public/no-auth"). - `test_credential_fields_have_helper_text_and_link` extended to validate `headerFields` password fields too. Closes #363. Co-authored-by: openhands <openhands@all-hands.dev>
|
✅ Review complete. This review was performed through OpenHands Cloud Automation. You can log in and view the conversation here. |
all-hands-bot
left a comment
There was a problem hiding this comment.
🟢 Good taste — Elegant, minimal extension that solves a real problem cleanly.
Summary
This PR adds headerFields?: MarketplaceField[] to the shttp and sse transport variants in IntegrationTransport, directly mirroring the envFields/argFields pattern already established on stdio. The Datadog catalog entry is a solid first consumer, properly wiring up DD-API-KEY and DD-APPLICATION-KEY for users who cannot or do not want to go through the Datadog Technology Partner OAuth flow.
What makes this good:
- Pattern conformance: The new field is a direct analog of
envFieldsonstdio— no new concepts, just a consistent extension of an existing idiom to a different transport kind. - Design choice is sound: Using
auth.strategy: "none"withheaderFieldskeeps the auth strategy enum focused on how OpenHands manages credentials internally (Bearer injection, storage, etc.), whileheaderFieldshandles user-supplied transport-level headers. The PR description justifies this clearly, and the test has been correctly updated to preventstrategy: "none" + headerFieldsentries from being silently classified as "public/no-auth". - Test coverage: Both affected tests are updated correctly.
test_remote_no_auth_mcp_entries_are_intentionally_publicnow excludes header-credentialed options from the public-access guard, andtest_credential_fields_have_helper_text_and_linkcorrectly validatesheaderFieldspassword fields alongsideenvFieldsandargFields. All 5 tests pass. - Backward compatible: Only optional fields are added; no existing catalog entry or consumer is affected.
- Documentation: The JSDoc on
shttp.headerFieldscaptures genuinely non-obvious behavior — the composability with auth strategies and theAuthorizationheader conflict warning are useful for future catalog authors.
[RISK ASSESSMENT]
- [Overall PR]
⚠️ Risk Assessment: 🟢 LOW
Catalog type extension following an established pattern (envFields→headerFields). No auth logic changed; blast radius is limited to newheaderFieldsconsumers. The updated tests guard correctly against misclassification as public/no-auth. Safe to merge.
VERDICT:
✅ Worth merging: Clean, pattern-consistent implementation with appropriate test coverage.
KEY INSIGHT:
headerFields is the natural transport-layer analog of envFields, not a new auth concept — keeping the concern separation clean.
This review was generated by an AI agent (OpenHands) on behalf of the user through OpenHands Automation. View conversation
…tadog URL The Datadog MCP endpoint is site-specific: https://mcp.<site>.datadoghq.com/v1/mcp (e.g. mcp.us5.datadoghq.com for US5). A hardcoded US1 URL returns 403 for users on other sites. Add a generic `urlEditable?: boolean` flag to the shttp/sse transport so the install modal renders the URL as an editable input pre-filled with the catalog URL, letting users point at their region/account host. Pair with the entry's `installHint` to explain what to change. Datadog catalog updates: - Switch both connection options to the stable `/v1/mcp` path (was `/api/unstable/mcp-server/mcp`). - Mark the `api` (headerFields) option `urlEditable: true` so users can edit the host to match their Datadog site. - Add an `installHint` explaining the site-specific URL pattern (https://mcp.<site>.datadoghq.com/v1/mcp) with US5/EU examples. - Update `notes` to describe the site-specific host. Co-authored-by: openhands <openhands@all-hands.dev>
|
🚀 Released in v0.9.0. |
Closes #363.
Summary
Adds
headerFields?: MarketplaceField[]to theshttp/ssevariants ofIntegrationTransport, mirroring theenvFields/argFieldspattern onstdio. This lets a hosted MCP integration declare a list of named request headers the user must supply — the generalized mechanism behind header-based auth flows like Datadog'sDD-API-KEY/DD-APPLICATION-KEY(https://docs.datadoghq.com/mcp_server/setup/?tab=other#authentication), which were previously expressible only via OAuth.headerFieldscomposes with the existingapi_key/bearer/basic/oauth2strategies (the Bearer token fromapi_keyis folded intoAuthorizationseparately) — no new auth-strategy enum value is needed.Changes
integrations/index.d.ts: addheaderFields?: MarketplaceField[]toshttpandssetransports.integrations/catalog/datadog.json: add a non-OAuthapiconnection option declaringDD-API-KEYandDD-APPLICATION-KEYasheaderFields, so the marketplace install flow can surface the header-based path without Datadog Technology Partner Program enrollment.tests/test_catalogs.py:test_remote_no_auth_mcp_entries_are_intentionally_publictightened to exclude options withheaderFields(they still require credentials, just via named headers, so they are not "public/no-auth").test_credential_fields_have_helper_text_and_linkextended to validateheaderFieldspassword fields too.Why this is general
shttp/sseintegration can declare the headers it needs — not a Datadog-specific hack.MCPServerConfig.headers,MCPSSEServer.headers,mcp-service.api.ts,use-update-mcp-server.ts) and the backend (software-agent-sdkmcp_router._RemoteMCPServerSpec.headers,fastmcptransport, encrypted at rest bysettings/model.py) already support arbitrary headers end-to-end.Test plan
uv run --group test pytest -q— 364 passednpm run build:integrations—catalog-index.jsunchanged (datadog.json already imported)