@@ -4,13 +4,17 @@ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
4
4
import { Session } from "../session.js" ;
5
5
import logger , { LogId } from "../logger.js" ;
6
6
import { Telemetry , isTelemetryEnabled } from "../telemetry/telemetry.js" ;
7
- import { type ToolEvent } from "../telemetry/types.js" ;
7
+ import { type ToolEvent } from "../telemetry/types.js" ;
8
8
import { UserConfig } from "../config.js" ;
9
9
10
10
export type ToolArgs < Args extends ZodRawShape > = z . objectOutputType < Args , ZodNever > ;
11
11
12
12
export type OperationType = "metadata" | "read" | "create" | "delete" | "update" ;
13
13
export type ToolCategory = "mongodb" | "atlas" ;
14
+ export type ToolMetadata = {
15
+ projectId ?: string ;
16
+ orgId ?: string ;
17
+ }
14
18
15
19
export abstract class ToolBase {
16
20
protected abstract name : string ;
@@ -23,6 +27,7 @@ export abstract class ToolBase {
23
27
24
28
protected abstract argsShape : ZodRawShape ;
25
29
30
+
26
31
protected abstract execute ( ...args : Parameters < ToolCallback < typeof this . argsShape > > ) : Promise < CallToolResult > ;
27
32
28
33
constructor (
@@ -31,31 +36,6 @@ export abstract class ToolBase {
31
36
protected readonly telemetry : Telemetry
32
37
) { }
33
38
34
- /**
35
- * Creates and emits a tool telemetry event
36
- * @param startTime - Start time in milliseconds
37
- * @param result - Whether the command succeeded or failed
38
- * @param error - Optional error if the command failed
39
- */
40
- private async emitToolEvent ( startTime : number , result : CallToolResult ) : Promise < void > {
41
- if ( ! isTelemetryEnabled ( ) ) {
42
- return ;
43
- }
44
- const duration = Date . now ( ) - startTime ;
45
- const event : ToolEvent = {
46
- timestamp : new Date ( ) . toISOString ( ) ,
47
- source : "mdbmcp" ,
48
- properties : {
49
- command : this . name ,
50
- category : this . category ,
51
- component : "tool" ,
52
- duration_ms : duration ,
53
- result : result . isError ? "failure" : "success" ,
54
- } ,
55
- } ;
56
- await this . telemetry . emitEvents ( [ event ] ) ;
57
- }
58
-
59
39
public register ( server : McpServer ) : void {
60
40
if ( ! this . verifyAllowed ( ) ) {
61
41
return ;
@@ -67,12 +47,12 @@ export abstract class ToolBase {
67
47
logger . debug ( LogId . toolExecute , "tool" , `Executing ${ this . name } with args: ${ JSON . stringify ( args ) } ` ) ;
68
48
69
49
const result = await this . execute ( ...args ) ;
70
- await this . emitToolEvent ( startTime , result ) ;
50
+ await this . emitToolEvent ( startTime , result , ... args ) ;
71
51
return result ;
72
52
} catch ( error : unknown ) {
73
53
logger . error ( LogId . toolExecuteFailure , "tool" , `Error executing ${ this . name } : ${ error as string } ` ) ;
74
54
const toolResult = await this . handleError ( error , args [ 0 ] as ToolArgs < typeof this . argsShape > ) ;
75
- await this . emitToolEvent ( startTime , toolResult ) . catch ( ( ) => { } ) ;
55
+ await this . emitToolEvent ( startTime , toolResult , ... args ) . catch ( ( ) => { } ) ;
76
56
return toolResult ;
77
57
}
78
58
} ;
@@ -152,4 +132,71 @@ export abstract class ToolBase {
152
132
] ,
153
133
} ;
154
134
}
135
+
136
+
137
+ /**
138
+ *
139
+ * Resolves the tool metadata from the arguments passed to the tool
140
+ *
141
+ * @param args - The arguments passed to the tool
142
+ * @returns The tool metadata
143
+ */
144
+ protected resolveToolMetadata (
145
+ ...args : Parameters < ToolCallback < typeof this . argsShape > >
146
+ ) : ToolMetadata {
147
+ const toolMetadata : ToolMetadata = { } ;
148
+ try {
149
+ // Parse the arguments to extract project_id and org_id
150
+ const argsShape = z . object ( this . argsShape ) ;
151
+ const parsedArgs = argsShape . safeParse ( args [ 0 ] ) ;
152
+ if ( parsedArgs . success && parsedArgs . data ?. projectId ) {
153
+ toolMetadata . projectId = parsedArgs . data ?. projectId ;
154
+ }
155
+
156
+ if ( parsedArgs . success && parsedArgs . data ?. orgId ) {
157
+ toolMetadata . orgId = parsedArgs . data ?. orgId ;
158
+ }
159
+ }
160
+ catch ( error ) {
161
+ logger . info ( LogId . telmetryMetadataError , "tool" , `Error resolving tool metadata: ${ error as string } ` ) ;
162
+ }
163
+ return toolMetadata ;
164
+ }
165
+
166
+
167
+ /**
168
+ * Creates and emits a tool telemetry event
169
+ * @param startTime - Start time in milliseconds
170
+ * @param result - Whether the command succeeded or failed
171
+ * @param args - The arguments passed to the tool
172
+ */
173
+ private async emitToolEvent ( startTime : number , result : CallToolResult , ...args : Parameters < ToolCallback < typeof this . argsShape > > ) : Promise < void > {
174
+ if ( ! isTelemetryEnabled ( ) ) {
175
+ return ;
176
+ }
177
+ const duration = Date . now ( ) - startTime ;
178
+ const metadata = this . resolveToolMetadata ( ...args ) ;
179
+ const event : ToolEvent = {
180
+ timestamp : new Date ( ) . toISOString ( ) ,
181
+ source : "mdbmcp" ,
182
+ properties : {
183
+ ...this . telemetry . getCommonProperties ( ) ,
184
+ command : this . name ,
185
+ category : this . category ,
186
+ component : "tool" ,
187
+ duration_ms : duration ,
188
+ result : result . isError ? "failure" : "success" ,
189
+ } ,
190
+ } ;
191
+
192
+ if ( metadata ?. orgId ) {
193
+ event . properties . org_id = metadata . orgId ;
194
+ }
195
+
196
+ if ( metadata ?. projectId ) {
197
+ event . properties . project_id = metadata . projectId ;
198
+ }
199
+
200
+ await this . telemetry . emitEvents ( [ event ] ) ;
201
+ }
155
202
}
0 commit comments