dd-trace 6.2.0 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +3 -4
- package/packages/datadog-instrumentations/src/cookie.js +7 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/modelcontextprotocol-sdk.js +30 -54
- package/packages/datadog-instrumentations/src/http/server.js +27 -0
- package/packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js +248 -1
- package/packages/datadog-instrumentations/src/playwright.js +40 -3
- package/packages/datadog-plugin-azure-functions/src/index.js +2 -1
- package/packages/datadog-plugin-graphql/src/request.js +1 -1
- package/packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js +144 -26
- package/packages/datadog-plugin-modelcontextprotocol-sdk/src/utils.js +30 -0
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-shimmer/src/shimmer.js +9 -2
- package/packages/dd-trace/src/appsec/channels.js +1 -0
- package/packages/dd-trace/src/appsec/index.js +6 -3
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +1 -1
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +23 -16
- package/packages/dd-trace/src/config/config-types.d.ts +8 -0
- package/packages/dd-trace/src/config/defaults.js +20 -15
- package/packages/dd-trace/src/config/index.js +13 -3
- package/packages/dd-trace/src/config/parsers.js +79 -0
- package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
- package/packages/dd-trace/src/llmobs/tagger.js +15 -1
- package/packages/dd-trace/src/opentelemetry/span_processor.js +7 -5
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +32 -16
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +20 -11
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ Most of the documentation for `dd-trace` is available on these webpages:
|
|
|
28
28
|
| [`v2`](https://github.com/DataDog/dd-trace-js/tree/v2.x) |  | `>= v12` | NO | NO | **EOL** | 2022-01-28 | 2023-08-15 |
|
|
29
29
|
| [`v3`](https://github.com/DataDog/dd-trace-js/tree/v3.x) |  | `>= v14` | NO | YES | **EOL** | 2022-08-15 | 2024-05-15 |
|
|
30
30
|
| [`v4`](https://github.com/DataDog/dd-trace-js/tree/v4.x) |  | `>= v16` | YES | YES | **EOL** | 2023-05-12 | 2025-01-11 |
|
|
31
|
-
| [`v5`](https://github.com/DataDog/dd-trace-js/tree/v5.x) |  | `>= v18` | YES | YES | **Maintenance** | 2024-01-11 |
|
|
31
|
+
| [`v5`](https://github.com/DataDog/dd-trace-js/tree/v5.x) |  | `>= v18` | YES | YES | **Maintenance** | 2024-01-11 | 2027-07-02 |
|
|
32
32
|
| [`v6`](https://github.com/DataDog/dd-trace-js/tree/v6.x) |  | `>= v22` | YES | YES | **Current** | 2026-07-02 | Unknown |
|
|
33
33
|
|
|
34
34
|
* EOL = End-of-life
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"lint:inspect": "npx @eslint/config-inspector@latest",
|
|
24
24
|
"lint:codeowners": "codeowners-audit",
|
|
25
25
|
"lint:codeowners:ci": "codeowners-audit --glob='**/*.spec.js' --glob='benchmark/sirun/**' --glob='.agents/**' --glob='.claude/**' --glob='integration-tests/**'",
|
|
26
|
-
"lint:editorconfig": "editorconfig-checker",
|
|
26
|
+
"lint:editorconfig": "docker build -q -t ec .github/editorconfig-checker && docker run --rm --volume=$PWD:/check ec",
|
|
27
27
|
"release:proposal": "node scripts/release/proposal",
|
|
28
28
|
"services": "node ./scripts/install_plugin_modules && node packages/dd-trace/test/setup/services",
|
|
29
29
|
"test": "echo '\nError: The root \"npm test\" command is intentionally disabled.\n\nInstead, run specific test suites:\n - npm run test:trace:core\n - npm run test:appsec\n - etc.\n\nOr run individual test files:\n npx mocha path/to/test.spec.js\n\nSee CONTRIBUTING.md (Testing section) for more details.\n' && exit 1",
|
|
@@ -198,11 +198,10 @@
|
|
|
198
198
|
"bun": "1.3.14",
|
|
199
199
|
"c8": "^11.0.0",
|
|
200
200
|
"codeowners-audit": "^2.9.0",
|
|
201
|
-
"editorconfig-checker": "^6.1.1",
|
|
202
201
|
"eslint": "^9.39.2",
|
|
203
202
|
"eslint-plugin-cypress": "^6.4.2",
|
|
204
203
|
"eslint-plugin-import": "^2.32.0",
|
|
205
|
-
"eslint-plugin-jsdoc": "^63.0.
|
|
204
|
+
"eslint-plugin-jsdoc": "^63.0.12",
|
|
206
205
|
"eslint-plugin-mocha": "^11.3.0",
|
|
207
206
|
"eslint-plugin-n": "^18.2.1",
|
|
208
207
|
"eslint-plugin-promise": "^7.3.0",
|
|
@@ -15,7 +15,13 @@ function wrapParse (originalParse) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/** @param {typeof import('cookie')} cookie */
|
|
18
19
|
addHook({ name: 'cookie', versions: ['>=0.4'] }, cookie => {
|
|
19
|
-
|
|
20
|
+
for (const name of ['parse', 'parseCookie']) {
|
|
21
|
+
if (typeof cookie[name] === 'function') {
|
|
22
|
+
// shimmer returns a mutable replacement when an ESM namespace export is non-configurable.
|
|
23
|
+
cookie = shimmer.wrap(cookie, name, wrapParse)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
20
26
|
return cookie
|
|
21
27
|
})
|
|
@@ -20,7 +20,7 @@ module.exports = {
|
|
|
20
20
|
'@apollo/server': () => require('../apollo-server'),
|
|
21
21
|
'@apollo/gateway': () => require('../apollo'),
|
|
22
22
|
'@langchain/langgraph': { esmFirst: true, fn: () => require('../langgraph') },
|
|
23
|
-
'@modelcontextprotocol/sdk': () => require('../modelcontextprotocol-sdk'),
|
|
23
|
+
'@modelcontextprotocol/sdk': { esmFirst: true, fn: () => require('../modelcontextprotocol-sdk') },
|
|
24
24
|
'apollo-server-core': () => require('../apollo-server-core'),
|
|
25
25
|
'@aws-sdk/smithy-client': () => require('../aws-sdk'),
|
|
26
26
|
'@aws/durable-execution-sdk-js': () => require('../aws-durable-execution-sdk-js'),
|
|
@@ -1,59 +1,35 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const CLIENT_METHODS = [
|
|
4
|
+
'callTool',
|
|
5
|
+
'listTools',
|
|
6
|
+
'listResources',
|
|
7
|
+
'readResource',
|
|
8
|
+
'listPrompts',
|
|
9
|
+
'getPrompt',
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
const SERVER_METHODS = [
|
|
13
|
+
'executeToolHandler',
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
function clientEntries (methodName) {
|
|
17
|
+
return ['dist/esm/client/index.js', 'dist/cjs/client/index.js'].map(filePath => ({
|
|
18
|
+
module: { name: '@modelcontextprotocol/sdk', versionRange: '>=1.27.1', filePath },
|
|
19
|
+
functionQuery: { methodName, className: 'Client', kind: 'Async' },
|
|
20
|
+
channelName: `Client_${methodName}`,
|
|
21
|
+
}))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function serverEntries (methodName) {
|
|
25
|
+
return ['dist/esm/server/mcp.js', 'dist/cjs/server/mcp.js'].map(filePath => ({
|
|
26
|
+
module: { name: '@modelcontextprotocol/sdk', versionRange: '>=1.27.1', filePath },
|
|
27
|
+
functionQuery: { methodName, className: 'McpServer', kind: 'Async' },
|
|
28
|
+
channelName: `McpServer_${methodName}`,
|
|
29
|
+
}))
|
|
30
|
+
}
|
|
5
31
|
|
|
6
32
|
module.exports = [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
name: '@modelcontextprotocol/sdk',
|
|
10
|
-
versionRange: '>=1.27.1',
|
|
11
|
-
filePath: 'dist/esm/client/index.js',
|
|
12
|
-
},
|
|
13
|
-
functionQuery: {
|
|
14
|
-
methodName: 'callTool',
|
|
15
|
-
className: 'Client',
|
|
16
|
-
kind: 'Async',
|
|
17
|
-
},
|
|
18
|
-
channelName: 'Client_callTool',
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
module: {
|
|
22
|
-
name: '@modelcontextprotocol/sdk',
|
|
23
|
-
versionRange: '>=1.27.1',
|
|
24
|
-
filePath: 'dist/cjs/client/index.js',
|
|
25
|
-
},
|
|
26
|
-
functionQuery: {
|
|
27
|
-
methodName: 'callTool',
|
|
28
|
-
className: 'Client',
|
|
29
|
-
kind: 'Async',
|
|
30
|
-
},
|
|
31
|
-
channelName: 'Client_callTool',
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
module: {
|
|
35
|
-
name: '@modelcontextprotocol/sdk',
|
|
36
|
-
versionRange: '>=1.27.1',
|
|
37
|
-
filePath: 'dist/esm/client/index.js',
|
|
38
|
-
},
|
|
39
|
-
functionQuery: {
|
|
40
|
-
methodName: 'listTools',
|
|
41
|
-
className: 'Client',
|
|
42
|
-
kind: 'Async',
|
|
43
|
-
},
|
|
44
|
-
channelName: 'Client_listTools',
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
module: {
|
|
48
|
-
name: '@modelcontextprotocol/sdk',
|
|
49
|
-
versionRange: '>=1.27.1',
|
|
50
|
-
filePath: 'dist/cjs/client/index.js',
|
|
51
|
-
},
|
|
52
|
-
functionQuery: {
|
|
53
|
-
methodName: 'listTools',
|
|
54
|
-
className: 'Client',
|
|
55
|
-
kind: 'Async',
|
|
56
|
-
},
|
|
57
|
-
channelName: 'Client_listTools',
|
|
58
|
-
},
|
|
33
|
+
...CLIENT_METHODS.flatMap(clientEntries),
|
|
34
|
+
...SERVER_METHODS.flatMap(serverEntries),
|
|
59
35
|
]
|
|
@@ -13,6 +13,7 @@ const finishServerCh = channel('apm:http:server:request:finish')
|
|
|
13
13
|
const startWriteHeadCh = channel('apm:http:server:response:writeHead:start')
|
|
14
14
|
const finishSetHeaderCh = channel('datadog:http:server:response:set-header:finish')
|
|
15
15
|
const startSetHeaderCh = channel('datadog:http:server:response:set-header:start')
|
|
16
|
+
const startInformationalResponseCh = channel('datadog:http:server:informational-response:start')
|
|
16
17
|
|
|
17
18
|
const requestFinishedSet = new WeakSet()
|
|
18
19
|
|
|
@@ -24,10 +25,19 @@ addHook({ name: 'http' }, http => {
|
|
|
24
25
|
shimmer.wrap(http.ServerResponse.prototype, 'end', wrapEnd)
|
|
25
26
|
shimmer.wrap(http.ServerResponse.prototype, 'setHeader', wrapSetHeader)
|
|
26
27
|
shimmer.wrap(http.ServerResponse.prototype, 'removeHeader', wrapAppendOrRemoveHeader)
|
|
28
|
+
if (http.ServerResponse.prototype.writeContinue) {
|
|
29
|
+
shimmer.wrap(http.ServerResponse.prototype, 'writeContinue', wrapInformationalResponse)
|
|
30
|
+
}
|
|
31
|
+
if (http.ServerResponse.prototype.writeProcessing) {
|
|
32
|
+
shimmer.wrap(http.ServerResponse.prototype, 'writeProcessing', wrapInformationalResponse)
|
|
33
|
+
}
|
|
27
34
|
// Added in node v16.17.0
|
|
28
35
|
if (http.ServerResponse.prototype.appendHeader) {
|
|
29
36
|
shimmer.wrap(http.ServerResponse.prototype, 'appendHeader', wrapAppendOrRemoveHeader)
|
|
30
37
|
}
|
|
38
|
+
if (http.ServerResponse.prototype.writeEarlyHints) {
|
|
39
|
+
shimmer.wrap(http.ServerResponse.prototype, 'writeEarlyHints', wrapInformationalResponse)
|
|
40
|
+
}
|
|
31
41
|
return http
|
|
32
42
|
})
|
|
33
43
|
|
|
@@ -212,6 +222,23 @@ function wrapAppendOrRemoveHeader (originalMethod) {
|
|
|
212
222
|
}
|
|
213
223
|
}
|
|
214
224
|
|
|
225
|
+
function wrapInformationalResponse (originalMethod) {
|
|
226
|
+
return function wrappedInformationalResponse (...args) {
|
|
227
|
+
if (!startInformationalResponseCh.hasSubscribers) {
|
|
228
|
+
return Reflect.apply(originalMethod, this, args)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const abortController = new AbortController()
|
|
232
|
+
startInformationalResponseCh.publish({ res: this, abortController })
|
|
233
|
+
|
|
234
|
+
if (abortController.signal.aborted) {
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return Reflect.apply(originalMethod, this, args)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
215
242
|
function wrapEnd (end) {
|
|
216
243
|
return function wrappedEnd (...args) {
|
|
217
244
|
if (!startWriteHeadCh.hasSubscribers) {
|
|
@@ -1,7 +1,254 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { tracingChannel } = require('dc-polyfill')
|
|
4
|
+
const shimmer = require('../../datadog-shimmer')
|
|
5
|
+
const { addHook, channel, getHooks } = require('./helpers/instrument')
|
|
4
6
|
|
|
5
7
|
for (const hook of getHooks('@modelcontextprotocol/sdk')) {
|
|
6
8
|
addHook(hook, exports => exports)
|
|
7
9
|
}
|
|
10
|
+
|
|
11
|
+
const serverRequestCh = tracingChannel('apm:mcp:server:request')
|
|
12
|
+
const clientRequestInjectCh = channel('apm:mcp:client:request:inject')
|
|
13
|
+
const serverToolRegisteredCh = channel('apm:mcp:server:tool:registered')
|
|
14
|
+
|
|
15
|
+
const DISTRIBUTED_TRACE_META_KEY = '_dd_trace_context'
|
|
16
|
+
const TRACED_METHOD_PREFIX = /^(?:tools|resources|prompts)\//
|
|
17
|
+
|
|
18
|
+
// Maps Protocol instance → Map<requestId, ctx>. Shares the ctx object between
|
|
19
|
+
// _onrequest (span start, in the correct HTTP async context), the SDK request
|
|
20
|
+
// handler wrapper (result/error capture), and the SDK cleanup path (span finish).
|
|
21
|
+
const pendingContexts = new WeakMap()
|
|
22
|
+
const wrappedAbortControllerMaps = new WeakSet()
|
|
23
|
+
const wrappedRequestHandlerMaps = new WeakSet()
|
|
24
|
+
const wrappedRequestHandlers = new WeakSet()
|
|
25
|
+
|
|
26
|
+
function addTraceContextToRequest (request, traceContext) {
|
|
27
|
+
return {
|
|
28
|
+
...request,
|
|
29
|
+
params: {
|
|
30
|
+
...request.params,
|
|
31
|
+
_meta: {
|
|
32
|
+
...request.params?._meta,
|
|
33
|
+
[DISTRIBUTED_TRACE_META_KEY]: traceContext,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getPendingContext (protocol, requestId) {
|
|
40
|
+
return pendingContexts.get(protocol)?.get(requestId)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function finishServerRequest (protocol, requestId) {
|
|
44
|
+
const pending = pendingContexts.get(protocol)
|
|
45
|
+
const ctx = pending?.get(requestId)
|
|
46
|
+
if (!ctx) return
|
|
47
|
+
|
|
48
|
+
pending.delete(requestId)
|
|
49
|
+
serverRequestCh.asyncEnd.publish(ctx)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function wrapRequestHandler (protocol, handler) {
|
|
53
|
+
if (wrappedRequestHandlers.has(handler)) return handler
|
|
54
|
+
|
|
55
|
+
const wrappedHandler = function requestHandlerWithTrace (request, extra) {
|
|
56
|
+
const ctx = getPendingContext(protocol, extra?.requestId)
|
|
57
|
+
if (!ctx) return handler.apply(this, arguments)
|
|
58
|
+
|
|
59
|
+
let result
|
|
60
|
+
try {
|
|
61
|
+
result = handler.apply(this, arguments)
|
|
62
|
+
} catch (err) {
|
|
63
|
+
ctx.error = err
|
|
64
|
+
throw err
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (result && typeof result.then === 'function') {
|
|
68
|
+
result.then(result => {
|
|
69
|
+
ctx.result = result
|
|
70
|
+
}, err => {
|
|
71
|
+
ctx.error = err
|
|
72
|
+
})
|
|
73
|
+
return result
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
ctx.result = result
|
|
77
|
+
return result
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
wrappedRequestHandlers.add(wrappedHandler)
|
|
81
|
+
return wrappedHandler
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function wrapRequestHandlers (protocol) {
|
|
85
|
+
const handlers = protocol._requestHandlers
|
|
86
|
+
if (!handlers || wrappedRequestHandlerMaps.has(handlers)) return
|
|
87
|
+
|
|
88
|
+
wrappedRequestHandlerMaps.add(handlers)
|
|
89
|
+
shimmer.wrap(handlers, 'set', function (original) {
|
|
90
|
+
return function setRequestHandlerWithTrace (method, handler) {
|
|
91
|
+
if (typeof handler === 'function') {
|
|
92
|
+
return original.call(this, method, wrapRequestHandler(protocol, handler))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return original.apply(this, arguments)
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function wrapAbortControllers (protocol) {
|
|
101
|
+
const controllers = protocol._requestHandlerAbortControllers
|
|
102
|
+
if (!controllers || wrappedAbortControllerMaps.has(controllers)) return
|
|
103
|
+
|
|
104
|
+
wrappedAbortControllerMaps.add(controllers)
|
|
105
|
+
shimmer.wrap(controllers, 'delete', function (original) {
|
|
106
|
+
return function deleteAbortControllerWithTrace (requestId) {
|
|
107
|
+
finishServerRequest(protocol, requestId)
|
|
108
|
+
return original.apply(this, arguments)
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
shimmer.wrap(controllers, 'clear', function (original) {
|
|
112
|
+
return function clearAbortControllersWithTrace () {
|
|
113
|
+
for (const requestId of this.keys()) {
|
|
114
|
+
finishServerRequest(protocol, requestId)
|
|
115
|
+
}
|
|
116
|
+
return original.apply(this, arguments)
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function wrapProtocol (Protocol) {
|
|
122
|
+
// Inject trace context into MCP request metadata so out-of-process MCP servers
|
|
123
|
+
// can parent server spans to the client operation span.
|
|
124
|
+
shimmer.wrap(Protocol.prototype, 'request', function (original) {
|
|
125
|
+
return function requestWithTraceContext (request, resultSchema, options) {
|
|
126
|
+
if (!clientRequestInjectCh.hasSubscribers || !TRACED_METHOD_PREFIX.test(request?.method)) {
|
|
127
|
+
return original.apply(this, arguments)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const ctx = {}
|
|
131
|
+
clientRequestInjectCh.publish(ctx)
|
|
132
|
+
|
|
133
|
+
if (!ctx.traceContext) {
|
|
134
|
+
return original.apply(this, arguments)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return original.call(this, addTraceContextToRequest(request, ctx.traceContext), resultSchema, options)
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
// Start spans in _onrequest — this runs inside the express POST /mcp handler's
|
|
142
|
+
// async context, so ALS correctly parents server spans under the HTTP span.
|
|
143
|
+
shimmer.wrap(Protocol.prototype, '_onrequest', function (original) {
|
|
144
|
+
return function _onrequestWithTrace (request, extra) {
|
|
145
|
+
if (!serverRequestCh.hasSubscribers || !TRACED_METHOD_PREFIX.test(request.method)) {
|
|
146
|
+
return original.call(this, request, extra)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
wrapAbortControllers(this)
|
|
150
|
+
const ctx = { request, extra }
|
|
151
|
+
serverRequestCh.start.runStores(ctx, () => {
|
|
152
|
+
let pending = pendingContexts.get(this)
|
|
153
|
+
if (!pending) {
|
|
154
|
+
pending = new Map()
|
|
155
|
+
pendingContexts.set(this, pending)
|
|
156
|
+
}
|
|
157
|
+
pending.set(request.id, ctx)
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
original.call(this, request, extra)
|
|
161
|
+
} catch (err) {
|
|
162
|
+
ctx.error = err
|
|
163
|
+
finishServerRequest(this, request.id)
|
|
164
|
+
throw err
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// The SDK registers an AbortController only when it actually dispatches a handler.
|
|
168
|
+
// If none was registered (MethodNotFound path), no handler will run and our
|
|
169
|
+
// SDK cleanup hook will never fire — finish the span immediately.
|
|
170
|
+
if (!this._requestHandlerAbortControllers?.has(request.id)) {
|
|
171
|
+
ctx.error = { name: 'McpError', message: 'Method not found' }
|
|
172
|
+
finishServerRequest(this, request.id)
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
// The SDK's handler closure Zod-parses the request, stripping the JSON-RPC `id`.
|
|
179
|
+
// Use `extra.requestId` (the SDK's fullExtra field) to correlate with the pending ctx.
|
|
180
|
+
shimmer.wrap(Protocol.prototype, 'setRequestHandler', function (original) {
|
|
181
|
+
return function setRequestHandlerWithTrace () {
|
|
182
|
+
wrapRequestHandlers(this)
|
|
183
|
+
return original.apply(this, arguments)
|
|
184
|
+
}
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
return Protocol
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function wrapMcpServer (McpServer) {
|
|
191
|
+
// Both public registration methods (tool/registerTool) delegate here — one hook covers both.
|
|
192
|
+
// Publishes to serverToolRegisteredCh so tracing.js can build a WeakMap of tool → name
|
|
193
|
+
// for O(1) lookup in McpServerToolCallPlugin (executeToolHandler receives the tool object,
|
|
194
|
+
// not the name string).
|
|
195
|
+
shimmer.wrap(McpServer.prototype, '_createRegisteredTool', function (original) {
|
|
196
|
+
return function (name) {
|
|
197
|
+
const result = original.apply(this, arguments)
|
|
198
|
+
if (serverToolRegisteredCh.hasSubscribers) {
|
|
199
|
+
const tool = this._registeredTools?.[name]
|
|
200
|
+
if (tool) {
|
|
201
|
+
serverToolRegisteredCh.publish({ tool, name })
|
|
202
|
+
shimmer.wrap(tool, 'update', function (update) {
|
|
203
|
+
return function updateWithTrace (updates) {
|
|
204
|
+
const result = update.apply(this, arguments)
|
|
205
|
+
if (updates && Object.hasOwn(updates, 'name')) {
|
|
206
|
+
serverToolRegisteredCh.publish({ tool, name: updates.name || undefined })
|
|
207
|
+
}
|
|
208
|
+
return result
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return result
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
return McpServer
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
addHook({
|
|
221
|
+
name: '@modelcontextprotocol/sdk',
|
|
222
|
+
versions: ['>=1.27.1'],
|
|
223
|
+
file: 'dist/cjs/shared/protocol.js',
|
|
224
|
+
}, exports => {
|
|
225
|
+
wrapProtocol(exports.Protocol)
|
|
226
|
+
return exports
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
addHook({
|
|
230
|
+
name: '@modelcontextprotocol/sdk',
|
|
231
|
+
versions: ['>=1.27.1'],
|
|
232
|
+
file: 'dist/esm/shared/protocol.js',
|
|
233
|
+
}, exports => {
|
|
234
|
+
wrapProtocol(exports.Protocol)
|
|
235
|
+
return exports
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
addHook({
|
|
239
|
+
name: '@modelcontextprotocol/sdk',
|
|
240
|
+
versions: ['>=1.27.1'],
|
|
241
|
+
file: 'dist/cjs/server/mcp.js',
|
|
242
|
+
}, exports => {
|
|
243
|
+
wrapMcpServer(exports.McpServer)
|
|
244
|
+
return exports
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
addHook({
|
|
248
|
+
name: '@modelcontextprotocol/sdk',
|
|
249
|
+
versions: ['>=1.27.1'],
|
|
250
|
+
file: 'dist/esm/server/mcp.js',
|
|
251
|
+
}, exports => {
|
|
252
|
+
wrapMcpServer(exports.McpServer)
|
|
253
|
+
return exports
|
|
254
|
+
})
|
|
@@ -105,6 +105,7 @@ const efdStartedOriginalTestKeys = new Set()
|
|
|
105
105
|
const efdSlowAbortedTests = new Set()
|
|
106
106
|
const ddPropertiesByTestId = new Map()
|
|
107
107
|
const ddPropertiesRequestsByTestId = new Map()
|
|
108
|
+
const disabledTestIds = new Set()
|
|
108
109
|
let rootDir = ''
|
|
109
110
|
let sessionProjects = []
|
|
110
111
|
|
|
@@ -113,6 +114,8 @@ const EFD_RETRY_COUNT_REQUEST = 'ddEfdRetryCountRequest'
|
|
|
113
114
|
const EFD_RETRY_COUNT_RESPONSE = 'ddEfdRetryCountResponse'
|
|
114
115
|
const DD_PROPERTIES_REQUEST = 'ddPropertiesRequest'
|
|
115
116
|
const DD_PROPERTIES_RESPONSE = 'ddProperties'
|
|
117
|
+
const kDdPlaywrightDisabledTestIds = Symbol('ddPlaywrightDisabledTestIds')
|
|
118
|
+
const kDdPlaywrightWorkerHostInstrumented = Symbol('ddPlaywrightWorkerHostInstrumented')
|
|
116
119
|
const kDdPlaywrightWorkerInstrumented = Symbol('ddPlaywrightWorkerInstrumented')
|
|
117
120
|
|
|
118
121
|
function isValidKnownTests (receivedKnownTests) {
|
|
@@ -991,14 +994,36 @@ function onDispatcherCreateWorker (dispatcher, worker) {
|
|
|
991
994
|
const projects = getProjectsFromDispatcher(dispatcher)
|
|
992
995
|
sessionProjects = projects
|
|
993
996
|
|
|
997
|
+
if (disabledTestIds.size && !worker[kDdPlaywrightWorkerHostInstrumented] &&
|
|
998
|
+
typeof worker.runTestGroup === 'function') {
|
|
999
|
+
Object.defineProperty(worker, kDdPlaywrightWorkerHostInstrumented, { value: true })
|
|
1000
|
+
shimmer.wrap(worker, 'runTestGroup', runTestGroup => function (runPayload) {
|
|
1001
|
+
// Serial retries can restore disabled tests that were filtered from the initial dispatcher groups.
|
|
1002
|
+
let disabledIds
|
|
1003
|
+
for (const { testId } of runPayload.entries) {
|
|
1004
|
+
if (disabledTestIds.has(testId)) {
|
|
1005
|
+
disabledIds ??= []
|
|
1006
|
+
disabledIds.push(testId)
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
if (disabledIds) {
|
|
1010
|
+
runPayload._ddDisabledTestIds = disabledIds
|
|
1011
|
+
}
|
|
1012
|
+
return runTestGroup.apply(this, arguments)
|
|
1013
|
+
})
|
|
1014
|
+
}
|
|
1015
|
+
|
|
994
1016
|
worker.on('testBegin', ({ testId }) => {
|
|
995
1017
|
const test = getTestByTestId(dispatcher, testId)
|
|
1018
|
+
if (!test) return
|
|
1019
|
+
|
|
996
1020
|
const browser = getBrowserNameFromProjects(projects, test)
|
|
997
1021
|
const shouldCreateTestSpan = test.expectedStatus === 'skipped'
|
|
998
1022
|
testBeginHandler(test, browser, shouldCreateTestSpan)
|
|
999
1023
|
})
|
|
1000
1024
|
worker.on('testEnd', ({ testId, status, errors, annotations }) => {
|
|
1001
1025
|
const test = getTestByTestId(dispatcher, testId)
|
|
1026
|
+
if (!test) return
|
|
1002
1027
|
|
|
1003
1028
|
const isTimeout = status === 'timedOut'
|
|
1004
1029
|
const testStatus = STATUS_TO_TEST_STATUS[status]
|
|
@@ -1308,6 +1333,7 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
1308
1333
|
efdSlowAbortedTests.clear()
|
|
1309
1334
|
ddPropertiesByTestId.clear()
|
|
1310
1335
|
ddPropertiesRequestsByTestId.clear()
|
|
1336
|
+
disabledTestIds.clear()
|
|
1311
1337
|
|
|
1312
1338
|
// TODO: we can trick playwright into thinking the session passed by returning
|
|
1313
1339
|
// 'passed' here. We might be able to use this for both EFD and Test Management tests.
|
|
@@ -1552,6 +1578,7 @@ function processRootSuite (createRootSuiteReturnValue) {
|
|
|
1552
1578
|
if (testProperties.disabled) {
|
|
1553
1579
|
test._ddIsDisabled = true
|
|
1554
1580
|
if (!testProperties.attemptToFix) {
|
|
1581
|
+
disabledTestIds.add(test.id)
|
|
1555
1582
|
test.expectedStatus = 'skipped'
|
|
1556
1583
|
// setting test.expectedStatus to 'skipped' does not work for every case,
|
|
1557
1584
|
// so we need to filter out disabled tests in dispatcherRunWrapperNew,
|
|
@@ -1795,7 +1822,17 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
1795
1822
|
let steps = []
|
|
1796
1823
|
const stepInfoByStepId = {}
|
|
1797
1824
|
|
|
1825
|
+
shimmer.wrap(workerMain, 'runTestGroup', runTestGroup => function (runPayload) {
|
|
1826
|
+
const disabledIds = runPayload._ddDisabledTestIds
|
|
1827
|
+
this[kDdPlaywrightDisabledTestIds] = disabledIds ? new Set(disabledIds) : undefined
|
|
1828
|
+
return runTestGroup.apply(this, arguments)
|
|
1829
|
+
})
|
|
1830
|
+
|
|
1798
1831
|
shimmer.wrap(workerMain, '_runTest', _runTest => async function (test) {
|
|
1832
|
+
if (this[kDdPlaywrightDisabledTestIds]?.has(test.id)) {
|
|
1833
|
+
test._ddIsDisabled = true
|
|
1834
|
+
test.expectedStatus = 'skipped'
|
|
1835
|
+
}
|
|
1799
1836
|
await waitForEfdRetryCount(test)
|
|
1800
1837
|
if (shouldSkipEfdRetry(test)) {
|
|
1801
1838
|
test._ddShouldSkipEfdRetry = true
|
|
@@ -2042,9 +2079,9 @@ function generateSummaryWrapper (generateSummary) {
|
|
|
2042
2079
|
return function (...args) {
|
|
2043
2080
|
for (const test of this.suite.allTests()) {
|
|
2044
2081
|
// https://github.com/microsoft/playwright/blob/bf92ffecff6f30a292b53430dbaee0207e0c61ad/packages/playwright/src/reporters/base.ts#L279
|
|
2045
|
-
const
|
|
2046
|
-
(!test.results.length || test.expectedStatus !== 'skipped')
|
|
2047
|
-
if (
|
|
2082
|
+
const shouldReportAsSkipped = test.outcome() === 'skipped' &&
|
|
2083
|
+
(disabledTestIds.has(test.id) || !test.results.length || test.expectedStatus !== 'skipped')
|
|
2084
|
+
if (shouldReportAsSkipped && !testsReportedInGenerateSummary.has(test)) {
|
|
2048
2085
|
testsReportedInGenerateSummary.add(test)
|
|
2049
2086
|
const {
|
|
2050
2087
|
_requireFile: testSuiteAbsolutePath,
|
|
@@ -65,6 +65,7 @@ class AzureFunctionsPlugin extends TracingPlugin {
|
|
|
65
65
|
service: this.serviceName(),
|
|
66
66
|
type: 'serverless',
|
|
67
67
|
meta,
|
|
68
|
+
childOf: null,
|
|
68
69
|
}, ctx)
|
|
69
70
|
|
|
70
71
|
if (isMessagingService) {
|
|
@@ -142,7 +143,7 @@ function mapTriggerTag (methodName) {
|
|
|
142
143
|
// message & messages & batch with cardinality of 1 == applicationProperties
|
|
143
144
|
// messages with cardinality of many == applicationPropertiesArray
|
|
144
145
|
function setSpanLinks (triggerType, tracer, span, ctx) {
|
|
145
|
-
const cardinality = ctx.invocationContext.options.trigger.cardinality
|
|
146
|
+
const cardinality = ctx.invocationContext.options.trigger.cardinality ?? 'one'
|
|
146
147
|
const triggerMetadata = ctx.invocationContext.triggerMetadata
|
|
147
148
|
const isServiceBus = triggerType === 'ServiceBus'
|
|
148
149
|
|
|
@@ -82,7 +82,7 @@ class GraphQLRequestPlugin extends TracingPlugin {
|
|
|
82
82
|
if (result?.errors?.length) {
|
|
83
83
|
span.setTag('error', result.errors[0])
|
|
84
84
|
for (const error of result.errors) {
|
|
85
|
-
extractErrorIntoSpanEvent(this.
|
|
85
|
+
extractErrorIntoSpanEvent(this.config, span, error)
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|