Skip to content

Commit 2425908

Browse files
committed
First draft
1 parent eb22a84 commit 2425908

6 files changed

Lines changed: 2809 additions & 30 deletions

File tree

packages/b2c-dx-mcp/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ yarn.lock
1515
package-lock.json
1616

1717
dw.json
18+
config.json
1819

1920
export/

packages/b2c-dx-mcp/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@
8585
"@modelcontextprotocol/sdk": "^1.18.0",
8686
"@oclif/core": "^4",
8787
"@salesforce/b2c-tooling-sdk": "workspace:*",
88+
"@salesforce/telemetry": "^6.1.0",
89+
"applicationinsights": "^3.13.0",
8890
"zod": "^3.25.76"
8991
},
9092
"devDependencies": {

packages/b2c-dx-mcp/src/commands/mcp.ts

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
* | `--toolsets` | `SFCC_TOOLSETS` | Comma-separated toolsets to enable (case-insensitive) |
2020
* | `--tools` | `SFCC_TOOLS` | Comma-separated individual tools to enable (case-insensitive) |
2121
* | `--allow-non-ga-tools` | `SFCC_ALLOW_NON_GA_TOOLS` | Enable experimental/non-GA tools |
22+
* | `--no-telemetry` | `SFCC_NO_TELEMETRY` | Disable telemetry collection |
2223
*
2324
* ### MRT Flags (from MrtCommand.baseFlags)
2425
* | Flag | Env Variable | Description |
@@ -134,6 +135,7 @@ import {B2CDxMcpServer} from '../server.js';
134135
import {Services} from '../services.js';
135136
import {registerToolsets} from '../registry.js';
136137
import {TOOLSETS, type StartupFlags} from '../utils/index.js';
138+
import {Telemetry} from '../utils/telemetry.js';
137139

138140
/**
139141
* oclif Command that starts the B2C DX MCP server.
@@ -200,6 +202,11 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
200202
env: 'SFCC_ALLOW_NON_GA_TOOLS',
201203
default: false,
202204
}),
205+
'no-telemetry': Flags.boolean({
206+
description: 'Disable telemetry collection',
207+
env: 'SFCC_NO_TELEMETRY',
208+
default: false,
209+
}),
203210
};
204211

205212
/**
@@ -240,21 +247,35 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
240247
workingDirectory: this.flags['working-directory'],
241248
};
242249

243-
// TODO: Telemetry - Initialize telemetry unless disabled
244-
// if (!flags["no-telemetry"]) {
245-
// telemetry = new Telemetry({
246-
// toolsets: (startupFlags.toolsets ?? []).join(", "),
247-
// configDir,
248-
// version: this.config.version,
249-
// });
250-
// await telemetry.start();
251-
// process.stdin.on("close", (err) => {
252-
// telemetry?.sendEvent(err ? "SERVER_STOPPED_ERROR" : "SERVER_STOPPED_SUCCESS");
253-
// telemetry?.stop();
254-
// });
255-
// }
250+
// Initialize telemetry unless disabled
251+
let telemetry: Telemetry | undefined;
252+
if (!this.flags['no-telemetry']) {
253+
telemetry = new Telemetry({
254+
toolsets: (startupFlags.toolsets ?? []).join(','),
255+
version: this.config.version,
256+
});
257+
await telemetry.start();
258+
259+
// Set up process exit handlers to send SERVER_STATUS stopped events
260+
const sendStop = (signal: string): void => {
261+
telemetry?.sendEvent('SERVER_STATUS', {status: 'stopped', signal});
262+
telemetry?.stop();
263+
};
264+
265+
process.on('exit', () => sendStop('exit'));
266+
process.on('SIGINT', () => {
267+
sendStop('SIGINT');
268+
// eslint-disable-next-line n/no-process-exit
269+
process.exit(0);
270+
});
271+
process.on('SIGTERM', () => {
272+
sendStop('SIGTERM');
273+
// eslint-disable-next-line n/no-process-exit
274+
process.exit(0);
275+
});
276+
}
256277

257-
// Create MCP server
278+
// Create MCP server with telemetry
258279
const server = new B2CDxMcpServer(
259280
{
260281
name: this.config.name,
@@ -265,6 +286,7 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
265286
resources: {},
266287
tools: {},
267288
},
289+
telemetry,
268290
},
269291
);
270292

packages/b2c-dx-mcp/src/server.ts

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,27 @@ import type {ServerOptions} from '@modelcontextprotocol/sdk/server/index.js';
1515
import type {RequestHandlerExtra} from '@modelcontextprotocol/sdk/shared/protocol.js';
1616
import type {Transport} from '@modelcontextprotocol/sdk/shared/transport.js';
1717
import type {ZodRawShape} from 'zod';
18+
import type {Telemetry} from './utils/telemetry.js';
1819

1920
/**
2021
* Extended server options.
2122
*/
22-
export type B2CDxMcpServerOptions = ServerOptions;
23+
export interface B2CDxMcpServerOptions extends ServerOptions {
24+
/**
25+
* Telemetry instance for tracking server and tool events.
26+
* If not provided, telemetry is disabled.
27+
*/
28+
telemetry?: Telemetry;
29+
}
2330

2431
/**
2532
* A server implementation that extends the base MCP server.
2633
*
27-
*
2834
* @augments {McpServer}
2935
*/
3036
export class B2CDxMcpServer extends McpServer {
37+
private telemetry?: Telemetry;
38+
3139
/**
3240
* Creates a new B2CDxMcpServer instance
3341
*
@@ -36,15 +44,17 @@ export class B2CDxMcpServer extends McpServer {
3644
*/
3745
public constructor(serverInfo: Implementation, options?: B2CDxMcpServerOptions) {
3846
super(serverInfo, options);
47+
this.telemetry = options?.telemetry;
3948

4049
// Set up oninitialized handler
4150
this.server.oninitialized = (): void => {
4251
const clientInfo = this.server.getClientVersion();
4352
if (clientInfo) {
44-
// TODO: Telemetry - Add client info attributes
45-
// telemetry.addAttributes({ clientName: clientInfo.name, clientVersion: clientInfo.version });
53+
this.telemetry?.addAttributes({
54+
clientName: clientInfo.name,
55+
clientVersion: clientInfo.version,
56+
});
4657
}
47-
// TODO: Telemetry - Send SERVER_START_SUCCESS event
4858
};
4959
}
5060

@@ -68,13 +78,29 @@ export class B2CDxMcpServer extends McpServer {
6878
args: Record<string, unknown>,
6979
_extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
7080
): Promise<CallToolResult> => {
71-
// TODO: Telemetry - Track timing and send TOOL_CALLED event
72-
// const startTime = Date.now();
73-
const result = await handler(args);
74-
// const runtimeMs = Date.now() - startTime;
75-
// telemetry.sendEvent('TOOL_CALLED', { name, runtimeMs, isError: result.isError });
81+
const startTime = Date.now();
82+
try {
83+
const result = await handler(args);
84+
const runTimeMs = Date.now() - startTime;
7685

77-
return result;
86+
this.telemetry?.sendEvent('TOOL_CALLED', {
87+
toolName: name,
88+
runTimeMs,
89+
isError: result.isError ?? false,
90+
});
91+
92+
return result;
93+
} catch (error) {
94+
const runTimeMs = Date.now() - startTime;
95+
96+
this.telemetry?.sendEvent('TOOL_CALLED', {
97+
toolName: name,
98+
runTimeMs,
99+
isError: true,
100+
});
101+
102+
throw error;
103+
}
78104
};
79105

80106
// Use the new registerTool API (tool() is deprecated)
@@ -85,10 +111,22 @@ export class B2CDxMcpServer extends McpServer {
85111
* Connect to a transport.
86112
*/
87113
public override async connect(transport: Transport): Promise<void> {
88-
await super.connect(transport);
89-
if (!this.isConnected()) {
90-
// TODO: Telemetry - Send SERVER_START_ERROR event with "Server not connected"
114+
try {
115+
await super.connect(transport);
116+
if (this.isConnected()) {
117+
this.telemetry?.sendEvent('SERVER_STATUS', {status: 'started'});
118+
} else {
119+
this.telemetry?.sendEvent('SERVER_STATUS', {
120+
status: 'error',
121+
errorMessage: 'Server not connected after connect() call',
122+
});
123+
}
124+
} catch (error) {
125+
this.telemetry?.sendEvent('SERVER_STATUS', {
126+
status: 'error',
127+
errorMessage: error instanceof Error ? error.message : String(error),
128+
});
129+
throw error;
91130
}
92-
// TODO: Telemetry - wrap with try/catch to send SERVER_START_ERROR event with error details
93131
}
94132
}

0 commit comments

Comments
 (0)