Skip to content

feat: attribute outbound Databricks requests to AppKit consistently#453

Merged
MarioCadenas merged 6 commits into
mainfrom
feat/appkit-attribution-headers
Jul 1, 2026
Merged

feat: attribute outbound Databricks requests to AppKit consistently#453
MarioCadenas merged 6 commits into
mainfrom
feat/appkit-attribution-headers

Conversation

@MarioCadenas

Copy link
Copy Markdown
Collaborator

What

Make every runtime outbound Databricks request consistently identify itself as AppKit via the SDK's User-Agent stamp (@databricks/appkit/<version>).

Why

Only the service and user WorkspaceClients passed getClientOptions(), so only they carried the AppKit User-Agent. The other runtime paths were unattributed:

  • Cache client (cache/index.ts) used bare new WorkspaceClient({}) and sent unknown/0.0.0.
  • Files upload and MCP connectors use raw fetch that bypasses the SDK apiClient, so they carried no AppKit User-Agent at all.

The @databricks/sdk-experimental SDK has no default-headers hook (ClientOptions exposes only agent/product/productVersion/userAgentExtra, and apiClient.request() overwrites User-Agent per call), so User-Agent is the correct lever.

Changes

  • New packages/appkit/src/context/client-options.ts: extracts the previously-private getClientOptions() and adds APPKIT_USER_AGENT for raw-fetch sites that have no apiClient.
  • service-context.ts: imports getClientOptions from the new module.
  • cache/index.ts: passes getClientOptions() to the WorkspaceClient.
  • connectors/files/client.ts: upload PUT now sets User-Agent from client.apiClient.userAgent().
  • connectors/mcp/client.ts: both fetches now carry User-Agent: APPKIT_USER_AGENT.

Scope

Attribution only -- no new X- header, no DATABRICKS_APP_NAME/per-app identity, no SDK monkeypatching. Out of scope: the standalone @databricks/lakebase package, build-time type-generators, and Postgres wire traffic (cannot carry HTTP headers).

Verification

  • Extended MCP + files connector unit tests assert the User-Agent is attached (70 tests pass).
  • pnpm -r typecheck, Biome on changed files, pnpm build, and pnpm docs:build all clean.
  • Sanity-checked locally with a temporary debug log in the dev-playground: outbound calls logged User-Agent: @databricks/appkit/0.41.5 databricks-sdk-js/0.17.0 nodejs/24 os/darwin auth/undefined mode/dev (no SP token configured locally; mode/dev from dev mode). The debug log is not part of this PR.

Only the service and user WorkspaceClients carried the AppKit User-Agent
(@databricks/appkit/<version>). The cache client sent unknown/0.0.0, and
the two raw-fetch sites (files upload, MCP connector) sent no AppKit
User-Agent at all, since they bypass the SDK apiClient.

Extract getClientOptions() into a shared context/client-options module
(adding APPKIT_USER_AGENT for fetch sites with no apiClient), pass it to
the cache client, and stamp the User-Agent on the files upload fetch (via
apiClient.userAgent()) and both MCP fetches.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
@MarioCadenas MarioCadenas requested a review from a team as a code owner June 19, 2026 03:52
MarioCadenas and others added 5 commits July 1, 2026 11:21
Close two remaining outbound-request attribution gaps and fix the OBO
upload test mock:

- agents: fromModelServing() auto-created WorkspaceClient now gets
  getClientOptions(); the adapter raw-fetch path stamps User-Agent.
- lakebase: inject an attributed SP WorkspaceClient into the pool config
  so the username lookup and OAuth credential generation are attributed
  (OBO already used the context client).
- files plugin test: add apiClient.userAgent to the OBO upload userClient
  mock so upload reaches authenticate() (fixes the failing unit test).

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
In-process http.Server captures the User-Agent from a real WorkspaceClient
(SDK apiClient.request) and the real raw-fetch upload connector, asserting
both carry @databricks/appkit/<version> plus the SDK segments, and that dev
mode adds mode/dev. Closes the one gap mocks can't cover: that the SDK
actually turns getClientOptions() into the wire header.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
…tion

Compute the normalized product version once in client-options.ts and reuse
it for both getClientOptions() and APPKIT_USER_AGENT. Remove the redundant
APPKIT_USER_AGENT format assertion in the MCP client test, which duplicated
the header equality check above it.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
@MarioCadenas MarioCadenas merged commit d37f5f1 into main Jul 1, 2026
9 checks passed
@MarioCadenas MarioCadenas deleted the feat/appkit-attribution-headers branch July 1, 2026 11:46
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