Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

chore: expose a hook to specify telemetry hosting mode MCP-166 #501

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
nirinchev merged 4 commits into main from ni/telemetry-hosting-mode
Sep 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/telemetry/telemetry.ts
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,21 @@ export class Telemetry {
userConfig: UserConfig,
deviceId: DeviceId,
{
commonProperties = { ...MACHINE_METADATA },
commonProperties = {},
eventCache = EventCache.getInstance(),
}: {
commonProperties?: Partial<CommonProperties>;
eventCache?: EventCache;
commonProperties?: CommonProperties;
} = {}
): Telemetry {
Copy link
Preview

Copilot AI Sep 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment will throw a TypeError if commonProperties is undefined. The code should check if commonProperties exists or provide a default object before assignment.

Copilot uses AI. Check for mistakes.

const instance = new Telemetry(session, userConfig, commonProperties, { eventCache, deviceId });
const mergedProperties = {
...MACHINE_METADATA,
...commonProperties,
};
const instance = new Telemetry(session, userConfig, mergedProperties, {
eventCache,
deviceId,
});

void instance.setup();
return instance;
Expand Down
61 changes: 61 additions & 0 deletions src/telemetry/types.ts
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -53,24 +53,85 @@ export type ServerEvent = TelemetryEvent<ServerEventProperties>;
* Interface for static properties, they can be fetched once and reused.
*/
export type CommonStaticProperties = {
/**
* The version of the MCP server (as read from package.json).
*/
mcp_server_version: string;

/**
* The name of the MCP server (as read from package.json).
*/
mcp_server_name: string;

/**
* The platform/OS the MCP server is running on.
*/
platform: string;

/**
* The architecture of the OS the server is running on.
*/
arch: string;

/**
* Same as platform.
*/
os_type: string;

/**
* The version of the OS the server is running on.
*/
os_version?: string;
};

/**
* Common properties for all events that might change.
*/
export type CommonProperties = {
/**
* The device id - will be populated with the machine id when it resolves.
*/
device_id?: string;

/**
* A boolean indicating whether the server is running in a container environment.
*/
is_container_env?: boolean;

/**
* The version of the MCP client as reported by the client on session establishment.
*/
mcp_client_version?: string;

/**
* The name of the MCP client as reported by the client on session establishment.
*/
mcp_client_name?: string;

/**
* The transport protocol used by the MCP server.
*/
transport?: "stdio" | "http";

/**
* A boolean indicating whether Atlas credentials are configured.
*/
config_atlas_auth?: TelemetryBoolSet;

/**
* A boolean indicating whether a connection string is configured.
*/
config_connection_string?: TelemetryBoolSet;

/**
* The randomly generated session id.
*/
session_id?: string;

/**
* The way the MCP server is hosted - e.g. standalone for a server running independently or
* "vscode" if embedded in the VSCode extension. This field should be populated by the hosting
* application to differentiate events coming from an MCP server it's hosting.
*/
hosting_mode?: string;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we set the accepted strings here?

Copy link
Collaborator Author

@nirinchev nirinchev Sep 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MCP server does not know them at this point - essentially, I want this to be a field populated by the application that hosts the server and we don't know beforehand what these applications are.

himanshusinghs and blva reacted with thumbs up emoji
} & CommonStaticProperties;
9 changes: 8 additions & 1 deletion src/transports/base.ts
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ import {
type ConnectionErrorHandler,
connectionErrorHandler as defaultConnectionErrorHandler,
} from "../common/connectionErrorHandler.js";
import type { CommonProperties } from "../telemetry/types.js";

export type TransportRunnerConfig = {
userConfig: UserConfig;
createConnectionManager?: ConnectionManagerFactoryFn;
connectionErrorHandler?: ConnectionErrorHandler;
additionalLoggers?: LoggerBase[];
telemetryProperties?: Partial<CommonProperties>;
};

export abstract class TransportRunnerBase {
Expand All @@ -27,16 +29,19 @@ export abstract class TransportRunnerBase {
protected readonly userConfig: UserConfig;
private readonly createConnectionManager: ConnectionManagerFactoryFn;
private readonly connectionErrorHandler: ConnectionErrorHandler;
private readonly telemetryProperties: Partial<CommonProperties>;

protected constructor({
userConfig,
createConnectionManager = createMCPConnectionManager,
connectionErrorHandler = defaultConnectionErrorHandler,
additionalLoggers = [],
telemetryProperties = {},
}: TransportRunnerConfig) {
this.userConfig = userConfig;
this.createConnectionManager = createConnectionManager;
this.connectionErrorHandler = connectionErrorHandler;
this.telemetryProperties = telemetryProperties;
const loggers: LoggerBase[] = [...additionalLoggers];
if (this.userConfig.loggers.includes("stderr")) {
loggers.push(new ConsoleLogger());
Expand Down Expand Up @@ -79,7 +84,9 @@ export abstract class TransportRunnerBase {
connectionManager,
});

const telemetry = Telemetry.create(session, this.userConfig, this.deviceId);
const telemetry = Telemetry.create(session, this.userConfig, this.deviceId, {
commonProperties: this.telemetryProperties,
});

const result = new Server({
mcpServer,
Expand Down
24 changes: 23 additions & 1 deletion tests/integration/transports/streamableHttp.test.ts
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { StreamableHttpRunner } from "../../../src/transports/streamableHttp.js";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { describe, expect, it, beforeAll, afterAll, beforeEach } from "vitest";
import { describe, expect, it, beforeAll, afterAll, beforeEach, afterEach } from "vitest";
import { config } from "../../../src/common/config.js";
import type { LoggerType, LogLevel, LogPayload } from "../../../src/common/logger.js";
import { LoggerBase, LogId } from "../../../src/common/logger.js";
Expand Down Expand Up @@ -158,4 +158,26 @@ describe("StreamableHttpRunner", () => {
expect(serverStartedMessage?.level).toBe("info");
});
});

describe("with telemetry properties", () => {
afterEach(async () => {
await runner.close();
config.telemetry = oldTelemetry;
config.loggers = oldLoggers;
config.httpHeaders = {};
});

it("merges them with the base properties", async () => {
config.telemetry = "enabled";
runner = new StreamableHttpRunner({
userConfig: config,
telemetryProperties: { hosting_mode: "vscode-extension" },
});
await runner.start();

const server = await runner["setupServer"]();
const properties = server["telemetry"].getCommonProperties();
expect(properties.hosting_mode).toBe("vscode-extension");
});
});
});
Loading
Loading

AltStyle によって変換されたページ (->オリジナル) /