Skip to content

Commit 1f68c3e

Browse files
authored
chore: expose a hook to specify telemetry hosting mode MCP-166 (#501)
1 parent 6bfaad4 commit 1f68c3e

File tree

5 files changed

+247
-131
lines changed

5 files changed

+247
-131
lines changed

src/telemetry/telemetry.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,21 @@ export class Telemetry {
3535
userConfig: UserConfig,
3636
deviceId: DeviceId,
3737
{
38-
commonProperties = { ...MACHINE_METADATA },
38+
commonProperties = {},
3939
eventCache = EventCache.getInstance(),
4040
}: {
41+
commonProperties?: Partial<CommonProperties>;
4142
eventCache?: EventCache;
42-
commonProperties?: CommonProperties;
4343
} = {}
4444
): Telemetry {
45-
const instance = new Telemetry(session, userConfig, commonProperties, { eventCache, deviceId });
45+
const mergedProperties = {
46+
...MACHINE_METADATA,
47+
...commonProperties,
48+
};
49+
const instance = new Telemetry(session, userConfig, mergedProperties, {
50+
eventCache,
51+
deviceId,
52+
});
4653

4754
void instance.setup();
4855
return instance;

src/telemetry/types.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,24 +53,85 @@ export type ServerEvent = TelemetryEvent<ServerEventProperties>;
5353
* Interface for static properties, they can be fetched once and reused.
5454
*/
5555
export type CommonStaticProperties = {
56+
/**
57+
* The version of the MCP server (as read from package.json).
58+
*/
5659
mcp_server_version: string;
60+
61+
/**
62+
* The name of the MCP server (as read from package.json).
63+
*/
5764
mcp_server_name: string;
65+
66+
/**
67+
* The platform/OS the MCP server is running on.
68+
*/
5869
platform: string;
70+
71+
/**
72+
* The architecture of the OS the server is running on.
73+
*/
5974
arch: string;
75+
76+
/**
77+
* Same as platform.
78+
*/
6079
os_type: string;
80+
81+
/**
82+
* The version of the OS the server is running on.
83+
*/
6184
os_version?: string;
6285
};
6386

6487
/**
6588
* Common properties for all events that might change.
6689
*/
6790
export type CommonProperties = {
91+
/**
92+
* The device id - will be populated with the machine id when it resolves.
93+
*/
6894
device_id?: string;
95+
96+
/**
97+
* A boolean indicating whether the server is running in a container environment.
98+
*/
6999
is_container_env?: boolean;
100+
101+
/**
102+
* The version of the MCP client as reported by the client on session establishment.
103+
*/
70104
mcp_client_version?: string;
105+
106+
/**
107+
* The name of the MCP client as reported by the client on session establishment.
108+
*/
71109
mcp_client_name?: string;
110+
111+
/**
112+
* The transport protocol used by the MCP server.
113+
*/
72114
transport?: "stdio" | "http";
115+
116+
/**
117+
* A boolean indicating whether Atlas credentials are configured.
118+
*/
73119
config_atlas_auth?: TelemetryBoolSet;
120+
121+
/**
122+
* A boolean indicating whether a connection string is configured.
123+
*/
74124
config_connection_string?: TelemetryBoolSet;
125+
126+
/**
127+
* The randomly generated session id.
128+
*/
75129
session_id?: string;
130+
131+
/**
132+
* The way the MCP server is hosted - e.g. standalone for a server running independently or
133+
* "vscode" if embedded in the VSCode extension. This field should be populated by the hosting
134+
* application to differentiate events coming from an MCP server it's hosting.
135+
*/
136+
hosting_mode?: string;
76137
} & CommonStaticProperties;

src/transports/base.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ import {
1414
type ConnectionErrorHandler,
1515
connectionErrorHandler as defaultConnectionErrorHandler,
1616
} from "../common/connectionErrorHandler.js";
17+
import type { CommonProperties } from "../telemetry/types.js";
1718

1819
export type TransportRunnerConfig = {
1920
userConfig: UserConfig;
2021
createConnectionManager?: ConnectionManagerFactoryFn;
2122
connectionErrorHandler?: ConnectionErrorHandler;
2223
additionalLoggers?: LoggerBase[];
24+
telemetryProperties?: Partial<CommonProperties>;
2325
};
2426

2527
export abstract class TransportRunnerBase {
@@ -28,16 +30,19 @@ export abstract class TransportRunnerBase {
2830
protected readonly userConfig: UserConfig;
2931
private readonly createConnectionManager: ConnectionManagerFactoryFn;
3032
private readonly connectionErrorHandler: ConnectionErrorHandler;
33+
private readonly telemetryProperties: Partial<CommonProperties>;
3134

3235
protected constructor({
3336
userConfig,
3437
createConnectionManager = createMCPConnectionManager,
3538
connectionErrorHandler = defaultConnectionErrorHandler,
3639
additionalLoggers = [],
40+
telemetryProperties = {},
3741
}: TransportRunnerConfig) {
3842
this.userConfig = userConfig;
3943
this.createConnectionManager = createConnectionManager;
4044
this.connectionErrorHandler = connectionErrorHandler;
45+
this.telemetryProperties = telemetryProperties;
4146
const loggers: LoggerBase[] = [...additionalLoggers];
4247
if (this.userConfig.loggers.includes("stderr")) {
4348
loggers.push(new ConsoleLogger(Keychain.root));
@@ -85,7 +90,9 @@ export abstract class TransportRunnerBase {
8590
keychain: Keychain.root,
8691
});
8792

88-
const telemetry = Telemetry.create(session, this.userConfig, this.deviceId);
93+
const telemetry = Telemetry.create(session, this.userConfig, this.deviceId, {
94+
commonProperties: this.telemetryProperties,
95+
});
8996

9097
const result = new Server({
9198
mcpServer,

tests/integration/transports/streamableHttp.test.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { StreamableHttpRunner } from "../../../src/transports/streamableHttp.js";
22
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
33
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
4-
import { describe, expect, it, beforeAll, afterAll, beforeEach } from "vitest";
4+
import { describe, expect, it, beforeAll, afterAll, beforeEach, afterEach } from "vitest";
55
import { config } from "../../../src/common/config.js";
66
import type { LoggerType, LogLevel, LogPayload } from "../../../src/common/logger.js";
77
import { LoggerBase, LogId } from "../../../src/common/logger.js";
@@ -159,4 +159,26 @@ describe("StreamableHttpRunner", () => {
159159
expect(serverStartedMessage?.level).toBe("info");
160160
});
161161
});
162+
163+
describe("with telemetry properties", () => {
164+
afterEach(async () => {
165+
await runner.close();
166+
config.telemetry = oldTelemetry;
167+
config.loggers = oldLoggers;
168+
config.httpHeaders = {};
169+
});
170+
171+
it("merges them with the base properties", async () => {
172+
config.telemetry = "enabled";
173+
runner = new StreamableHttpRunner({
174+
userConfig: config,
175+
telemetryProperties: { hosting_mode: "vscode-extension" },
176+
});
177+
await runner.start();
178+
179+
const server = await runner["setupServer"]();
180+
const properties = server["telemetry"].getCommonProperties();
181+
expect(properties.hosting_mode).toBe("vscode-extension");
182+
});
183+
});
162184
});

0 commit comments

Comments
 (0)