@apify/actors-mcp-server 0.10.5-beta.0 → 0.10.5-beta.10
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/apify_client.d.ts.map +1 -1
- package/dist/apify_client.js +16 -1
- package/dist/apify_client.js.map +1 -1
- package/dist/const.d.ts +1 -1
- package/dist/const.d.ts.map +1 -1
- package/dist/const.js +1 -3
- package/dist/const.js.map +1 -1
- package/dist/dev_server.d.ts.map +1 -1
- package/dist/dev_server.js +24 -5
- package/dist/dev_server.js.map +1 -1
- package/dist/mcp/server.d.ts +3 -2
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +96 -51
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/utils.d.ts +1 -1
- package/dist/mcp/utils.js +1 -1
- package/dist/tools/actor_executor.d.ts +10 -0
- package/dist/tools/actor_executor.d.ts.map +1 -0
- package/dist/tools/actor_executor.js +79 -0
- package/dist/tools/actor_executor.js.map +1 -0
- package/dist/tools/apps/call_actor.d.ts +0 -1
- package/dist/tools/apps/call_actor.d.ts.map +1 -1
- package/dist/tools/apps/call_actor.js +8 -54
- package/dist/tools/apps/call_actor.js.map +1 -1
- package/dist/tools/apps/call_actor_widget.d.ts.map +1 -1
- package/dist/tools/apps/call_actor_widget.js +8 -32
- package/dist/tools/apps/call_actor_widget.js.map +1 -1
- package/dist/tools/apps/get_actor_run_widget.d.ts.map +1 -1
- package/dist/tools/apps/get_actor_run_widget.js +15 -9
- package/dist/tools/apps/get_actor_run_widget.js.map +1 -1
- package/dist/tools/categories.d.ts +6 -0
- package/dist/tools/categories.d.ts.map +1 -1
- package/dist/tools/categories.js +7 -1
- package/dist/tools/categories.js.map +1 -1
- package/dist/tools/core/actor_run_response.d.ts +127 -0
- package/dist/tools/core/actor_run_response.d.ts.map +1 -0
- package/dist/tools/core/actor_run_response.js +551 -0
- package/dist/tools/core/actor_run_response.js.map +1 -0
- package/dist/tools/core/actor_tools_factory.d.ts +8 -3
- package/dist/tools/core/actor_tools_factory.d.ts.map +1 -1
- package/dist/tools/core/actor_tools_factory.js +39 -15
- package/dist/tools/core/actor_tools_factory.js.map +1 -1
- package/dist/tools/core/call_actor_common.d.ts +19 -39
- package/dist/tools/core/call_actor_common.d.ts.map +1 -1
- package/dist/tools/core/call_actor_common.js +196 -129
- package/dist/tools/core/call_actor_common.js.map +1 -1
- package/dist/tools/core/fetch_actor_details_common.d.ts.map +1 -1
- package/dist/tools/core/fetch_actor_details_common.js +1 -0
- package/dist/tools/core/fetch_actor_details_common.js.map +1 -1
- package/dist/tools/core/get_actor_run_common.d.ts +10 -49
- package/dist/tools/core/get_actor_run_common.d.ts.map +1 -1
- package/dist/tools/core/get_actor_run_common.js +51 -86
- package/dist/tools/core/get_actor_run_common.js.map +1 -1
- package/dist/tools/default/call_actor.d.ts +0 -2
- package/dist/tools/default/call_actor.d.ts.map +1 -1
- package/dist/tools/default/call_actor.js +5 -85
- package/dist/tools/default/call_actor.js.map +1 -1
- package/dist/tools/default/get_actor_run.d.ts +1 -2
- package/dist/tools/default/get_actor_run.d.ts.map +1 -1
- package/dist/tools/default/get_actor_run.js +12 -6
- package/dist/tools/default/get_actor_run.js.map +1 -1
- package/dist/tools/index.d.ts +1 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/structured_output_schemas.d.ts +258 -51
- package/dist/tools/structured_output_schemas.d.ts.map +1 -1
- package/dist/tools/structured_output_schemas.js +128 -68
- package/dist/tools/structured_output_schemas.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +26 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/mcp.d.ts +6 -0
- package/dist/utils/mcp.d.ts.map +1 -1
- package/dist/utils/mcp.js +30 -0
- package/dist/utils/mcp.js.map +1 -1
- package/dist/utils/progress.d.ts +1 -0
- package/dist/utils/progress.d.ts.map +1 -1
- package/dist/utils/progress.js +2 -2
- package/dist/utils/progress.js.map +1 -1
- package/dist/utils/server-instructions/index.d.ts.map +1 -1
- package/dist/utils/server-instructions/index.js +6 -7
- package/dist/utils/server-instructions/index.js.map +1 -1
- package/dist/utils/tools_loader.d.ts.map +1 -1
- package/dist/utils/tools_loader.js +9 -3
- package/dist/utils/tools_loader.js.map +1 -1
- package/dist/web/dist/actor-detail-widget.js +3 -0
- package/dist/web/dist/actor-run-widget.js +219 -216
- package/dist/web/dist/search-actors-widget.js +3 -0
- package/package.json +7 -7
- package/dist/tools/apps/actor_executor.d.ts +0 -8
- package/dist/tools/apps/actor_executor.d.ts.map +0 -1
- package/dist/tools/apps/actor_executor.js +0 -29
- package/dist/tools/apps/actor_executor.js.map +0 -1
- package/dist/tools/core/actor_execution.d.ts +0 -39
- package/dist/tools/core/actor_execution.d.ts.map +0 -1
- package/dist/tools/core/actor_execution.js +0 -135
- package/dist/tools/core/actor_execution.js.map +0 -1
- package/dist/tools/core/actor_response.d.ts +0 -37
- package/dist/tools/core/actor_response.d.ts.map +0 -1
- package/dist/tools/core/actor_response.js +0 -94
- package/dist/tools/core/actor_response.js.map +0 -1
- package/dist/tools/default/actor_executor.d.ts +0 -7
- package/dist/tools/default/actor_executor.d.ts.map +0 -1
- package/dist/tools/default/actor_executor.js +0 -31
- package/dist/tools/default/actor_executor.js.map +0 -1
package/dist/mcp/server.js
CHANGED
|
@@ -8,7 +8,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
8
8
|
import { getUiCapability, RESOURCE_MIME_TYPE } from '@modelcontextprotocol/ext-apps/server';
|
|
9
9
|
import { InMemoryTaskStore } from '@modelcontextprotocol/sdk/experimental/tasks/stores/in-memory.js';
|
|
10
10
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
11
|
-
import { CallToolRequestSchema, CallToolResultSchema, CancelTaskRequestSchema, ErrorCode, GetPromptRequestSchema, GetTaskPayloadRequestSchema, GetTaskRequestSchema, InitializeRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListTasksRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema, ServerNotificationSchema, SetLevelRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
11
|
+
import { CallToolRequestSchema, CallToolResultSchema, CancelTaskRequestSchema, ErrorCode, GetPromptRequestSchema, GetTaskPayloadRequestSchema, GetTaskRequestSchema, InitializeRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListTasksRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema, RELATED_TASK_META_KEY, ServerNotificationSchema, SetLevelRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
12
12
|
import dedent from 'dedent';
|
|
13
13
|
import log from '@apify/log';
|
|
14
14
|
import { parseBooleanOrNull } from '@apify/utilities';
|
|
@@ -19,14 +19,13 @@ 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 { getTelemetryEnv, trackToolCall } from '../telemetry.js';
|
|
22
|
-
import {
|
|
22
|
+
import { actorExecutor } from '../tools/actor_executor.js';
|
|
23
23
|
import { buildPermissionApprovalResponse, isPermissionApprovalError } from '../tools/core/call_actor_common.js';
|
|
24
|
-
import { defaultActorExecutor } from '../tools/default/actor_executor.js';
|
|
25
24
|
import { getActorsAsTools } from '../tools/index.js';
|
|
26
25
|
import { decodeDotPropertyNames, legacyToolNameToNew } from '../tools/utils.js';
|
|
27
26
|
import { ServerMode } from '../types.js';
|
|
28
27
|
import { getHttpStatusCode, logHttpError } from '../utils/logging.js';
|
|
29
|
-
import { buildMCPResponse, getToolCallErrorUserText } from '../utils/mcp.js';
|
|
28
|
+
import { buildMCPResponse, computeToolResponseSizeBytes, getToolCallErrorUserText } from '../utils/mcp.js';
|
|
30
29
|
import { buildPaymentRequiredResponse } from '../utils/payment_errors.js';
|
|
31
30
|
import { createProgressTracker } from '../utils/progress.js';
|
|
32
31
|
import { parseServerMode, resolveServerMode } from '../utils/server_mode.js';
|
|
@@ -39,11 +38,6 @@ import { getPackageVersion } from '../utils/version.js';
|
|
|
39
38
|
import { connectMCPClient } from './client.js';
|
|
40
39
|
import { EXTERNAL_TOOL_CALL_TIMEOUT_MSEC, LOG_LEVEL_MAP } from './const.js';
|
|
41
40
|
import { createTaskCancellationWatcher, isTaskCancelled, parseInputParamsFromUrl } from './utils.js';
|
|
42
|
-
/** Mode → actor executor. Add new modes here. */
|
|
43
|
-
const actorExecutorsByMode = {
|
|
44
|
-
[ServerMode.DEFAULT]: defaultActorExecutor,
|
|
45
|
-
[ServerMode.APPS]: appsActorExecutor,
|
|
46
|
-
};
|
|
47
41
|
/**
|
|
48
42
|
* Returns true when the initialize request advertises the MCP Apps UI extension
|
|
49
43
|
* with the widget MIME type. Used to resolve `'auto'` server mode.
|
|
@@ -57,6 +51,33 @@ function isUiSupportedByClient(request) {
|
|
|
57
51
|
const uiCap = getUiCapability((_a = request === null || request === void 0 ? void 0 : request.params) === null || _a === void 0 ? void 0 : _a.capabilities);
|
|
58
52
|
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;
|
|
59
53
|
}
|
|
54
|
+
/** Send notifications/tasks/status for taskId. Routes via session transport (no relatedRequestId).
|
|
55
|
+
* Swallows errors — notifications are advisory. */
|
|
56
|
+
export async function emitTaskStatusNotification(taskId, mcpSessionId, taskStore, server) {
|
|
57
|
+
try {
|
|
58
|
+
const task = await taskStore.getTask(taskId, mcpSessionId);
|
|
59
|
+
if (!task)
|
|
60
|
+
return;
|
|
61
|
+
// Per spec: notifications/tasks/status MUST NOT carry _meta.related-task (task ID is in params).
|
|
62
|
+
// Called without options so the notification routes through the session transport,
|
|
63
|
+
// not the request-scoped stream (which closes once the initial { task } response is flushed).
|
|
64
|
+
await server.notification({
|
|
65
|
+
method: 'notifications/tasks/status',
|
|
66
|
+
params: {
|
|
67
|
+
taskId: task.taskId,
|
|
68
|
+
status: task.status,
|
|
69
|
+
createdAt: task.createdAt,
|
|
70
|
+
lastUpdatedAt: task.lastUpdatedAt,
|
|
71
|
+
ttl: task.ttl,
|
|
72
|
+
...(task.statusMessage != null && { statusMessage: task.statusMessage }),
|
|
73
|
+
...(task.pollInterval != null && { pollInterval: task.pollInterval }),
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Silent fail — notifications are advisory
|
|
79
|
+
}
|
|
80
|
+
}
|
|
60
81
|
/**
|
|
61
82
|
* Create Apify MCP server
|
|
62
83
|
*/
|
|
@@ -121,13 +142,6 @@ export class ActorsMcpServer {
|
|
|
121
142
|
writable: true,
|
|
122
143
|
value: void 0
|
|
123
144
|
});
|
|
124
|
-
/** Mode-specific executor for direct actor tools (`type: 'actor'`). Finalized with `serverMode`. */
|
|
125
|
-
Object.defineProperty(this, "actorExecutor", {
|
|
126
|
-
enumerable: true,
|
|
127
|
-
configurable: true,
|
|
128
|
-
writable: true,
|
|
129
|
-
value: void 0
|
|
130
|
-
});
|
|
131
145
|
/**
|
|
132
146
|
* Raw option captured from `options.serverMode` (or the legacy `uiMode`). Re-resolved
|
|
133
147
|
* inside the initialize handler when set to `'auto'`; explicit `'default'`/`'apps'`
|
|
@@ -211,7 +225,6 @@ export class ActorsMcpServer {
|
|
|
211
225
|
// client capabilities are known (only for 'auto').
|
|
212
226
|
this.serverMode = resolveServerMode(this.serverModeOption, false);
|
|
213
227
|
this.serverModeResolved = this.serverModeOption !== 'auto';
|
|
214
|
-
this.actorExecutor = actorExecutorsByMode[this.serverMode];
|
|
215
228
|
const { setupSigintHandler = true } = options;
|
|
216
229
|
this.server = new Server({
|
|
217
230
|
name: SERVER_NAME,
|
|
@@ -300,7 +313,6 @@ export class ActorsMcpServer {
|
|
|
300
313
|
const resolved = resolveServerMode('auto', this.clientSupportsUi);
|
|
301
314
|
if (resolved !== this.serverMode) {
|
|
302
315
|
this.serverMode = resolved;
|
|
303
|
-
this.actorExecutor = actorExecutorsByMode[this.serverMode];
|
|
304
316
|
}
|
|
305
317
|
this.serverModeResolved = true;
|
|
306
318
|
}
|
|
@@ -684,7 +696,15 @@ export class ActorsMcpServer {
|
|
|
684
696
|
if (task.status !== 'completed' && task.status !== 'failed') {
|
|
685
697
|
throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" is not completed yet. Current status: ${task.status}`);
|
|
686
698
|
}
|
|
687
|
-
|
|
699
|
+
const result = await this.taskStore.getTaskResult(taskId, mcpSessionId);
|
|
700
|
+
// taskId is not in the result body — _meta.related-task lets clients correlate them
|
|
701
|
+
return {
|
|
702
|
+
...result,
|
|
703
|
+
_meta: {
|
|
704
|
+
...result._meta,
|
|
705
|
+
[RELATED_TASK_META_KEY]: { taskId },
|
|
706
|
+
},
|
|
707
|
+
};
|
|
688
708
|
});
|
|
689
709
|
// Cancel task
|
|
690
710
|
this.server.setRequestHandler(CancelTaskRequestSchema, async (request) => {
|
|
@@ -713,6 +733,7 @@ export class ActorsMcpServer {
|
|
|
713
733
|
await this.taskStore.updateTaskStatus(taskId, 'cancelled', 'Cancelled by client', mcpSessionId);
|
|
714
734
|
const updatedTask = await this.taskStore.getTask(taskId, mcpSessionId);
|
|
715
735
|
log.debug('[CancelTaskRequestSchema] Task cancelled successfully', { taskId, mcpSessionId });
|
|
736
|
+
await emitTaskStatusNotification(taskId, mcpSessionId, this.taskStore, this.server);
|
|
716
737
|
return updatedTask;
|
|
717
738
|
});
|
|
718
739
|
}
|
|
@@ -755,6 +776,12 @@ export class ActorsMcpServer {
|
|
|
755
776
|
let callDiagnostics = {};
|
|
756
777
|
let shouldTrackTelemetry = true;
|
|
757
778
|
let resolvedToolName = name;
|
|
779
|
+
// Captured by `captureResult` so the `finally` block can measure response size for telemetry.
|
|
780
|
+
let toolResult = null;
|
|
781
|
+
const captureResult = (r) => {
|
|
782
|
+
toolResult = r;
|
|
783
|
+
return r;
|
|
784
|
+
};
|
|
758
785
|
const failInvalidParams = async (message, details, logFields) => {
|
|
759
786
|
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
760
787
|
callDiagnostics = details;
|
|
@@ -869,11 +896,11 @@ export class ActorsMcpServer {
|
|
|
869
896
|
if (request.params.task) {
|
|
870
897
|
const task = await this.taskStore.createTask({
|
|
871
898
|
ttl: request.params.task.ttl,
|
|
872
|
-
}, `call-tool-${name}-${randomUUID()}`, request);
|
|
899
|
+
}, `call-tool-${name}-${randomUUID()}`, request, mcpSessionId);
|
|
873
900
|
log.debug('Created task for tool execution', { taskId: task.taskId, toolName: tool.name, mcpSessionId });
|
|
874
901
|
// Execute the tool asynchronously and update task status
|
|
875
|
-
setImmediate(
|
|
876
|
-
|
|
902
|
+
setImmediate(() => {
|
|
903
|
+
this.executeToolAndUpdateTask({
|
|
877
904
|
taskId: task.taskId,
|
|
878
905
|
tool,
|
|
879
906
|
toolArgs: toolArgs,
|
|
@@ -887,7 +914,7 @@ export class ActorsMcpServer {
|
|
|
887
914
|
actorName,
|
|
888
915
|
actorId,
|
|
889
916
|
userRentedActorIds,
|
|
890
|
-
});
|
|
917
|
+
}).catch((error) => log.error('executeToolAndUpdateTask failed unexpectedly', { taskId: task.taskId, error }));
|
|
891
918
|
});
|
|
892
919
|
// Return the task immediately; execution continues asynchronously
|
|
893
920
|
shouldTrackTelemetry = false;
|
|
@@ -901,12 +928,15 @@ export class ActorsMcpServer {
|
|
|
901
928
|
failure_http_status: 402,
|
|
902
929
|
...buildActorFields(actorName, actorId),
|
|
903
930
|
};
|
|
904
|
-
return paymentRequiredResult;
|
|
931
|
+
return captureResult(paymentRequiredResult);
|
|
905
932
|
}
|
|
906
933
|
// Handle internal tool
|
|
907
934
|
if (tool.type === 'internal') {
|
|
908
|
-
//
|
|
909
|
-
|
|
935
|
+
// Tools that may emit notifications/progress during a sync wait must be opted in here.
|
|
936
|
+
// call-actor: emits during start+waitForFinish. get-actor-run: emits when waitSecs > 0.
|
|
937
|
+
const progressTrackerOptIn = tool.name === HelperTools.ACTOR_CALL
|
|
938
|
+
|| tool.name === HelperTools.ACTOR_RUNS_GET;
|
|
939
|
+
const progressTracker = progressTrackerOptIn
|
|
910
940
|
? createProgressTracker(progressToken, extra.sendNotification)
|
|
911
941
|
: null;
|
|
912
942
|
try {
|
|
@@ -926,7 +956,7 @@ export class ActorsMcpServer {
|
|
|
926
956
|
const diag = extractToolTelemetry(res, actorName, actorId);
|
|
927
957
|
toolStatus = diag.toolStatus;
|
|
928
958
|
callDiagnostics = { ...callDiagnostics, ...diag.callDiagnostics };
|
|
929
|
-
return res;
|
|
959
|
+
return captureResult(res);
|
|
930
960
|
}
|
|
931
961
|
finally {
|
|
932
962
|
progressTracker === null || progressTracker === void 0 ? void 0 : progressTracker.stop();
|
|
@@ -945,7 +975,7 @@ export class ActorsMcpServer {
|
|
|
945
975
|
await this.server.sendLoggingMessage({ level: 'error', data: msg });
|
|
946
976
|
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
947
977
|
callDiagnostics = { ...callDiagnostics, failure_category: FAILURE_CATEGORY.INTERNAL_ERROR };
|
|
948
|
-
return buildMCPResponse({ texts: [msg], isError: true });
|
|
978
|
+
return captureResult(buildMCPResponse({ texts: [msg], isError: true }));
|
|
949
979
|
}
|
|
950
980
|
// Only set up notification handlers if progressToken is provided by the client
|
|
951
981
|
if (progressToken !== undefined && progressToken !== null) {
|
|
@@ -985,7 +1015,7 @@ export class ActorsMcpServer {
|
|
|
985
1015
|
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
986
1016
|
callDiagnostics = { failure_category: FAILURE_CATEGORY.INTERNAL_ERROR, ...buildActorFields(actorName, actorId) };
|
|
987
1017
|
}
|
|
988
|
-
return { ...res };
|
|
1018
|
+
return captureResult({ ...res });
|
|
989
1019
|
}
|
|
990
1020
|
catch (error) {
|
|
991
1021
|
toolStatus = getToolStatusFromError(error, Boolean((_e = extra.signal) === null || _e === void 0 ? void 0 : _e.aborted));
|
|
@@ -1000,10 +1030,10 @@ export class ActorsMcpServer {
|
|
|
1000
1030
|
toolName: tool.originToolName,
|
|
1001
1031
|
failureCategory: callDiagnostics.failure_category,
|
|
1002
1032
|
});
|
|
1003
|
-
return buildMCPResponse({
|
|
1033
|
+
return captureResult(buildMCPResponse({
|
|
1004
1034
|
texts: [`Failed to call MCP tool '${tool.originToolName}' on Actor '${tool.actorId}': ${error instanceof Error ? error.message : String(error)}. The MCP server may be temporarily unavailable.`],
|
|
1005
1035
|
isError: true,
|
|
1006
|
-
});
|
|
1036
|
+
}));
|
|
1007
1037
|
}
|
|
1008
1038
|
finally {
|
|
1009
1039
|
if (client)
|
|
@@ -1015,7 +1045,7 @@ export class ActorsMcpServer {
|
|
|
1015
1045
|
const progressTracker = createProgressTracker(progressToken, extra.sendNotification);
|
|
1016
1046
|
try {
|
|
1017
1047
|
log.info('Calling Actor', { toolName: tool.name, actorName: tool.actorFullName, mcpSessionId, input: logSafeArgs });
|
|
1018
|
-
const executorResult = await
|
|
1048
|
+
const executorResult = await actorExecutor.executeActorTool({
|
|
1019
1049
|
actorFullName: tool.actorFullName,
|
|
1020
1050
|
input: toolArgs,
|
|
1021
1051
|
apifyClient: apifyClient,
|
|
@@ -1023,14 +1053,15 @@ export class ActorsMcpServer {
|
|
|
1023
1053
|
progressTracker,
|
|
1024
1054
|
abortSignal: extra.signal,
|
|
1025
1055
|
mcpSessionId,
|
|
1056
|
+
datasetItemsSchema: tool.datasetItemsSchema,
|
|
1026
1057
|
});
|
|
1027
1058
|
if (!executorResult) {
|
|
1028
1059
|
toolStatus = TOOL_STATUS.ABORTED;
|
|
1029
1060
|
// Receivers of cancellation notifications SHOULD NOT send a response for the cancelled request
|
|
1030
1061
|
// https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/cancellation#behavior-requirements
|
|
1031
|
-
return {};
|
|
1062
|
+
return captureResult({});
|
|
1032
1063
|
}
|
|
1033
|
-
return executorResult;
|
|
1064
|
+
return captureResult(executorResult);
|
|
1034
1065
|
}
|
|
1035
1066
|
finally {
|
|
1036
1067
|
if (progressTracker) {
|
|
@@ -1052,7 +1083,7 @@ export class ActorsMcpServer {
|
|
|
1052
1083
|
failure_http_status: 402,
|
|
1053
1084
|
...buildActorFields(actorName, actorId),
|
|
1054
1085
|
};
|
|
1055
|
-
return buildPaymentRequiredResponse(error);
|
|
1086
|
+
return captureResult(buildPaymentRequiredResponse(error));
|
|
1056
1087
|
}
|
|
1057
1088
|
if (isPermissionApprovalError(error)) {
|
|
1058
1089
|
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
@@ -1062,7 +1093,7 @@ export class ActorsMcpServer {
|
|
|
1062
1093
|
...buildActorFields(actorName, actorId),
|
|
1063
1094
|
};
|
|
1064
1095
|
logHttpError(error, 'Permission approval required while calling tool', { toolName: name, mcpSessionId });
|
|
1065
|
-
return buildPermissionApprovalResponse(error);
|
|
1096
|
+
return captureResult(buildPermissionApprovalResponse(error));
|
|
1066
1097
|
}
|
|
1067
1098
|
// Re-throw MCP protocol errors (e.g. from failInvalidParams) so the SDK
|
|
1068
1099
|
// returns them as JSON-RPC errors. failInvalidParams already set callDiagnostics
|
|
@@ -1093,11 +1124,11 @@ export class ActorsMcpServer {
|
|
|
1093
1124
|
validationMissingProperty: callDiagnostics.validation_missing_property,
|
|
1094
1125
|
validationAdditionalProperty: callDiagnostics.validation_additional_property,
|
|
1095
1126
|
});
|
|
1096
|
-
return buildMCPResponse({
|
|
1127
|
+
return captureResult(buildMCPResponse({
|
|
1097
1128
|
texts: [getToolCallErrorUserText(name, error)],
|
|
1098
1129
|
isError: true,
|
|
1099
1130
|
telemetry: { toolStatus },
|
|
1100
|
-
});
|
|
1131
|
+
}));
|
|
1101
1132
|
}
|
|
1102
1133
|
finally {
|
|
1103
1134
|
if (shouldTrackTelemetry) {
|
|
@@ -1109,6 +1140,7 @@ export class ActorsMcpServer {
|
|
|
1109
1140
|
telemetryData,
|
|
1110
1141
|
userId,
|
|
1111
1142
|
callDiagnostics,
|
|
1143
|
+
responseSizeBytes: computeToolResponseSizeBytes(toolResult),
|
|
1112
1144
|
});
|
|
1113
1145
|
}
|
|
1114
1146
|
}
|
|
@@ -1137,6 +1169,7 @@ export class ActorsMcpServer {
|
|
|
1137
1169
|
mcpSessionId: params.mcpSessionId,
|
|
1138
1170
|
toolStatus: params.toolStatus,
|
|
1139
1171
|
durationMs,
|
|
1172
|
+
...(params.responseSizeBytes !== undefined && { responseSizeBytes: params.responseSizeBytes }),
|
|
1140
1173
|
...(params.taskId !== undefined && { taskId: params.taskId }),
|
|
1141
1174
|
});
|
|
1142
1175
|
if (params.telemetryData) {
|
|
@@ -1144,6 +1177,7 @@ export class ActorsMcpServer {
|
|
|
1144
1177
|
...params.telemetryData,
|
|
1145
1178
|
tool_status: params.toolStatus,
|
|
1146
1179
|
tool_exec_time_ms: durationMs,
|
|
1180
|
+
...(params.responseSizeBytes !== undefined && { tool_response_size_bytes: params.responseSizeBytes }),
|
|
1147
1181
|
// Always include actor_name/actor_id; failure-specific fields are only present when callDiagnostics has them.
|
|
1148
1182
|
...params.callDiagnostics,
|
|
1149
1183
|
};
|
|
@@ -1177,7 +1211,7 @@ export class ActorsMcpServer {
|
|
|
1177
1211
|
// Prepare telemetry before try-catch so it's accessible to both paths.
|
|
1178
1212
|
// This avoids re-fetching user data in the error handler.
|
|
1179
1213
|
const { telemetryData, userId } = await this.prepareTelemetryData(getToolFullName(tool), mcpSessionId, apifyToken);
|
|
1180
|
-
const finishTaskTracking = (status, diagnostics) => {
|
|
1214
|
+
const finishTaskTracking = (status, diagnostics, responseSizeBytes) => {
|
|
1181
1215
|
this.logToolCallAndTelemetry({
|
|
1182
1216
|
toolName: tool.name,
|
|
1183
1217
|
mcpSessionId,
|
|
@@ -1187,6 +1221,7 @@ export class ActorsMcpServer {
|
|
|
1187
1221
|
telemetryData,
|
|
1188
1222
|
userId,
|
|
1189
1223
|
callDiagnostics: diagnostics,
|
|
1224
|
+
responseSizeBytes,
|
|
1190
1225
|
});
|
|
1191
1226
|
};
|
|
1192
1227
|
// Once a task is cancelled the spec forbids writing a result; every storage path
|
|
@@ -1229,6 +1264,7 @@ export class ActorsMcpServer {
|
|
|
1229
1264
|
return;
|
|
1230
1265
|
throw err;
|
|
1231
1266
|
}
|
|
1267
|
+
await emitTaskStatusNotification(taskId, mcpSessionId, this.taskStore, this.server);
|
|
1232
1268
|
// Execute the tool and get the result
|
|
1233
1269
|
let result = {};
|
|
1234
1270
|
// Check payment validation (already computed by prepareToolCallContext in the caller)
|
|
@@ -1241,13 +1277,12 @@ export class ActorsMcpServer {
|
|
|
1241
1277
|
};
|
|
1242
1278
|
result = paymentRequiredResult;
|
|
1243
1279
|
}
|
|
1244
|
-
// Callback to propagate Actor run statusMessage into the task store.
|
|
1245
|
-
// Clients retrieve it via tasks/get and tasks/list polling.
|
|
1246
|
-
// TODO: Also send notifications/tasks/status so clients get real-time push updates
|
|
1280
|
+
// Callback to propagate Actor run statusMessage into the task store and emit a push notification.
|
|
1247
1281
|
// TODO(TC-3): cancel arriving while this is scheduled throws cancelled → working;
|
|
1248
1282
|
// currently swallowed by progress.ts's tick catch — guard or catch explicitly.
|
|
1249
1283
|
const onStatusMessage = async (message) => {
|
|
1250
1284
|
await this.taskStore.updateTaskStatus(taskId, 'working', message, mcpSessionId);
|
|
1285
|
+
await emitTaskStatusNotification(taskId, mcpSessionId, this.taskStore, this.server);
|
|
1251
1286
|
};
|
|
1252
1287
|
// Handle internal tool execution in task mode
|
|
1253
1288
|
if (toolStatus === TOOL_STATUS.SUCCEEDED && tool.type === 'internal') {
|
|
@@ -1264,6 +1299,7 @@ export class ActorsMcpServer {
|
|
|
1264
1299
|
userRentedActorIds,
|
|
1265
1300
|
progressTracker,
|
|
1266
1301
|
mcpSessionId,
|
|
1302
|
+
taskMode: true,
|
|
1267
1303
|
});
|
|
1268
1304
|
const diag = extractToolTelemetry(res, actorName, actorId);
|
|
1269
1305
|
toolStatus = diag.toolStatus;
|
|
@@ -1281,7 +1317,7 @@ export class ActorsMcpServer {
|
|
|
1281
1317
|
const progressTracker = createProgressTracker(progressToken, extra.sendNotification, taskId, onStatusMessage);
|
|
1282
1318
|
try {
|
|
1283
1319
|
log.info('Calling Actor for task', { taskId, toolName: tool.name, actorName: tool.actorFullName, mcpSessionId, input: logSafeArgs });
|
|
1284
|
-
const executorResult = await
|
|
1320
|
+
const executorResult = await actorExecutor.executeActorTool({
|
|
1285
1321
|
actorFullName: tool.actorFullName,
|
|
1286
1322
|
input: toolArgs,
|
|
1287
1323
|
apifyClient,
|
|
@@ -1289,6 +1325,8 @@ export class ActorsMcpServer {
|
|
|
1289
1325
|
progressTracker,
|
|
1290
1326
|
abortSignal: cancelWatcher.signal,
|
|
1291
1327
|
mcpSessionId,
|
|
1328
|
+
datasetItemsSchema: tool.datasetItemsSchema,
|
|
1329
|
+
taskMode: true,
|
|
1292
1330
|
});
|
|
1293
1331
|
if (!executorResult) {
|
|
1294
1332
|
toolStatus = TOOL_STATUS.ABORTED;
|
|
@@ -1316,7 +1354,8 @@ export class ActorsMcpServer {
|
|
|
1316
1354
|
});
|
|
1317
1355
|
await this.taskStore.storeTaskResult(taskId, 'completed', result, mcpSessionId);
|
|
1318
1356
|
log.debug('Task completed successfully', { taskId, toolName: tool.name, mcpSessionId });
|
|
1319
|
-
|
|
1357
|
+
await emitTaskStatusNotification(taskId, mcpSessionId, this.taskStore, this.server);
|
|
1358
|
+
finishTaskTracking(toolStatus, callDiagnostics, computeToolResponseSizeBytes(result));
|
|
1320
1359
|
}
|
|
1321
1360
|
catch (error) {
|
|
1322
1361
|
// Handle 402 Payment Required — return structured x402 result so clients can auto-pay
|
|
@@ -1327,12 +1366,14 @@ export class ActorsMcpServer {
|
|
|
1327
1366
|
// so guard storeTaskResult against a cancel that raced with this 402.
|
|
1328
1367
|
if (await skipIfTaskCancelled(', skipping 402 result storage', TOOL_STATUS.ABORTED, { ...buildActorFields(actorName, actorId) }))
|
|
1329
1368
|
return;
|
|
1330
|
-
|
|
1369
|
+
const paymentResponse = buildPaymentRequiredResponse(error);
|
|
1370
|
+
await this.taskStore.storeTaskResult(taskId, 'completed', paymentResponse, mcpSessionId);
|
|
1371
|
+
await emitTaskStatusNotification(taskId, mcpSessionId, this.taskStore, this.server);
|
|
1331
1372
|
finishTaskTracking(TOOL_STATUS.SOFT_FAIL, {
|
|
1332
1373
|
failure_category: FAILURE_CATEGORY.INVALID_INPUT,
|
|
1333
1374
|
failure_http_status: 402,
|
|
1334
1375
|
...buildActorFields(actorName, actorId),
|
|
1335
|
-
});
|
|
1376
|
+
}, computeToolResponseSizeBytes(paymentResponse));
|
|
1336
1377
|
return;
|
|
1337
1378
|
}
|
|
1338
1379
|
if (isPermissionApprovalError(error)) {
|
|
@@ -1341,12 +1382,14 @@ export class ActorsMcpServer {
|
|
|
1341
1382
|
// so guard storeTaskResult against a cancel that raced with this approval error.
|
|
1342
1383
|
if (await skipIfTaskCancelled(', skipping permission-approval result storage', TOOL_STATUS.ABORTED, { ...buildActorFields(actorName, actorId) }))
|
|
1343
1384
|
return;
|
|
1344
|
-
|
|
1385
|
+
const approvalResponse = buildPermissionApprovalResponse(error);
|
|
1386
|
+
await this.taskStore.storeTaskResult(taskId, 'completed', approvalResponse, mcpSessionId);
|
|
1387
|
+
await emitTaskStatusNotification(taskId, mcpSessionId, this.taskStore, this.server);
|
|
1345
1388
|
finishTaskTracking(TOOL_STATUS.SOFT_FAIL, {
|
|
1346
1389
|
failure_category: FAILURE_CATEGORY.PERMISSION_APPROVAL_REQUIRED,
|
|
1347
1390
|
failure_http_status: error.statusCode,
|
|
1348
1391
|
...buildActorFields(actorName, actorId),
|
|
1349
|
-
});
|
|
1392
|
+
}, computeToolResponseSizeBytes(approvalResponse));
|
|
1350
1393
|
return;
|
|
1351
1394
|
}
|
|
1352
1395
|
toolStatus = getToolStatusFromError(error, Boolean(cancelWatcher.signal.aborted));
|
|
@@ -1395,15 +1438,17 @@ export class ActorsMcpServer {
|
|
|
1395
1438
|
taskId,
|
|
1396
1439
|
mcpSessionId,
|
|
1397
1440
|
});
|
|
1398
|
-
|
|
1441
|
+
const failedResult = {
|
|
1399
1442
|
content: [{
|
|
1400
1443
|
type: 'text',
|
|
1401
1444
|
text: userText,
|
|
1402
1445
|
}],
|
|
1403
1446
|
isError: true,
|
|
1404
1447
|
internalToolStatus: toolStatus,
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1448
|
+
};
|
|
1449
|
+
await this.taskStore.storeTaskResult(taskId, 'failed', failedResult, mcpSessionId);
|
|
1450
|
+
await emitTaskStatusNotification(taskId, mcpSessionId, this.taskStore, this.server);
|
|
1451
|
+
finishTaskTracking(toolStatus, callDiagnostics, computeToolResponseSizeBytes(failedResult));
|
|
1407
1452
|
}
|
|
1408
1453
|
finally {
|
|
1409
1454
|
cancelWatcher.dispose();
|