Skip to content

Commit 34dd8e2

Browse files
betegonclaude
andcommitted
test(e2e): add MCP error tool e2e test for span.status=internal_error
Adds an `always-error` tool to the MCP e2e test apps and a test step that verifies the resulting transaction has span.status=internal_error when a tool throws. This covers the fix in the span status correlation path end-to-end, complementing the existing unit tests. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
1 parent 4d504db commit 34dd8e2

File tree

6 files changed

+63
-0
lines changed

6 files changed

+63
-0
lines changed

dev-packages/e2e-tests/test-applications/node-express-v5/src/mcp.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ server.prompt('echo', { message: z.string() }, ({ message }, extra) => ({
4747
],
4848
}));
4949

50+
server.tool('always-error', {}, async () => {
51+
throw new Error('intentional error for span status testing');
52+
});
53+
5054
const transports: Record<string, SSEServerTransport> = {};
5155

5256
mcpRouter.get('/sse', async (_, res) => {

dev-packages/e2e-tests/test-applications/node-express-v5/tests/mcp.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,23 @@ test('Should record transactions for mcp handlers', async ({ baseURL }) => {
120120

121121
// TODO: When https://github.com/modelcontextprotocol/typescript-sdk/pull/358 is released check for trace id equality between the post transaction and the handler transaction
122122
});
123+
124+
await test.step('error tool sets span status to internal_error', async () => {
125+
const toolTransactionPromise = waitForTransaction('node-express-v5', transactionEvent => {
126+
return transactionEvent.transaction === 'tools/call always-error';
127+
});
128+
129+
try {
130+
await client.callTool({ name: 'always-error', arguments: {} });
131+
} catch {
132+
// Expected: MCP SDK throws when the tool returns a JSON-RPC error
133+
}
134+
135+
const toolTransaction = await toolTransactionPromise;
136+
expect(toolTransaction).toBeDefined();
137+
expect(toolTransaction.contexts?.trace?.op).toEqual('mcp.server');
138+
expect(toolTransaction.contexts?.trace?.status).toEqual('internal_error');
139+
});
123140
});
124141

125142
/**

dev-packages/e2e-tests/test-applications/node-express/src/mcp.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ server.prompt('echo', { message: z.string() }, ({ message }, extra) => ({
4747
],
4848
}));
4949

50+
server.tool('always-error', {}, async () => {
51+
throw new Error('intentional error for span status testing');
52+
});
53+
5054
const transports: Record<string, SSEServerTransport> = {};
5155

5256
mcpRouter.get('/sse', async (_, res) => {

dev-packages/e2e-tests/test-applications/node-express/tests/mcp.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,23 @@ test('Should record transactions for mcp handlers', async ({ baseURL }) => {
126126
expect(promptTransaction.contexts?.trace?.data?.['mcp.method.name']).toEqual('prompts/get');
127127
// TODO: When https://github.com/modelcontextprotocol/typescript-sdk/pull/358 is released check for trace id equality between the post transaction and the handler transaction
128128
});
129+
130+
await test.step('error tool sets span status to internal_error', async () => {
131+
const toolTransactionPromise = waitForTransaction('node-express', transactionEvent => {
132+
return transactionEvent.transaction === 'tools/call always-error';
133+
});
134+
135+
try {
136+
await client.callTool({ name: 'always-error', arguments: {} });
137+
} catch {
138+
// Expected: MCP SDK throws when the tool returns a JSON-RPC error
139+
}
140+
141+
const toolTransaction = await toolTransactionPromise;
142+
expect(toolTransaction).toBeDefined();
143+
expect(toolTransaction.contexts?.trace?.op).toEqual('mcp.server');
144+
expect(toolTransaction.contexts?.trace?.status).toEqual('internal_error');
145+
});
129146
});
130147

131148
/**

dev-packages/e2e-tests/test-applications/tsx-express/src/mcp.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ server.prompt('echo', { message: z.string() }, ({ message }, extra) => ({
4747
],
4848
}));
4949

50+
server.tool('always-error', {}, async () => {
51+
throw new Error('intentional error for span status testing');
52+
});
53+
5054
const transports: Record<string, SSEServerTransport> = {};
5155

5256
mcpRouter.get('/sse', async (_, res) => {

dev-packages/e2e-tests/test-applications/tsx-express/tests/mcp.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,23 @@ test('Records transactions for mcp handlers', async ({ baseURL }) => {
123123

124124
// TODO: When https://github.com/modelcontextprotocol/typescript-sdk/pull/358 is released check for trace id equality between the post transaction and the handler transaction
125125
});
126+
127+
await test.step('error tool sets span status to internal_error', async () => {
128+
const toolTransactionPromise = waitForTransaction('tsx-express', transactionEvent => {
129+
return transactionEvent.transaction === 'tools/call always-error';
130+
});
131+
132+
try {
133+
await client.callTool({ name: 'always-error', arguments: {} });
134+
} catch {
135+
// Expected: MCP SDK throws when the tool returns a JSON-RPC error
136+
}
137+
138+
const toolTransaction = await toolTransactionPromise;
139+
expect(toolTransaction).toBeDefined();
140+
expect(toolTransaction.contexts?.trace?.op).toEqual('mcp.server');
141+
expect(toolTransaction.contexts?.trace?.status).toEqual('internal_error');
142+
});
126143
});
127144

128145
/**

0 commit comments

Comments
 (0)