Identify the CLI as the caller via X-Shopify-Agent-Source header - #12
Merged
Conversation
Send X-Shopify-Agent-Source: shop-cli on every outbound MCP request from the single callMcp chokepoint, so the catalog/UCP endpoints can attribute Shop CLI traffic server-side. This closes the gap where unauthenticated global-catalog searches otherwise collapse into one anonymous bucket. The value is self-asserted analytics-only metadata: it is spoofable and must never be used for trust, fraud, or rate-limiting decisions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
What
Send an
X-Shopify-Agent-Source: shop-cliheader on every outbound MCP request so the catalog / UCP endpoints can attribute Shop CLI traffic server-side.The header is added in the single
callMcpchokepoint insrc/shop-client.ts, so it rides on all MCP calls — global-catalog reads (search_catalog,lookup_catalog,get_product) and per-shop UCP checkout calls — for both authenticated and unauthenticated requests. The caller's own headers are spread last so an explicit override always wins.Why
We're moving the Shop Skill (and other surfaces) to call the Global Catalog MCP endpoint directly instead of going through the Shop Proxy. Today, unauthenticated catalog calls carry no
client_idand collapse into a single anonymous bucket keyed only by source IP — so we can't say "X searches came from Shop CLI" and we lose top-of-funnel attribution.Per the Slack discussion, the agreed approach is a transport header rather than a UCP schema/API change: it's orthogonal to the protocol, invisible to the public UCP contract, trivial for first-party clients to set, and trivial to log into the existing tool-call Monorail event server-side. This PR is the client half of that; the server side picks the header up and adds it to the Monorail event.
Important caveat
The value is self-asserted analytics/funnel metadata only. It is spoofable, so it must never feed trust, fraud, or rate-limiting decisions — those stay keyed on auth tier + IP. Documented as such in
references/catalog-mcp.mdand the code comments.Changes
src/constants.ts— addAGENT_SOURCE_HEADER(X-Shopify-Agent-Source) andAGENT_SOURCE(shop-cli).src/shop-client.ts— inject the header intocallMcp.tests/catalog.test.ts— assert the header is sent on catalog calls.references/catalog-mcp.md— document the header.Testing
pnpm typecheck✅pnpm test✅ (80 passing, including the new case)pnpm build✅