@apify/actors-mcp-server 0.9.17-beta.2 → 0.9.17
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.js +4 -1
- package/dist/apify_client.js.map +1 -1
- package/dist/const.d.ts +9 -1
- package/dist/const.d.ts.map +1 -1
- package/dist/const.js +13 -2
- package/dist/const.js.map +1 -1
- package/dist/dev_server.js +4 -1
- package/dist/dev_server.js.map +1 -1
- package/dist/errors.js +4 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/index_internals.js +4 -1
- package/dist/index_internals.js.map +1 -1
- package/dist/input.js +4 -1
- package/dist/input.js.map +1 -1
- package/dist/instrument.js +4 -1
- package/dist/instrument.js.map +1 -1
- package/dist/mcp/actors.js +4 -1
- package/dist/mcp/actors.js.map +1 -1
- package/dist/mcp/client.js +4 -1
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/const.js +4 -1
- package/dist/mcp/const.js.map +1 -1
- package/dist/mcp/proxy.js +4 -1
- package/dist/mcp/proxy.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +302 -181
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/utils.js +4 -1
- package/dist/mcp/utils.js.map +1 -1
- package/dist/payments/helpers.d.ts +18 -13
- package/dist/payments/helpers.d.ts.map +1 -1
- package/dist/payments/helpers.js +25 -17
- package/dist/payments/helpers.js.map +1 -1
- package/dist/payments/index.d.ts +2 -2
- package/dist/payments/index.d.ts.map +1 -1
- package/dist/payments/index.js +5 -2
- package/dist/payments/index.js.map +1 -1
- package/dist/payments/resolve.js +4 -1
- package/dist/payments/resolve.js.map +1 -1
- package/dist/payments/skyfire.js +4 -1
- package/dist/payments/skyfire.js.map +1 -1
- package/dist/payments/types.js +4 -1
- package/dist/payments/types.js.map +1 -1
- package/dist/payments/x402.js +4 -1
- package/dist/payments/x402.js.map +1 -1
- package/dist/prompts/index.js +4 -1
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/latest_news_on_topic.js +4 -1
- package/dist/prompts/latest_news_on_topic.js.map +1 -1
- package/dist/resources/resource_service.js +4 -1
- package/dist/resources/resource_service.js.map +1 -1
- package/dist/resources/widgets.js +4 -1
- package/dist/resources/widgets.js.map +1 -1
- package/dist/server_card.js +4 -1
- package/dist/server_card.js.map +1 -1
- package/dist/state.js +4 -1
- package/dist/state.js.map +1 -1
- package/dist/stdio.js +4 -1
- package/dist/stdio.js.map +1 -1
- package/dist/telemetry.js +4 -1
- package/dist/telemetry.js.map +1 -1
- package/dist/tools/build.js +4 -1
- package/dist/tools/build.js.map +1 -1
- package/dist/tools/categories.js +4 -1
- package/dist/tools/categories.js.map +1 -1
- package/dist/tools/common/abort_actor_run.js +4 -1
- package/dist/tools/common/abort_actor_run.js.map +1 -1
- package/dist/tools/common/add_actor.js +4 -1
- package/dist/tools/common/add_actor.js.map +1 -1
- package/dist/tools/common/dataset_collection.js +4 -1
- package/dist/tools/common/dataset_collection.js.map +1 -1
- package/dist/tools/common/fetch_apify_docs.d.ts.map +1 -1
- package/dist/tools/common/fetch_apify_docs.js +13 -5
- package/dist/tools/common/fetch_apify_docs.js.map +1 -1
- package/dist/tools/common/get_actor_output.js +6 -3
- package/dist/tools/common/get_actor_output.js.map +1 -1
- package/dist/tools/common/get_actor_run_log.js +4 -1
- package/dist/tools/common/get_actor_run_log.js.map +1 -1
- package/dist/tools/common/get_dataset.js +6 -3
- package/dist/tools/common/get_dataset.js.map +1 -1
- package/dist/tools/common/get_dataset_items.js +5 -2
- package/dist/tools/common/get_dataset_items.js.map +1 -1
- package/dist/tools/common/get_dataset_schema.js +6 -3
- package/dist/tools/common/get_dataset_schema.js.map +1 -1
- package/dist/tools/common/get_key_value_store.js +4 -1
- package/dist/tools/common/get_key_value_store.js.map +1 -1
- package/dist/tools/common/get_key_value_store_keys.js +4 -1
- package/dist/tools/common/get_key_value_store_keys.js.map +1 -1
- package/dist/tools/common/get_key_value_store_record.js +4 -1
- package/dist/tools/common/get_key_value_store_record.js.map +1 -1
- package/dist/tools/common/key_value_store_collection.js +4 -1
- package/dist/tools/common/key_value_store_collection.js.map +1 -1
- package/dist/tools/common/run_collection.js +4 -1
- package/dist/tools/common/run_collection.js.map +1 -1
- package/dist/tools/common/search_apify_docs.js +4 -1
- package/dist/tools/common/search_apify_docs.js.map +1 -1
- package/dist/tools/core/actor_execution.js +4 -1
- package/dist/tools/core/actor_execution.js.map +1 -1
- package/dist/tools/core/actor_response.js +4 -1
- package/dist/tools/core/actor_response.js.map +1 -1
- package/dist/tools/core/actor_tools_factory.d.ts +1 -0
- package/dist/tools/core/actor_tools_factory.d.ts.map +1 -1
- package/dist/tools/core/actor_tools_factory.js +9 -2
- package/dist/tools/core/actor_tools_factory.js.map +1 -1
- package/dist/tools/core/call_actor_common.d.ts +1 -11
- package/dist/tools/core/call_actor_common.d.ts.map +1 -1
- package/dist/tools/core/call_actor_common.js +67 -19
- package/dist/tools/core/call_actor_common.js.map +1 -1
- package/dist/tools/core/fetch_actor_details_common.js +4 -1
- package/dist/tools/core/fetch_actor_details_common.js.map +1 -1
- package/dist/tools/core/get_actor_run_common.js +6 -3
- package/dist/tools/core/get_actor_run_common.js.map +1 -1
- package/dist/tools/core/search_actors_common.js +4 -1
- package/dist/tools/core/search_actors_common.js.map +1 -1
- package/dist/tools/default/actor_executor.js +4 -1
- package/dist/tools/default/actor_executor.js.map +1 -1
- package/dist/tools/default/call_actor.d.ts.map +1 -1
- package/dist/tools/default/call_actor.js +26 -5
- package/dist/tools/default/call_actor.js.map +1 -1
- package/dist/tools/default/fetch_actor_details.js +4 -1
- package/dist/tools/default/fetch_actor_details.js.map +1 -1
- package/dist/tools/default/get_actor_run.js +5 -2
- package/dist/tools/default/get_actor_run.js.map +1 -1
- package/dist/tools/default/search_actors.js +4 -1
- package/dist/tools/default/search_actors.js.map +1 -1
- package/dist/tools/index.js +4 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/openai/actor_executor.js +4 -1
- package/dist/tools/openai/actor_executor.js.map +1 -1
- package/dist/tools/openai/call_actor.d.ts.map +1 -1
- package/dist/tools/openai/call_actor.js +25 -5
- package/dist/tools/openai/call_actor.js.map +1 -1
- package/dist/tools/openai/fetch_actor_details.js +4 -1
- package/dist/tools/openai/fetch_actor_details.js.map +1 -1
- package/dist/tools/openai/fetch_actor_details_internal.js +4 -1
- package/dist/tools/openai/fetch_actor_details_internal.js.map +1 -1
- package/dist/tools/openai/get_actor_run.js +5 -2
- package/dist/tools/openai/get_actor_run.js.map +1 -1
- package/dist/tools/openai/search_actors.js +4 -1
- package/dist/tools/openai/search_actors.js.map +1 -1
- package/dist/tools/openai/search_actors_internal.js +4 -1
- package/dist/tools/openai/search_actors_internal.js.map +1 -1
- package/dist/tools/structured_output_schemas.js +4 -1
- package/dist/tools/structured_output_schemas.js.map +1 -1
- package/dist/tools/utils.js +4 -1
- package/dist/tools/utils.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +28 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/actor.js +4 -1
- package/dist/utils/actor.js.map +1 -1
- package/dist/utils/actor_card.js +4 -1
- package/dist/utils/actor_card.js.map +1 -1
- package/dist/utils/actor_details.js +6 -3
- package/dist/utils/actor_details.js.map +1 -1
- package/dist/utils/actor_search.js +4 -1
- package/dist/utils/actor_search.js.map +1 -1
- package/dist/utils/ajv.js +4 -1
- package/dist/utils/ajv.js.map +1 -1
- package/dist/utils/apify_docs.js +4 -1
- package/dist/utils/apify_docs.js.map +1 -1
- package/dist/utils/apify_properties.js +4 -1
- package/dist/utils/apify_properties.js.map +1 -1
- package/dist/utils/auth.js +4 -1
- package/dist/utils/auth.js.map +1 -1
- package/dist/utils/generic.js +4 -1
- package/dist/utils/generic.js.map +1 -1
- package/dist/utils/logging.js +4 -1
- package/dist/utils/logging.js.map +1 -1
- package/dist/utils/mcp.d.ts +9 -18
- package/dist/utils/mcp.d.ts.map +1 -1
- package/dist/utils/mcp.js +12 -18
- package/dist/utils/mcp.js.map +1 -1
- package/dist/utils/mcp_clients.js +4 -1
- package/dist/utils/mcp_clients.js.map +1 -1
- package/dist/utils/payment_errors.d.ts +1 -1
- package/dist/utils/payment_errors.js +4 -1
- package/dist/utils/payment_errors.js.map +1 -1
- package/dist/utils/pricing_info.js +4 -1
- package/dist/utils/pricing_info.js.map +1 -1
- package/dist/utils/progress.js +4 -1
- package/dist/utils/progress.js.map +1 -1
- package/dist/utils/schema_generation.js +4 -1
- package/dist/utils/schema_generation.js.map +1 -1
- package/dist/utils/server-instructions/common.js +4 -1
- package/dist/utils/server-instructions/common.js.map +1 -1
- package/dist/utils/server-instructions/default.js +4 -1
- package/dist/utils/server-instructions/default.js.map +1 -1
- package/dist/utils/server-instructions/index.js +4 -1
- package/dist/utils/server-instructions/index.js.map +1 -1
- package/dist/utils/server-instructions/openai.js +4 -1
- package/dist/utils/server-instructions/openai.js.map +1 -1
- package/dist/utils/tool_categories_helpers.js +4 -1
- package/dist/utils/tool_categories_helpers.js.map +1 -1
- package/dist/utils/tool_status.d.ts +20 -2
- package/dist/utils/tool_status.d.ts.map +1 -1
- package/dist/utils/tool_status.js +94 -3
- package/dist/utils/tool_status.js.map +1 -1
- package/dist/utils/tools.d.ts +22 -1
- package/dist/utils/tools.d.ts.map +1 -1
- package/dist/utils/tools.js +54 -1
- package/dist/utils/tools.js.map +1 -1
- package/dist/utils/tools_loader.js +4 -1
- package/dist/utils/tools_loader.js.map +1 -1
- package/dist/utils/ttl_lru.js +4 -1
- package/dist/utils/ttl_lru.js.map +1 -1
- package/dist/utils/userid_cache.js +4 -1
- package/dist/utils/userid_cache.js.map +1 -1
- package/dist/utils/version.js +4 -1
- package/dist/utils/version.js.map +1 -1
- package/dist/web/dist/actor-detail-widget.js +2 -0
- package/dist/web/dist/actor-run-widget.js +2 -0
- package/dist/web/dist/search-actors-widget.js +2 -0
- package/package.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Model Context Protocol (MCP) server for Apify Actors
|
|
3
3
|
*/
|
|
4
|
+
|
|
5
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ea73e14a-6566-5941-a0ab-5d6299dec132")}catch(e){}}();
|
|
4
6
|
import { randomUUID } from 'node:crypto';
|
|
5
7
|
import { InMemoryTaskStore } from '@modelcontextprotocol/sdk/experimental/tasks/stores/in-memory.js';
|
|
6
8
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
7
9
|
import { CallToolRequestSchema, CallToolResultSchema, CancelTaskRequestSchema, ErrorCode, GetPromptRequestSchema, GetTaskPayloadRequestSchema, GetTaskRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListTasksRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema, ServerNotificationSchema, SetLevelRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
10
|
+
import dedent from 'dedent';
|
|
8
11
|
import log from '@apify/log';
|
|
9
12
|
import { parseBooleanOrNull } from '@apify/utilities';
|
|
10
13
|
import { ApifyClient } from '../apify_client.js';
|
|
11
|
-
import { ALLOWED_TASK_TOOL_EXECUTION_MODES, APIFY_MCP_URL, DEFAULT_TELEMETRY_ENABLED, DEFAULT_TELEMETRY_ENV, HelperTools, HTTP_PAYMENT_REQUIRED, SERVER_NAME, TOOL_STATUS, } from '../const.js';
|
|
12
|
-
import {
|
|
14
|
+
import { ALLOWED_TASK_TOOL_EXECUTION_MODES, APIFY_MCP_URL, DEFAULT_TELEMETRY_ENABLED, DEFAULT_TELEMETRY_ENV, FAILURE_CATEGORY, HelperTools, HTTP_PAYMENT_REQUIRED, SERVER_NAME, TOOL_STATUS, } from '../const.js';
|
|
15
|
+
import { prepareToolCallContext } from '../payments/helpers.js';
|
|
13
16
|
import { prompts } from '../prompts/index.js';
|
|
14
17
|
import { createResourceService } from '../resources/resource_service.js';
|
|
15
18
|
import { resolveAvailableWidgets, RESOURCE_MIME_TYPE } from '../resources/widgets.js';
|
|
@@ -23,8 +26,8 @@ import { buildMCPResponse } from '../utils/mcp.js';
|
|
|
23
26
|
import { buildPaymentRequiredResponse } from '../utils/payment_errors.js';
|
|
24
27
|
import { createProgressTracker } from '../utils/progress.js';
|
|
25
28
|
import { getServerInstructions } from '../utils/server-instructions/index.js';
|
|
26
|
-
import { getToolStatusFromError } from '../utils/tool_status.js';
|
|
27
|
-
import { getToolPublicFieldOnly } from '../utils/tools.js';
|
|
29
|
+
import { classifyFailureCategory, extractAjvErrorDetails, extractToolTelemetry, getToolStatusFromError } from '../utils/tool_status.js';
|
|
30
|
+
import { buildActorFields, extractActorId, extractActorName, getToolFullName, getToolPublicFieldOnly } from '../utils/tools.js';
|
|
28
31
|
import { getUserIdFromTokenCached } from '../utils/userid_cache.js';
|
|
29
32
|
import { getPackageVersion } from '../utils/version.js';
|
|
30
33
|
import { connectMCPClient } from './client.js';
|
|
@@ -591,7 +594,7 @@ export class ActorsMcpServer {
|
|
|
591
594
|
* @throws {McpError} - based on the McpServer class code from the typescript MCP SDK
|
|
592
595
|
*/
|
|
593
596
|
this.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
594
|
-
var _a, _b, _c, _d, _e;
|
|
597
|
+
var _a, _b, _c, _d, _e, _f;
|
|
595
598
|
const params = request.params;
|
|
596
599
|
// eslint-disable-next-line prefer-const
|
|
597
600
|
let { name, arguments: args, _meta: meta } = params;
|
|
@@ -605,104 +608,156 @@ export class ActorsMcpServer {
|
|
|
605
608
|
log.error('MCP Session ID is missing in tool call request. This should never happen.');
|
|
606
609
|
throw new Error('MCP Session ID is required for tool calls');
|
|
607
610
|
}
|
|
608
|
-
// Validate token
|
|
609
|
-
if (!apifyToken && !((_a = this.options.paymentProvider) === null || _a === void 0 ? void 0 : _a.allowsUnauthenticated) && !this.options.allowUnauthMode) {
|
|
610
|
-
const msg = `Apify API token is required but was not provided.
|
|
611
|
-
Please set the APIFY_TOKEN environment variable or pass it as a parameter in the request header as Authorization Bearer <token>.
|
|
612
|
-
You can obtain your Apify token from https://console.apify.com/account/integrations.`;
|
|
613
|
-
log.softFail(msg, { mcpSessionId, statusCode: 400 });
|
|
614
|
-
await this.server.sendLoggingMessage({ level: 'error', data: msg });
|
|
615
|
-
throw new McpError(ErrorCode.InvalidParams, msg);
|
|
616
|
-
}
|
|
617
|
-
// TODO - if connection is /mcp client will not receive notification on tool change
|
|
618
|
-
// Find tool by name, actor full name, or legacy tool name (e.g. apify-slash-rag-web-browser → apify--rag-web-browser)
|
|
619
|
-
const newName = (_b = legacyToolNameToNew(name)) !== null && _b !== void 0 ? _b : name;
|
|
620
|
-
const tool = Array.from(this.tools.values())
|
|
621
|
-
.find((t) => t.name === newName || (t.type === 'actor' && t.actorFullName === newName));
|
|
622
|
-
if (!tool) {
|
|
623
|
-
const availableTools = this.listToolNames();
|
|
624
|
-
const msg = `Tool "${name}" was not found.
|
|
625
|
-
Available tools: ${availableTools.length > 0 ? availableTools.join(', ') : 'none'}.
|
|
626
|
-
Please verify the tool name is correct. You can list all available tools using the tools/list request.`;
|
|
627
|
-
log.softFail(msg, { mcpSessionId, statusCode: 404 });
|
|
628
|
-
await this.server.sendLoggingMessage({ level: 'error', data: msg });
|
|
629
|
-
throw new McpError(ErrorCode.InvalidParams, msg);
|
|
630
|
-
}
|
|
631
|
-
if (!args) {
|
|
632
|
-
const msg = `Missing arguments for tool "${name}".
|
|
633
|
-
Please provide the required arguments for this tool. Check the tool's input schema using ${HelperTools.ACTOR_GET_DETAILS} tool to see what parameters are required.`;
|
|
634
|
-
log.softFail(msg, { mcpSessionId, statusCode: 400 });
|
|
635
|
-
await this.server.sendLoggingMessage({ level: 'error', data: msg });
|
|
636
|
-
throw new McpError(ErrorCode.InvalidParams, msg);
|
|
637
|
-
}
|
|
638
|
-
// Decode dot property names in arguments before validation,
|
|
639
|
-
// since validation expects the original, non-encoded property names.
|
|
640
|
-
args = decodeDotPropertyNames(args);
|
|
641
|
-
// Centralize all payment processing: validate, strip payment fields, create client.
|
|
642
|
-
// Must run before ajv validation so cleanArgs doesn't contain provider-specific fields.
|
|
643
|
-
const payment = preparePayment({
|
|
644
|
-
provider: this.options.paymentProvider,
|
|
645
|
-
tool,
|
|
646
|
-
args: args,
|
|
647
|
-
apifyToken,
|
|
648
|
-
meta,
|
|
649
|
-
requestHeaders: (_c = extra.requestInfo) === null || _c === void 0 ? void 0 : _c.headers,
|
|
650
|
-
});
|
|
651
|
-
log.debug('Validate arguments for tool', { toolName: tool.name, mcpSessionId, input: payment.logArgs });
|
|
652
|
-
if (!tool.ajvValidate(payment.cleanArgs)) {
|
|
653
|
-
const errors = (tool === null || tool === void 0 ? void 0 : tool.ajvValidate.errors) || [];
|
|
654
|
-
const errorMessages = errors.map((e) => `${e.instancePath || 'root'}: ${e.message || 'validation error'}`).join('; ');
|
|
655
|
-
const msg = `Invalid arguments for tool "${tool.name}".
|
|
656
|
-
Validation errors: ${errorMessages}.
|
|
657
|
-
Please check the tool's input schema using ${HelperTools.ACTOR_GET_DETAILS} tool and ensure all required parameters are provided with correct types and values.`;
|
|
658
|
-
log.softFail(msg, { mcpSessionId, statusCode: 400 });
|
|
659
|
-
await this.server.sendLoggingMessage({ level: 'error', data: msg });
|
|
660
|
-
throw new McpError(ErrorCode.InvalidParams, msg);
|
|
661
|
-
}
|
|
662
|
-
// TODO: we should split this huge method into smaller parts as it is slowly getting out of hand
|
|
663
|
-
// Check if tool call is a long running task and the tool supports that
|
|
664
|
-
// Cast to allowed task mode types ('optional' | 'required') for type-safe includes() check
|
|
665
|
-
const taskSupport = (_d = tool.execution) === null || _d === void 0 ? void 0 : _d.taskSupport;
|
|
666
|
-
if (request.params.task && !ALLOWED_TASK_TOOL_EXECUTION_MODES.includes(taskSupport)) {
|
|
667
|
-
const msg = `Tool "${tool.name}" does not support long running task calls.
|
|
668
|
-
Please remove the "task" parameter from the tool call request or use a different tool that supports long running tasks.`;
|
|
669
|
-
log.softFail(msg, { mcpSessionId, statusCode: 400 });
|
|
670
|
-
await this.server.sendLoggingMessage({ level: 'error', data: msg });
|
|
671
|
-
throw new McpError(ErrorCode.InvalidParams, msg);
|
|
672
|
-
}
|
|
673
|
-
// Handle long-running task request
|
|
674
|
-
if (request.params.task) {
|
|
675
|
-
const task = await this.taskStore.createTask({
|
|
676
|
-
ttl: request.params.task.ttl,
|
|
677
|
-
}, `call-tool-${name}-${randomUUID()}`, request);
|
|
678
|
-
log.debug('Created task for tool execution', { taskId: task.taskId, toolName: tool.name, mcpSessionId });
|
|
679
|
-
// Execute the tool asynchronously and update task status
|
|
680
|
-
setImmediate(async () => {
|
|
681
|
-
await this.executeToolAndUpdateTask({
|
|
682
|
-
taskId: task.taskId,
|
|
683
|
-
tool,
|
|
684
|
-
cleanArgs: payment.cleanArgs,
|
|
685
|
-
logArgs: payment.logArgs,
|
|
686
|
-
paymentErrorResult: payment.errorResult,
|
|
687
|
-
apifyClient: payment.client,
|
|
688
|
-
apifyToken,
|
|
689
|
-
progressToken,
|
|
690
|
-
extra,
|
|
691
|
-
mcpSessionId,
|
|
692
|
-
userRentedActorIds,
|
|
693
|
-
});
|
|
694
|
-
});
|
|
695
|
-
// Return the task immediately; execution continues asynchronously
|
|
696
|
-
return { task };
|
|
697
|
-
}
|
|
698
|
-
const { telemetryData, userId } = await this.prepareTelemetryData(tool, mcpSessionId, apifyToken);
|
|
699
611
|
const startTime = Date.now();
|
|
612
|
+
let telemetryData;
|
|
613
|
+
let userId;
|
|
700
614
|
let toolStatus = TOOL_STATUS.SUCCEEDED;
|
|
615
|
+
let callDiagnostics = {};
|
|
616
|
+
let shouldTrackTelemetry = true;
|
|
617
|
+
const failInvalidParams = async (message, details, logFields) => {
|
|
618
|
+
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
619
|
+
callDiagnostics = details;
|
|
620
|
+
log.softFail(message, {
|
|
621
|
+
mcpSessionId,
|
|
622
|
+
failureCategory: details.failure_category,
|
|
623
|
+
actorName: details.actor_name,
|
|
624
|
+
validationKeyword: details.validation_keyword,
|
|
625
|
+
validationPath: details.validation_path,
|
|
626
|
+
validationMissingProperty: details.validation_missing_property,
|
|
627
|
+
validationAdditionalProperty: details.validation_additional_property,
|
|
628
|
+
...logFields,
|
|
629
|
+
});
|
|
630
|
+
await this.server.sendLoggingMessage({ level: 'error', data: message });
|
|
631
|
+
throw new McpError(ErrorCode.InvalidParams, message);
|
|
632
|
+
};
|
|
633
|
+
// Initialize telemetry with raw tool name — may be overwritten below once the tool is resolved.
|
|
634
|
+
// This ensures telemetry is available even for early failures (missing token, tool not found).
|
|
635
|
+
({ telemetryData, userId } = await this.prepareTelemetryData(name, mcpSessionId, apifyToken));
|
|
636
|
+
// actorName/actorId are declared here so they're available in the catch block for telemetry.
|
|
637
|
+
// Set after tool resolution (inside the try block).
|
|
638
|
+
let actorName;
|
|
639
|
+
let actorId;
|
|
701
640
|
try {
|
|
702
|
-
//
|
|
703
|
-
if (
|
|
641
|
+
// Validate token
|
|
642
|
+
if (!apifyToken && !((_a = this.options.paymentProvider) === null || _a === void 0 ? void 0 : _a.allowsUnauthenticated) && !this.options.allowUnauthMode) {
|
|
643
|
+
await failInvalidParams(dedent `
|
|
644
|
+
Apify API token is required but was not provided.
|
|
645
|
+
Please set the APIFY_TOKEN environment variable or pass it as a parameter in the request header as Authorization Bearer <token>.
|
|
646
|
+
You can get your Apify token from https://console.apify.com/account/integrations.
|
|
647
|
+
`, {
|
|
648
|
+
failure_category: FAILURE_CATEGORY.AUTH,
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
// TODO - if connection is /mcp client will not receive notification on tool change
|
|
652
|
+
// Find tool by name, actor full name, or legacy tool name (e.g. apify-slash-rag-web-browser → apify--rag-web-browser)
|
|
653
|
+
const newName = (_b = legacyToolNameToNew(name)) !== null && _b !== void 0 ? _b : name;
|
|
654
|
+
const toolEntry = Array.from(this.tools.values())
|
|
655
|
+
.find((t) => t.name === newName || getToolFullName(t) === newName);
|
|
656
|
+
if (!toolEntry) {
|
|
657
|
+
const availableTools = this.listToolNames();
|
|
658
|
+
await failInvalidParams(dedent `
|
|
659
|
+
Tool "${name}" was not found.
|
|
660
|
+
Available tools: ${availableTools.length > 0 ? availableTools.join(', ') : 'none'}.
|
|
661
|
+
Please verify the tool name is correct. You can list all available tools using the tools/list request.
|
|
662
|
+
`, {
|
|
663
|
+
failure_category: FAILURE_CATEGORY.INVALID_INPUT,
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
const tool = toolEntry;
|
|
667
|
+
// Re-initialize telemetry with the resolved tool (uses actorFullName for actor tools).
|
|
668
|
+
({ telemetryData, userId } = await this.prepareTelemetryData(getToolFullName(tool), mcpSessionId, apifyToken));
|
|
669
|
+
// Extract actor name/id for telemetry — available even when validation fails later.
|
|
670
|
+
actorName = extractActorName(tool, args);
|
|
671
|
+
actorId = extractActorId(tool);
|
|
672
|
+
// Always populate actor fields so they're tracked on both success and failure paths.
|
|
673
|
+
callDiagnostics = { ...callDiagnostics, ...buildActorFields(actorName, actorId) };
|
|
674
|
+
if (!args) {
|
|
675
|
+
await failInvalidParams(dedent `
|
|
676
|
+
Missing arguments for tool "${name}".
|
|
677
|
+
Please provide the required arguments for this tool. Check the tool's input schema using ${HelperTools.ACTOR_GET_DETAILS} tool to see what parameters are required.
|
|
678
|
+
`, {
|
|
679
|
+
failure_category: FAILURE_CATEGORY.INVALID_INPUT,
|
|
680
|
+
...buildActorFields(actorName, actorId),
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
// Decode dot property names in arguments before validation,
|
|
684
|
+
// since validation expects the original, non-encoded property names.
|
|
685
|
+
args = decodeDotPropertyNames(args);
|
|
686
|
+
// Centralize all payment processing: validate, strip payment fields, create client.
|
|
687
|
+
// Must run before ajv validation so toolArgs doesn't contain provider-specific fields.
|
|
688
|
+
const { toolArgs, logSafeArgs, apifyClient, paymentRequiredResult } = prepareToolCallContext({
|
|
689
|
+
provider: this.options.paymentProvider,
|
|
690
|
+
tool,
|
|
691
|
+
args: args,
|
|
692
|
+
apifyToken,
|
|
693
|
+
meta,
|
|
694
|
+
requestHeaders: (_c = extra.requestInfo) === null || _c === void 0 ? void 0 : _c.headers,
|
|
695
|
+
});
|
|
696
|
+
log.debug('Validate arguments for tool', { toolName: tool.name, mcpSessionId, input: logSafeArgs });
|
|
697
|
+
if (!tool.ajvValidate(toolArgs)) {
|
|
698
|
+
const errors = tool.ajvValidate.errors || [];
|
|
699
|
+
const ajvErrorDetails = extractAjvErrorDetails(errors);
|
|
700
|
+
const errorMessages = errors.map((e) => `${e.instancePath || 'root'}: ${e.message || 'validation error'}`).join('; ');
|
|
701
|
+
await failInvalidParams(dedent `
|
|
702
|
+
Invalid arguments for tool "${tool.name}".
|
|
703
|
+
Validation errors: ${errorMessages}.
|
|
704
|
+
Please check the tool's input schema using ${HelperTools.ACTOR_GET_DETAILS} tool and ensure all required parameters are provided with correct types and values.
|
|
705
|
+
`, {
|
|
706
|
+
failure_category: FAILURE_CATEGORY.INVALID_INPUT,
|
|
707
|
+
...ajvErrorDetails,
|
|
708
|
+
...buildActorFields(actorName, actorId),
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
// Check if tool call is a long running task and the tool supports that
|
|
712
|
+
// Cast to allowed task mode types ('optional' | 'required') for type-safe includes() check
|
|
713
|
+
const taskSupport = (_d = tool.execution) === null || _d === void 0 ? void 0 : _d.taskSupport;
|
|
714
|
+
if (request.params.task && !ALLOWED_TASK_TOOL_EXECUTION_MODES.includes(taskSupport)) {
|
|
715
|
+
await failInvalidParams(dedent `
|
|
716
|
+
Tool "${tool.name}" does not support long running task calls.
|
|
717
|
+
Please remove the "task" parameter from the tool call request or use a different tool that supports long running tasks.
|
|
718
|
+
`, {
|
|
719
|
+
failure_category: FAILURE_CATEGORY.INVALID_INPUT,
|
|
720
|
+
...buildActorFields(actorName, actorId),
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
// TODO: we should split this huge method into smaller parts as it is slowly getting out of hand
|
|
724
|
+
// Handle long-running task request
|
|
725
|
+
if (request.params.task) {
|
|
726
|
+
const task = await this.taskStore.createTask({
|
|
727
|
+
ttl: request.params.task.ttl,
|
|
728
|
+
}, `call-tool-${name}-${randomUUID()}`, request);
|
|
729
|
+
log.debug('Created task for tool execution', { taskId: task.taskId, toolName: tool.name, mcpSessionId });
|
|
730
|
+
// Execute the tool asynchronously and update task status
|
|
731
|
+
setImmediate(async () => {
|
|
732
|
+
await this.executeToolAndUpdateTask({
|
|
733
|
+
taskId: task.taskId,
|
|
734
|
+
tool,
|
|
735
|
+
toolArgs: toolArgs,
|
|
736
|
+
logSafeArgs,
|
|
737
|
+
paymentRequiredResult,
|
|
738
|
+
apifyClient: apifyClient,
|
|
739
|
+
apifyToken,
|
|
740
|
+
progressToken,
|
|
741
|
+
extra,
|
|
742
|
+
mcpSessionId,
|
|
743
|
+
actorName,
|
|
744
|
+
actorId,
|
|
745
|
+
userRentedActorIds,
|
|
746
|
+
});
|
|
747
|
+
});
|
|
748
|
+
// Return the task immediately; execution continues asynchronously
|
|
749
|
+
shouldTrackTelemetry = false;
|
|
750
|
+
return { task };
|
|
751
|
+
}
|
|
752
|
+
// Check payment validation (already computed by prepareToolCallContext)
|
|
753
|
+
if (paymentRequiredResult) {
|
|
704
754
|
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
705
|
-
|
|
755
|
+
callDiagnostics = {
|
|
756
|
+
failure_category: FAILURE_CATEGORY.INVALID_INPUT,
|
|
757
|
+
failure_http_status: 402,
|
|
758
|
+
...buildActorFields(actorName, actorId),
|
|
759
|
+
};
|
|
760
|
+
return paymentRequiredResult;
|
|
706
761
|
}
|
|
707
762
|
// Handle internal tool
|
|
708
763
|
if (tool.type === 'internal') {
|
|
@@ -710,45 +765,42 @@ Please remove the "task" parameter from the tool call request or use a different
|
|
|
710
765
|
const progressTracker = tool.name === 'call-actor'
|
|
711
766
|
? createProgressTracker(progressToken, extra.sendNotification)
|
|
712
767
|
: null;
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
toolStatus = internalToolStatus;
|
|
732
|
-
}
|
|
733
|
-
else if ('isError' in rest && rest.isError) {
|
|
734
|
-
toolStatus = TOOL_STATUS.FAILED;
|
|
768
|
+
try {
|
|
769
|
+
log.info('Calling internal tool', { name: tool.name, mcpSessionId, input: logSafeArgs });
|
|
770
|
+
const res = await tool.call({
|
|
771
|
+
args: toolArgs,
|
|
772
|
+
extra,
|
|
773
|
+
apifyMcpServer: this,
|
|
774
|
+
mcpServer: this.server,
|
|
775
|
+
apifyToken,
|
|
776
|
+
apifyClient: apifyClient,
|
|
777
|
+
userRentedActorIds,
|
|
778
|
+
progressTracker,
|
|
779
|
+
mcpSessionId,
|
|
780
|
+
});
|
|
781
|
+
// Extract diagnostics and strip internal fields from res before returning to client.
|
|
782
|
+
const diag = extractToolTelemetry(res, actorName, actorId);
|
|
783
|
+
toolStatus = diag.toolStatus;
|
|
784
|
+
callDiagnostics = { ...callDiagnostics, ...diag.callDiagnostics };
|
|
785
|
+
return res;
|
|
735
786
|
}
|
|
736
|
-
|
|
737
|
-
|
|
787
|
+
finally {
|
|
788
|
+
progressTracker === null || progressTracker === void 0 ? void 0 : progressTracker.stop();
|
|
738
789
|
}
|
|
739
|
-
// Never expose internalToolStatus to MCP clients
|
|
740
|
-
return { ...rest };
|
|
741
790
|
}
|
|
742
791
|
if (tool.type === 'actor-mcp') {
|
|
743
792
|
let client = null;
|
|
744
793
|
try {
|
|
745
794
|
client = await connectMCPClient(tool.serverUrl, apifyToken, mcpSessionId);
|
|
746
795
|
if (!client) {
|
|
747
|
-
const msg = `
|
|
748
|
-
|
|
749
|
-
|
|
796
|
+
const msg = dedent `
|
|
797
|
+
Failed to connect to MCP server at "${tool.serverUrl}".
|
|
798
|
+
Please verify the server URL is correct and accessible, and ensure you have a valid Apify token with appropriate permissions.
|
|
799
|
+
`;
|
|
800
|
+
log.softFail(msg, { mcpSessionId, failureCategory: FAILURE_CATEGORY.INTERNAL_ERROR });
|
|
750
801
|
await this.server.sendLoggingMessage({ level: 'error', data: msg });
|
|
751
802
|
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
803
|
+
callDiagnostics = { ...callDiagnostics, failure_category: FAILURE_CATEGORY.INTERNAL_ERROR };
|
|
752
804
|
return buildMCPResponse({ texts: [msg], isError: true });
|
|
753
805
|
}
|
|
754
806
|
// Only set up notification handlers if progressToken is provided by the client
|
|
@@ -771,27 +823,38 @@ Please verify the server URL is correct and accessible, and ensure you have a va
|
|
|
771
823
|
actorId: tool.actorId,
|
|
772
824
|
toolName: tool.originToolName,
|
|
773
825
|
mcpSessionId,
|
|
774
|
-
input:
|
|
826
|
+
input: logSafeArgs,
|
|
775
827
|
});
|
|
776
828
|
const res = await client.callTool({
|
|
777
829
|
name: tool.originToolName,
|
|
778
|
-
arguments:
|
|
779
|
-
_meta: {
|
|
780
|
-
progressToken,
|
|
781
|
-
},
|
|
830
|
+
arguments: toolArgs,
|
|
831
|
+
_meta: { progressToken },
|
|
782
832
|
}, CallToolResultSchema, {
|
|
783
833
|
timeout: EXTERNAL_TOOL_CALL_TIMEOUT_MSEC,
|
|
784
834
|
});
|
|
785
|
-
//
|
|
786
|
-
//
|
|
835
|
+
// TODO: actor-mcp responses are opaque — isError could be a user input problem
|
|
836
|
+
// (e.g. invalid query) or a genuine server failure. We can't distinguish without
|
|
837
|
+
// parsing the error text. Defaulting to INTERNAL_ERROR for now; revisit when
|
|
838
|
+
// actor-mcp gets deeper telemetry treatment.
|
|
839
|
+
if ('isError' in res && res.isError) {
|
|
840
|
+
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
841
|
+
callDiagnostics = { failure_category: FAILURE_CATEGORY.INTERNAL_ERROR, ...buildActorFields(actorName, actorId) };
|
|
842
|
+
}
|
|
787
843
|
return { ...res };
|
|
788
844
|
}
|
|
789
845
|
catch (error) {
|
|
846
|
+
toolStatus = getToolStatusFromError(error, Boolean((_e = extra.signal) === null || _e === void 0 ? void 0 : _e.aborted));
|
|
847
|
+
const failureDetail = error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200);
|
|
848
|
+
callDiagnostics = {
|
|
849
|
+
failure_category: classifyFailureCategory(error),
|
|
850
|
+
failure_detail: failureDetail,
|
|
851
|
+
...buildActorFields(actorName, actorId),
|
|
852
|
+
};
|
|
790
853
|
logHttpError(error, `Failed to call MCP tool '${tool.originToolName}' on Actor '${tool.actorId}'`, {
|
|
791
854
|
actorId: tool.actorId,
|
|
792
855
|
toolName: tool.originToolName,
|
|
856
|
+
failureCategory: callDiagnostics.failure_category,
|
|
793
857
|
});
|
|
794
|
-
toolStatus = TOOL_STATUS.FAILED;
|
|
795
858
|
return buildMCPResponse({
|
|
796
859
|
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.`],
|
|
797
860
|
isError: true,
|
|
@@ -806,11 +869,11 @@ Please verify the server URL is correct and accessible, and ensure you have a va
|
|
|
806
869
|
if (tool.type === 'actor') {
|
|
807
870
|
const progressTracker = createProgressTracker(progressToken, extra.sendNotification);
|
|
808
871
|
try {
|
|
809
|
-
log.info('Calling Actor', { actorName: tool.actorFullName, mcpSessionId, input:
|
|
872
|
+
log.info('Calling Actor', { actorName: tool.actorFullName, mcpSessionId, input: logSafeArgs });
|
|
810
873
|
const executorResult = await this.actorExecutor.executeActorTool({
|
|
811
874
|
actorFullName: tool.actorFullName,
|
|
812
|
-
input:
|
|
813
|
-
apifyClient:
|
|
875
|
+
input: toolArgs,
|
|
876
|
+
apifyClient: apifyClient,
|
|
814
877
|
callOptions: { memory: tool.memoryMbytes },
|
|
815
878
|
progressTracker,
|
|
816
879
|
abortSignal: extra.signal,
|
|
@@ -834,30 +897,65 @@ Please verify the server URL is correct and accessible, and ensure you have a va
|
|
|
834
897
|
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
835
898
|
}
|
|
836
899
|
catch (error) {
|
|
900
|
+
const httpStatus = getHttpStatusCode(error);
|
|
837
901
|
// Propagate 402 Payment Required as a tool result per x402 MCP transport spec:
|
|
838
902
|
// content[0].text (JSON) + isError: true
|
|
839
|
-
const httpStatus = getHttpStatusCode(error);
|
|
840
903
|
if (httpStatus === HTTP_PAYMENT_REQUIRED) {
|
|
841
|
-
logHttpError(error, 'Payment required while calling tool', { toolName: name });
|
|
842
904
|
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
905
|
+
callDiagnostics = {
|
|
906
|
+
failure_category: FAILURE_CATEGORY.INVALID_INPUT,
|
|
907
|
+
failure_http_status: 402,
|
|
908
|
+
...buildActorFields(actorName, actorId),
|
|
909
|
+
};
|
|
843
910
|
return buildPaymentRequiredResponse(error);
|
|
844
911
|
}
|
|
845
|
-
|
|
846
|
-
|
|
912
|
+
// Re-throw MCP protocol errors (e.g. from failInvalidParams) so the SDK
|
|
913
|
+
// returns them as JSON-RPC errors. failInvalidParams already set callDiagnostics
|
|
914
|
+
// with the correct semantic category (e.g. AUTH), so we must not overwrite it.
|
|
915
|
+
if (error instanceof McpError) {
|
|
916
|
+
throw error;
|
|
917
|
+
}
|
|
918
|
+
toolStatus = getToolStatusFromError(error, Boolean((_f = extra.signal) === null || _f === void 0 ? void 0 : _f.aborted));
|
|
919
|
+
const failureDetail = error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200);
|
|
920
|
+
callDiagnostics = {
|
|
921
|
+
// Spread existing diagnostics first (e.g. validation_keyword from failInvalidParams),
|
|
922
|
+
// then overwrite with freshly computed fields so they take precedence.
|
|
923
|
+
...callDiagnostics,
|
|
924
|
+
failure_category: classifyFailureCategory(error),
|
|
925
|
+
...(httpStatus !== undefined ? { failure_http_status: httpStatus } : {}),
|
|
926
|
+
failure_detail: failureDetail,
|
|
927
|
+
...buildActorFields(actorName, actorId),
|
|
928
|
+
};
|
|
929
|
+
logHttpError(error, 'Error occurred while calling tool', {
|
|
930
|
+
toolName: name,
|
|
931
|
+
toolStatus,
|
|
932
|
+
mcpSessionId,
|
|
933
|
+
failureCategory: callDiagnostics.failure_category,
|
|
934
|
+
failureHttpStatus: callDiagnostics.failure_http_status,
|
|
935
|
+
actorName: callDiagnostics.actor_name,
|
|
936
|
+
validationKeyword: callDiagnostics.validation_keyword,
|
|
937
|
+
validationPath: callDiagnostics.validation_path,
|
|
938
|
+
validationMissingProperty: callDiagnostics.validation_missing_property,
|
|
939
|
+
validationAdditionalProperty: callDiagnostics.validation_additional_property,
|
|
940
|
+
});
|
|
847
941
|
const errorMessage = (error instanceof Error) ? error.message : 'Unknown error';
|
|
848
942
|
return buildMCPResponse({
|
|
849
943
|
texts: [`Error calling tool "${name}": ${errorMessage}. Please verify the tool name, input parameters, and ensure all required resources are available.`],
|
|
850
944
|
isError: true,
|
|
851
|
-
toolStatus,
|
|
945
|
+
telemetry: { toolStatus },
|
|
852
946
|
});
|
|
853
947
|
}
|
|
854
948
|
finally {
|
|
855
|
-
|
|
949
|
+
if (shouldTrackTelemetry) {
|
|
950
|
+
this.finalizeAndTrackTelemetry(telemetryData, userId, startTime, toolStatus, callDiagnostics);
|
|
951
|
+
}
|
|
856
952
|
}
|
|
857
953
|
const availableTools = this.listToolNames();
|
|
858
|
-
const msg = `
|
|
859
|
-
|
|
860
|
-
|
|
954
|
+
const msg = dedent `
|
|
955
|
+
Unknown tool type for "${name}".
|
|
956
|
+
Available tools: ${availableTools.length > 0 ? availableTools.join(', ') : 'none'}.
|
|
957
|
+
Please verify the tool name and ensure the tool is properly registered.
|
|
958
|
+
`;
|
|
861
959
|
log.softFail(msg, { mcpSessionId, statusCode: 404 });
|
|
862
960
|
await this.server.sendLoggingMessage({
|
|
863
961
|
level: 'error',
|
|
@@ -874,8 +972,9 @@ Please verify the tool name and ensure the tool is properly registered.`;
|
|
|
874
972
|
* @param userId - Apify user ID (string or null if not available)
|
|
875
973
|
* @param startTime - Timestamp when the tool call started
|
|
876
974
|
* @param toolStatus - Final status of the tool call
|
|
975
|
+
* @param callDiagnostics - Telemetry fields: always includes actor_name/actor_id when available; failure-specific fields only on non-success
|
|
877
976
|
*/
|
|
878
|
-
finalizeAndTrackTelemetry(telemetryData, userId, startTime, toolStatus) {
|
|
977
|
+
finalizeAndTrackTelemetry(telemetryData, userId, startTime, toolStatus, callDiagnostics) {
|
|
879
978
|
if (!telemetryData) {
|
|
880
979
|
return;
|
|
881
980
|
}
|
|
@@ -884,6 +983,8 @@ Please verify the tool name and ensure the tool is properly registered.`;
|
|
|
884
983
|
...telemetryData,
|
|
885
984
|
tool_status: toolStatus,
|
|
886
985
|
tool_exec_time_ms: execTime,
|
|
986
|
+
// Always include actor_name/actor_id; failure-specific fields are only present when callDiagnostics has them.
|
|
987
|
+
...callDiagnostics,
|
|
887
988
|
};
|
|
888
989
|
trackToolCall(userId, this.telemetryEnv, finalizedTelemetryData);
|
|
889
990
|
}
|
|
@@ -902,8 +1003,10 @@ Please verify the tool name and ensure the tool is properly registered.`;
|
|
|
902
1003
|
*/
|
|
903
1004
|
async executeToolAndUpdateTask(params) {
|
|
904
1005
|
var _a;
|
|
905
|
-
const { taskId, tool,
|
|
1006
|
+
const { taskId, tool, toolArgs, logSafeArgs, paymentRequiredResult, apifyClient, apifyToken, progressToken, extra, mcpSessionId, actorName, actorId, userRentedActorIds, } = params;
|
|
906
1007
|
let toolStatus = TOOL_STATUS.SUCCEEDED;
|
|
1008
|
+
// Always populate actor fields so they're tracked on both success and failure paths.
|
|
1009
|
+
let callDiagnostics = { ...buildActorFields(actorName, actorId) };
|
|
907
1010
|
const startTime = Date.now();
|
|
908
1011
|
log.debug('[executeToolAndUpdateTask] Starting task execution', {
|
|
909
1012
|
taskId,
|
|
@@ -912,7 +1015,7 @@ Please verify the tool name and ensure the tool is properly registered.`;
|
|
|
912
1015
|
});
|
|
913
1016
|
// Prepare telemetry before try-catch so it's accessible to both paths.
|
|
914
1017
|
// This avoids re-fetching user data in the error handler.
|
|
915
|
-
const { telemetryData, userId } = await this.prepareTelemetryData(tool, mcpSessionId, apifyToken);
|
|
1018
|
+
const { telemetryData, userId } = await this.prepareTelemetryData(getToolFullName(tool), mcpSessionId, apifyToken);
|
|
916
1019
|
try {
|
|
917
1020
|
// Check if task was already cancelled before we start execution.
|
|
918
1021
|
// Critical: if a client cancels the task immediately after creation (race condition),
|
|
@@ -923,7 +1026,9 @@ Please verify the tool name and ensure the tool is properly registered.`;
|
|
|
923
1026
|
taskId,
|
|
924
1027
|
mcpSessionId,
|
|
925
1028
|
});
|
|
926
|
-
this.finalizeAndTrackTelemetry(telemetryData, userId, startTime, TOOL_STATUS.ABORTED
|
|
1029
|
+
this.finalizeAndTrackTelemetry(telemetryData, userId, startTime, TOOL_STATUS.ABORTED, {
|
|
1030
|
+
...buildActorFields(actorName, actorId),
|
|
1031
|
+
});
|
|
927
1032
|
return;
|
|
928
1033
|
}
|
|
929
1034
|
log.debug('[executeToolAndUpdateTask] Updating task status to working', {
|
|
@@ -933,10 +1038,15 @@ Please verify the tool name and ensure the tool is properly registered.`;
|
|
|
933
1038
|
await this.taskStore.updateTaskStatus(taskId, 'working', undefined, mcpSessionId);
|
|
934
1039
|
// Execute the tool and get the result
|
|
935
1040
|
let result = {};
|
|
936
|
-
// Check payment validation (already computed by
|
|
937
|
-
if (
|
|
1041
|
+
// Check payment validation (already computed by prepareToolCallContext in the caller)
|
|
1042
|
+
if (paymentRequiredResult) {
|
|
938
1043
|
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
939
|
-
|
|
1044
|
+
callDiagnostics = {
|
|
1045
|
+
failure_category: FAILURE_CATEGORY.INVALID_INPUT,
|
|
1046
|
+
failure_http_status: 402,
|
|
1047
|
+
...buildActorFields(actorName, actorId),
|
|
1048
|
+
};
|
|
1049
|
+
result = paymentRequiredResult;
|
|
940
1050
|
}
|
|
941
1051
|
// Callback to propagate Actor run statusMessage into the task store.
|
|
942
1052
|
// Clients retrieve it via tasks/get and tasks/list polling.
|
|
@@ -948,9 +1058,9 @@ Please verify the tool name and ensure the tool is properly registered.`;
|
|
|
948
1058
|
if (toolStatus === TOOL_STATUS.SUCCEEDED && tool.type === 'internal') {
|
|
949
1059
|
const progressTracker = createProgressTracker(progressToken, extra.sendNotification, taskId, onStatusMessage);
|
|
950
1060
|
try {
|
|
951
|
-
log.info('Calling internal tool for task', { taskId, name: tool.name, mcpSessionId, input:
|
|
1061
|
+
log.info('Calling internal tool for task', { taskId, name: tool.name, mcpSessionId, input: logSafeArgs });
|
|
952
1062
|
const res = await tool.call({
|
|
953
|
-
args:
|
|
1063
|
+
args: toolArgs,
|
|
954
1064
|
extra,
|
|
955
1065
|
apifyMcpServer: this,
|
|
956
1066
|
mcpServer: this.server,
|
|
@@ -960,19 +1070,10 @@ Please verify the tool name and ensure the tool is properly registered.`;
|
|
|
960
1070
|
progressTracker,
|
|
961
1071
|
mcpSessionId,
|
|
962
1072
|
});
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
}
|
|
968
|
-
else if ('isError' in rest && rest.isError) {
|
|
969
|
-
toolStatus = TOOL_STATUS.FAILED;
|
|
970
|
-
}
|
|
971
|
-
else {
|
|
972
|
-
toolStatus = TOOL_STATUS.SUCCEEDED;
|
|
973
|
-
}
|
|
974
|
-
// Never expose internalToolStatus to MCP clients
|
|
975
|
-
result = rest;
|
|
1073
|
+
const diag = extractToolTelemetry(res, actorName, actorId);
|
|
1074
|
+
toolStatus = diag.toolStatus;
|
|
1075
|
+
callDiagnostics = { ...callDiagnostics, ...diag.callDiagnostics };
|
|
1076
|
+
result = res;
|
|
976
1077
|
}
|
|
977
1078
|
finally {
|
|
978
1079
|
if (progressTracker) {
|
|
@@ -984,10 +1085,10 @@ Please verify the tool name and ensure the tool is properly registered.`;
|
|
|
984
1085
|
if (toolStatus === TOOL_STATUS.SUCCEEDED && tool.type === 'actor') {
|
|
985
1086
|
const progressTracker = createProgressTracker(progressToken, extra.sendNotification, taskId, onStatusMessage);
|
|
986
1087
|
try {
|
|
987
|
-
log.info('Calling Actor for task', { taskId, actorName: tool.actorFullName, mcpSessionId, input:
|
|
1088
|
+
log.info('Calling Actor for task', { taskId, actorName: tool.actorFullName, mcpSessionId, input: logSafeArgs });
|
|
988
1089
|
const executorResult = await this.actorExecutor.executeActorTool({
|
|
989
1090
|
actorFullName: tool.actorFullName,
|
|
990
|
-
input:
|
|
1091
|
+
input: toolArgs,
|
|
991
1092
|
apifyClient,
|
|
992
1093
|
callOptions: { memory: tool.memoryMbytes },
|
|
993
1094
|
progressTracker,
|
|
@@ -1026,19 +1127,39 @@ Please verify the tool name and ensure the tool is properly registered.`;
|
|
|
1026
1127
|
});
|
|
1027
1128
|
await this.taskStore.storeTaskResult(taskId, 'completed', result, mcpSessionId);
|
|
1028
1129
|
log.debug('Task completed successfully', { taskId, toolName: tool.name, mcpSessionId });
|
|
1029
|
-
this.finalizeAndTrackTelemetry(telemetryData, userId, startTime, toolStatus);
|
|
1130
|
+
this.finalizeAndTrackTelemetry(telemetryData, userId, startTime, toolStatus, callDiagnostics);
|
|
1030
1131
|
}
|
|
1031
1132
|
catch (error) {
|
|
1032
|
-
log.error('Error executing tool for task', { taskId, mcpSessionId, error });
|
|
1033
1133
|
// Handle 402 Payment Required — return structured x402 result so clients can auto-pay
|
|
1034
1134
|
const httpStatus = getHttpStatusCode(error);
|
|
1035
1135
|
if (httpStatus === HTTP_PAYMENT_REQUIRED) {
|
|
1036
1136
|
logHttpError(error, 'Payment required while calling tool (task mode)', { toolName: tool.name });
|
|
1037
1137
|
await this.taskStore.storeTaskResult(taskId, 'completed', buildPaymentRequiredResponse(error), mcpSessionId);
|
|
1038
|
-
this.finalizeAndTrackTelemetry(telemetryData, userId, startTime, TOOL_STATUS.SOFT_FAIL
|
|
1138
|
+
this.finalizeAndTrackTelemetry(telemetryData, userId, startTime, TOOL_STATUS.SOFT_FAIL, {
|
|
1139
|
+
failure_category: FAILURE_CATEGORY.INVALID_INPUT,
|
|
1140
|
+
failure_http_status: 402,
|
|
1141
|
+
...buildActorFields(actorName, actorId),
|
|
1142
|
+
});
|
|
1039
1143
|
return;
|
|
1040
1144
|
}
|
|
1041
1145
|
toolStatus = getToolStatusFromError(error, Boolean((_a = extra.signal) === null || _a === void 0 ? void 0 : _a.aborted));
|
|
1146
|
+
const failureDetail = error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200);
|
|
1147
|
+
callDiagnostics = {
|
|
1148
|
+
failure_category: classifyFailureCategory(error),
|
|
1149
|
+
...(httpStatus !== undefined ? { failure_http_status: httpStatus } : {}),
|
|
1150
|
+
failure_detail: failureDetail,
|
|
1151
|
+
...buildActorFields(actorName, actorId),
|
|
1152
|
+
};
|
|
1153
|
+
log.error('Error executing tool for task', {
|
|
1154
|
+
taskId,
|
|
1155
|
+
toolName: tool.name,
|
|
1156
|
+
toolStatus,
|
|
1157
|
+
mcpSessionId,
|
|
1158
|
+
failureCategory: callDiagnostics.failure_category,
|
|
1159
|
+
failureHttpStatus: callDiagnostics.failure_http_status,
|
|
1160
|
+
actorName: callDiagnostics.actor_name,
|
|
1161
|
+
error,
|
|
1162
|
+
});
|
|
1042
1163
|
const errorMessage = (error instanceof Error) ? error.message : 'Unknown error';
|
|
1043
1164
|
// Check if task was cancelled before storing result
|
|
1044
1165
|
// TODO: In future, we should actually stop execution via AbortController,
|
|
@@ -1048,7 +1169,7 @@ Please verify the tool name and ensure the tool is properly registered.`;
|
|
|
1048
1169
|
taskId,
|
|
1049
1170
|
mcpSessionId,
|
|
1050
1171
|
});
|
|
1051
|
-
this.finalizeAndTrackTelemetry(telemetryData, userId, startTime, toolStatus);
|
|
1172
|
+
this.finalizeAndTrackTelemetry(telemetryData, userId, startTime, toolStatus, callDiagnostics);
|
|
1052
1173
|
return;
|
|
1053
1174
|
}
|
|
1054
1175
|
log.debug('[executeToolAndUpdateTask] Storing failed result', {
|
|
@@ -1064,18 +1185,17 @@ Please verify the tool name and ensure the tool is properly registered.`;
|
|
|
1064
1185
|
isError: true,
|
|
1065
1186
|
internalToolStatus: toolStatus,
|
|
1066
1187
|
}, mcpSessionId);
|
|
1067
|
-
this.finalizeAndTrackTelemetry(telemetryData, userId, startTime, toolStatus);
|
|
1188
|
+
this.finalizeAndTrackTelemetry(telemetryData, userId, startTime, toolStatus, callDiagnostics);
|
|
1068
1189
|
}
|
|
1069
1190
|
}
|
|
1070
1191
|
/*
|
|
1071
1192
|
* Creates telemetry data for a tool call.
|
|
1072
1193
|
*/
|
|
1073
|
-
async prepareTelemetryData(
|
|
1194
|
+
async prepareTelemetryData(toolName, mcpSessionId, apifyToken) {
|
|
1074
1195
|
var _a, _b, _c, _d, _e;
|
|
1075
1196
|
if (!this.telemetryEnabled) {
|
|
1076
1197
|
return { telemetryData: null, userId: null };
|
|
1077
1198
|
}
|
|
1078
|
-
const toolFullName = tool.type === 'actor' ? tool.actorFullName : tool.name;
|
|
1079
1199
|
// Get userId from cache or fetch from API
|
|
1080
1200
|
let userId = null;
|
|
1081
1201
|
if (apifyToken) {
|
|
@@ -1094,7 +1214,7 @@ Please verify the tool name and ensure the tool is properly registered.`;
|
|
|
1094
1214
|
mcp_client_capabilities: capabilities || null,
|
|
1095
1215
|
mcp_session_id: mcpSessionId || '',
|
|
1096
1216
|
transport_type: this.options.transportType || '',
|
|
1097
|
-
tool_name:
|
|
1217
|
+
tool_name: toolName,
|
|
1098
1218
|
tool_status: TOOL_STATUS.SUCCEEDED, // Will be updated in finally
|
|
1099
1219
|
tool_exec_time_ms: 0, // Will be calculated in finally
|
|
1100
1220
|
};
|
|
@@ -1166,4 +1286,5 @@ Please verify the tool name and ensure the tool is properly registered.`;
|
|
|
1166
1286
|
await this.server.close();
|
|
1167
1287
|
}
|
|
1168
1288
|
}
|
|
1169
|
-
//# sourceMappingURL=server.js.map
|
|
1289
|
+
//# sourceMappingURL=server.js.map
|
|
1290
|
+
//# debugId=ea73e14a-6566-5941-a0ab-5d6299dec132
|