Skip to content

add support for custom HTTP headers#13

Merged
igrigorik merged 2 commits into
mainfrom
feat/custom-request-headers
May 22, 2026
Merged

add support for custom HTTP headers#13
igrigorik merged 2 commits into
mainfrom
feat/custom-request-headers

Conversation

@igrigorik
Copy link
Copy Markdown
Contributor

Adds generic --header 'Name: Value' flag (repeatable) and a
per-profile persistent file ~/.ucp/profiles/<name>/headers.json with
default + businesses[<origin>] sections — shape mirrors git's
[http] / [http "<URL>"]. Seed a built-in
User-Agent: @shopify/ucp-cli/<version> at lowest priority on every
outbound fetch (mcp-client + cache), overridable from every other source.

Four-source merge, higher wins on case-insensitive header-name conflict:
built-in → config.default → config.businesses[origin] → --header.
Non-conflicting headers from every source ship.

Reserved framing headers (Content-Type, Accept, Host, hop-by-hop,
MCP-Protocol-Version) are silently dropped from user sources. Sensitive
values (Authorization, Cookie, suffix -Token / -Key / -Secret /
-Password) are redacted in verbose traces.

   Adds generic `--header 'Name: Value'` flag (repeatable) and a
   per-profile persistent file `~/.ucp/profiles/<name>/headers.json` with
   `default` + `businesses[<origin>]` sections — shape mirrors git's
   `[http]` / `[http "<URL>"]`. Seed a built-in
   `User-Agent: @shopify/ucp-cli/<version>` at lowest priority on every
   outbound fetch (mcp-client + cache), overridable from every other source.

   Four-source merge, higher wins on case-insensitive header-name conflict:
   built-in → config.default → config.businesses[origin] → --header.
   Non-conflicting headers from every source ship.

   Reserved framing headers (Content-Type, Accept, Host, hop-by-hop,
   MCP-Protocol-Version) are silently dropped from user sources. Sensitive
   values (Authorization, Cookie, suffix -Token / -Key / -Secret /
   -Password) are redacted in verbose traces.
@igrigorik igrigorik force-pushed the feat/custom-request-headers branch from b2b524c to 917c375 Compare May 21, 2026 16:26
@TateLyman
Copy link
Copy Markdown

Reviewed the custom-header flow in this PR. The resolver/merge/redaction layer looks solid, and operation dispatch plus --input-schema both forward the resolved headers through discovery and MCP calls.

One gap I noticed: the top-level ucp discover command does not get the new header path. Its option schema currently has business, profile, profileUrl, refresh, and view, but no header; then the command calls:

const discoverResult = await discoverImpl(businessUrl, {
  force: c.options.refresh,
  profileUrl: requireProfileUrl(session.profile.profileUrl),
})

So a merchant that requires auth/tenant headers even for /.well-known/ucp or tools/list can be used via catalog/cart/checkout ... --header ... and via --input-schema, but cannot be introspected with the natural first command:

ucp discover --business https://merchant.example --header "Authorization: Bearer $TOKEN"

That seems like the one place where the feature promise and CLI surface diverge. Suggested fix: add the same repeatable header option to the discover command, resolve with the session profile + canonical business origin, and pass headers into discoverImpl. A regression test could stub discover and assert the second arg includes { headers: { Authorization: "Bearer token" } } for ucp discover --business ... --header ....

I did not test against private merchants or send credentials; this is just a static review of the public PR diff.

   1. Single outbound HTTP entry point (src/core/http-client.ts).
      `ucpFetch(url, opts)` owns:
      - built-in User-Agent at lowest priority (overridable from any source),
      - merging caller-supplied resolved headers,
      - applying dispatcher-owned framing headers (Content-Type, Accept) last,
      - verbose-mode trace of the redacted outbound header bag.
      mcpRpc, fetchCached, and the doctor profile-URL probe all route through
      ucpFetch. The doctor probe now identifies as @shopify/ucp-cli/<version>
      in merchant logs like every other fetch.

   2. Single header-resolution helper (`resolveCallHeaders` in src/cli.ts).
      Replaces three near-identical inline blocks. Consumed by opRun,
      inputSchemaOperation, and the discover command. Future commands have
      one obvious function to call.
@igrigorik
Copy link
Copy Markdown
Contributor Author

@TateLyman great catch sir, ty! Fixed and centralized the logic so we have a unified path.

@igrigorik igrigorik merged commit 4d9afe3 into main May 22, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants