feat: ProtocolSettlement sends X-ATXP-App-Name header to auth#167
Merged
Conversation
…d /verify/* Lets auth (see circuitandchisel/auth#254) attribute settlement observability events to the calling service — so Honeycomb/PostHog dashboards can slice revenue by llm / music / search / etc. Companion to the auth-side change that reads the header. ProtocolSettlement is used by two call paths: 1. Direct construction by callers (LLM today) 2. Hardcoded inside @atxp/express's middleware (tool servers) Both need to be able to set the app name. This PR plumbs it through both: - New ProtocolSettlementOptions with `appName?: string` as 5th constructor arg (options bag, backwards-compatible positional add). - New `appName?: string` field on ATXPConfig (exposed via ATXPArgs, so `atxpExpress({ appName: 'music' })` works). - atxpExpress forwards config.appName into `new ProtocolSettlement(...)`. Resolution order (documented on both surfaces): 1. explicit option value, if set to non-empty string 2. process.env.APP_NAME, if set to non-empty string 3. header omitted An explicit empty string disables the env fallback (useful in tests). The env fallback follows the Pattern-A precedent already established in the SDK (NODE_ENV → allowHttp / allowInsecureRequests in oAuthResource.ts, atxpFetcher.ts, atxpClient.ts, serverConfig.ts). Callers that already set APP_NAME for turtle's PostHog events get correct auth observability with zero code changes on SDK bump. appName added to BuildableATXPConfigFields alongside minimumPayment since both are optional fields that have no DEFAULT_CONFIG value. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…xpress test) Per PR review: 1. Export ProtocolSettlementOptions from @atxp/server's index.ts — the new type was previously only deep-importable. 2. Switch header name to X-ATXP-APP-NAME to align with the existing X-ATXP-PAYMENT / X-ATXP-TOKEN screaming-case precedent in the SDK. Wire-level it's case-insensitive, but downstream log/metric pipelines key on the literal casing. 3. Rewrite the "resolved once at construction" comment — accurate for long-lived callers (LLM), but @atxp/express instantiates ProtocolSettlement per-request so env IS re-read each time. The old comment overpromised. 4. Add an atxp-express integration test that actually sends an MCP request with a payment credential, stubs global fetch, and asserts the outgoing /settle/* fetch carries the X-ATXP-APP-NAME header when config.appName is set (and omits it when neither config nor env has a value). Closes the loop on the one-line forwarding glue. No runtime behavior change beyond the header casing.
Cleans up 4 stragglers from the earlier casing switch that weren't caught in the first sweep: - types.ts JSDoc on ATXPConfig.appName - protocol.ts JSDoc on ProtocolSettlementOptions.appName - protocol.ts inline comment on buildHeaders - protocol.test.ts test title Also adds a "Expected format" note to both JSDocs pointing at the format auth's readAppNameHeader enforces (1–64 chars, [a-zA-Z0-9._-]+). The SDK doesn't validate — it trusts callers and sends whatever non-empty trimmed string it has — so a value outside that format produces a missing span attribute on the auth side rather than a failed settle. The docstring flags this so operators debugging absent observability know where to look. No runtime behavior change.
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.
Summary
Companion to auth#254.
ProtocolSettlementnow sends anX-ATXP-App-Nameheader on every/settle/*and/verify/*request so auth can attribute settlement observability events to the calling service (llm, music, search, etc.).ProtocolSettlementhas two construction sites in this repo:atxpExpress— the express middleware instantiates it per-request for tool servers using@atxp/express.This PR plumbs
appNamethrough both paths:ProtocolSettlementOptionswithappName?: stringas the 5th (options bag) arg on the constructor. Positional-compatible, no breaking change.appName?: stringfield onATXPConfigsoatxpExpress({ appName: 'music' })works.atxpExpress.ts:124forwardsconfig.appNameintonew ProtocolSettlement(...).Resolution order
Documented on both
ProtocolSettlementOptionsandATXPConfig:An explicit empty string (
{ appName: '' }) disables the env fallback — useful for tests and multi-service processes.Why env fallback?
Follows the SDK's existing Pattern-A precedent —
NODE_ENVdrives the default forallowHttp/allowInsecureRequestsacrossoAuthResource.ts,atxpFetcher.ts,atxpClient.ts,serverConfig.ts, with explicit options exposed as overrides. This is the established convention for non-secret env-driven defaults. (Pattern B — silent env reads with no API exposure — is reserved for secrets likeALCHEMY_API_KEYandATXP_OPAQUE_KEY.)Practical benefit: every service in our ecosystem already sets
APP_NAMEfor turtle's PostHog events. With this SDK bump, auth-side observability attribution works with zero code changes in LLM, tool servers, etc.Implementation notes
appNameadded toBuildableATXPConfigFieldsalongsideminimumPayment— both are optional fields that have noDEFAULT_CONFIGvalue, consistent precedent.process.envmutation can't change behavior mid-flight.verify()andsettle()for consistency — both are observability surfaces auth would want attributed.Test plan
npx vitest run src/protocol.test.tsin atxp-server — 33/33 passing (7 new underX-ATXP-App-Name headerdescribe):process.env.APP_NAMEnpx vitest runin atxp-express — 45/45 passingnpx tsc --noEmit— clean in atxp-server and atxp-expressnpm run lint— clean, no new warningsRollout
@atxp/server/@atxp/expressin LLM, tool servers, any ProtocolSettlement consumers.payment.app_name.