@apify/actors-mcp-server 0.11.7-beta.9 → 0.11.8-beta.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/dist/const.d.ts.map +1 -1
- package/dist/const.js +3 -1
- package/dist/const.js.map +1 -1
- package/dist/mcp/proxy.d.ts +1 -4
- package/dist/mcp/proxy.d.ts.map +1 -1
- package/dist/mcp/proxy.js +3 -8
- package/dist/mcp/proxy.js.map +1 -1
- package/dist/mcp/server.d.ts +10 -43
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +56 -430
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/task_execution.d.ts +42 -0
- package/dist/mcp/task_execution.d.ts.map +1 -0
- package/dist/mcp/task_execution.js +310 -0
- package/dist/mcp/task_execution.js.map +1 -0
- package/dist/mcp/tool_call_telemetry.d.ts +37 -0
- package/dist/mcp/tool_call_telemetry.d.ts.map +1 -0
- package/dist/mcp/tool_call_telemetry.js +85 -0
- package/dist/mcp/tool_call_telemetry.js.map +1 -0
- package/dist/mcp/utils.d.ts +1 -2
- package/dist/mcp/utils.d.ts.map +1 -1
- package/dist/mcp/utils.js +1 -2
- package/dist/mcp/utils.js.map +1 -1
- package/dist/resources/widgets.d.ts.map +1 -1
- package/dist/resources/widgets.js +3 -2
- package/dist/resources/widgets.js.map +1 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +3 -4
- package/dist/telemetry.js.map +1 -1
- package/dist/tools/actor_input_schema.d.ts +10 -20
- package/dist/tools/actor_input_schema.d.ts.map +1 -1
- package/dist/tools/actor_input_schema.js +10 -20
- package/dist/tools/actor_input_schema.js.map +1 -1
- package/dist/tools/actors/actor_definition.js +3 -5
- package/dist/tools/actors/actor_definition.js.map +1 -1
- package/dist/tools/actors/actor_tools_factory.d.ts +3 -8
- package/dist/tools/actors/actor_tools_factory.d.ts.map +1 -1
- package/dist/tools/actors/actor_tools_factory.js +5 -12
- package/dist/tools/actors/actor_tools_factory.js.map +1 -1
- package/dist/tools/actors/call_actor.d.ts.map +1 -1
- package/dist/tools/actors/call_actor.js +0 -1
- package/dist/tools/actors/call_actor.js.map +1 -1
- package/dist/tools/actors/fetch_actor_details.d.ts +2 -3
- package/dist/tools/actors/fetch_actor_details.d.ts.map +1 -1
- package/dist/tools/actors/fetch_actor_details.js +2 -3
- package/dist/tools/actors/fetch_actor_details.js.map +1 -1
- package/dist/tools/dev/report_problem.d.ts +13 -0
- package/dist/tools/dev/report_problem.d.ts.map +1 -1
- package/dist/tools/dev/report_problem.js +14 -0
- package/dist/tools/dev/report_problem.js.map +1 -1
- package/dist/tools/docs/search_apify_docs.d.ts.map +1 -1
- package/dist/tools/docs/search_apify_docs.js +0 -2
- package/dist/tools/docs/search_apify_docs.js.map +1 -1
- package/dist/tools/runs/get_actor_run_log.d.ts +1 -1
- package/dist/tools/runs/get_actor_run_log.js +1 -1
- package/dist/tools/storage/get_dataset_schema.d.ts.map +1 -1
- package/dist/tools/storage/get_dataset_schema.js +0 -1
- package/dist/tools/storage/get_dataset_schema.js.map +1 -1
- package/dist/tools/structured_output_schemas.d.ts +3 -0
- package/dist/tools/structured_output_schemas.d.ts.map +1 -1
- package/dist/tools/structured_output_schemas.js +29 -26
- package/dist/tools/structured_output_schemas.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +3 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/actor_card.d.ts.map +1 -1
- package/dist/utils/actor_card.js +0 -1
- package/dist/utils/actor_card.js.map +1 -1
- package/dist/utils/apify_docs.d.ts.map +1 -1
- package/dist/utils/apify_docs.js +0 -3
- package/dist/utils/apify_docs.js.map +1 -1
- package/dist/utils/auth.d.ts.map +1 -1
- package/dist/utils/auth.js +4 -14
- package/dist/utils/auth.js.map +1 -1
- package/dist/utils/logging.d.ts +2 -0
- package/dist/utils/logging.d.ts.map +1 -1
- package/dist/utils/logging.js +44 -0
- package/dist/utils/logging.js.map +1 -1
- package/dist/utils/progress.d.ts.map +1 -1
- package/dist/utils/progress.js +3 -2
- package/dist/utils/progress.js.map +1 -1
- package/dist/utils/tool_status.d.ts.map +1 -1
- package/dist/utils/tool_status.js +0 -1
- package/dist/utils/tool_status.js.map +1 -1
- package/dist/utils/ttl_lru.d.ts +2 -4
- package/dist/utils/ttl_lru.d.ts.map +1 -1
- package/dist/utils/ttl_lru.js +3 -8
- package/dist/utils/ttl_lru.js.map +1 -1
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/server.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -19,28 +19,28 @@ import { prompts } from '../prompts/index.js';
|
|
|
19
19
|
import { createResourceService } from '../resources/resource_service.js';
|
|
20
20
|
import { resolveAvailableWidgets } from '../resources/widgets.js';
|
|
21
21
|
import { getServerInfo } from '../server_card.js';
|
|
22
|
-
import {
|
|
22
|
+
import { getTelemetryEnv } from '../telemetry.js';
|
|
23
23
|
import { decodeDotPropertyNames } from '../tools/actor_input_schema.js';
|
|
24
24
|
import { legacyToolNameToNew } from '../tools/actor_tool_naming.js';
|
|
25
25
|
import { checkPaymentProviderStandbyConflict } from '../tools/actors/call_actor.js';
|
|
26
|
-
import {
|
|
26
|
+
import { withReportProblemNudge } from '../tools/dev/report_problem.js';
|
|
27
27
|
import { getActorsAsTools } from '../tools/index.js';
|
|
28
28
|
import { SERVER_MODE, TOOL_TYPE } from '../types.js';
|
|
29
29
|
import { isMcpClientFaultMessage, logHttpError, sanitizeMezmoMessage } from '../utils/logging.js';
|
|
30
|
-
import {
|
|
30
|
+
import { respondOk } from '../utils/mcp.js';
|
|
31
31
|
import { isReportProblemBlockedForClient } from '../utils/mcp_clients.js';
|
|
32
32
|
import { createProgressTracker } from '../utils/progress.js';
|
|
33
33
|
import { getServerInstructions } from '../utils/server-instructions/index.js';
|
|
34
34
|
import { parseServerMode, resolveServerMode } from '../utils/server_mode.js';
|
|
35
|
-
import {
|
|
35
|
+
import { extractAjvErrorDetails } from '../utils/tool_status.js';
|
|
36
36
|
import { buildActorFields, extractActorId, extractActorName, getToolFullName, getToolPublicFieldOnly, } from '../utils/tools.js';
|
|
37
37
|
import { getActors, getToolsForServerMode, toolNamesToInput } from '../utils/tools_loader.js';
|
|
38
|
-
import { getUserInfoCached } from '../utils/userid_cache.js';
|
|
39
|
-
import { getPackageVersion } from '../utils/version.js';
|
|
40
38
|
import { LOG_LEVEL_MAP } from './const.js';
|
|
39
|
+
import { emitTaskStatusNotification, executeToolAndUpdateTask } from './task_execution.js';
|
|
41
40
|
import { buildToolCallErrorResult, TOOL_CALL_ERROR_KIND } from './tool_call_error_mapper.js';
|
|
41
|
+
import { logToolCallAndTelemetry, prepareTelemetryData } from './tool_call_telemetry.js';
|
|
42
42
|
import { dispatchToolCall } from './tool_dispatch.js';
|
|
43
|
-
import {
|
|
43
|
+
import { parseInputParamsFromUrl, storeTaskResultOrSkipIfExpired } from './utils.js';
|
|
44
44
|
/**
|
|
45
45
|
* Returns true when the initialize request advertises the MCP Apps UI extension
|
|
46
46
|
* with the widget MIME type. Used to resolve `'auto'` server mode.
|
|
@@ -54,33 +54,6 @@ function isUiSupportedByClient(request) {
|
|
|
54
54
|
const uiCap = getUiCapability((_a = request === null || request === void 0 ? void 0 : request.params) === null || _a === void 0 ? void 0 : _a.capabilities);
|
|
55
55
|
return (_c = (_b = uiCap === null || uiCap === void 0 ? void 0 : uiCap.mimeTypes) === null || _b === void 0 ? void 0 : _b.includes(RESOURCE_MIME_TYPE)) !== null && _c !== void 0 ? _c : false;
|
|
56
56
|
}
|
|
57
|
-
/** Send notifications/tasks/status for taskId. Routes via session transport (no relatedRequestId).
|
|
58
|
-
* Swallows errors — notifications are advisory. */
|
|
59
|
-
export async function emitTaskStatusNotification(taskId, mcpSessionId, taskStore, server) {
|
|
60
|
-
try {
|
|
61
|
-
const task = await taskStore.getTask(taskId, mcpSessionId);
|
|
62
|
-
if (!task)
|
|
63
|
-
return;
|
|
64
|
-
// Per spec: notifications/tasks/status MUST NOT carry _meta.related-task (task ID is in params).
|
|
65
|
-
// Called without options so the notification routes through the session transport,
|
|
66
|
-
// not the request-scoped stream (which closes once the initial { task } response is flushed).
|
|
67
|
-
await server.notification({
|
|
68
|
-
method: 'notifications/tasks/status',
|
|
69
|
-
params: {
|
|
70
|
-
taskId: task.taskId,
|
|
71
|
-
status: task.status,
|
|
72
|
-
createdAt: task.createdAt,
|
|
73
|
-
lastUpdatedAt: task.lastUpdatedAt,
|
|
74
|
-
ttl: task.ttl,
|
|
75
|
-
...(task.statusMessage != null && { statusMessage: task.statusMessage }),
|
|
76
|
-
...(task.pollInterval != null && { pollInterval: task.pollInterval }),
|
|
77
|
-
},
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
// Silent fail — notifications are advisory
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
57
|
/**
|
|
85
58
|
* Shared diagnostics for a pre-flight failure (standby-provider conflict or missing/invalid payment
|
|
86
59
|
* signature) — a call outcome already known before any Actor runs. Standby rejection wins over the
|
|
@@ -197,18 +170,18 @@ export class ActorsMcpServer {
|
|
|
197
170
|
writable: true,
|
|
198
171
|
value: []
|
|
199
172
|
});
|
|
200
|
-
// Telemetry configuration (resolved from options and env vars
|
|
173
|
+
// Telemetry configuration (resolved from options and env vars, see setupTelemetry)
|
|
201
174
|
Object.defineProperty(this, "telemetryEnabled", {
|
|
202
175
|
enumerable: true,
|
|
203
176
|
configurable: true,
|
|
204
177
|
writable: true,
|
|
205
|
-
value:
|
|
178
|
+
value: void 0
|
|
206
179
|
});
|
|
207
180
|
Object.defineProperty(this, "telemetryEnv", {
|
|
208
181
|
enumerable: true,
|
|
209
182
|
configurable: true,
|
|
210
183
|
writable: true,
|
|
211
|
-
value:
|
|
184
|
+
value: void 0
|
|
212
185
|
});
|
|
213
186
|
// List of widgets that are ready to be served
|
|
214
187
|
Object.defineProperty(this, "availableWidgets", {
|
|
@@ -265,17 +238,16 @@ export class ActorsMcpServer {
|
|
|
265
238
|
},
|
|
266
239
|
},
|
|
267
240
|
},
|
|
268
|
-
|
|
269
|
-
* Declaring resources even though we are not using them
|
|
270
|
-
* to prevent clients like Claude desktop from failing.
|
|
271
|
-
*/
|
|
241
|
+
// Declared but unused — some clients (e.g. Claude Desktop) fail without it.
|
|
272
242
|
resources: {},
|
|
273
243
|
prompts: {},
|
|
274
244
|
logging: {},
|
|
275
245
|
},
|
|
276
246
|
instructions: getServerInstructions(),
|
|
277
247
|
});
|
|
278
|
-
this.setupTelemetry();
|
|
248
|
+
const { telemetryEnabled, telemetryEnv } = this.setupTelemetry();
|
|
249
|
+
this.telemetryEnabled = telemetryEnabled;
|
|
250
|
+
this.telemetryEnv = telemetryEnv;
|
|
279
251
|
this.setupInitializeHandler();
|
|
280
252
|
this.setupLoggingProxy();
|
|
281
253
|
this.tools = new Map();
|
|
@@ -283,9 +255,7 @@ export class ActorsMcpServer {
|
|
|
283
255
|
this.setupLoggingHandlers();
|
|
284
256
|
this.setupToolHandlers();
|
|
285
257
|
this.setupPromptHandlers();
|
|
286
|
-
|
|
287
|
-
* We need to handle resource requests to prevent clients like Claude desktop from failing.
|
|
288
|
-
*/
|
|
258
|
+
// Handle resource requests so clients like Claude Desktop don't fail.
|
|
289
259
|
this.setupResourceHandlers();
|
|
290
260
|
this.setupTaskHandlers();
|
|
291
261
|
}
|
|
@@ -294,18 +264,21 @@ export class ActorsMcpServer {
|
|
|
294
264
|
*/
|
|
295
265
|
setupTelemetry() {
|
|
296
266
|
var _a, _b, _c;
|
|
267
|
+
let telemetryEnabled;
|
|
297
268
|
const explicitEnabled = parseBooleanOrNull((_a = this.options.telemetry) === null || _a === void 0 ? void 0 : _a.enabled);
|
|
298
269
|
if (explicitEnabled !== null) {
|
|
299
|
-
|
|
270
|
+
telemetryEnabled = explicitEnabled;
|
|
300
271
|
}
|
|
301
272
|
else {
|
|
302
273
|
const envEnabled = parseBooleanOrNull(process.env.TELEMETRY_ENABLED);
|
|
303
|
-
|
|
274
|
+
telemetryEnabled = envEnabled !== null && envEnabled !== void 0 ? envEnabled : DEFAULT_TELEMETRY_ENABLED;
|
|
304
275
|
}
|
|
305
276
|
// Configure telemetryEnv: explicit option > env var > default ('PROD')
|
|
306
|
-
|
|
307
|
-
|
|
277
|
+
let telemetryEnv = DEFAULT_TELEMETRY_ENV;
|
|
278
|
+
if (telemetryEnabled) {
|
|
279
|
+
telemetryEnv = getTelemetryEnv((_c = (_b = this.options.telemetry) === null || _b === void 0 ? void 0 : _b.env) !== null && _c !== void 0 ? _c : process.env.TELEMETRY_ENV);
|
|
308
280
|
}
|
|
281
|
+
return { telemetryEnabled, telemetryEnv };
|
|
309
282
|
}
|
|
310
283
|
/**
|
|
311
284
|
* Override the SDK's `initialize` request handler to run mode resolution and
|
|
@@ -387,23 +360,10 @@ export class ActorsMcpServer {
|
|
|
387
360
|
* survive a load that never sees an initialize.
|
|
388
361
|
*/
|
|
389
362
|
isReportProblemServable() {
|
|
390
|
-
return (
|
|
363
|
+
return (this.telemetryEnabled &&
|
|
391
364
|
this.clientKnown &&
|
|
392
365
|
!isReportProblemBlockedForClient(this.options.initializeRequestData));
|
|
393
366
|
}
|
|
394
|
-
/**
|
|
395
|
-
* Append the report-problem nudge to a failed tool result. Thin wrapper over
|
|
396
|
-
* {@link appendReportProblemNudge} that fills in whether report-problem is served on this
|
|
397
|
-
* connection; suppression by category/402 is handled inside the helper.
|
|
398
|
-
*/
|
|
399
|
-
withReportProblemNudge(result, failingToolName, failureCategory, failureHttpStatus) {
|
|
400
|
-
return appendReportProblemNudge(result, {
|
|
401
|
-
failingToolName,
|
|
402
|
-
available: this.tools.has(HELPER_TOOLS.PROBLEM_REPORT),
|
|
403
|
-
failureCategory,
|
|
404
|
-
failureHttpStatus,
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
367
|
composePendingToolsForClient() {
|
|
408
368
|
if (this.pendingToolsUntilClientKnown.length === 0)
|
|
409
369
|
return;
|
|
@@ -418,7 +378,6 @@ export class ActorsMcpServer {
|
|
|
418
378
|
}
|
|
419
379
|
/**
|
|
420
380
|
* Returns an array of tool names.
|
|
421
|
-
* @returns {string[]} - An array of tool names.
|
|
422
381
|
*/
|
|
423
382
|
listToolNames() {
|
|
424
383
|
return Array.from(this.tools.keys());
|
|
@@ -448,8 +407,7 @@ export class ActorsMcpServer {
|
|
|
448
407
|
this.toolsChangedHandler = undefined;
|
|
449
408
|
}
|
|
450
409
|
/**
|
|
451
|
-
* Returns the list of all internal tool names
|
|
452
|
-
* @returns {string[]} - Array of loaded tool IDs (e.g., 'apify/rag-web-browser')
|
|
410
|
+
* Returns the list of all internal tool names (e.g., 'call-actor', 'search-actors').
|
|
453
411
|
*/
|
|
454
412
|
listInternalToolNames() {
|
|
455
413
|
return Array.from(this.tools.values())
|
|
@@ -457,8 +415,7 @@ export class ActorsMcpServer {
|
|
|
457
415
|
.map((tool) => tool.name);
|
|
458
416
|
}
|
|
459
417
|
/**
|
|
460
|
-
* Returns the
|
|
461
|
-
* @returns {string[]} - Array of loaded Actor tool IDs (e.g., 'apify/rag-web-browser')
|
|
418
|
+
* Returns the currently loaded Actor tool full names (e.g., 'apify/rag-web-browser').
|
|
462
419
|
*/
|
|
463
420
|
listActorToolNames() {
|
|
464
421
|
return Array.from(this.tools.values())
|
|
@@ -466,19 +423,17 @@ export class ActorsMcpServer {
|
|
|
466
423
|
.map((tool) => tool.actorFullName);
|
|
467
424
|
}
|
|
468
425
|
/**
|
|
469
|
-
* Returns
|
|
470
|
-
* @returns {string[]} - An array of Actor MCP server Actor IDs (e.g., 'apify/actors-mcp-server').
|
|
426
|
+
* Returns the unique Actor IDs registered as MCP servers (e.g., 'apify/actors-mcp-server').
|
|
471
427
|
*/
|
|
472
428
|
listActorMcpServerToolIds() {
|
|
473
429
|
const ids = Array.from(this.tools.values())
|
|
474
430
|
.filter((tool) => tool.type === TOOL_TYPE.ACTOR_MCP)
|
|
475
431
|
.map((tool) => tool.actorId);
|
|
476
|
-
// Ensure uniqueness
|
|
477
432
|
return Array.from(new Set(ids));
|
|
478
433
|
}
|
|
479
434
|
/**
|
|
480
|
-
* Returns
|
|
481
|
-
*
|
|
435
|
+
* Returns the combined internal tool names, Actor full names, and Actor-MCP server Actor IDs
|
|
436
|
+
* currently loaded.
|
|
482
437
|
*/
|
|
483
438
|
listAllToolNames() {
|
|
484
439
|
return [...this.listInternalToolNames(), ...this.listActorToolNames(), ...this.listActorMcpServerToolIds()];
|
|
@@ -512,7 +467,6 @@ export class ActorsMcpServer {
|
|
|
512
467
|
* Loads missing toolNames from a provided list of tool names.
|
|
513
468
|
* Skips toolNames that are already loaded and loads only the missing ones.
|
|
514
469
|
* @param toolNames - Array of tool names to ensure are loaded
|
|
515
|
-
* @param apifyClient
|
|
516
470
|
*/
|
|
517
471
|
async loadToolsByName(toolNames, apifyClient) {
|
|
518
472
|
const loadedTools = new Set(this.listAllToolNames());
|
|
@@ -603,10 +557,8 @@ export class ActorsMcpServer {
|
|
|
603
557
|
return tools;
|
|
604
558
|
}
|
|
605
559
|
notifyToolsChangedHandler() {
|
|
606
|
-
// If no handler is registered, do nothing
|
|
607
560
|
if (!this.toolsChangedHandler)
|
|
608
561
|
return;
|
|
609
|
-
// Get the list of tool names
|
|
610
562
|
this.toolsChangedHandler(this.listAllToolNames());
|
|
611
563
|
}
|
|
612
564
|
removeToolByName(toolName) {
|
|
@@ -640,13 +592,12 @@ export class ActorsMcpServer {
|
|
|
640
592
|
process.exit(0);
|
|
641
593
|
};
|
|
642
594
|
process.once('SIGINT', handler);
|
|
643
|
-
this.sigintHandler = handler;
|
|
595
|
+
this.sigintHandler = handler;
|
|
644
596
|
}
|
|
645
597
|
}
|
|
646
598
|
setupLoggingProxy() {
|
|
647
|
-
// Store original sendLoggingMessage
|
|
648
599
|
const originalSendLoggingMessage = this.server.sendLoggingMessage.bind(this.server);
|
|
649
|
-
//
|
|
600
|
+
// Filter outgoing log messages below the client's requested level.
|
|
650
601
|
this.server.sendLoggingMessage = async (params) => {
|
|
651
602
|
var _a, _b;
|
|
652
603
|
const messageLevelValue = (_a = LOG_LEVEL_MAP[params.level]) !== null && _a !== void 0 ? _a : -1; // Unknown levels get -1, discard
|
|
@@ -750,12 +701,13 @@ export class ActorsMcpServer {
|
|
|
750
701
|
}
|
|
751
702
|
/**
|
|
752
703
|
* Sets up MCP request handlers for long-running tasks.
|
|
704
|
+
* Each handler reads `_meta.mcpSessionId` (injected at the transport layer) to isolate
|
|
705
|
+
* per-session task stores.
|
|
753
706
|
*/
|
|
754
707
|
setupTaskHandlers() {
|
|
755
708
|
// List tasks
|
|
756
709
|
this.server.setRequestHandler(ListTasksRequestSchema, async (request) => {
|
|
757
710
|
var _a;
|
|
758
|
-
// mcpSessionId is injected at transport layer for session isolation in task stores
|
|
759
711
|
const params = (request.params || {});
|
|
760
712
|
const { cursor } = params;
|
|
761
713
|
const mcpSessionId = (_a = params._meta) === null || _a === void 0 ? void 0 : _a.mcpSessionId;
|
|
@@ -766,7 +718,6 @@ export class ActorsMcpServer {
|
|
|
766
718
|
// Get task status
|
|
767
719
|
this.server.setRequestHandler(GetTaskRequestSchema, async (request) => {
|
|
768
720
|
var _a;
|
|
769
|
-
// mcpSessionId is injected at transport layer for session isolation in task stores
|
|
770
721
|
const params = (request.params || {});
|
|
771
722
|
const { taskId } = params;
|
|
772
723
|
const mcpSessionId = (_a = params._meta) === null || _a === void 0 ? void 0 : _a.mcpSessionId;
|
|
@@ -776,7 +727,6 @@ export class ActorsMcpServer {
|
|
|
776
727
|
// Get task result payload
|
|
777
728
|
this.server.setRequestHandler(GetTaskPayloadRequestSchema, async (request) => {
|
|
778
729
|
var _a;
|
|
779
|
-
// mcpSessionId is injected at transport layer for session isolation in task stores
|
|
780
730
|
const params = (request.params || {});
|
|
781
731
|
const { taskId } = params;
|
|
782
732
|
const mcpSessionId = (_a = params._meta) === null || _a === void 0 ? void 0 : _a.mcpSessionId;
|
|
@@ -798,7 +748,6 @@ export class ActorsMcpServer {
|
|
|
798
748
|
// Cancel task
|
|
799
749
|
this.server.setRequestHandler(CancelTaskRequestSchema, async (request) => {
|
|
800
750
|
var _a;
|
|
801
|
-
// mcpSessionId is injected at transport layer for session isolation in task stores
|
|
802
751
|
const params = (request.params || {});
|
|
803
752
|
const { taskId } = params;
|
|
804
753
|
const mcpSessionId = (_a = params._meta) === null || _a === void 0 ? void 0 : _a.mcpSessionId;
|
|
@@ -822,11 +771,7 @@ export class ActorsMcpServer {
|
|
|
822
771
|
});
|
|
823
772
|
}
|
|
824
773
|
setupToolHandlers() {
|
|
825
|
-
|
|
826
|
-
* Handles the request to list tools.
|
|
827
|
-
* @param {object} request - The request object.
|
|
828
|
-
* @returns {object} - The response object containing the tools.
|
|
829
|
-
*/
|
|
774
|
+
// Handles the request to list tools.
|
|
830
775
|
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
831
776
|
const tools = Array.from(this.tools.values()).map((tool) => getToolPublicFieldOnly(tool, {
|
|
832
777
|
mode: this.serverMode,
|
|
@@ -835,10 +780,8 @@ export class ActorsMcpServer {
|
|
|
835
780
|
return { tools };
|
|
836
781
|
});
|
|
837
782
|
/**
|
|
838
|
-
* Handles the request to call a tool.
|
|
839
|
-
* @
|
|
840
|
-
* @param {object} extra - Extra data given to the request handler, such as sendNotification function.
|
|
841
|
-
* @throws {McpError} - based on the McpServer class code from the typescript MCP SDK
|
|
783
|
+
* Handles the request to call a tool. `extra` carries request-scoped helpers such as
|
|
784
|
+
* `sendNotification`. Throws {@link McpError} to mirror the MCP SDK's McpServer error codes.
|
|
842
785
|
*/
|
|
843
786
|
this.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
844
787
|
var _a, _b, _c, _d, _e;
|
|
@@ -847,7 +790,8 @@ export class ActorsMcpServer {
|
|
|
847
790
|
let { name, arguments: args, _meta: meta } = params;
|
|
848
791
|
const progressToken = meta === null || meta === void 0 ? void 0 : meta.progressToken;
|
|
849
792
|
const apifyToken = this.resolveApifyToken(meta);
|
|
850
|
-
//
|
|
793
|
+
// Injected upstream; required for long-running tasks — the task store keys on it and
|
|
794
|
+
// there is no other channel to pass it.
|
|
851
795
|
const mcpSessionId = meta === null || meta === void 0 ? void 0 : meta.mcpSessionId;
|
|
852
796
|
if (!mcpSessionId) {
|
|
853
797
|
log.error('MCP Session ID is missing in tool call request. This should never happen.');
|
|
@@ -868,7 +812,13 @@ export class ActorsMcpServer {
|
|
|
868
812
|
// On a failed result, nudge the agent to report the blocker via report-problem at the
|
|
869
813
|
// moment it decides what to do next. Gated on the tool actually being served (see
|
|
870
814
|
// isReportProblemServable), so clients where it is blocklisted or telemetry is off never see it.
|
|
871
|
-
const augmented =
|
|
815
|
+
const augmented = withReportProblemNudge({
|
|
816
|
+
result: r,
|
|
817
|
+
tools: this.tools,
|
|
818
|
+
failingToolName: resolvedToolName,
|
|
819
|
+
failureCategory: callDiagnostics.failure_category,
|
|
820
|
+
failureHttpStatus: callDiagnostics.failure_http_status,
|
|
821
|
+
});
|
|
872
822
|
toolResult = augmented;
|
|
873
823
|
return augmented;
|
|
874
824
|
};
|
|
@@ -890,7 +840,12 @@ export class ActorsMcpServer {
|
|
|
890
840
|
};
|
|
891
841
|
// Initialize telemetry with raw tool name — updated below once the tool is resolved.
|
|
892
842
|
// This ensures telemetry is available even for early failures (missing token, tool not found).
|
|
893
|
-
const { telemetryData, userId } = await
|
|
843
|
+
const { telemetryData, userId } = await prepareTelemetryData({
|
|
844
|
+
toolName: name,
|
|
845
|
+
mcpSessionId,
|
|
846
|
+
apifyToken,
|
|
847
|
+
apifyMcpServer: this,
|
|
848
|
+
});
|
|
894
849
|
// actorName/actorId are declared here so they're available in the catch block for telemetry.
|
|
895
850
|
// Set after tool resolution (inside the try block).
|
|
896
851
|
let actorName;
|
|
@@ -1058,7 +1013,7 @@ export class ActorsMcpServer {
|
|
|
1058
1013
|
}
|
|
1059
1014
|
// Execute the tool asynchronously and update task status
|
|
1060
1015
|
setImmediate(() => {
|
|
1061
|
-
|
|
1016
|
+
executeToolAndUpdateTask({
|
|
1062
1017
|
taskId: task.taskId,
|
|
1063
1018
|
tool,
|
|
1064
1019
|
toolArgs: toolArgs,
|
|
@@ -1070,6 +1025,7 @@ export class ActorsMcpServer {
|
|
|
1070
1025
|
mcpSessionId,
|
|
1071
1026
|
actorName,
|
|
1072
1027
|
actorId,
|
|
1028
|
+
apifyMcpServer: this,
|
|
1073
1029
|
}).catch((error) =>
|
|
1074
1030
|
// Benign task-expiry is handled in-method (see the catch block and
|
|
1075
1031
|
// storeTaskResultOrSkipIfExpired); anything reaching here is genuinely unexpected.
|
|
@@ -1182,7 +1138,7 @@ export class ActorsMcpServer {
|
|
|
1182
1138
|
}
|
|
1183
1139
|
finally {
|
|
1184
1140
|
if (shouldTrackTelemetry) {
|
|
1185
|
-
|
|
1141
|
+
logToolCallAndTelemetry({
|
|
1186
1142
|
toolName: resolvedToolName,
|
|
1187
1143
|
mcpSessionId,
|
|
1188
1144
|
toolStatus,
|
|
@@ -1193,340 +1149,12 @@ export class ActorsMcpServer {
|
|
|
1193
1149
|
args,
|
|
1194
1150
|
result: toolResult,
|
|
1195
1151
|
taskId: preflightTaskId,
|
|
1152
|
+
apifyMcpServer: this,
|
|
1196
1153
|
});
|
|
1197
1154
|
}
|
|
1198
1155
|
}
|
|
1199
1156
|
});
|
|
1200
1157
|
}
|
|
1201
|
-
/**
|
|
1202
|
-
* Logs tool call completion at INFO level and tracks telemetry.
|
|
1203
|
-
* Computes duration once so both the log line and telemetry event use the same value.
|
|
1204
|
-
* Response bytes and resource ids are derived here from the raw `result` (+ `args`) so every
|
|
1205
|
-
* call site stays a plain hand-off — no path can forget to compute or strip them.
|
|
1206
|
-
*/
|
|
1207
|
-
logToolCallAndTelemetry(params) {
|
|
1208
|
-
const durationMs = Date.now() - params.startTime;
|
|
1209
|
-
// `result` is undefined only on short-circuit paths that never produced a payload (e.g. a
|
|
1210
|
-
// cancelled task); skip byte accounting there. `null`/`{}` still measure as zero bytes.
|
|
1211
|
-
const responseBytes = params.result === undefined ? undefined : computeToolResponseBytes(params.result);
|
|
1212
|
-
log.info('Tool call completed', {
|
|
1213
|
-
toolName: params.toolName,
|
|
1214
|
-
mcpSessionId: params.mcpSessionId,
|
|
1215
|
-
toolStatus: params.toolStatus,
|
|
1216
|
-
durationMs,
|
|
1217
|
-
...(responseBytes !== undefined && {
|
|
1218
|
-
responseContentBytes: responseBytes.contentBytes,
|
|
1219
|
-
responseStructuredContentBytes: responseBytes.structuredContentBytes,
|
|
1220
|
-
responseFileBytes: responseBytes.fileBytes,
|
|
1221
|
-
}),
|
|
1222
|
-
...(params.taskId !== undefined && { taskId: params.taskId }),
|
|
1223
|
-
});
|
|
1224
|
-
if (params.telemetryData) {
|
|
1225
|
-
const finalizedTelemetryData = {
|
|
1226
|
-
...params.telemetryData,
|
|
1227
|
-
tool_status: params.toolStatus,
|
|
1228
|
-
tool_exec_time_ms: durationMs,
|
|
1229
|
-
...(responseBytes && {
|
|
1230
|
-
tool_response_content_bytes: responseBytes.contentBytes,
|
|
1231
|
-
tool_response_structured_content_bytes: responseBytes.structuredContentBytes,
|
|
1232
|
-
tool_response_file_bytes: responseBytes.fileBytes,
|
|
1233
|
-
}),
|
|
1234
|
-
// Always include actor_name/actor_id; failure-specific fields are only present when callDiagnostics has them.
|
|
1235
|
-
...params.callDiagnostics,
|
|
1236
|
-
// Resource ids are read once here from the args + the tool's public output; no tool
|
|
1237
|
-
// threads them back. Applied uniformly, last. See deriveResourceIds.
|
|
1238
|
-
...deriveResourceIds(params.args, params.result),
|
|
1239
|
-
};
|
|
1240
|
-
trackToolCall(params.userId, this.telemetryEnv, finalizedTelemetryData);
|
|
1241
|
-
// A successful report-problem call also emits a dedicated feedback event carrying the
|
|
1242
|
-
// submission. A downstream Segment destination fans it out to Slack/GitHub.
|
|
1243
|
-
if (params.toolName === HELPER_TOOLS.PROBLEM_REPORT &&
|
|
1244
|
-
params.toolStatus === TOOL_STATUS.SUCCEEDED &&
|
|
1245
|
-
params.args) {
|
|
1246
|
-
trackReportedProblem(params.userId, this.telemetryEnv, buildReportedProblemProperties(finalizedTelemetryData, params.args));
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
// TODO: outer orchestration here (pre-flight, telemetry bookkeeping, task-store handling) still duplicates the CallToolRequestSchema handler's logic; the dispatch ladder is now shared. Refactor.
|
|
1251
|
-
/**
|
|
1252
|
-
* Executes a tool asynchronously for a long-running task and updates task status.
|
|
1253
|
-
*
|
|
1254
|
-
* @param params - Tool execution parameters
|
|
1255
|
-
* @param params.taskId - The task identifier
|
|
1256
|
-
* @param params.tool - The tool to execute
|
|
1257
|
-
* @param params.args - Tool arguments
|
|
1258
|
-
* @param params.apifyToken - Apify API token
|
|
1259
|
-
* @param params.progressToken - Progress token for notifications
|
|
1260
|
-
* @param params.extra - Extra request handler context
|
|
1261
|
-
* @param params.mcpSessionId - MCP session ID for telemetry
|
|
1262
|
-
*/
|
|
1263
|
-
async executeToolAndUpdateTask(params) {
|
|
1264
|
-
const { taskId, tool, toolArgs, logSafeArgs, apifyClient, apifyToken, progressToken, extra, mcpSessionId, actorName, actorId, } = params;
|
|
1265
|
-
let toolStatus = TOOL_STATUS.SUCCEEDED;
|
|
1266
|
-
// Always populate actor fields so they're tracked on both success and failure paths.
|
|
1267
|
-
let callDiagnostics = { ...buildActorFields(actorName, actorId) };
|
|
1268
|
-
const startTime = Date.now();
|
|
1269
|
-
log.debug('[executeToolAndUpdateTask] Starting task execution', {
|
|
1270
|
-
taskId,
|
|
1271
|
-
toolName: tool.name,
|
|
1272
|
-
mcpSessionId,
|
|
1273
|
-
});
|
|
1274
|
-
// Prepare telemetry before try-catch so it's accessible to both paths.
|
|
1275
|
-
// This avoids re-fetching user data in the error handler.
|
|
1276
|
-
const { telemetryData, userId } = await this.prepareTelemetryData(getToolFullName(tool), mcpSessionId, apifyToken);
|
|
1277
|
-
const finishTaskTracking = (status, diagnostics, result) => {
|
|
1278
|
-
this.logToolCallAndTelemetry({
|
|
1279
|
-
toolName: tool.name,
|
|
1280
|
-
mcpSessionId,
|
|
1281
|
-
toolStatus: status,
|
|
1282
|
-
startTime,
|
|
1283
|
-
taskId,
|
|
1284
|
-
telemetryData,
|
|
1285
|
-
userId,
|
|
1286
|
-
callDiagnostics: diagnostics,
|
|
1287
|
-
args: toolArgs,
|
|
1288
|
-
result,
|
|
1289
|
-
});
|
|
1290
|
-
};
|
|
1291
|
-
// Terminal step shared by every task outcome: store the result (expiry-tolerant),
|
|
1292
|
-
// emit the status notification, record telemetry.
|
|
1293
|
-
const completeTask = async (storeStatus, taskResult, status, diagnostics) => {
|
|
1294
|
-
await storeTaskResultOrSkipIfExpired(this.taskStore, tool.name, taskId, storeStatus, taskResult, mcpSessionId);
|
|
1295
|
-
await emitTaskStatusNotification(taskId, mcpSessionId, this.taskStore, this.server);
|
|
1296
|
-
finishTaskTracking(status, diagnostics, taskResult);
|
|
1297
|
-
};
|
|
1298
|
-
// Once a task is cancelled the spec forbids writing a result; every storage path
|
|
1299
|
-
// must short-circuit here. `logSuffix` is concatenated after "Task was cancelled"
|
|
1300
|
-
// so we keep the existing log format and the existing telemetry status per path.
|
|
1301
|
-
const skipIfTaskCancelled = async (logSuffix, status, diagnostics) => {
|
|
1302
|
-
if (!(await isTaskCancelled(taskId, mcpSessionId, this.taskStore)))
|
|
1303
|
-
return false;
|
|
1304
|
-
log.debug(`[executeToolAndUpdateTask] Task was cancelled${logSuffix}`, { taskId, mcpSessionId });
|
|
1305
|
-
finishTaskTracking(status, diagnostics);
|
|
1306
|
-
return true;
|
|
1307
|
-
};
|
|
1308
|
-
// Bridges MCP `tasks/cancel` to the running handler: when the client
|
|
1309
|
-
// explicitly cancels the task, this signal aborts so the underlying
|
|
1310
|
-
// Actor run stops instead of consuming compute until natural completion.
|
|
1311
|
-
// Per MCP tasks spec, request-level aborts (client disconnect,
|
|
1312
|
-
// notifications/cancelled for the original request ID) MUST NOT cancel
|
|
1313
|
-
// the task — `extra.signal` is intentionally not chained here.
|
|
1314
|
-
const cancelWatcher = createTaskCancellationWatcher({
|
|
1315
|
-
taskId,
|
|
1316
|
-
mcpSessionId,
|
|
1317
|
-
taskStore: this.taskStore,
|
|
1318
|
-
});
|
|
1319
|
-
const taskExtra = { ...extra, signal: cancelWatcher.signal };
|
|
1320
|
-
try {
|
|
1321
|
-
log.debug('[executeToolAndUpdateTask] Updating task status to working', {
|
|
1322
|
-
taskId,
|
|
1323
|
-
mcpSessionId,
|
|
1324
|
-
});
|
|
1325
|
-
// The store rejects terminal → 'working' transitions. If `tasks/cancel` raced
|
|
1326
|
-
// with us (between handler dispatch and the first watcher tick at ~500 ms),
|
|
1327
|
-
// updateTaskStatus throws — re-check the store to tell a clean cancel-race
|
|
1328
|
-
// apart from a genuine store error.
|
|
1329
|
-
// noinspection ExceptionCaughtLocallyJS
|
|
1330
|
-
try {
|
|
1331
|
-
await this.taskStore.updateTaskStatus(taskId, 'working', undefined, mcpSessionId);
|
|
1332
|
-
}
|
|
1333
|
-
catch (err) {
|
|
1334
|
-
if (await skipIfTaskCancelled(' before execution started, skipping', TOOL_STATUS.ABORTED, {
|
|
1335
|
-
...buildActorFields(actorName, actorId),
|
|
1336
|
-
}))
|
|
1337
|
-
return;
|
|
1338
|
-
throw err;
|
|
1339
|
-
}
|
|
1340
|
-
await emitTaskStatusNotification(taskId, mcpSessionId, this.taskStore, this.server);
|
|
1341
|
-
// Execute the tool and get the result
|
|
1342
|
-
let result = {};
|
|
1343
|
-
// Callback to propagate Actor run statusMessage into the task store and emit a push notification.
|
|
1344
|
-
// TODO(TC-3): cancel arriving while this is scheduled throws cancelled → working;
|
|
1345
|
-
// currently swallowed by progress.ts's tick catch — guard or catch explicitly.
|
|
1346
|
-
const onStatusMessage = async (message) => {
|
|
1347
|
-
await this.taskStore.updateTaskStatus(taskId, 'working', message, mcpSessionId);
|
|
1348
|
-
await emitTaskStatusNotification(taskId, mcpSessionId, this.taskStore, this.server);
|
|
1349
|
-
};
|
|
1350
|
-
// ACTOR_MCP never reads the tracker (matching the sync path, which passes null for it);
|
|
1351
|
-
// INTERNAL/ACTOR get one built from taskId + onStatusMessage, which dispatch consumes
|
|
1352
|
-
// and stops.
|
|
1353
|
-
const progressTracker = tool.type === TOOL_TYPE.ACTOR_MCP
|
|
1354
|
-
? null
|
|
1355
|
-
: createProgressTracker(progressToken, extra.sendNotification, taskId, onStatusMessage);
|
|
1356
|
-
const dispatchResult = await dispatchToolCall({
|
|
1357
|
-
tool,
|
|
1358
|
-
toolArgs,
|
|
1359
|
-
logSafeArgs,
|
|
1360
|
-
apifyToken,
|
|
1361
|
-
apifyClient,
|
|
1362
|
-
apifyMcpServer: this,
|
|
1363
|
-
mcpSessionId,
|
|
1364
|
-
progressToken,
|
|
1365
|
-
progressTracker,
|
|
1366
|
-
shouldForwardNotifications: false,
|
|
1367
|
-
extra: taskExtra,
|
|
1368
|
-
actorName,
|
|
1369
|
-
actorId,
|
|
1370
|
-
taskMode: true,
|
|
1371
|
-
logContext: { messageSuffix: ' for task', fields: { taskId } },
|
|
1372
|
-
});
|
|
1373
|
-
result = dispatchResult.result;
|
|
1374
|
-
toolStatus = dispatchResult.toolStatus;
|
|
1375
|
-
callDiagnostics = dispatchResult.callDiagnostics;
|
|
1376
|
-
// Check if task was cancelled before storing result
|
|
1377
|
-
if (await skipIfTaskCancelled(', skipping result storage', toolStatus))
|
|
1378
|
-
return;
|
|
1379
|
-
// On a failed result, nudge the agent to report the blocker via report-problem (mirrors the
|
|
1380
|
-
// synchronous CallTool path, which task-mode calls like call-actor bypass).
|
|
1381
|
-
result = this.withReportProblemNudge(result, tool.name, callDiagnostics.failure_category, callDiagnostics.failure_http_status);
|
|
1382
|
-
// Store the result in the task store
|
|
1383
|
-
log.debug('[executeToolAndUpdateTask] Storing completed result', {
|
|
1384
|
-
taskId,
|
|
1385
|
-
mcpSessionId,
|
|
1386
|
-
});
|
|
1387
|
-
await completeTask('completed', result, toolStatus, callDiagnostics);
|
|
1388
|
-
log.debug('Task completed successfully', { taskId, toolName: tool.name, mcpSessionId });
|
|
1389
|
-
}
|
|
1390
|
-
catch (error) {
|
|
1391
|
-
// Reached only when the task expired before the `working` transition (updateTaskStatus
|
|
1392
|
-
// above rethrows the store's unknown-taskId error). The tool never ran and the task is
|
|
1393
|
-
// gone, so soft-fail, record telemetry, and stop. Every result store (success and error
|
|
1394
|
-
// paths) tolerates expiry via storeTaskResultOrSkipIfExpired, so they don't reach here.
|
|
1395
|
-
if (isTaskNotFoundError(error)) {
|
|
1396
|
-
log.softFail('Task expired before execution started', {
|
|
1397
|
-
taskId,
|
|
1398
|
-
toolName: tool.name,
|
|
1399
|
-
mcpSessionId,
|
|
1400
|
-
});
|
|
1401
|
-
finishTaskTracking(TOOL_STATUS.SOFT_FAIL, {
|
|
1402
|
-
failure_category: FAILURE_CATEGORY.INTERNAL_ERROR,
|
|
1403
|
-
...buildActorFields(actorName, actorId),
|
|
1404
|
-
});
|
|
1405
|
-
return;
|
|
1406
|
-
}
|
|
1407
|
-
const errorResult = buildToolCallErrorResult(error, {
|
|
1408
|
-
toolName: tool.name,
|
|
1409
|
-
actorName,
|
|
1410
|
-
actorId,
|
|
1411
|
-
isAborted: Boolean(cancelWatcher.signal.aborted),
|
|
1412
|
-
});
|
|
1413
|
-
// Handle 402 Payment Required — return structured x402 result so clients can auto-pay
|
|
1414
|
-
if (errorResult.kind === TOOL_CALL_ERROR_KIND.PAYMENT) {
|
|
1415
|
-
logHttpError(error, 'Payment required while calling tool (task mode)', { toolName: tool.name });
|
|
1416
|
-
// Per MCP tasks spec: once a task is cancelled it MUST remain cancelled,
|
|
1417
|
-
// so guard storeTaskResult against a cancel that raced with this 402.
|
|
1418
|
-
if (await skipIfTaskCancelled(', skipping 402 result storage', TOOL_STATUS.ABORTED, {
|
|
1419
|
-
...buildActorFields(actorName, actorId),
|
|
1420
|
-
}))
|
|
1421
|
-
return;
|
|
1422
|
-
await completeTask('completed', errorResult.response, errorResult.toolStatus, errorResult.callDiagnostics);
|
|
1423
|
-
return;
|
|
1424
|
-
}
|
|
1425
|
-
if (errorResult.kind === TOOL_CALL_ERROR_KIND.APPROVAL) {
|
|
1426
|
-
logHttpError(error, 'Permission approval required while calling tool (task mode)', {
|
|
1427
|
-
toolName: tool.name,
|
|
1428
|
-
});
|
|
1429
|
-
// Per MCP tasks spec: once a task is cancelled it MUST remain cancelled,
|
|
1430
|
-
// so guard storeTaskResult against a cancel that raced with this approval error.
|
|
1431
|
-
if (await skipIfTaskCancelled(', skipping permission-approval result storage', TOOL_STATUS.ABORTED, {
|
|
1432
|
-
...buildActorFields(actorName, actorId),
|
|
1433
|
-
}))
|
|
1434
|
-
return;
|
|
1435
|
-
await completeTask('completed', errorResult.response, errorResult.toolStatus, errorResult.callDiagnostics);
|
|
1436
|
-
return;
|
|
1437
|
-
}
|
|
1438
|
-
toolStatus = errorResult.toolStatus;
|
|
1439
|
-
callDiagnostics = errorResult.callDiagnostics;
|
|
1440
|
-
// Log level follows the already-classified toolStatus:
|
|
1441
|
-
// SOFT_FAIL (e.g. 402/403 user quota, client-side issues) → softFail
|
|
1442
|
-
// FAILED/ABORTED/other → error
|
|
1443
|
-
if (toolStatus === TOOL_STATUS.SOFT_FAIL) {
|
|
1444
|
-
// Mezmo promotes on "error" in message/keys — use errMessage key, sanitized.
|
|
1445
|
-
const errMessage = sanitizeMezmoMessage(error instanceof Error ? error.message : String(error));
|
|
1446
|
-
log.softFail('Tool execution soft-failed for task', {
|
|
1447
|
-
taskId,
|
|
1448
|
-
toolName: tool.name,
|
|
1449
|
-
toolStatus,
|
|
1450
|
-
mcpSessionId,
|
|
1451
|
-
failureCategory: callDiagnostics.failure_category,
|
|
1452
|
-
failureHttpStatus: callDiagnostics.failure_http_status,
|
|
1453
|
-
actorName: callDiagnostics.actor_name,
|
|
1454
|
-
errMessage,
|
|
1455
|
-
});
|
|
1456
|
-
}
|
|
1457
|
-
else {
|
|
1458
|
-
log.error('Error executing tool for task', {
|
|
1459
|
-
taskId,
|
|
1460
|
-
toolName: tool.name,
|
|
1461
|
-
toolStatus,
|
|
1462
|
-
mcpSessionId,
|
|
1463
|
-
failureCategory: callDiagnostics.failure_category,
|
|
1464
|
-
failureHttpStatus: callDiagnostics.failure_http_status,
|
|
1465
|
-
actorName: callDiagnostics.actor_name,
|
|
1466
|
-
error,
|
|
1467
|
-
});
|
|
1468
|
-
}
|
|
1469
|
-
const { userText } = errorResult;
|
|
1470
|
-
// Check if task was cancelled before storing result
|
|
1471
|
-
if (await skipIfTaskCancelled(', skipping result storage', toolStatus, callDiagnostics))
|
|
1472
|
-
return;
|
|
1473
|
-
log.debug('[executeToolAndUpdateTask] Storing failed result', {
|
|
1474
|
-
taskId,
|
|
1475
|
-
mcpSessionId,
|
|
1476
|
-
});
|
|
1477
|
-
// Nudge on a genuinely-failed task result (mirrors the completed path and the sync
|
|
1478
|
-
// captureResult). INTERNAL_ERROR and an unknown category get the full nudge; a genuine
|
|
1479
|
-
// INVALID_INPUT gets the softer nudge; payment (402) is suppressed via the HTTP status and
|
|
1480
|
-
// AUTH / PERMISSION_APPROVAL_REQUIRED via NON_NUDGE_FAILURE_CATEGORIES. The 402 branch above
|
|
1481
|
-
// returns before reaching here, so this only sees non-payment failures.
|
|
1482
|
-
const failedResult = this.withReportProblemNudge({
|
|
1483
|
-
content: [
|
|
1484
|
-
{
|
|
1485
|
-
type: 'text',
|
|
1486
|
-
text: userText,
|
|
1487
|
-
},
|
|
1488
|
-
],
|
|
1489
|
-
isError: true,
|
|
1490
|
-
internalToolStatus: toolStatus,
|
|
1491
|
-
}, tool.name, callDiagnostics.failure_category, callDiagnostics.failure_http_status);
|
|
1492
|
-
await completeTask('failed', failedResult, toolStatus, callDiagnostics);
|
|
1493
|
-
}
|
|
1494
|
-
finally {
|
|
1495
|
-
cancelWatcher.dispose();
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
|
-
/*
|
|
1499
|
-
* Creates telemetry data for a tool call.
|
|
1500
|
-
*/
|
|
1501
|
-
async prepareTelemetryData(toolName, mcpSessionId, apifyToken) {
|
|
1502
|
-
var _a, _b, _c, _d, _e;
|
|
1503
|
-
if (!this.telemetryEnabled) {
|
|
1504
|
-
return { telemetryData: null, userId: null };
|
|
1505
|
-
}
|
|
1506
|
-
// Get userId from cache or fetch from API
|
|
1507
|
-
let userId = null;
|
|
1508
|
-
if (apifyToken) {
|
|
1509
|
-
const apifyClient = new ApifyClient({ token: apifyToken });
|
|
1510
|
-
({ userId } = await getUserInfoCached(apifyToken, apifyClient));
|
|
1511
|
-
log.debug('Telemetry: fetched userId', { userId, mcpSessionId });
|
|
1512
|
-
}
|
|
1513
|
-
const capabilities = (_b = (_a = this.options.initializeRequestData) === null || _a === void 0 ? void 0 : _a.params) === null || _b === void 0 ? void 0 : _b.capabilities;
|
|
1514
|
-
const params = (_c = this.options.initializeRequestData) === null || _c === void 0 ? void 0 : _c.params;
|
|
1515
|
-
const telemetryData = {
|
|
1516
|
-
app: 'mcp',
|
|
1517
|
-
app_version: getPackageVersion() || '',
|
|
1518
|
-
mcp_client_name: ((_d = params === null || params === void 0 ? void 0 : params.clientInfo) === null || _d === void 0 ? void 0 : _d.name) || '',
|
|
1519
|
-
mcp_client_version: ((_e = params === null || params === void 0 ? void 0 : params.clientInfo) === null || _e === void 0 ? void 0 : _e.version) || '',
|
|
1520
|
-
mcp_protocol_version: (params === null || params === void 0 ? void 0 : params.protocolVersion) || '',
|
|
1521
|
-
mcp_client_capabilities: capabilities || null,
|
|
1522
|
-
mcp_session_id: mcpSessionId || '',
|
|
1523
|
-
transport_type: this.options.transportType || '',
|
|
1524
|
-
tool_name: toolName,
|
|
1525
|
-
tool_status: TOOL_STATUS.SUCCEEDED, // Will be updated in finally
|
|
1526
|
-
tool_exec_time_ms: 0, // Will be calculated in finally
|
|
1527
|
-
};
|
|
1528
|
-
return { telemetryData, userId };
|
|
1529
|
-
}
|
|
1530
1158
|
/**
|
|
1531
1159
|
* Resolves widgets and determines which ones are ready to be served.
|
|
1532
1160
|
*/
|
|
@@ -1573,23 +1201,21 @@ export class ActorsMcpServer {
|
|
|
1573
1201
|
await this.server.connect(transport);
|
|
1574
1202
|
}
|
|
1575
1203
|
async close() {
|
|
1576
|
-
// Remove SIGINT handler
|
|
1577
1204
|
if (this.sigintHandler) {
|
|
1578
1205
|
process.removeListener('SIGINT', this.sigintHandler);
|
|
1579
1206
|
this.sigintHandler = undefined;
|
|
1580
1207
|
}
|
|
1581
|
-
// Clear all tools and their compiled schemas
|
|
1208
|
+
// Clear all tools and null their compiled schemas.
|
|
1582
1209
|
for (const tool of this.tools.values()) {
|
|
1583
1210
|
if (tool.ajvValidate && typeof tool.ajvValidate === 'function') {
|
|
1584
1211
|
tool.ajvValidate = null;
|
|
1585
1212
|
}
|
|
1586
1213
|
}
|
|
1587
1214
|
this.tools.clear();
|
|
1588
|
-
// Unregister tools changed handler
|
|
1589
1215
|
if (this.toolsChangedHandler) {
|
|
1590
1216
|
this.unregisterToolsChangedHandler();
|
|
1591
1217
|
}
|
|
1592
|
-
//
|
|
1218
|
+
// Closing the server also removes its event handlers.
|
|
1593
1219
|
await this.server.close();
|
|
1594
1220
|
}
|
|
1595
1221
|
}
|