11import { expect , test } from '@playwright/test' ;
2- import { waitForTransaction } from '@sentry-internal/test-utils' ;
2+ import { waitForStreamedSpan , getSpanOp } from '@sentry-internal/test-utils' ;
33import { Client } from '@modelcontextprotocol/client' ;
44import { StreamableHTTPClientTransport } from '@modelcontextprotocol/client' ;
55
6- test ( 'records transactions for stable MCP SDK v2 handlers using the register API' , async ( { baseURL } ) => {
6+ test ( 'records spans for stable MCP SDK v2 handlers using the register API' , async ( { baseURL } ) => {
77 const transport = new StreamableHTTPClientTransport ( new URL ( `${ baseURL } /mcp` ) ) ;
88
99 const client = new Client ( {
1010 name : 'test-client-v2' ,
1111 version : '1.0.0' ,
1212 } ) ;
1313
14- const initializeTransactionPromise = waitForTransaction ( 'node-express-mcp-v2' , transactionEvent => {
15- return transactionEvent . transaction === 'initialize' ;
16- } ) ;
14+ const initializeSegmentPromise = waitForStreamedSpan (
15+ 'node-express-mcp-v2' ,
16+ segment => segment . is_segment && segment . name === 'initialize' ,
17+ ) ;
1718
1819 await client . connect ( transport ) ;
1920
2021 await test . step ( 'initialize handshake' , async ( ) => {
21- const initializeTransaction = await initializeTransactionPromise ;
22- expect ( initializeTransaction ) . toBeDefined ( ) ;
23- expect ( initializeTransaction . contexts ?. trace ?. op ) . toEqual ( 'mcp.server' ) ;
24- expect ( initializeTransaction . contexts ?. trace ?. data ?. [ 'mcp.method.name' ] ) . toEqual ( 'initialize' ) ;
25- expect ( initializeTransaction . contexts ?. trace ?. data ?. [ 'mcp.client.name' ] ) . toEqual ( 'test-client-v2' ) ;
26- expect ( initializeTransaction . contexts ?. trace ?. data ?. [ 'mcp.server.name' ] ) . toEqual ( 'Echo-V2' ) ;
27- expect ( initializeTransaction . contexts ?. trace ?. data ?. [ 'mcp.transport' ] ) . toMatch ( / S t r e a m a b l e H T T P S e r v e r T r a n s p o r t / ) ;
22+ const initializeSegment = await initializeSegmentPromise ;
23+ expect ( initializeSegment ) . toBeDefined ( ) ;
24+ expect ( getSpanOp ( initializeSegment ) ) . toEqual ( 'mcp.server' ) ;
25+ expect ( initializeSegment . attributes ?. [ 'mcp.method.name' ] ?. value ) . toEqual ( 'initialize' ) ;
26+ expect ( initializeSegment . attributes ?. [ 'mcp.client.name' ] ?. value ) . toEqual ( 'test-client-v2' ) ;
27+ expect ( initializeSegment . attributes ?. [ 'mcp.server.name' ] ?. value ) . toEqual ( 'Echo-V2' ) ;
28+ expect ( initializeSegment . attributes ?. [ 'mcp.transport' ] ?. value ) . toMatch ( / S t r e a m a b l e H T T P S e r v e r T r a n s p o r t / ) ;
2829 } ) ;
2930
3031 await test . step ( 'registerTool handler' , async ( ) => {
31- const toolTransactionPromise = waitForTransaction ( 'node-express-mcp-v2' , transactionEvent => {
32- return transactionEvent . transaction === 'tools/call echo' ;
33- } ) ;
32+ const toolSegmentPromise = waitForStreamedSpan (
33+ 'node-express-mcp-v2' ,
34+ segment => segment . is_segment && segment . name === 'tools/call echo' ,
35+ ) ;
3436
3537 const toolResult = await client . callTool ( {
3638 name : 'echo' ,
@@ -48,19 +50,20 @@ test('records transactions for stable MCP SDK v2 handlers using the register API
4850 ] ,
4951 } ) ;
5052
51- const toolTransaction = await toolTransactionPromise ;
52- expect ( toolTransaction ) . toBeDefined ( ) ;
53- expect ( toolTransaction . contexts ?. trace ?. op ) . toEqual ( 'mcp.server' ) ;
54- expect ( toolTransaction . contexts ?. trace ?. data ?. [ 'mcp.method.name' ] ) . toEqual ( 'tools/call' ) ;
55- expect ( toolTransaction . contexts ?. trace ?. data ?. [ 'mcp.tool.name' ] ) . toEqual ( 'echo' ) ;
53+ const toolSegment = await toolSegmentPromise ;
54+ expect ( toolSegment ) . toBeDefined ( ) ;
55+ expect ( getSpanOp ( toolSegment ) ) . toEqual ( 'mcp.server' ) ;
56+ expect ( toolSegment . attributes ?. [ 'mcp.method.name' ] ?. value ) . toEqual ( 'tools/call' ) ;
57+ expect ( toolSegment . attributes ?. [ 'mcp.tool.name' ] ?. value ) . toEqual ( 'echo' ) ;
5658 // Proves span was completed with results (span correlation worked end-to-end)
57- expect ( toolTransaction . contexts ?. trace ?. data ?. [ 'mcp.tool.result.content_count' ] ) . toEqual ( 1 ) ;
59+ expect ( toolSegment . attributes ?. [ 'mcp.tool.result.content_count' ] ?. value ) . toEqual ( 1 ) ;
5860 } ) ;
5961
6062 await test . step ( 'registerResource handler' , async ( ) => {
61- const resourceTransactionPromise = waitForTransaction ( 'node-express-mcp-v2' , transactionEvent => {
62- return transactionEvent . transaction === 'resources/read echo://foobar' ;
63- } ) ;
63+ const resourceSegmentPromise = waitForStreamedSpan (
64+ 'node-express-mcp-v2' ,
65+ segment => segment . is_segment && segment . name === 'resources/read' ,
66+ ) ;
6467
6568 const resourceResult = await client . readResource ( {
6669 uri : 'echo://foobar' ,
@@ -70,16 +73,17 @@ test('records transactions for stable MCP SDK v2 handlers using the register API
7073 contents : [ { text : 'Resource echo: foobar' , uri : 'echo://foobar' } ] ,
7174 } ) ;
7275
73- const resourceTransaction = await resourceTransactionPromise ;
74- expect ( resourceTransaction ) . toBeDefined ( ) ;
75- expect ( resourceTransaction . contexts ?. trace ?. op ) . toEqual ( 'mcp.server' ) ;
76- expect ( resourceTransaction . contexts ?. trace ?. data ?. [ 'mcp.method.name' ] ) . toEqual ( 'resources/read' ) ;
76+ const resourceSegment = await resourceSegmentPromise ;
77+ expect ( resourceSegment ) . toBeDefined ( ) ;
78+ expect ( getSpanOp ( resourceSegment ) ) . toEqual ( 'mcp.server' ) ;
79+ expect ( resourceSegment . attributes ?. [ 'mcp.method.name' ] ?. value ) . toEqual ( 'resources/read' ) ;
7780 } ) ;
7881
7982 await test . step ( 'registerPrompt handler' , async ( ) => {
80- const promptTransactionPromise = waitForTransaction ( 'node-express-mcp-v2' , transactionEvent => {
81- return transactionEvent . transaction === 'prompts/get echo' ;
82- } ) ;
83+ const promptSegmentPromise = waitForStreamedSpan (
84+ 'node-express-mcp-v2' ,
85+ segment => segment . is_segment && segment . name === 'prompts/get echo' ,
86+ ) ;
8387
8488 const promptResult = await client . getPrompt ( {
8589 name : 'echo' ,
@@ -100,27 +104,28 @@ test('records transactions for stable MCP SDK v2 handlers using the register API
100104 ] ,
101105 } ) ;
102106
103- const promptTransaction = await promptTransactionPromise ;
104- expect ( promptTransaction ) . toBeDefined ( ) ;
105- expect ( promptTransaction . contexts ?. trace ?. op ) . toEqual ( 'mcp.server' ) ;
106- expect ( promptTransaction . contexts ?. trace ?. data ?. [ 'mcp.method.name' ] ) . toEqual ( 'prompts/get' ) ;
107+ const promptSegment = await promptSegmentPromise ;
108+ expect ( promptSegment ) . toBeDefined ( ) ;
109+ expect ( getSpanOp ( promptSegment ) ) . toEqual ( 'mcp.server' ) ;
110+ expect ( promptSegment . attributes ?. [ 'mcp.method.name' ] ?. value ) . toEqual ( 'prompts/get' ) ;
107111 } ) ;
108112
109- await test . step ( 'error tool sets span status to internal_error' , async ( ) => {
110- const toolTransactionPromise = waitForTransaction ( 'node-express-mcp-v2' , transactionEvent => {
111- return transactionEvent . transaction === 'tools/call always-error' ;
112- } ) ;
113+ await test . step ( 'error tool sets span status to error' , async ( ) => {
114+ const toolSegmentPromise = waitForStreamedSpan (
115+ 'node-express-mcp-v2' ,
116+ segment => segment . is_segment && segment . name === 'tools/call always-error' ,
117+ ) ;
113118
114119 try {
115120 await client . callTool ( { name : 'always-error' , arguments : { } } ) ;
116121 } catch {
117122 // Expected: MCP SDK throws when the tool returns a JSON-RPC error
118123 }
119124
120- const toolTransaction = await toolTransactionPromise ;
121- expect ( toolTransaction ) . toBeDefined ( ) ;
122- expect ( toolTransaction . contexts ?. trace ?. op ) . toEqual ( 'mcp.server' ) ;
123- expect ( toolTransaction . contexts ?. trace ?. status ) . toEqual ( 'internal_error ' ) ;
125+ const toolSegment = await toolSegmentPromise ;
126+ expect ( toolSegment ) . toBeDefined ( ) ;
127+ expect ( getSpanOp ( toolSegment ) ) . toEqual ( 'mcp.server' ) ;
128+ expect ( toolSegment ?. status ) . toEqual ( 'error ' ) ;
124129 } ) ;
125130
126131 await client . close ( ) ;
0 commit comments