Skip to content

Commit 294216e

Browse files
committed
Add a streamed integration test for mcp.server span names
Claude-Session: https://claude.ai/code/session_013bjBXkGkJo8eL8hkz48byi
1 parent 44ad5fe commit 294216e

5 files changed

Lines changed: 139 additions & 1 deletion

File tree

‎dev-packages/node-integration-tests/package.json‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
"@langchain/core": "^0.3.80",
4545
"@langchain/openai": "^0.5.0",
4646
"@langchain/langgraph": "^0.2.32",
47+
"@modelcontextprotocol/client": "2.0.0",
48+
"@modelcontextprotocol/server": "2.0.0",
4749
"@nestjs/common": "^11",
4850
"@nestjs/core": "^11",
4951
"@nestjs/platform-express": "^11",
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as Sentry from '@sentry/node';
2+
import { loggingTransport } from '@sentry-internal/node-integration-tests';
3+
4+
Sentry.init({
5+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
transport: loggingTransport,
9+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Client } from '@modelcontextprotocol/client';
2+
import { InMemoryTransport, McpServer, ResourceTemplate } from '@modelcontextprotocol/server';
3+
import { wrapMcpServerWithSentry } from '@sentry/node';
4+
5+
const server = wrapMcpServerWithSentry(new McpServer({ name: 'Echo', version: '1.0.0' }));
6+
7+
server.registerResource('echo', new ResourceTemplate('echo://{message}', { list: undefined }), {}, async uri => ({
8+
contents: [{ uri: uri.href, text: 'Resource echo' }],
9+
}));
10+
11+
server.registerTool('echo', {}, async () => ({ content: [{ type: 'text', text: 'Tool echo' }] }));
12+
13+
async function run() {
14+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
15+
const client = new Client({ name: 'test-client', version: '1.0.0' });
16+
17+
await Promise.all([server.connect(serverTransport), client.connect(clientTransport)]);
18+
19+
await client.readResource({ uri: 'echo://foobar' });
20+
await client.callTool({ name: 'echo', arguments: {} });
21+
22+
await client.close();
23+
await server.close();
24+
}
25+
26+
run();
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type { SerializedStreamedSpanContainer } from '@sentry/core';
2+
import { afterAll, describe, expect } from 'vitest';
3+
import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner';
4+
5+
function mcpSpans(container: SerializedStreamedSpanContainer): SerializedStreamedSpanContainer['items'] {
6+
return container.items.filter(item => item.attributes['sentry.op']?.value === 'mcp.server');
7+
}
8+
9+
describe('MCP server spans (streamed)', () => {
10+
afterAll(() => {
11+
cleanupChildProcesses();
12+
});
13+
14+
createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createTestRunner, test) => {
15+
test('names resource spans after the method alone, keeping the URI on the attribute', async () => {
16+
await createTestRunner()
17+
.expect({
18+
span: container => {
19+
const resourceSpan = mcpSpans(container).find(
20+
span => span.attributes['mcp.method.name']?.value === 'resources/read',
21+
);
22+
23+
expect(resourceSpan?.name).toBe('resources/read');
24+
expect(resourceSpan?.attributes['mcp.resource.uri']?.value).toBe('echo://foobar');
25+
},
26+
})
27+
.start()
28+
.completed();
29+
});
30+
31+
test('keeps the tool name, which comes from a bounded registry', async () => {
32+
await createTestRunner()
33+
.expect({
34+
span: container => {
35+
const toolSpan = mcpSpans(container).find(
36+
span => span.attributes['mcp.method.name']?.value === 'tools/call',
37+
);
38+
39+
expect(toolSpan?.name).toBe('tools/call echo');
40+
},
41+
})
42+
.start()
43+
.completed();
44+
});
45+
});
46+
});

‎yarn.lock‎

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5579,6 +5579,34 @@
55795579
resolved "https://registry.yarnpkg.com/@mjackson/node-fetch-server/-/node-fetch-server-0.2.0.tgz#577c0c25d8aae9f69a97738b7b0d03d1471cdc49"
55805580
integrity sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng==
55815581

5582+
"@modelcontextprotocol/client@2.0.0":
5583+
version "2.0.0"
5584+
resolved "https://sfw.security.sentry.io/npm/@modelcontextprotocol/client/-/client-2.0.0.tgz#4dc18c983c7acb40040f08442123e3c5b88d6074"
5585+
integrity sha512-8f1OghQ2rjzIOfqgUCP+8GiUWqRs89njoWLNqAe8kWmDePv3s1fZXseej+QXemssEuuOvLLmLO/kqM3IQHtISw==
5586+
dependencies:
5587+
"@modelcontextprotocol/core" "2.0.0"
5588+
cross-spawn "^7.0.5"
5589+
eventsource "^3.0.2"
5590+
eventsource-parser "^3.0.0"
5591+
jose "^6.1.3"
5592+
pkce-challenge "^5.0.0"
5593+
zod "^4.2.0"
5594+
5595+
"@modelcontextprotocol/core@2.0.0":
5596+
version "2.0.0"
5597+
resolved "https://sfw.security.sentry.io/npm/@modelcontextprotocol/core/-/core-2.0.0.tgz#c918a4c6aef22a7c59dfb0aabc2a82c187cce157"
5598+
integrity sha512-pJCEwGG7Lfr/+PQp9ZTwKXNeO5wzbfKL7H3MYpCorM4oFBoQrdjnBgEoqG+RjhsvS1FKrDbKux+M1HhlnGWqcA==
5599+
dependencies:
5600+
zod "^4.2.0"
5601+
5602+
"@modelcontextprotocol/server@2.0.0":
5603+
version "2.0.0"
5604+
resolved "https://sfw.security.sentry.io/npm/@modelcontextprotocol/server/-/server-2.0.0.tgz#4fae5ccb8f7925ed9a6bf6595bb3b58617063ffd"
5605+
integrity sha512-YhHWdHfpFMQfd0prsEnxKeS3Qz3ytIGmsS0sth4KDjnacIT7hxk6hXHkJ9KysxlkvTM+WZAtQbbcUhdoP4Hvtw==
5606+
dependencies:
5607+
"@modelcontextprotocol/core" "2.0.0"
5608+
zod "^4.2.0"
5609+
55825610
"@mongodb-js/saslprep@^1.1.0", "@mongodb-js/saslprep@^1.3.0":
55835611
version "1.4.6"
55845612
resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.4.6.tgz#2edf5819fa0e69d86059f44d1fe57ae9d7817c12"
@@ -13277,7 +13305,7 @@ cross-inspect@1.0.1:
1327713305
dependencies:
1327813306
tslib "^2.4.0"
1327913307

