From 6b73226192fa6a8e683ab2b0b659c7b4d8de9d7b Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Mon, 1 Sep 2025 18:11:51 +0200 Subject: [PATCH 1/7] chore: set telemetry app name if not set or is vscode --- src/connectionController.ts | 4 +- src/mcp/mcpConnectionManager.ts | 36 +++++++- .../suite/mcp/mcpConnectionManager.test.ts | 91 +++++++++++++++++++ 3 files changed, 127 insertions(+), 4 deletions(-) diff --git a/src/connectionController.ts b/src/connectionController.ts index 03248ea93..e0f7a58ba 100644 --- a/src/connectionController.ts +++ b/src/connectionController.ts @@ -79,6 +79,8 @@ interface NewConnectionParams { reuseExisting?: boolean; } +export const DEFAULT_TELEMETRY_APP_NAME = `${packageJSON.name} ${packageJSON.version}`; + function isOIDCAuth(connectionString: string): boolean { const authMechanismString = ( new ConnectionString(connectionString).searchParams.get('authMechanism') || @@ -481,7 +483,7 @@ export default class ConnectionController { const connectionOptions = adjustConnectionOptionsBeforeConnect({ connectionOptions: connectionInfo.connectionOptions, connectionId, - defaultAppName: `${packageJSON.name} ${packageJSON.version}`, + defaultAppName: DEFAULT_TELEMETRY_APP_NAME, notifyDeviceFlow, preferences: { forceConnectionOptions: [], diff --git a/src/mcp/mcpConnectionManager.ts b/src/mcp/mcpConnectionManager.ts index a1e9edd61..7a04acf29 100644 --- a/src/mcp/mcpConnectionManager.ts +++ b/src/mcp/mcpConnectionManager.ts @@ -11,6 +11,8 @@ import { import type { ServiceProvider } from '@mongosh/service-provider-core'; import { isAtlasStream } from 'mongodb-build-info'; import { MCPLogIds } from './mcpLogIds'; +import ConnectionString from 'mongodb-connection-string-url'; +import { DEFAULT_TELEMETRY_APP_NAME } from '../connectionController'; export interface MCPConnectParams { connectionId: string; @@ -42,13 +44,15 @@ To connect, choose a connection from MongoDB VSCode extensions's sidepanel - htt connectParams: MCPConnectParams, ): Promise { try { + const { connectionId, connectOptions, connectionString } = + this.overrideAppNameIfContainsVSCode(connectParams); const serviceProvider = await NodeDriverServiceProvider.connect( - connectParams.connectionString, - connectParams.connectOptions, + connectionString, + connectOptions, ); await serviceProvider.runCommand('admin', { hello: 1 }); this.activeConnection = { - id: connectParams.connectionId, + id: connectionId, provider: serviceProvider, }; return this.changeState('connection-success', { @@ -114,4 +118,30 @@ To connect, choose a connection from MongoDB VSCode extensions's sidepanel - htt await this.connectToVSCodeConnection(connectParams); } + + overrideAppNameIfContainsVSCode( + connectParams: MCPConnectParams, + ): MCPConnectParams { + const connectionURL = new ConnectionString(connectParams.connectionString); + const connectOptions: DevtoolsConnectOptions = { + ...connectParams.connectOptions, + }; + const searchParamAppName = connectionURL.searchParams.get('appName'); + if ( + !searchParamAppName || + searchParamAppName === DEFAULT_TELEMETRY_APP_NAME + ) { + connectionURL.searchParams.set( + 'appName', + `${DEFAULT_TELEMETRY_APP_NAME} MongoDB MCP Server`, + ); + connectOptions.appName = `${DEFAULT_TELEMETRY_APP_NAME} MongoDB MCP Server`; + } + + return { + connectionId: connectParams.connectionId, + connectionString: connectionURL.toString(), + connectOptions, + }; + } } diff --git a/src/test/suite/mcp/mcpConnectionManager.test.ts b/src/test/suite/mcp/mcpConnectionManager.test.ts index b977a67c6..60386573c 100644 --- a/src/test/suite/mcp/mcpConnectionManager.test.ts +++ b/src/test/suite/mcp/mcpConnectionManager.test.ts @@ -2,11 +2,14 @@ import sinon from 'sinon'; import { afterEach, beforeEach } from 'mocha'; import chai, { expect } from 'chai'; import chaiAsPromised from 'chai-as-promised'; +import ConnectionString from 'mongodb-connection-string-url'; import type { LoggerBase } from '@himanshusinghs/mongodb-mcp-server'; import type { ConnectionStateErrored } from '@himanshusinghs/mongodb-mcp-server'; import type { DevtoolsConnectOptions } from '@mongosh/service-provider-node-driver'; import { NodeDriverServiceProvider } from '@mongosh/service-provider-node-driver'; +import type { MCPConnectParams } from '../../../mcp/mcpConnectionManager'; import { MCPConnectionManager } from '../../../mcp/mcpConnectionManager'; +import { DEFAULT_TELEMETRY_APP_NAME } from '../../../connectionController'; chai.use(chaiAsPromised); @@ -287,4 +290,92 @@ suite('MCPConnectionManager Test Suite', function () { }); }); }); + + suite('#overrideAppNameIfContainsVSCode', function () { + let connectionString: ConnectionString; + beforeEach(() => { + connectionString = new ConnectionString( + `mongodb://localhost:27017/?appName=${DEFAULT_TELEMETRY_APP_NAME}`, + ); + }); + + suite('if appName is not set', function () { + test('should set appName attribute both in connection string and connection options', function () { + connectionString.searchParams.delete('appName'); + const connectParams: MCPConnectParams = { + connectionId: '1', + connectionString: connectionString.toString(), + connectOptions: { + productName: 'VSCode', + productDocsLink: 'https://mongodb.com', + appName: DEFAULT_TELEMETRY_APP_NAME, + }, + }; + + const expectedAppName = `${DEFAULT_TELEMETRY_APP_NAME} MongoDB MCP Server`; + connectionString.searchParams.set('appName', expectedAppName); + expect( + mcpConnectionManager.overrideAppNameIfContainsVSCode(connectParams), + ).to.deep.equal({ + connectionId: '1', + connectionString: connectionString.toString(), + connectOptions: { + productName: 'VSCode', + productDocsLink: 'https://mongodb.com', + appName: expectedAppName, + }, + }); + }); + }); + + suite('if appName is set to default vscode app name', function () { + test('should set appName attribute both in connection string and connection options', function () { + const connectParams: MCPConnectParams = { + connectionId: '1', + connectionString: connectionString.toString(), + connectOptions: { + productName: 'VSCode', + productDocsLink: 'https://mongodb.com', + appName: DEFAULT_TELEMETRY_APP_NAME, + }, + }; + + const expectedAppName = `${DEFAULT_TELEMETRY_APP_NAME} MongoDB MCP Server`; + connectionString.searchParams.set('appName', expectedAppName); + expect( + mcpConnectionManager.overrideAppNameIfContainsVSCode(connectParams), + ).to.deep.equal({ + connectionId: '1', + connectionString: connectionString.toString(), + connectOptions: { + productName: 'VSCode', + productDocsLink: 'https://mongodb.com', + appName: expectedAppName, + }, + }); + }); + }); + + suite('if appName is set to something else', function () { + test('should not override appName attribute both in connection string and connection options', function () { + connectionString.searchParams.set( + 'appName', + 'MongoDB MCP Server 0.0.0', + ); + const connectParams: MCPConnectParams = { + connectionId: '1', + connectionString: connectionString.toString(), + connectOptions: { + productName: 'VSCode', + productDocsLink: 'https://mongodb.com', + appName: DEFAULT_TELEMETRY_APP_NAME, + }, + }; + + expect( + mcpConnectionManager.overrideAppNameIfContainsVSCode(connectParams), + ).to.deep.equal(connectParams); + }); + }); + }); }); From 41c194c8e85f32bcbaf094b4e7756595a1c3bdc0 Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Thu, 4 Sep 2025 12:26:29 +0200 Subject: [PATCH 2/7] Update src/mcp/mcpConnectionManager.ts Co-authored-by: Anna Henningsen --- src/mcp/mcpConnectionManager.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mcp/mcpConnectionManager.ts b/src/mcp/mcpConnectionManager.ts index 7a04acf29..dca4b547a 100644 --- a/src/mcp/mcpConnectionManager.ts +++ b/src/mcp/mcpConnectionManager.ts @@ -126,7 +126,9 @@ To connect, choose a connection from MongoDB VSCode extensions's sidepanel - htt const connectOptions: DevtoolsConnectOptions = { ...connectParams.connectOptions, }; - const searchParamAppName = connectionURL.searchParams.get('appName'); + const searchParamAppName = connectionURL + .typedSearchParams() + .get('appName'); if ( !searchParamAppName || searchParamAppName === DEFAULT_TELEMETRY_APP_NAME From 05c38e636d3e2f1a78c1896edca06504265ec25f Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Thu, 4 Sep 2025 12:26:35 +0200 Subject: [PATCH 3/7] Update src/mcp/mcpConnectionManager.ts Co-authored-by: Anna Henningsen --- src/mcp/mcpConnectionManager.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/mcp/mcpConnectionManager.ts b/src/mcp/mcpConnectionManager.ts index dca4b547a..e36f290b0 100644 --- a/src/mcp/mcpConnectionManager.ts +++ b/src/mcp/mcpConnectionManager.ts @@ -133,10 +133,9 @@ To connect, choose a connection from MongoDB VSCode extensions's sidepanel - htt !searchParamAppName || searchParamAppName === DEFAULT_TELEMETRY_APP_NAME ) { - connectionURL.searchParams.set( - 'appName', - `${DEFAULT_TELEMETRY_APP_NAME} MongoDB MCP Server`, - ); + connectionURL + .typedSearchParams() + .set('appName', `${DEFAULT_TELEMETRY_APP_NAME} MongoDB MCP Server`); connectOptions.appName = `${DEFAULT_TELEMETRY_APP_NAME} MongoDB MCP Server`; } From 83257fae5a24dfba65833d3b9f1c9107eb6cd272 Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Thu, 4 Sep 2025 14:08:49 +0200 Subject: [PATCH 4/7] chore: add anonymous user id and connection id to appName --- src/mcp/mcpConnectionManager.ts | 26 +- src/mcp/mcpController.ts | 3 +- src/mdbExtensionController.ts | 1 + .../suite/mcp/mcpConnectionManager.test.ts | 230 ++++++++++++------ src/test/suite/mcp/mcpController.test.ts | 6 +- 5 files changed, 178 insertions(+), 88 deletions(-) diff --git a/src/mcp/mcpConnectionManager.ts b/src/mcp/mcpConnectionManager.ts index e36f290b0..d09c856bd 100644 --- a/src/mcp/mcpConnectionManager.ts +++ b/src/mcp/mcpConnectionManager.ts @@ -9,7 +9,7 @@ import { type DevtoolsConnectOptions, } from '@mongosh/service-provider-node-driver'; import type { ServiceProvider } from '@mongosh/service-provider-core'; -import { isAtlasStream } from 'mongodb-build-info'; +import { isAtlas, isAtlasStream } from 'mongodb-build-info'; import { MCPLogIds } from './mcpLogIds'; import ConnectionString from 'mongodb-connection-string-url'; import { DEFAULT_TELEMETRY_APP_NAME } from '../connectionController'; @@ -20,13 +20,18 @@ export interface MCPConnectParams { connectOptions: DevtoolsConnectOptions; } +export const MCP_SERVER_TELEMETRY_APP_NAME_SUFFIX = 'MongoDB MCP Server'; + export class MCPConnectionManager extends ConnectionManager { private activeConnection: { id: string; provider: ServiceProvider; } | null = null; - constructor(private readonly logger: LoggerBase) { + constructor( + private readonly logger: LoggerBase, + private readonly getTelemetryAnonymousId: () => string, + ) { super(); } @@ -129,14 +134,25 @@ To connect, choose a connection from MongoDB VSCode extensions's sidepanel - htt const searchParamAppName = connectionURL .typedSearchParams() .get('appName'); + if ( !searchParamAppName || - searchParamAppName === DEFAULT_TELEMETRY_APP_NAME + (searchParamAppName.startsWith(DEFAULT_TELEMETRY_APP_NAME) && + !searchParamAppName.includes(MCP_SERVER_TELEMETRY_APP_NAME_SUFFIX)) ) { + const defaultAppName = `${DEFAULT_TELEMETRY_APP_NAME} ${MCP_SERVER_TELEMETRY_APP_NAME_SUFFIX}`; + const telemetryAnonymousId = this.getTelemetryAnonymousId(); + const connectionId = connectParams.connectionId; + const appName = isAtlas(connectParams.connectionString) + ? `${defaultAppName}${ + telemetryAnonymousId ? `--${telemetryAnonymousId}` : '' + }--${connectionId}` + : defaultAppName; + connectionURL .typedSearchParams() - .set('appName', `${DEFAULT_TELEMETRY_APP_NAME} MongoDB MCP Server`); - connectOptions.appName = `${DEFAULT_TELEMETRY_APP_NAME} MongoDB MCP Server`; + .set('appName', appName); + connectOptions.appName = appName; } return { diff --git a/src/mcp/mcpController.ts b/src/mcp/mcpController.ts index 0ca90a856..dd4115f76 100644 --- a/src/mcp/mcpController.ts +++ b/src/mcp/mcpController.ts @@ -44,6 +44,7 @@ export class MCPController { constructor( private readonly context: vscode.ExtensionContext, private readonly connectionController: ConnectionController, + private readonly getTelemetryAnonymousId: () => string, ) { this.context.subscriptions.push( vscode.lm.registerMcpServerDefinitionProvider('mongodb', { @@ -86,7 +87,7 @@ export class MCPController { logger, }) => { const connectionManager = (this.mcpConnectionManager = - new MCPConnectionManager(logger)); + new MCPConnectionManager(logger, this.getTelemetryAnonymousId)); await this.switchConnectionManagerToCurrentConnection(); return connectionManager; }; diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index 329935105..276db31be 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -170,6 +170,7 @@ export default class MDBExtensionController implements vscode.Disposable { this._mcpController = new MCPController( context, this._connectionController, + () => this._connectionStorage.getUserAnonymousId(), ); } diff --git a/src/test/suite/mcp/mcpConnectionManager.test.ts b/src/test/suite/mcp/mcpConnectionManager.test.ts index 60386573c..99ac484c6 100644 --- a/src/test/suite/mcp/mcpConnectionManager.test.ts +++ b/src/test/suite/mcp/mcpConnectionManager.test.ts @@ -8,21 +8,27 @@ import type { ConnectionStateErrored } from '@himanshusinghs/mongodb-mcp-server' import type { DevtoolsConnectOptions } from '@mongosh/service-provider-node-driver'; import { NodeDriverServiceProvider } from '@mongosh/service-provider-node-driver'; import type { MCPConnectParams } from '../../../mcp/mcpConnectionManager'; -import { MCPConnectionManager } from '../../../mcp/mcpConnectionManager'; +import { + MCP_SERVER_TELEMETRY_APP_NAME_SUFFIX, + MCPConnectionManager, +} from '../../../mcp/mcpConnectionManager'; import { DEFAULT_TELEMETRY_APP_NAME } from '../../../connectionController'; chai.use(chaiAsPromised); const sandbox = sinon.createSandbox(); -suite('MCPConnectionManager Test Suite', function () { +suite.only('MCPConnectionManager Test Suite', function () { let mcpConnectionManager: MCPConnectionManager; let fakeServiceProvider: NodeDriverServiceProvider; beforeEach(() => { - mcpConnectionManager = new MCPConnectionManager({ - error: () => {}, - warning: () => {}, - } as unknown as LoggerBase); + mcpConnectionManager = new MCPConnectionManager( + { + error: () => {}, + warning: () => {}, + } as unknown as LoggerBase, + () => '1FOO', + ); fakeServiceProvider = { runCommand: (() => Promise.resolve({})) as NodeDriverServiceProvider['runCommand'], @@ -292,90 +298,152 @@ suite('MCPConnectionManager Test Suite', function () { }); suite('#overrideAppNameIfContainsVSCode', function () { - let connectionString: ConnectionString; + let localConnectionURL: ConnectionString; + let atlasConnectionURL: ConnectionString; beforeEach(() => { - connectionString = new ConnectionString( + localConnectionURL = new ConnectionString( `mongodb://localhost:27017/?appName=${DEFAULT_TELEMETRY_APP_NAME}`, ); + atlasConnectionURL = new ConnectionString( + 'mongodb://cat-data-sets.cats.mongodb.net/admin', + ); }); - suite('if appName is not set', function () { - test('should set appName attribute both in connection string and connection options', function () { - connectionString.searchParams.delete('appName'); - const connectParams: MCPConnectParams = { - connectionId: '1', - connectionString: connectionString.toString(), - connectOptions: { - productName: 'VSCode', - productDocsLink: 'https://mongodb.com', - appName: DEFAULT_TELEMETRY_APP_NAME, - }, - }; - - const expectedAppName = `${DEFAULT_TELEMETRY_APP_NAME} MongoDB MCP Server`; - connectionString.searchParams.set('appName', expectedAppName); - expect( - mcpConnectionManager.overrideAppNameIfContainsVSCode(connectParams), - ).to.deep.equal({ - connectionId: '1', - connectionString: connectionString.toString(), - connectOptions: { - productName: 'VSCode', - productDocsLink: 'https://mongodb.com', - appName: expectedAppName, - }, + for (const { + suiteName, + getConnectionURL, + getConnectionManager, + expectedAppName, + expectedString, + } of [ + { + suiteName: 'when connection string is not atlas', + getConnectionURL: (): ConnectionString => localConnectionURL.clone(), + getConnectionManager: (): MCPConnectionManager => mcpConnectionManager, + expectedAppName: `${DEFAULT_TELEMETRY_APP_NAME} ${MCP_SERVER_TELEMETRY_APP_NAME_SUFFIX}`, + expectedString: (): string => { + const url = localConnectionURL.clone(); + const expectedAppName = `${DEFAULT_TELEMETRY_APP_NAME} ${MCP_SERVER_TELEMETRY_APP_NAME_SUFFIX}`; + url.searchParams.set('appName', expectedAppName); + return url.toString(); + }, + }, + { + suiteName: 'when connection string is atlas', + getConnectionURL: (): ConnectionString => atlasConnectionURL.clone(), + getConnectionManager: (): MCPConnectionManager => mcpConnectionManager, + expectedAppName: `${DEFAULT_TELEMETRY_APP_NAME} ${MCP_SERVER_TELEMETRY_APP_NAME_SUFFIX}--1FOO--1`, + expectedString: (): string => { + const url = atlasConnectionURL.clone(); + const expectedAppName = `${DEFAULT_TELEMETRY_APP_NAME} ${MCP_SERVER_TELEMETRY_APP_NAME_SUFFIX}--1FOO--1`; + url.searchParams.set('appName', expectedAppName); + return url.toString(); + }, + }, + ]) { + suite(suiteName, function () { + suite('and appName is not set', function () { + test('should set appName attribute both in connection string and connection options', function () { + const url = getConnectionURL(); + url.searchParams.delete('appName'); + const connectParams: MCPConnectParams = { + connectionId: '1', + connectionString: url.toString(), + connectOptions: { + productName: 'VSCode', + productDocsLink: 'https://mongodb.com', + appName: DEFAULT_TELEMETRY_APP_NAME, + }, + }; + + expect( + getConnectionManager().overrideAppNameIfContainsVSCode( + connectParams, + ), + ).to.deep.equal({ + connectionId: '1', + connectionString: expectedString(), + connectOptions: { + productName: 'VSCode', + productDocsLink: 'https://mongodb.com', + appName: expectedAppName, + }, + }); + }); }); - }); - }); - suite('if appName is set to default vscode app name', function () { - test('should set appName attribute both in connection string and connection options', function () { - const connectParams: MCPConnectParams = { - connectionId: '1', - connectionString: connectionString.toString(), - connectOptions: { - productName: 'VSCode', - productDocsLink: 'https://mongodb.com', - appName: DEFAULT_TELEMETRY_APP_NAME, - }, - }; - - const expectedAppName = `${DEFAULT_TELEMETRY_APP_NAME} MongoDB MCP Server`; - connectionString.searchParams.set('appName', expectedAppName); - expect( - mcpConnectionManager.overrideAppNameIfContainsVSCode(connectParams), - ).to.deep.equal({ - connectionId: '1', - connectionString: connectionString.toString(), - connectOptions: { - productName: 'VSCode', - productDocsLink: 'https://mongodb.com', - appName: expectedAppName, - }, + suite('if appName is set to default vscode app name', function () { + test('should set appName attribute both in connection string and connection options', function () { + const url = getConnectionURL(); + const connectParams: MCPConnectParams = { + connectionId: '1', + connectionString: url.toString(), + connectOptions: { + productName: 'VSCode', + productDocsLink: 'https://mongodb.com', + appName: DEFAULT_TELEMETRY_APP_NAME, + }, + }; + + expect( + getConnectionManager().overrideAppNameIfContainsVSCode( + connectParams, + ), + ).to.deep.equal({ + connectionId: '1', + connectionString: expectedString(), + connectOptions: { + productName: 'VSCode', + productDocsLink: 'https://mongodb.com', + appName: expectedAppName, + }, + }); + }); }); - }); - }); - - suite('if appName is set to something else', function () { - test('should not override appName attribute both in connection string and connection options', function () { - connectionString.searchParams.set( - 'appName', - 'MongoDB MCP Server 0.0.0', - ); - const connectParams: MCPConnectParams = { - connectionId: '1', - connectionString: connectionString.toString(), - connectOptions: { - productName: 'VSCode', - productDocsLink: 'https://mongodb.com', - appName: DEFAULT_TELEMETRY_APP_NAME, - }, - }; - expect( - mcpConnectionManager.overrideAppNameIfContainsVSCode(connectParams), - ).to.deep.equal(connectParams); + suite('if appName is set to something else', function () { + test('should not override appName attribute both in connection string and connection options', function () { + const url = getConnectionURL(); + url.searchParams.set('appName', 'MongoDB MCP Server 0.0.0'); + const connectParams: MCPConnectParams = { + connectionId: '1', + connectionString: url.toString(), + connectOptions: { + productName: 'VSCode', + productDocsLink: 'https://mongodb.com', + appName: DEFAULT_TELEMETRY_APP_NAME, + }, + }; + + expect( + getConnectionManager().overrideAppNameIfContainsVSCode( + connectParams, + ), + ).to.deep.equal(connectParams); + + // Now for the case when appName is already set to expected MCP server appname + url.searchParams.set( + 'appName', + `${DEFAULT_TELEMETRY_APP_NAME} ${MCP_SERVER_TELEMETRY_APP_NAME_SUFFIX}`, + ); + const nextConnectParams: MCPConnectParams = { + connectionId: '1', + connectionString: url.toString(), + connectOptions: { + productName: 'VSCode', + productDocsLink: 'https://mongodb.com', + appName: DEFAULT_TELEMETRY_APP_NAME, + }, + }; + + expect( + getConnectionManager().overrideAppNameIfContainsVSCode( + nextConnectParams, + ), + ).to.deep.equal(nextConnectParams); + }); + }); }); - }); + } }); }); diff --git a/src/test/suite/mcp/mcpController.test.ts b/src/test/suite/mcp/mcpController.test.ts index 02e3c20b4..8ad5a4514 100644 --- a/src/test/suite/mcp/mcpController.test.ts +++ b/src/test/suite/mcp/mcpController.test.ts @@ -33,7 +33,11 @@ suite('MCPController test suite', function () { telemetryService: testTelemetryService, }); - mcpController = new MCPController(extensionContext, connectionController); + mcpController = new MCPController( + extensionContext, + connectionController, + () => '1FOO', + ); }); afterEach(async () => { From 20e0bfa60447a1895f90ad0d66b89fbbb96c197e Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Thu, 4 Sep 2025 14:30:30 +0200 Subject: [PATCH 5/7] chore: no exclusive tests --- src/test/suite/mcp/mcpConnectionManager.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/suite/mcp/mcpConnectionManager.test.ts b/src/test/suite/mcp/mcpConnectionManager.test.ts index 99ac484c6..753dec9a3 100644 --- a/src/test/suite/mcp/mcpConnectionManager.test.ts +++ b/src/test/suite/mcp/mcpConnectionManager.test.ts @@ -17,7 +17,7 @@ import { DEFAULT_TELEMETRY_APP_NAME } from '../../../connectionController'; chai.use(chaiAsPromised); const sandbox = sinon.createSandbox(); -suite.only('MCPConnectionManager Test Suite', function () { +suite('MCPConnectionManager Test Suite', function () { let mcpConnectionManager: MCPConnectionManager; let fakeServiceProvider: NodeDriverServiceProvider; From cb2e930f683a9bfea14165f0195a492ad2471654 Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Thu, 4 Sep 2025 16:30:43 +0200 Subject: [PATCH 6/7] Update src/mcp/mcpConnectionManager.ts Co-authored-by: Gagik Amaryan --- src/mcp/mcpConnectionManager.ts | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/mcp/mcpConnectionManager.ts b/src/mcp/mcpConnectionManager.ts index d09c856bd..aa09f98d5 100644 --- a/src/mcp/mcpConnectionManager.ts +++ b/src/mcp/mcpConnectionManager.ts @@ -131,28 +131,26 @@ To connect, choose a connection from MongoDB VSCode extensions's sidepanel - htt const connectOptions: DevtoolsConnectOptions = { ...connectParams.connectOptions, }; - const searchParamAppName = connectionURL - .typedSearchParams() - .get('appName'); + const searchParams = connectionURL + .typedSearchParams(); + const appName = searchParams.get('appName') if ( - !searchParamAppName || - (searchParamAppName.startsWith(DEFAULT_TELEMETRY_APP_NAME) && - !searchParamAppName.includes(MCP_SERVER_TELEMETRY_APP_NAME_SUFFIX)) + !appName || + (appName.startsWith(DEFAULT_TELEMETRY_APP_NAME) && + !appName.includes(MCP_SERVER_TELEMETRY_APP_NAME_SUFFIX)) ) { const defaultAppName = `${DEFAULT_TELEMETRY_APP_NAME} ${MCP_SERVER_TELEMETRY_APP_NAME_SUFFIX}`; const telemetryAnonymousId = this.getTelemetryAnonymousId(); const connectionId = connectParams.connectionId; - const appName = isAtlas(connectParams.connectionString) + const newAppName = isAtlas(connectParams.connectionString) ? `${defaultAppName}${ telemetryAnonymousId ? `--${telemetryAnonymousId}` : '' }--${connectionId}` : defaultAppName; - connectionURL - .typedSearchParams() - .set('appName', appName); - connectOptions.appName = appName; + searchParams.set('appName', newAppName); + connectOptions.appName = newAppName; } return { From b319745ad9ae889caf45cc5a683c33331c727292 Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Thu, 4 Sep 2025 16:41:51 +0200 Subject: [PATCH 7/7] chore: PR feedback --- src/mcp/mcpConnectionManager.ts | 26 ++++++++++------- src/mcp/mcpController.ts | 29 +++++++++++++++---- src/mdbExtensionController.ts | 9 +++--- .../suite/mcp/mcpConnectionManager.test.ts | 27 +++++------------ src/test/suite/mcp/mcpController.test.ts | 10 +++---- 5 files changed, 56 insertions(+), 45 deletions(-) diff --git a/src/mcp/mcpConnectionManager.ts b/src/mcp/mcpConnectionManager.ts index aa09f98d5..1020e88b8 100644 --- a/src/mcp/mcpConnectionManager.ts +++ b/src/mcp/mcpConnectionManager.ts @@ -22,17 +22,23 @@ export interface MCPConnectParams { export const MCP_SERVER_TELEMETRY_APP_NAME_SUFFIX = 'MongoDB MCP Server'; +type MCPConnectionManagerConfig = { + logger: LoggerBase; + getTelemetryAnonymousId: () => string; +}; + export class MCPConnectionManager extends ConnectionManager { + private logger: LoggerBase; + private getTelemetryAnonymousId: () => string; private activeConnection: { id: string; provider: ServiceProvider; } | null = null; - constructor( - private readonly logger: LoggerBase, - private readonly getTelemetryAnonymousId: () => string, - ) { + constructor({ logger, getTelemetryAnonymousId }: MCPConnectionManagerConfig) { super(); + this.logger = logger; + this.getTelemetryAnonymousId = getTelemetryAnonymousId; } connect(): Promise { @@ -50,7 +56,7 @@ To connect, choose a connection from MongoDB VSCode extensions's sidepanel - htt ): Promise { try { const { connectionId, connectOptions, connectionString } = - this.overrideAppNameIfContainsVSCode(connectParams); + this.overridePresetAppName(connectParams); const serviceProvider = await NodeDriverServiceProvider.connect( connectionString, connectOptions, @@ -124,16 +130,14 @@ To connect, choose a connection from MongoDB VSCode extensions's sidepanel - htt await this.connectToVSCodeConnection(connectParams); } - overrideAppNameIfContainsVSCode( - connectParams: MCPConnectParams, - ): MCPConnectParams { + overridePresetAppName(connectParams: MCPConnectParams): MCPConnectParams { const connectionURL = new ConnectionString(connectParams.connectionString); const connectOptions: DevtoolsConnectOptions = { ...connectParams.connectOptions, }; - const searchParams = connectionURL - .typedSearchParams(); - const appName = searchParams.get('appName') + const searchParams = + connectionURL.typedSearchParams(); + const appName = searchParams.get('appName'); if ( !appName || diff --git a/src/mcp/mcpController.ts b/src/mcp/mcpController.ts index dd4115f76..0e264c628 100644 --- a/src/mcp/mcpController.ts +++ b/src/mcp/mcpController.ts @@ -36,16 +36,30 @@ export type MCPServerInfo = { runner: StreamableHttpRunner; headers: Record; }; + +type MCPControllerConfig = { + context: vscode.ExtensionContext; + connectionController: ConnectionController; + getTelemetryAnonymousId: () => string; +}; + export class MCPController { + private context: vscode.ExtensionContext; + private connectionController: ConnectionController; + private getTelemetryAnonymousId: () => string; + private didChangeEmitter = new vscode.EventEmitter(); private server?: MCPServerInfo; private mcpConnectionManager?: MCPConnectionManager; - constructor( - private readonly context: vscode.ExtensionContext, - private readonly connectionController: ConnectionController, - private readonly getTelemetryAnonymousId: () => string, - ) { + constructor({ + context, + connectionController, + getTelemetryAnonymousId, + }: MCPControllerConfig) { + this.context = context; + this.connectionController = connectionController; + this.getTelemetryAnonymousId = getTelemetryAnonymousId; this.context.subscriptions.push( vscode.lm.registerMcpServerDefinitionProvider('mongodb', { onDidChangeMcpServerDefinitions: this.didChangeEmitter.event, @@ -87,7 +101,10 @@ export class MCPController { logger, }) => { const connectionManager = (this.mcpConnectionManager = - new MCPConnectionManager(logger, this.getTelemetryAnonymousId)); + new MCPConnectionManager({ + logger, + getTelemetryAnonymousId: this.getTelemetryAnonymousId, + })); await this.switchConnectionManagerToCurrentConnection(); return connectionManager; }; diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index 276db31be..d633f7eb6 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -167,11 +167,12 @@ export default class MDBExtensionController implements vscode.Disposable { telemetryService: this._telemetryService, }); this._editorsController.registerProviders(); - this._mcpController = new MCPController( + this._mcpController = new MCPController({ context, - this._connectionController, - () => this._connectionStorage.getUserAnonymousId(), - ); + connectionController: this._connectionController, + getTelemetryAnonymousId: (): string => + this._connectionStorage.getUserAnonymousId(), + }); } subscribeToConfigurationChanges(): void { diff --git a/src/test/suite/mcp/mcpConnectionManager.test.ts b/src/test/suite/mcp/mcpConnectionManager.test.ts index 753dec9a3..e753d7cc4 100644 --- a/src/test/suite/mcp/mcpConnectionManager.test.ts +++ b/src/test/suite/mcp/mcpConnectionManager.test.ts @@ -22,13 +22,10 @@ suite('MCPConnectionManager Test Suite', function () { let fakeServiceProvider: NodeDriverServiceProvider; beforeEach(() => { - mcpConnectionManager = new MCPConnectionManager( - { - error: () => {}, - warning: () => {}, - } as unknown as LoggerBase, - () => '1FOO', - ); + mcpConnectionManager = new MCPConnectionManager({ + logger: { error: () => {}, warning: () => {} } as unknown as LoggerBase, + getTelemetryAnonymousId: (): string => '1FOO', + }); fakeServiceProvider = { runCommand: (() => Promise.resolve({})) as NodeDriverServiceProvider['runCommand'], @@ -357,9 +354,7 @@ suite('MCPConnectionManager Test Suite', function () { }; expect( - getConnectionManager().overrideAppNameIfContainsVSCode( - connectParams, - ), + getConnectionManager().overridePresetAppName(connectParams), ).to.deep.equal({ connectionId: '1', connectionString: expectedString(), @@ -386,9 +381,7 @@ suite('MCPConnectionManager Test Suite', function () { }; expect( - getConnectionManager().overrideAppNameIfContainsVSCode( - connectParams, - ), + getConnectionManager().overridePresetAppName(connectParams), ).to.deep.equal({ connectionId: '1', connectionString: expectedString(), @@ -416,9 +409,7 @@ suite('MCPConnectionManager Test Suite', function () { }; expect( - getConnectionManager().overrideAppNameIfContainsVSCode( - connectParams, - ), + getConnectionManager().overridePresetAppName(connectParams), ).to.deep.equal(connectParams); // Now for the case when appName is already set to expected MCP server appname @@ -437,9 +428,7 @@ suite('MCPConnectionManager Test Suite', function () { }; expect( - getConnectionManager().overrideAppNameIfContainsVSCode( - nextConnectParams, - ), + getConnectionManager().overridePresetAppName(nextConnectParams), ).to.deep.equal(nextConnectParams); }); }); diff --git a/src/test/suite/mcp/mcpController.test.ts b/src/test/suite/mcp/mcpController.test.ts index 8ad5a4514..e63f3d95b 100644 --- a/src/test/suite/mcp/mcpController.test.ts +++ b/src/test/suite/mcp/mcpController.test.ts @@ -33,11 +33,11 @@ suite('MCPController test suite', function () { telemetryService: testTelemetryService, }); - mcpController = new MCPController( - extensionContext, - connectionController, - () => '1FOO', - ); + mcpController = new MCPController({ + context: extensionContext, + connectionController: connectionController, + getTelemetryAnonymousId: (): string => '1FOO', + }); }); afterEach(async () => {