From 6afd93712d6fa2306df6a220ff3d92566fbb658b Mon Sep 17 00:00:00 2001 From: Trevin Chow Date: Thu, 23 Apr 2026 05:25:07 -0700 Subject: [PATCH 1/2] feat(app): tag User-Agent with concrete receiver class name The App constructor now emits a second `addAppMetadata` entry whose name includes the receiver's class name (e.g. `@slack/bolt-HTTPReceiver`). Having the receiver type in outbound WebClient User-Agent strings lets the Slack team see which receivers are actually deployed in the wild and prioritize missing features accordingly. Closes #1150 --- src/App.ts | 15 +++++++++++++++ test/unit/App/basic.spec.ts | 17 +++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/App.ts b/src/App.ts index f0e4de9f3..36d2d1984 100644 --- a/src/App.ts +++ b/src/App.ts @@ -366,6 +366,21 @@ export default class App // Since v3.4, WebClient starts sharing logger with App this.clientOptions.logger = this.logger; } + + // Tag the User-Agent with the concrete receiver class before any + // WebClient is constructed. WebClient snapshots `getUserAgent()` into + // its axios headers at construction time, so this must run before + // `new WebClient(...)` below (see #1150). + const receiverTypeName = receiver + ? receiver.constructor.name + : this.socketMode + ? 'SocketModeReceiver' + : 'HTTPReceiver'; + addAppMetadata({ + name: `${packageJson.name}-${receiverTypeName}`, + version: packageJson.version, + }); + // The public WebClient instance (app.client) // Since v3.4, it can have the passed token in the case of single workspace installation. this.client = new WebClient(token, this.clientOptions); diff --git a/test/unit/App/basic.spec.ts b/test/unit/App/basic.spec.ts index b7a8ad78c..4d4b5f0b8 100644 --- a/test/unit/App/basic.spec.ts +++ b/test/unit/App/basic.spec.ts @@ -346,6 +346,23 @@ describe('App basic features', () => { // TODO: tests for providing endpoints option }); + describe('receiver-type app metadata', () => { + it('should tag the user-agent metadata with the concrete receiver class name', () => { + const addAppMetadata = sinon.fake(); + const customOverrides = mergeOverrides( + { '@slack/web-api': { addAppMetadata } }, + withSuccessfulBotUserFetchingWebClient(fakeBotId, fakeBotUserId), + ); + const MockApp = importApp(customOverrides); + new MockApp({ receiver: new FakeReceiver(), authorize: noop }); + const names: string[] = addAppMetadata.getCalls().map((call) => call.args[0].name); + assert.isTrue( + names.some((name) => name.endsWith('-FakeReceiver')), + `expected an addAppMetadata call ending in "-FakeReceiver", got ${JSON.stringify(names)}`, + ); + }); + }); + describe('#start', () => { it('should pass calls through to receiver', async () => { // Arrange From 34e907976abcbf3bb5bff4347d18641718210833 Mon Sep 17 00:00:00 2001 From: Trevin Chow Date: Tue, 28 Apr 2026 18:03:32 -0700 Subject: [PATCH 2/2] chore: add changeset for User-Agent receiver tag --- .changeset/good-ads-juggle.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/good-ads-juggle.md diff --git a/.changeset/good-ads-juggle.md b/.changeset/good-ads-juggle.md new file mode 100644 index 000000000..e6ba6f7ab --- /dev/null +++ b/.changeset/good-ads-juggle.md @@ -0,0 +1,5 @@ +--- +"@slack/bolt": minor +--- + +feat(app): tag User-Agent with concrete receiver class name (#1150)