13280-
cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6:
13308+
cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.5, cross-spawn@^7.0.6:
1328113309
version "7.0.6"
1328213310
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
1328313311
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
@@ -15418,11 +15446,23 @@ events@^3.0.0, events@^3.2.0, events@^3.3.0:
1541815446
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
1541915447
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
1542015448

15449+
eventsource-parser@^3.0.0, eventsource-parser@^3.0.1:
15450+
version "3.1.1"
15451+
resolved "https://sfw.security.sentry.io/npm/eventsource-parser/-/eventsource-parser-3.1.1.tgz#b96cbb7dace4f3774f58a9e3b1ae9a4a524872e2"
15452+
integrity sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==
15453+
1542115454
eventsource-parser@^3.0.8:
1542215455
version "3.1.0"
1542315456
resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.1.0.tgz#4e198eb91cd333d0a8ddcc036502b3618a25f449"
1542415457
integrity sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==
1542515458

15459+
eventsource@^3.0.2:
15460+
version "3.0.7"
15461+
resolved "https://sfw.security.sentry.io/npm/eventsource/-/eventsource-3.0.7.tgz#1157622e2f5377bb6aef2114372728ba0c156989"
15462+
integrity sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==
15463+
dependencies:
15464+
eventsource-parser "^3.0.1"
15465+
1542615466
exact-mirror@^0.2.7:
1542715467
version "0.2.7"
1542815468
resolved "https://registry.yarnpkg.com/exact-mirror/-/exact-mirror-0.2.7.tgz#ee8e75c362a67ca0e07cb13fea92b61adaabfa29"
@@ -18239,6 +18279,11 @@ jiti@^2.4.2, jiti@^2.6.1, jiti@^2.7.0:
1823918279
resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.7.0.tgz#974228f2f4ca2bc21885a1797b45fea68e950c64"
1824018280
integrity sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==
1824118281

18282+
jose@^6.1.3:
18283+
version "6.2.10"
18284+
resolved "https://sfw.security.sentry.io/npm/jose/-/jose-6.2.10.tgz#b70436c920c4b3f97314c28a8b8612c15b1d9e7f"
18285+
integrity sha512-iiW7J9qRFlGxvCOIBDBDxFePQSn7ZMAnrYGhrrOo6siO/MIqwfyilLR27pkfDgUk+raLuzADS8A3S/KLBisc0g==
18286+
1824218287
js-md4@^0.3.2:
1824318288
version "0.3.2"
1824418289
resolved "https://registry.yarnpkg.com/js-md4/-/js-md4-0.3.2.tgz#cd3b3dc045b0c404556c81ddb5756c23e59d7cf5"
@@ -22278,6 +22323,11 @@ piscina@3.2.0:
2227822323
optionalDependencies:
2227922324
nice-napi "^1.0.2"
2228022325

22326+
pkce-challenge@^5.0.0:
22327+
version "5.0.1"
22328+
resolved "https://sfw.security.sentry.io/npm/pkce-challenge/-/pkce-challenge-5.0.1.tgz#3b4446865b17b1745e9ace2016a31f48ddf6230d"
22329+
integrity sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==
22330+
2228122331
pkg-dir@^4.1.0, pkg-dir@^4.2.0:
2228222332
version "4.2.0"
2228322333
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
@@ -28537,6 +28587,11 @@ zod@^4.0.0:
2853728587
resolved "https://registry.yarnpkg.com/zod/-/zod-4.3.6.tgz#89c56e0aa7d2b05107d894412227087885ab112a"
2853828588
integrity sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==
2853928589

28590+
zod@^4.2.0:
28591+
version "4.4.3"
28592+
resolved "https://sfw.security.sentry.io/npm/zod/-/zod-4.4.3.tgz#b680f172885d18bbebf21a834ea25e55a1bbf356"
28593+
integrity sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==
28594+
2854028595
zone.js@^0.12.0:
2854128596
version "0.12.0"
2854228597
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.12.0.tgz#a4a6e5fab6d34bd37d89c77e89ac2e6f4a3d2c30"

0 commit comments

Comments
 (0)