@@ -2,13 +2,11 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
2
import { wrapMcpServerWithSentry } from '../../src/mcp-server' ;
3
3
import * as tracingModule from '../../src/tracing' ;
4
4
5
- vi . mock ( '../../src/tracing' ) ;
6
-
7
5
describe ( 'wrapMcpServerWithSentry' , ( ) => {
6
+ const startSpanSpy = vi . spyOn ( tracingModule , 'startSpan' ) ;
7
+
8
8
beforeEach ( ( ) => {
9
9
vi . clearAllMocks ( ) ;
10
- // @ts -expect-error mocking span is annoying
11
- vi . mocked ( tracingModule . startSpan ) . mockImplementation ( ( _ , cb ) => cb ( ) ) ;
12
10
} ) ;
13
11
14
12
it ( 'should return the same instance (modified) if it is a valid MCP server instance' , ( ) => {
@@ -33,7 +31,7 @@ describe('wrapMcpServerWithSentry', () => {
33
31
expect ( result . tool ) . toBe ( invalidMcpServer . tool ) ;
34
32
35
33
// No calls to startSpan
36
- expect ( tracingModule . startSpan ) . not . toHaveBeenCalled ( ) ;
34
+ expect ( startSpanSpy ) . not . toHaveBeenCalled ( ) ;
37
35
} ) ;
38
36
39
37
it ( 'should not wrap the same instance twice' , ( ) => {
@@ -114,7 +112,7 @@ describe('wrapMcpServerWithSentry', () => {
114
112
// Simulate incoming message
115
113
mockTransport . onmessage ?.( jsonRpcRequest , { } ) ;
116
114
117
- expect ( tracingModule . startSpan ) . toHaveBeenCalledWith (
115
+ expect ( startSpanSpy ) . toHaveBeenCalledWith (
118
116
expect . objectContaining ( {
119
117
name : 'tools/call get-weather' ,
120
118
forceTransaction : true ,
@@ -135,7 +133,7 @@ describe('wrapMcpServerWithSentry', () => {
135
133
// Simulate incoming notification
136
134
mockTransport . onmessage ?.( jsonRpcNotification , { } ) ;
137
135
138
- expect ( tracingModule . startSpan ) . toHaveBeenCalledWith (
136
+ expect ( startSpanSpy ) . toHaveBeenCalledWith (
139
137
expect . objectContaining ( {
140
138
name : 'notifications/initialized' ,
141
139
forceTransaction : true ,
@@ -156,7 +154,7 @@ describe('wrapMcpServerWithSentry', () => {
156
154
// Simulate outgoing notification
157
155
await mockTransport . send ?.( outgoingNotification ) ;
158
156
159
- expect ( tracingModule . startSpan ) . toHaveBeenCalledWith (
157
+ expect ( startSpanSpy ) . toHaveBeenCalledWith (
160
158
expect . objectContaining ( {
161
159
name : 'notifications/tools/list_changed' ,
162
160
forceTransaction : true ,
@@ -171,7 +169,7 @@ describe('wrapMcpServerWithSentry', () => {
171
169
// Simulate non-JSON-RPC message
172
170
mockTransport . onmessage ?.( { some : 'data' } , { } ) ;
173
171
174
- expect ( tracingModule . startSpan ) . not . toHaveBeenCalled ( ) ;
172
+ expect ( startSpanSpy ) . not . toHaveBeenCalled ( ) ;
175
173
} ) ;
176
174
177
175
it ( 'should handle transport onclose events' , async ( ) => {
@@ -203,13 +201,7 @@ describe('wrapMcpServerWithSentry', () => {
203
201
jsonrpc : '2.0' ,
204
202
method : 'tools/call' ,
205
203
id : 'req-1' ,
206
- params : {
207
- name : 'get-weather' ,
208
- arguments : {
209
- location : 'Seattle, WA' ,
210
- units : 'metric'
211
- }
212
- }
204
+ params : { name : 'get-weather' , arguments : { location : 'Seattle, WA' } }
213
205
} ;
214
206
215
207
const extraWithClientInfo = {
@@ -221,33 +213,26 @@ describe('wrapMcpServerWithSentry', () => {
221
213
222
214
mockTransport . onmessage ?.( jsonRpcRequest , extraWithClientInfo ) ;
223
215
224
- expect ( tracingModule . startSpan ) . toHaveBeenCalledWith (
225
- expect . objectContaining ( {
216
+ expect ( startSpanSpy ) . toHaveBeenCalledWith (
217
+ {
226
218
name : 'tools/call get-weather' ,
227
219
forceTransaction : true ,
228
- attributes : expect . objectContaining ( {
229
- // Required
220
+ attributes : {
230
221
'mcp.method.name' : 'tools/call' ,
231
- // Conditionally Required (tool operation)
232
222
'mcp.tool.name' : 'get-weather' ,
233
223
'mcp.request.id' : 'req-1' ,
234
- // Recommended
235
224
'mcp.session.id' : 'test-session-123' ,
236
225
'client.address' : '192.168.1.100' ,
237
226
'client.port' : 54321 ,
238
- // Transport attributes
239
227
'mcp.transport' : 'http' ,
240
228
'network.transport' : 'tcp' ,
241
229
'network.protocol.version' : '2.0' ,
242
- // Tool arguments (JSON-stringified)
243
230
'mcp.request.argument.location' : '"Seattle, WA"' ,
244
- 'mcp.request.argument.units' : '"metric"' ,
245
- // Sentry-specific
246
231
'sentry.op' : 'mcp.server' ,
247
232
'sentry.origin' : 'auto.function.mcp_server' ,
248
233
'sentry.source' : 'route' ,
249
- } ) ,
250
- } ) ,
234
+ } ,
235
+ } ,
251
236
expect . any ( Function )
252
237
) ;
253
238
} ) ;
@@ -264,30 +249,24 @@ describe('wrapMcpServerWithSentry', () => {
264
249
265
250
mockTransport . onmessage ?.( jsonRpcRequest , { } ) ;
266
251
267
- expect ( tracingModule . startSpan ) . toHaveBeenCalledWith (
268
- expect . objectContaining ( {
252
+ expect ( startSpanSpy ) . toHaveBeenCalledWith (
253
+ {
269
254
name : 'resources/read file:///docs/api.md' ,
270
255
forceTransaction : true ,
271
- attributes : expect . objectContaining ( {
272
- // Required
256
+ attributes : {
273
257
'mcp.method.name' : 'resources/read' ,
274
- // Conditionally Required (resource operation)
275
258
'mcp.resource.uri' : 'file:///docs/api.md' ,
276
259
'mcp.request.id' : 'req-2' ,
277
- // Recommended
278
260
'mcp.session.id' : 'test-session-123' ,
279
- // Transport attributes
280
261
'mcp.transport' : 'http' ,
281
262
'network.transport' : 'tcp' ,
282
263
'network.protocol.version' : '2.0' ,
283
- // Request arguments (JSON-stringified)
284
264
'mcp.request.argument.uri' : '"file:///docs/api.md"' ,
285
- // Sentry-specific
286
265
'sentry.op' : 'mcp.server' ,
287
266
'sentry.origin' : 'auto.function.mcp_server' ,
288
267
'sentry.source' : 'route' ,
289
- } ) ,
290
- } ) ,
268
+ } ,
269
+ } ,
291
270
expect . any ( Function )
292
271
) ;
293
272
} ) ;
@@ -304,30 +283,24 @@ describe('wrapMcpServerWithSentry', () => {
304
283
305
284
mockTransport . onmessage ?.( jsonRpcRequest , { } ) ;
306
285
307
- expect ( tracingModule . startSpan ) . toHaveBeenCalledWith (
308
- expect . objectContaining ( {
286
+ expect ( startSpanSpy ) . toHaveBeenCalledWith (
287
+ {
309
288
name : 'prompts/get analyze-code' ,
310
289
forceTransaction : true ,
311
- attributes : expect . objectContaining ( {
312
- // Required
290
+ attributes : {
313
291
'mcp.method.name' : 'prompts/get' ,
314
- // Conditionally Required (prompt operation)
315
292
'mcp.prompt.name' : 'analyze-code' ,
316
293
'mcp.request.id' : 'req-3' ,
317
- // Recommended
318
294
'mcp.session.id' : 'test-session-123' ,
319
- // Transport attributes
320
295
'mcp.transport' : 'http' ,
321
296
'network.transport' : 'tcp' ,
322
297
'network.protocol.version' : '2.0' ,
323
- // Request arguments (JSON-stringified)
324
298
'mcp.request.argument.name' : '"analyze-code"' ,
325
- // Sentry-specific
326
299
'sentry.op' : 'mcp.server' ,
327
300
'sentry.origin' : 'auto.function.mcp_server' ,
328
301
'sentry.source' : 'route' ,
329
- } ) ,
330
- } ) ,
302
+ } ,
303
+ } ,
331
304
expect . any ( Function )
332
305
) ;
333
306
} ) ;
@@ -343,27 +316,22 @@ describe('wrapMcpServerWithSentry', () => {
343
316
344
317
mockTransport . onmessage ?.( jsonRpcNotification , { } ) ;
345
318
346
- expect ( tracingModule . startSpan ) . toHaveBeenCalledWith (
347
- expect . objectContaining ( {
319
+ expect ( startSpanSpy ) . toHaveBeenCalledWith (
320
+ {
348
321
name : 'notifications/tools/list_changed' ,
349
322
forceTransaction : true ,
350
- attributes : expect . objectContaining ( {
351
- // Required
323
+ attributes : {
352
324
'mcp.method.name' : 'notifications/tools/list_changed' ,
353
- // Recommended
354
325
'mcp.session.id' : 'test-session-123' ,
355
- // Notification-specific
356
326
'mcp.notification.direction' : 'client_to_server' ,
357
- // Transport attributes
358
327
'mcp.transport' : 'http' ,
359
328
'network.transport' : 'tcp' ,
360
329
'network.protocol.version' : '2.0' ,
361
- // Sentry-specific
362
330
'sentry.op' : 'mcp.server' ,
363
331
'sentry.origin' : 'auto.mcp.notification' ,
364
332
'sentry.source' : 'route' ,
365
- } ) ,
366
- } ) ,
333
+ } ,
334
+ } ,
367
335
expect . any ( Function )
368
336
) ;
369
337
0 commit comments