feat: attribute outbound Databricks requests to AppKit consistently#453
Merged
Conversation
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>
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>
atilafassina
approved these changes
Jul 1, 2026
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
Make every runtime outbound Databricks request consistently identify itself as AppKit via the SDK's
User-Agentstamp (@databricks/appkit/<version>).Why
Only the service and user
WorkspaceClients passedgetClientOptions(), so only they carried the AppKit User-Agent. The other runtime paths were unattributed:cache/index.ts) used barenew WorkspaceClient({})and sentunknown/0.0.0.fetchthat bypasses the SDKapiClient, so they carried no AppKit User-Agent at all.The
@databricks/sdk-experimentalSDK has no default-headers hook (ClientOptionsexposes onlyagent/product/productVersion/userAgentExtra, andapiClient.request()overwritesUser-Agentper call), so User-Agent is the correct lever.Changes
packages/appkit/src/context/client-options.ts: extracts the previously-privategetClientOptions()and addsAPPKIT_USER_AGENTfor raw-fetch sites that have noapiClient.service-context.ts: importsgetClientOptionsfrom the new module.cache/index.ts: passesgetClientOptions()to theWorkspaceClient.connectors/files/client.ts: upload PUT now setsUser-Agentfromclient.apiClient.userAgent().connectors/mcp/client.ts: both fetches now carryUser-Agent: APPKIT_USER_AGENT.Scope
Attribution only -- no new
X-header, noDATABRICKS_APP_NAME/per-app identity, no SDK monkeypatching. Out of scope: the standalone@databricks/lakebasepackage, build-time type-generators, and Postgres wire traffic (cannot carry HTTP headers).Verification
User-Agentis attached (70 tests pass).pnpm -r typecheck, Biome on changed files,pnpm build, andpnpm docs:buildall clean.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/devfrom dev mode). The debug log is not part of this PR.