Skip to content

Set a base User-Agent and append agent/<id> when a coding agent is detected#509

Draft
ctufts wants to merge 1 commit into
mainfrom
ctufts/AGI-1012-node-user-agent-tag
Draft

Set a base User-Agent and append agent/<id> when a coding agent is detected#509
ctufts wants to merge 1 commit into
mainfrom
ctufts/AGI-1012-node-user-agent-tag

Conversation

@ctufts

@ctufts ctufts commented Jul 17, 2026

Copy link
Copy Markdown

Summary

The Node SDK currently sends no User-Agent of its own — requests fall back to got's default (got/11.8.5 ...). This PR establishes a first-party base UA, mapbox-sdk-js/<version>, on every request, and appends agent/<id> when the process environment indicates it's being driven by a coding agent (Claude Code, Codex, Cursor, etc.).

This is a behavior change on every Node request this SDK makes (previously an unset/got-default UA, now always mapbox-sdk-js/<version>[ agent/<id>]) — flagging explicitly for reviewer sign-off before merge, since it's visible in server-side logs for every consumer.

Changes

  • New lib/helpers/agent-detect.js — Node-only detectAgent(), checking a hardcoded allowlist of ~24 known coding-agent environment indicators (by presence or exact value) in priority order, then falling back to AI_AGENT/AGENT if set. Fallback values are validated against a safe charset (/^[\w.-]{1,64}$/) before use, since an arbitrary env var value must never reach an HTTP header unsanitized (a stray newline would otherwise crash every request). Returns null outside Node (e.g. the browser bundle) or on any unexpected error reading process.env.
  • New lib/helpers/sdk-version.js — resolves mapbox-sdk-js/<version> from package.json at runtime, so the UA can never drift from the published version.
  • lib/classes/mapi-request.jsdefaultHeaders now always sets user-agent, appending agent/<id> when detected. A caller-supplied User-Agent header still overrides it (existing lowercase-dedup behavior), with no duplicate header.
  • lib/browser/browser-layer.js — filters the user-agent header key out before calling xhr.setRequestHeader, since browsers forbid script-set User-Agent via XHR (this SDK's new default would otherwise be a no-op there at best, and throws in some stricter XHR implementations at worst — this is exactly what broke while developing this change, see Testing). Browser agent-tagging is intentionally out of scope here; it's expected to ride a separate mechanism.
  • rollup.config.js / package.json — added rollup-plugin-json so the UMD browser bundle can inline package.json's version for sdk-version.js.

Testing

  • npm test — full suite passing (490/490), including new coverage for: all ~24 allowlist entries individually, table-order precedence, the AI_AGENT/AGENT fallback and its charset validation (including a header-injection attempt with an embedded newline, unicode, and other unsafe characters), the Node-vs-browser guard, a throwing process.env (permission-gated environments), the browser UA-filter, and the header-merge/override/dedup path in mapi-request.js.
  • npm run lint clean.
  • npm run bundle succeeds; confirmed the version string is correctly inlined into umd/mapbox-sdk.js.
  • Manual end-to-end checks against a real MapiRequest:
    • CLAUDECODE=1user-agent: mapbox-sdk-js/0.16.3 agent/claude-code
    • clean env → user-agent: mapbox-sdk-js/0.16.3 (negative control, no suffix)
    • AI_AGENT set to a value containing an embedded newline → safely rejected, base UA only, no crash
  • Self-reviewed via 5 parallel dimension-specific passes (correctness, tests, security, error handling, maintainability) against the full diff; all actionable findings were fixed in this same changeset (notably: the process.env error-handling gap above, and the missing browser-layer regression test).

Downstream impact

Every Node consumer's outbound requests to api.mapbox.com will show a new User-Agent string going forward — relevant to anyone with UA-based logging, analytics, or allowlisting on their own systems. No behavior change for browser consumers (the UA header is filtered out before it would ever reach the network, same as today's got-default-only browser behavior). No change to auth, response handling, retries, or any other request semantics.

Operational considerations

None — pure client-side header change, no deploy/migration/feature flag required. Takes effect on the next version bump.

…tected

The Node SDK sends no User-Agent today, so requests fall back to got's
default. Establish mapbox-sdk-js/<version> as the base UA on every request,
and append " agent/<id>" when a coding-agent env indicator is present
(Node only; the allowlist + precedence mirrors mapbox/tilesets-cli's
agent_detect.py). The browser client explicitly filters the user-agent
header back out, since browsers forbid script-set UA via XHR.
@ctufts ctufts added the locationai-team-skills Opened via the location-ai PR creation skill label Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

locationai-team-skills Opened via the location-ai PR creation skill

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant