@apify/actors-mcp-server 0.11.6-beta.9 → 0.11.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/apify_client.js +4 -1
- package/dist/apify_client.js.map +1 -1
- package/dist/const.d.ts +10 -0
- package/dist/const.d.ts.map +1 -1
- package/dist/const.js +14 -1
- 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.d.ts +1 -0
- package/dist/index_internals.d.ts.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 +55 -20
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +209 -86
- 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/const.js +4 -1
- package/dist/payments/const.js.map +1 -1
- package/dist/payments/helpers.js +4 -1
- package/dist/payments/helpers.js.map +1 -1
- package/dist/payments/index.js +4 -1
- 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/resources/resource_service.js +4 -1
- package/dist/resources/resource_service.js.map +1 -1
- package/dist/resources/widgets.d.ts +5 -0
- package/dist/resources/widgets.d.ts.map +1 -1
- package/dist/resources/widgets.js +15 -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.d.ts +14 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +45 -1
- package/dist/telemetry.js.map +1 -1
- package/dist/tools/actor_input_schema.js +4 -1
- package/dist/tools/actor_input_schema.js.map +1 -1
- package/dist/tools/actor_tool_naming.js +4 -1
- package/dist/tools/actor_tool_naming.js.map +1 -1
- package/dist/tools/actors/actor_definition.js +4 -1
- package/dist/tools/actors/actor_definition.js.map +1 -1
- package/dist/tools/actors/actor_executor.d.ts.map +1 -1
- package/dist/tools/actors/actor_executor.js +6 -4
- package/dist/tools/actors/actor_executor.js.map +1 -1
- package/dist/tools/actors/actor_run_response.d.ts +11 -9
- package/dist/tools/actors/actor_run_response.d.ts.map +1 -1
- package/dist/tools/actors/actor_run_response.js +37 -25
- package/dist/tools/actors/actor_run_response.js.map +1 -1
- package/dist/tools/actors/actor_tools_factory.js +4 -1
- package/dist/tools/actors/actor_tools_factory.js.map +1 -1
- package/dist/tools/actors/add_actor.js +4 -1
- package/dist/tools/actors/add_actor.js.map +1 -1
- package/dist/tools/actors/call_actor.js +6 -3
- package/dist/tools/actors/call_actor.js.map +1 -1
- package/dist/tools/actors/fetch_actor_details.js +4 -1
- package/dist/tools/actors/fetch_actor_details.js.map +1 -1
- package/dist/tools/actors/search_actors.js +4 -1
- package/dist/tools/actors/search_actors.js.map +1 -1
- package/dist/tools/dev/report_problem.d.ts +40 -0
- package/dist/tools/dev/report_problem.d.ts.map +1 -0
- package/dist/tools/dev/report_problem.js +115 -0
- package/dist/tools/dev/report_problem.js.map +1 -0
- package/dist/tools/docs/fetch_apify_docs.js +4 -1
- package/dist/tools/docs/fetch_apify_docs.js.map +1 -1
- package/dist/tools/docs/search_apify_docs.js +4 -1
- package/dist/tools/docs/search_apify_docs.js.map +1 -1
- package/dist/tools/index.js +4 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/registry.d.ts +1 -1
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +6 -2
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/runs/abort_actor_run.js +4 -1
- package/dist/tools/runs/abort_actor_run.js.map +1 -1
- package/dist/tools/runs/get_actor_run.d.ts +7 -4
- package/dist/tools/runs/get_actor_run.d.ts.map +1 -1
- package/dist/tools/runs/get_actor_run.js +25 -20
- package/dist/tools/runs/get_actor_run.js.map +1 -1
- package/dist/tools/runs/get_actor_run_list.js +4 -1
- package/dist/tools/runs/get_actor_run_list.js.map +1 -1
- package/dist/tools/runs/get_actor_run_log.js +4 -1
- package/dist/tools/runs/get_actor_run_log.js.map +1 -1
- package/dist/tools/storage/get_dataset.js +4 -1
- package/dist/tools/storage/get_dataset.js.map +1 -1
- package/dist/tools/storage/get_dataset_items.js +4 -1
- package/dist/tools/storage/get_dataset_items.js.map +1 -1
- package/dist/tools/storage/get_dataset_list.js +4 -1
- package/dist/tools/storage/get_dataset_list.js.map +1 -1
- package/dist/tools/storage/get_dataset_schema.js +4 -1
- package/dist/tools/storage/get_dataset_schema.js.map +1 -1
- package/dist/tools/storage/get_key_value_store.js +4 -1
- package/dist/tools/storage/get_key_value_store.js.map +1 -1
- package/dist/tools/storage/get_key_value_store_keys.js +4 -1
- package/dist/tools/storage/get_key_value_store_keys.js.map +1 -1
- package/dist/tools/storage/get_key_value_store_list.js +4 -1
- package/dist/tools/storage/get_key_value_store_list.js.map +1 -1
- package/dist/tools/storage/get_key_value_store_record.js +4 -1
- package/dist/tools/storage/get_key_value_store_record.js.map +1 -1
- package/dist/tools/storage/storage_helpers.js +4 -1
- package/dist/tools/storage/storage_helpers.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/widgets/call_actor_widget.js +6 -3
- package/dist/tools/widgets/call_actor_widget.js.map +1 -1
- package/dist/tools/widgets/fetch_actor_details_widget.js +4 -1
- package/dist/tools/widgets/fetch_actor_details_widget.js.map +1 -1
- package/dist/tools/widgets/get_actor_run_widget.js +6 -3
- package/dist/tools/widgets/get_actor_run_widget.js.map +1 -1
- package/dist/tools/widgets/search_actors_widget.js +4 -1
- package/dist/tools/widgets/search_actors_widget.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +11 -0
- 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 +4 -1
- 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_errors.js +4 -1
- package/dist/utils/apify_errors.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/console_link.js +4 -1
- package/dist/utils/console_link.js.map +1 -1
- package/dist/utils/encode_text.js +4 -1
- package/dist/utils/encode_text.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.js +4 -1
- package/dist/utils/mcp.js.map +1 -1
- package/dist/utils/mcp_clients.d.ts +10 -0
- package/dist/utils/mcp_clients.d.ts.map +1 -0
- package/dist/utils/mcp_clients.js +17 -0
- package/dist/utils/mcp_clients.js.map +1 -0
- 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/index.d.ts +5 -2
- package/dist/utils/server-instructions/index.d.ts.map +1 -1
- package/dist/utils/server-instructions/index.js +14 -4
- package/dist/utils/server-instructions/index.js.map +1 -1
- package/dist/utils/server_mode.js +4 -1
- package/dist/utils/server_mode.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 +10 -0
- package/dist/utils/tool_status.d.ts.map +1 -1
- package/dist/utils/tool_status.js +14 -1
- package/dist/utils/tool_status.js.map +1 -1
- package/dist/utils/tools.js +4 -1
- package/dist/utils/tools.js.map +1 -1
- package/dist/utils/tools_loader.d.ts.map +1 -1
- package/dist/utils/tools_loader.js +10 -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,6 +1,8 @@
|
|
|
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]="5bffa047-752e-51d3-a0f8-131d529ca2f9")}catch(e){}}();
|
|
4
6
|
import { randomUUID } from 'node:crypto';
|
|
5
7
|
// The ext-apps package exposes `./server` via conditional exports only (no `./server/index.js`
|
|
6
8
|
// wildcard), so we can't satisfy the `import/extensions` rule on this subpath.
|
|
@@ -19,21 +21,23 @@ import { prompts } from '../prompts/index.js';
|
|
|
19
21
|
import { createResourceService } from '../resources/resource_service.js';
|
|
20
22
|
import { resolveAvailableWidgets } from '../resources/widgets.js';
|
|
21
23
|
import { getServerInfo } from '../server_card.js';
|
|
22
|
-
import { getTelemetryEnv, trackToolCall } from '../telemetry.js';
|
|
24
|
+
import { buildReportedProblemProperties, getTelemetryEnv, trackReportedProblem, trackToolCall } from '../telemetry.js';
|
|
23
25
|
import { decodeDotPropertyNames } from '../tools/actor_input_schema.js';
|
|
24
26
|
import { legacyToolNameToNew } from '../tools/actor_tool_naming.js';
|
|
25
27
|
import { actorExecutor } from '../tools/actors/actor_executor.js';
|
|
26
28
|
import { buildPermissionApprovalResponse, checkPaymentProviderStandbyConflict } from '../tools/actors/call_actor.js';
|
|
29
|
+
import { appendReportProblemNudge } from '../tools/dev/report_problem.js';
|
|
27
30
|
import { getActorsAsTools } from '../tools/index.js';
|
|
28
31
|
import { SERVER_MODE, TOOL_TYPE } from '../types.js';
|
|
29
32
|
import { isPermissionApprovalError, remoteMcpFailureDetail } from '../utils/apify_errors.js';
|
|
30
33
|
import { getHttpStatusCode, isMcpClientFaultMessage, logHttpError, sanitizeMezmoMessage } from '../utils/logging.js';
|
|
31
34
|
import { computeToolResponseBytes, getToolCallErrorUserText, respondErrorNoTelemetry, respondOk, } from '../utils/mcp.js';
|
|
35
|
+
import { isReportProblemBlockedForClient } from '../utils/mcp_clients.js';
|
|
32
36
|
import { buildPaymentRequiredResponse, isX402PaymentRequiredError } from '../utils/payment_errors.js';
|
|
33
37
|
import { createProgressTracker } from '../utils/progress.js';
|
|
34
38
|
import { getServerInstructions } from '../utils/server-instructions/index.js';
|
|
35
39
|
import { parseServerMode, resolveServerMode } from '../utils/server_mode.js';
|
|
36
|
-
import { classifyFailureCategory, deriveResourceIds, extractAjvErrorDetails,
|
|
40
|
+
import { applyToolTelemetry, classifyFailureCategory, deriveResourceIds, extractAjvErrorDetails, getToolStatusFromError, } from '../utils/tool_status.js';
|
|
37
41
|
import { buildActorFields, extractActorId, extractActorName, getToolFullName, getToolPublicFieldOnly, } from '../utils/tools.js';
|
|
38
42
|
import { getActors, getToolsForServerMode, toolNamesToInput } from '../utils/tools_loader.js';
|
|
39
43
|
import { getUserInfoCached } from '../utils/userid_cache.js';
|
|
@@ -81,6 +85,24 @@ export async function emitTaskStatusNotification(taskId, mcpSessionId, taskStore
|
|
|
81
85
|
// Silent fail — notifications are advisory
|
|
82
86
|
}
|
|
83
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Shared diagnostics for a pre-flight failure (standby-provider conflict or missing/invalid payment
|
|
90
|
+
* signature) — a call outcome already known before any Actor runs. Standby rejection wins over the
|
|
91
|
+
* generic payment-required failure so the agent gets the precise reason instead of a generic 402.
|
|
92
|
+
* Pure: callers own their own post-handling (return the result directly, or store + notify + synthesize
|
|
93
|
+
* a terminal task) — call this only once `standbyRejection ?? paymentRequiredResult` is already truthy.
|
|
94
|
+
*/
|
|
95
|
+
function buildPreflightFailureOutcome(standbyRejection, paymentRequiredResult, actorName, actorId) {
|
|
96
|
+
return {
|
|
97
|
+
toolStatus: TOOL_STATUS.SOFT_FAIL,
|
|
98
|
+
callDiagnostics: {
|
|
99
|
+
failure_category: FAILURE_CATEGORY.INVALID_INPUT,
|
|
100
|
+
...(standbyRejection ? {} : { failure_http_status: 402 }),
|
|
101
|
+
...buildActorFields(actorName, actorId),
|
|
102
|
+
},
|
|
103
|
+
result: (standbyRejection !== null && standbyRejection !== void 0 ? standbyRejection : paymentRequiredResult),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
84
106
|
/**
|
|
85
107
|
* Create Apify MCP server
|
|
86
108
|
*/
|
|
@@ -156,7 +178,10 @@ export class ActorsMcpServer {
|
|
|
156
178
|
writable: true,
|
|
157
179
|
value: void 0
|
|
158
180
|
});
|
|
159
|
-
/** True once mode is final
|
|
181
|
+
/** True once the server mode is final: at construction for explicit `default`/`apps`, or after
|
|
182
|
+
* the initialize handler resolves `'auto'`. Composing before this in `'auto'` mode would use
|
|
183
|
+
* the preliminary DEFAULT mode and produce the wrong (non-widget) tool variants, so composition
|
|
184
|
+
* waits for it. Distinct from {@link clientKnown}, which only withholds client-gated tools. */
|
|
160
185
|
Object.defineProperty(this, "serverModeResolved", {
|
|
161
186
|
enumerable: true,
|
|
162
187
|
configurable: true,
|
|
@@ -164,12 +189,13 @@ export class ActorsMcpServer {
|
|
|
164
189
|
value: void 0
|
|
165
190
|
});
|
|
166
191
|
/**
|
|
167
|
-
* Tool
|
|
168
|
-
* (
|
|
169
|
-
*
|
|
170
|
-
*
|
|
192
|
+
* Tool sources queued until composition is possible. Enqueued when the mode is not yet resolved
|
|
193
|
+
* (`'auto'` before initialize), and re-composed by the initialize flush — which is also when the
|
|
194
|
+
* client becomes known, so any client-gated tools withheld by an eager compose are added then.
|
|
195
|
+
* We capture the exact actor-tool slice fetched for each request so the flush composes every
|
|
196
|
+
* entry against *its own* actor list rather than the accumulated union across unrelated requests.
|
|
171
197
|
*/
|
|
172
|
-
Object.defineProperty(this, "
|
|
198
|
+
Object.defineProperty(this, "pendingToolsUntilClientKnown", {
|
|
173
199
|
enumerable: true,
|
|
174
200
|
configurable: true,
|
|
175
201
|
writable: true,
|
|
@@ -314,6 +340,8 @@ export class ActorsMcpServer {
|
|
|
314
340
|
}
|
|
315
341
|
this.serverModeResolved = true;
|
|
316
342
|
}
|
|
343
|
+
// Setting this makes `clientKnown` true, so the queued compose below (and any later
|
|
344
|
+
// load) resolves the tool set for this client and applies the per-client blocklist.
|
|
317
345
|
this.options.initializeRequestData = request;
|
|
318
346
|
log.info('Resolved server mode for client capabilities', {
|
|
319
347
|
serverMode: this.serverMode,
|
|
@@ -321,23 +349,74 @@ export class ActorsMcpServer {
|
|
|
321
349
|
clientSupportsUi: this.clientSupportsUi,
|
|
322
350
|
capabilities: (_a = request === null || request === void 0 ? void 0 : request.params) === null || _a === void 0 ? void 0 : _a.capabilities,
|
|
323
351
|
});
|
|
324
|
-
this.
|
|
352
|
+
this.composePendingToolsForClient();
|
|
325
353
|
await this.resolveWidgets();
|
|
326
354
|
const result = await sdkInitHandler(request);
|
|
327
|
-
|
|
355
|
+
// Tools are final here (composePendingToolsForClient ran above, applying the per-client
|
|
356
|
+
// blocklist), so tool presence is the ground truth for whether to advertise
|
|
357
|
+
// report-problem in the instructions.
|
|
358
|
+
result.instructions = getServerInstructions(this.serverMode, this.tools.has(HELPER_TOOLS.PROBLEM_REPORT));
|
|
328
359
|
return result;
|
|
329
360
|
});
|
|
330
361
|
}
|
|
331
|
-
|
|
332
|
-
|
|
362
|
+
/** True once the connecting client is known (set in the initialize handler, or hydrated by a
|
|
363
|
+
* recovery path). Only client-gated tools wait for this so the per-client blocklist can be
|
|
364
|
+
* applied; client-agnostic tools compose regardless. */
|
|
365
|
+
get clientKnown() {
|
|
366
|
+
return this.options.initializeRequestData != null;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Compose the tool list for the current connection: resolve mode-specific tools, then drop
|
|
370
|
+
* report-problem unless it is currently servable (see {@link isReportProblemServable}).
|
|
371
|
+
* report-problem is a default-injected tool (via tools_loader) rather than a category member,
|
|
372
|
+
* gated here by servability. Every other tool composes eagerly, so a recovery/rehydration load
|
|
373
|
+
* without an initialize still restores them. report-problem is withheld until the client is known
|
|
374
|
+
* and re-added by the initialize flush. Used by every input-driven load path and the flush.
|
|
375
|
+
* (loadActorsAsTools upserts actor tools directly; actor tools are never gated, so they need no
|
|
376
|
+
* filtering.)
|
|
377
|
+
*/
|
|
378
|
+
composeToolsForClient(input, actorTools) {
|
|
379
|
+
const tools = getToolsForServerMode(input, actorTools, this.serverMode);
|
|
380
|
+
if (this.isReportProblemServable())
|
|
381
|
+
return tools;
|
|
382
|
+
return tools.filter((tool) => tool.name !== HELPER_TOOLS.PROBLEM_REPORT);
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Whether report-problem may be served on this connection right now:
|
|
386
|
+
* - Its only function is forwarding submissions via telemetry, so it is never servable when
|
|
387
|
+
* telemetry is disabled (it would just fake an acknowledgement into the void).
|
|
388
|
+
* - It cannot be judged until the connecting client is known, so it is withheld until then;
|
|
389
|
+
* the initialize flush re-composes and adds it if the client allows.
|
|
390
|
+
* Every other tool is unconditionally servable, so recovery loads compose them eagerly and they
|
|
391
|
+
* survive a load that never sees an initialize.
|
|
392
|
+
*/
|
|
393
|
+
isReportProblemServable() {
|
|
394
|
+
return (!!this.telemetryEnabled &&
|
|
395
|
+
this.clientKnown &&
|
|
396
|
+
!isReportProblemBlockedForClient(this.options.initializeRequestData));
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Append the report-problem nudge to a failed tool result. Thin wrapper over
|
|
400
|
+
* {@link appendReportProblemNudge} that fills in whether report-problem is served on this
|
|
401
|
+
* connection; suppression by category/402 is handled inside the helper.
|
|
402
|
+
*/
|
|
403
|
+
withReportProblemNudge(result, failingToolName, failureCategory, failureHttpStatus) {
|
|
404
|
+
return appendReportProblemNudge(result, {
|
|
405
|
+
failingToolName,
|
|
406
|
+
available: this.tools.has(HELPER_TOOLS.PROBLEM_REPORT),
|
|
407
|
+
failureCategory,
|
|
408
|
+
failureHttpStatus,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
composePendingToolsForClient() {
|
|
412
|
+
if (this.pendingToolsUntilClientKnown.length === 0)
|
|
333
413
|
return;
|
|
334
|
-
const tools = this.
|
|
335
|
-
this.
|
|
336
|
-
// Notify after the flush so shared-state handlers (e.g. Redis recovery) see
|
|
337
|
-
//
|
|
338
|
-
//
|
|
339
|
-
//
|
|
340
|
-
// don't notify at all — this call reconciles both paths to the complete set.
|
|
414
|
+
const tools = this.pendingToolsUntilClientKnown.flatMap(({ input, actorTools }) => this.composeToolsForClient(input, actorTools));
|
|
415
|
+
this.pendingToolsUntilClientKnown = [];
|
|
416
|
+
// Notify after the flush so shared-state handlers (e.g. Redis recovery) see the final tool
|
|
417
|
+
// set. Load paths already upserted the client-agnostic tools pre-init; re-upserting is
|
|
418
|
+
// idempotent, and this pass adds the client-gated tools (e.g. report-problem) now that the
|
|
419
|
+
// client is known, reconciling shared state to the complete set.
|
|
341
420
|
if (tools.length > 0)
|
|
342
421
|
this.upsertTools(tools, true);
|
|
343
422
|
}
|
|
@@ -409,28 +488,29 @@ export class ActorsMcpServer {
|
|
|
409
488
|
return [...this.listInternalToolNames(), ...this.listActorToolNames(), ...this.listActorMcpServerToolIds()];
|
|
410
489
|
}
|
|
411
490
|
/**
|
|
412
|
-
* Buffer-or-compose gate shared by the actor-tools loaders
|
|
413
|
-
*
|
|
414
|
-
* (if non-empty) upsert
|
|
415
|
-
* compose the
|
|
416
|
-
*
|
|
491
|
+
* Buffer-or-compose gate shared by the actor-tools loaders. If the server mode isn't resolved
|
|
492
|
+
* yet ('auto' before initialize), queue the whole source for `composePendingToolsForClient` and
|
|
493
|
+
* (if non-empty) upsert the mode-agnostic actor tools immediately with the given `shouldNotify`.
|
|
494
|
+
* Once the mode is resolved, compose the client-specific set via `composeToolsForClient` (which
|
|
495
|
+
* withholds report-problem until the client is known) and upsert it; if the client still isn't
|
|
496
|
+
* known, queue the source so the initialize flush re-composes and adds the client-gated tools.
|
|
417
497
|
*
|
|
418
|
-
* Callers pass different `shouldNotify` values: `loadToolsByName` forwards
|
|
419
|
-
*
|
|
420
|
-
*
|
|
421
|
-
* post-initialize reconcile. See `updateToolsAfterServerModeResolved` for the full
|
|
422
|
-
* rationale.
|
|
498
|
+
* Callers pass different `shouldNotify` values: `loadToolsByName` forwards `actorTools.length > 0`
|
|
499
|
+
* (notify only when actor tools were fetched), while `loadToolsFromUrl` and `loadToolsFromInput`
|
|
500
|
+
* pass `false` and defer to the post-initialize reconcile. See `composePendingToolsForClient`.
|
|
423
501
|
*/
|
|
424
502
|
registerFetchedActorTools(input, actorTools, shouldNotify) {
|
|
425
503
|
if (!this.serverModeResolved) {
|
|
426
|
-
this.
|
|
504
|
+
this.pendingToolsUntilClientKnown.push({ input, actorTools });
|
|
427
505
|
if (actorTools.length > 0)
|
|
428
506
|
this.upsertTools(actorTools, shouldNotify);
|
|
429
507
|
return;
|
|
430
508
|
}
|
|
431
|
-
const tools =
|
|
509
|
+
const tools = this.composeToolsForClient(input, actorTools);
|
|
432
510
|
if (tools.length > 0)
|
|
433
511
|
this.upsertTools(tools, shouldNotify);
|
|
512
|
+
if (!this.clientKnown)
|
|
513
|
+
this.pendingToolsUntilClientKnown.push({ input, actorTools });
|
|
434
514
|
}
|
|
435
515
|
/**
|
|
436
516
|
* Loads missing toolNames from a provided list of tool names.
|
|
@@ -477,9 +557,10 @@ export class ActorsMcpServer {
|
|
|
477
557
|
this.registerFetchedActorTools(input, actorTools, false);
|
|
478
558
|
}
|
|
479
559
|
/**
|
|
480
|
-
* Two-phase: getActors (async,
|
|
481
|
-
* (sync
|
|
482
|
-
* the
|
|
560
|
+
* Two-phase: getActors (async, client-agnostic Apify fetch) then composeToolsForClient
|
|
561
|
+
* (sync compose + servability filter). If the mode isn't resolved yet ('auto' before initialize)
|
|
562
|
+
* the whole source is queued for the flush. Otherwise tools compose immediately; client-gated
|
|
563
|
+
* tools are withheld until the client is known, and the source is queued so the flush adds them.
|
|
483
564
|
*
|
|
484
565
|
* Don't move the getActors await into the initialize handler — clients time out
|
|
485
566
|
* waiting for InitializeResult. The queue buffers already-fetched data, not network
|
|
@@ -514,6 +595,9 @@ export class ActorsMcpServer {
|
|
|
514
595
|
* @returns Array of added/updated tool wrappers
|
|
515
596
|
*/
|
|
516
597
|
upsertTools(tools, shouldNotifyToolsChangedHandler = false) {
|
|
598
|
+
// Client gating (e.g. hiding report-problem from Anthropic surfaces) is applied earlier, in
|
|
599
|
+
// composeToolsForClient — the single compose choke point where the client is known. Do not
|
|
600
|
+
// filter here: this is a low-level commit point reached before the client is known too.
|
|
517
601
|
for (const tool of tools) {
|
|
518
602
|
const stored = this.options.paymentProvider ? this.options.paymentProvider.decorateToolSchema(tool) : tool;
|
|
519
603
|
this.tools.set(stored.name, stored);
|
|
@@ -765,11 +849,19 @@ export class ActorsMcpServer {
|
|
|
765
849
|
let callDiagnostics = {};
|
|
766
850
|
let shouldTrackTelemetry = true;
|
|
767
851
|
let resolvedToolName = name;
|
|
852
|
+
// Set only on the pre-flight task path — the one task-mode flow whose telemetry rides
|
|
853
|
+
// this handler's `finally` — so its `Tool call completed` log line keeps the taskId the
|
|
854
|
+
// async path logs via finishTaskTracking.
|
|
855
|
+
let preflightTaskId;
|
|
768
856
|
// Captured by `captureResult` so the `finally` block can measure response size for telemetry.
|
|
769
857
|
let toolResult = null;
|
|
770
858
|
const captureResult = (r) => {
|
|
771
|
-
|
|
772
|
-
|
|
859
|
+
// On a failed result, nudge the agent to report the blocker via report-problem at the
|
|
860
|
+
// moment it decides what to do next. Gated on the tool actually being served (see
|
|
861
|
+
// isReportProblemServable), so clients where it is blocklisted or telemetry is off never see it.
|
|
862
|
+
const augmented = this.withReportProblemNudge(r, resolvedToolName, callDiagnostics.failure_category, callDiagnostics.failure_http_status);
|
|
863
|
+
toolResult = augmented;
|
|
864
|
+
return augmented;
|
|
773
865
|
};
|
|
774
866
|
const failInvalidParams = async (message, details, logFields) => {
|
|
775
867
|
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
@@ -911,6 +1003,50 @@ export class ActorsMcpServer {
|
|
|
911
1003
|
toolName: tool.name,
|
|
912
1004
|
mcpSessionId,
|
|
913
1005
|
});
|
|
1006
|
+
// Pre-flight failure is already known — the outcome needs no work. Resolve the
|
|
1007
|
+
// task synchronously: store the failure as the terminal `completed` result and emit
|
|
1008
|
+
// exactly one `completed` status notification (no `updateTaskStatus('working')`, so no
|
|
1009
|
+
// `working` notification). Standby rejection wins over the generic payment-required
|
|
1010
|
+
// short-circuit, matching the sync path's precedence. Telemetry rides the handler's
|
|
1011
|
+
// outer `finally` (shouldTrackTelemetry stays true), firing once with the sync-path
|
|
1012
|
+
// properties plus the taskId on the log line.
|
|
1013
|
+
const preflightResult = standbyRejection !== null && standbyRejection !== void 0 ? standbyRejection : paymentRequiredResult;
|
|
1014
|
+
if (preflightResult) {
|
|
1015
|
+
const outcome = buildPreflightFailureOutcome(standbyRejection, paymentRequiredResult, actorName, actorId);
|
|
1016
|
+
toolStatus = outcome.toolStatus;
|
|
1017
|
+
callDiagnostics = outcome.callDiagnostics;
|
|
1018
|
+
preflightTaskId = task.taskId;
|
|
1019
|
+
try {
|
|
1020
|
+
await storeTaskResultOrSkipIfExpired(this.taskStore, tool.name, task.taskId, 'completed', outcome.result, mcpSessionId);
|
|
1021
|
+
}
|
|
1022
|
+
catch (error) {
|
|
1023
|
+
// A store failure (not expiry) would otherwise fall through to the generic
|
|
1024
|
+
// catch and return a task-less tool result the client rejects as a
|
|
1025
|
+
// CreateTaskResult parse error; surface it as a protocol error instead.
|
|
1026
|
+
// The pre-flight outcome left toolStatus=SOFT_FAIL, but a genuine store
|
|
1027
|
+
// outage is a hard failure — correct it before throwing so the handler
|
|
1028
|
+
// `finally` logs FAILED/INTERNAL_ERROR (the outer McpError re-throw preserves
|
|
1029
|
+
// these), not the stale pre-flight SOFT_FAIL.
|
|
1030
|
+
toolStatus = TOOL_STATUS.FAILED;
|
|
1031
|
+
callDiagnostics = {
|
|
1032
|
+
failure_category: FAILURE_CATEGORY.INTERNAL_ERROR,
|
|
1033
|
+
...buildActorFields(actorName, actorId),
|
|
1034
|
+
};
|
|
1035
|
+
throw new McpError(ErrorCode.InternalError, `Failed to store the pre-flight result for task "${task.taskId}": ${error instanceof Error ? error.message : String(error)}`);
|
|
1036
|
+
}
|
|
1037
|
+
// Defer so the client sees the CreateTaskResult (and learns the taskId) before
|
|
1038
|
+
// the terminal status notification — the async path's post-response ordering.
|
|
1039
|
+
// emitTaskStatusNotification never throws and no-ops if the task expired.
|
|
1040
|
+
setImmediate(() => {
|
|
1041
|
+
void emitTaskStatusNotification(task.taskId, mcpSessionId, this.taskStore, this.server);
|
|
1042
|
+
});
|
|
1043
|
+
captureResult(outcome.result);
|
|
1044
|
+
// createTask returned status `working`; synthesize the terminal status instead of
|
|
1045
|
+
// re-fetching — if the task expired before the result store (the one case
|
|
1046
|
+
// storeTaskResultOrSkipIfExpired tolerates), a re-fetch would come back empty and a
|
|
1047
|
+
// `working` fallback would contradict the tasks/get 404 the client sees next.
|
|
1048
|
+
return { task: { ...task, status: 'completed' } };
|
|
1049
|
+
}
|
|
914
1050
|
// Execute the tool asynchronously and update task status
|
|
915
1051
|
setImmediate(() => {
|
|
916
1052
|
this.executeToolAndUpdateTask({
|
|
@@ -918,8 +1054,6 @@ export class ActorsMcpServer {
|
|
|
918
1054
|
tool,
|
|
919
1055
|
toolArgs: toolArgs,
|
|
920
1056
|
logSafeArgs,
|
|
921
|
-
standbyRejection,
|
|
922
|
-
paymentRequiredResult,
|
|
923
1057
|
apifyClient: apifyClient,
|
|
924
1058
|
apifyToken,
|
|
925
1059
|
progressToken,
|
|
@@ -936,25 +1070,14 @@ export class ActorsMcpServer {
|
|
|
936
1070
|
shouldTrackTelemetry = false;
|
|
937
1071
|
return { task };
|
|
938
1072
|
}
|
|
939
|
-
// Sync path:
|
|
940
|
-
//
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
return captureResult(standbyRejection);
|
|
948
|
-
}
|
|
949
|
-
// Check payment validation (already computed by prepareToolCallContext)
|
|
950
|
-
if (paymentRequiredResult) {
|
|
951
|
-
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
952
|
-
callDiagnostics = {
|
|
953
|
-
failure_category: FAILURE_CATEGORY.INVALID_INPUT,
|
|
954
|
-
failure_http_status: 402,
|
|
955
|
-
...buildActorFields(actorName, actorId),
|
|
956
|
-
};
|
|
957
|
-
return captureResult(paymentRequiredResult);
|
|
1073
|
+
// Sync path: short-circuit on either pre-flight failure. buildPreflightFailureOutcome
|
|
1074
|
+
// encodes the precedence — standby rejection wins over the generic payment-required
|
|
1075
|
+
// 402, so the agent gets the precise reason.
|
|
1076
|
+
if (standbyRejection || paymentRequiredResult) {
|
|
1077
|
+
const outcome = buildPreflightFailureOutcome(standbyRejection, paymentRequiredResult, actorName, actorId);
|
|
1078
|
+
toolStatus = outcome.toolStatus;
|
|
1079
|
+
callDiagnostics = outcome.callDiagnostics;
|
|
1080
|
+
return captureResult(outcome.result);
|
|
958
1081
|
}
|
|
959
1082
|
// Handle internal tool
|
|
960
1083
|
if (tool.type === TOOL_TYPE.INTERNAL) {
|
|
@@ -977,9 +1100,7 @@ export class ActorsMcpServer {
|
|
|
977
1100
|
mcpSessionId,
|
|
978
1101
|
}));
|
|
979
1102
|
// Extract diagnostics and strip internal fields from res before returning to client.
|
|
980
|
-
|
|
981
|
-
toolStatus = diag.toolStatus;
|
|
982
|
-
callDiagnostics = { ...callDiagnostics, ...diag.callDiagnostics };
|
|
1103
|
+
({ toolStatus, callDiagnostics } = applyToolTelemetry(res, actorName, actorId, callDiagnostics));
|
|
983
1104
|
return captureResult(res);
|
|
984
1105
|
}
|
|
985
1106
|
finally {
|
|
@@ -1090,6 +1211,10 @@ export class ActorsMcpServer {
|
|
|
1090
1211
|
// https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/cancellation#behavior-requirements
|
|
1091
1212
|
return captureResult({});
|
|
1092
1213
|
}
|
|
1214
|
+
// Mirror the INTERNAL branch: read the telemetry the executor embedded on error
|
|
1215
|
+
// results (e.g. respondUserError → SOFT_FAIL/INVALID_INPUT), strip it from the wire,
|
|
1216
|
+
// and set failure_category so the report-problem nudge picks the softer variant.
|
|
1217
|
+
({ toolStatus, callDiagnostics } = applyToolTelemetry(executorResult, actorName, actorId, callDiagnostics));
|
|
1093
1218
|
return captureResult(executorResult);
|
|
1094
1219
|
}
|
|
1095
1220
|
finally {
|
|
@@ -1179,6 +1304,7 @@ export class ActorsMcpServer {
|
|
|
1179
1304
|
callDiagnostics,
|
|
1180
1305
|
args,
|
|
1181
1306
|
result: toolResult,
|
|
1307
|
+
taskId: preflightTaskId,
|
|
1182
1308
|
});
|
|
1183
1309
|
}
|
|
1184
1310
|
}
|
|
@@ -1236,6 +1362,13 @@ export class ActorsMcpServer {
|
|
|
1236
1362
|
...deriveResourceIds(params.args, params.result),
|
|
1237
1363
|
};
|
|
1238
1364
|
trackToolCall(params.userId, this.telemetryEnv, finalizedTelemetryData);
|
|
1365
|
+
// A successful report-problem call also emits a dedicated feedback event carrying the
|
|
1366
|
+
// submission. A downstream Segment destination fans it out to Slack/GitHub.
|
|
1367
|
+
if (params.toolName === HELPER_TOOLS.PROBLEM_REPORT &&
|
|
1368
|
+
params.toolStatus === TOOL_STATUS.SUCCEEDED &&
|
|
1369
|
+
params.args) {
|
|
1370
|
+
trackReportedProblem(params.userId, this.telemetryEnv, buildReportedProblemProperties(finalizedTelemetryData, params.args));
|
|
1371
|
+
}
|
|
1239
1372
|
}
|
|
1240
1373
|
}
|
|
1241
1374
|
// TODO: this function quite duplicates the main tool call login the CallToolRequestSchema handler, we should refactor
|
|
@@ -1252,7 +1385,7 @@ export class ActorsMcpServer {
|
|
|
1252
1385
|
* @param params.mcpSessionId - MCP session ID for telemetry
|
|
1253
1386
|
*/
|
|
1254
1387
|
async executeToolAndUpdateTask(params) {
|
|
1255
|
-
const { taskId, tool, toolArgs, logSafeArgs,
|
|
1388
|
+
const { taskId, tool, toolArgs, logSafeArgs, apifyClient, apifyToken, progressToken, extra, mcpSessionId, actorName, actorId, } = params;
|
|
1256
1389
|
let toolStatus = TOOL_STATUS.SUCCEEDED;
|
|
1257
1390
|
// Always populate actor fields so they're tracked on both success and failure paths.
|
|
1258
1391
|
let callDiagnostics = { ...buildActorFields(actorName, actorId) };
|
|
@@ -1324,25 +1457,6 @@ export class ActorsMcpServer {
|
|
|
1324
1457
|
await emitTaskStatusNotification(taskId, mcpSessionId, this.taskStore, this.server);
|
|
1325
1458
|
// Execute the tool and get the result
|
|
1326
1459
|
let result = {};
|
|
1327
|
-
// Mirror the sync-path ordering: standby rejection wins over the generic 402 so the
|
|
1328
|
-
// stored task result carries the precise reason the agent should act on.
|
|
1329
|
-
if (standbyRejection) {
|
|
1330
|
-
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
1331
|
-
callDiagnostics = {
|
|
1332
|
-
failure_category: FAILURE_CATEGORY.INVALID_INPUT,
|
|
1333
|
-
...buildActorFields(actorName, actorId),
|
|
1334
|
-
};
|
|
1335
|
-
result = standbyRejection;
|
|
1336
|
-
}
|
|
1337
|
-
else if (paymentRequiredResult) {
|
|
1338
|
-
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
1339
|
-
callDiagnostics = {
|
|
1340
|
-
failure_category: FAILURE_CATEGORY.INVALID_INPUT,
|
|
1341
|
-
failure_http_status: 402,
|
|
1342
|
-
...buildActorFields(actorName, actorId),
|
|
1343
|
-
};
|
|
1344
|
-
result = paymentRequiredResult;
|
|
1345
|
-
}
|
|
1346
1460
|
// Callback to propagate Actor run statusMessage into the task store and emit a push notification.
|
|
1347
1461
|
// TODO(TC-3): cancel arriving while this is scheduled throws cancelled → working;
|
|
1348
1462
|
// currently swallowed by progress.ts's tick catch — guard or catch explicitly.
|
|
@@ -1371,9 +1485,7 @@ export class ActorsMcpServer {
|
|
|
1371
1485
|
mcpSessionId,
|
|
1372
1486
|
taskMode: true,
|
|
1373
1487
|
}));
|
|
1374
|
-
|
|
1375
|
-
toolStatus = diag.toolStatus;
|
|
1376
|
-
callDiagnostics = { ...callDiagnostics, ...diag.callDiagnostics };
|
|
1488
|
+
({ toolStatus, callDiagnostics } = applyToolTelemetry(res, actorName, actorId, callDiagnostics));
|
|
1377
1489
|
result = res;
|
|
1378
1490
|
}
|
|
1379
1491
|
finally {
|
|
@@ -1411,6 +1523,9 @@ export class ActorsMcpServer {
|
|
|
1411
1523
|
result = {};
|
|
1412
1524
|
}
|
|
1413
1525
|
else {
|
|
1526
|
+
// Mirror the INTERNAL task branch: extract embedded telemetry so failure_category
|
|
1527
|
+
// drives the success-path nudge below and toolTelemetry is stripped from the wire.
|
|
1528
|
+
({ toolStatus, callDiagnostics } = applyToolTelemetry(executorResult, actorName, actorId, callDiagnostics));
|
|
1414
1529
|
result = executorResult;
|
|
1415
1530
|
}
|
|
1416
1531
|
}
|
|
@@ -1423,6 +1538,9 @@ export class ActorsMcpServer {
|
|
|
1423
1538
|
// Check if task was cancelled before storing result
|
|
1424
1539
|
if (await skipIfTaskCancelled(', skipping result storage', toolStatus))
|
|
1425
1540
|
return;
|
|
1541
|
+
// On a failed result, nudge the agent to report the blocker via report-problem (mirrors the
|
|
1542
|
+
// synchronous CallTool path, which task-mode calls like call-actor bypass).
|
|
1543
|
+
result = this.withReportProblemNudge(result, tool.name, callDiagnostics.failure_category, callDiagnostics.failure_http_status);
|
|
1426
1544
|
// Store the result in the task store
|
|
1427
1545
|
log.debug('[executeToolAndUpdateTask] Storing completed result', {
|
|
1428
1546
|
taskId,
|
|
@@ -1535,7 +1653,11 @@ export class ActorsMcpServer {
|
|
|
1535
1653
|
taskId,
|
|
1536
1654
|
mcpSessionId,
|
|
1537
1655
|
});
|
|
1538
|
-
|
|
1656
|
+
// Nudge on a genuinely-failed task result. INTERNAL_ERROR and an unknown category get the
|
|
1657
|
+
// full nudge; a genuine INVALID_INPUT gets the softer nudge; payment (402) is suppressed via
|
|
1658
|
+
// the HTTP status and AUTH / PERMISSION_APPROVAL_REQUIRED via NON_NUDGE_FAILURE_CATEGORIES.
|
|
1659
|
+
// The 402 branch above returns before reaching here, so this only sees non-payment failures.
|
|
1660
|
+
const failedResult = this.withReportProblemNudge({
|
|
1539
1661
|
content: [
|
|
1540
1662
|
{
|
|
1541
1663
|
type: 'text',
|
|
@@ -1544,7 +1666,7 @@ export class ActorsMcpServer {
|
|
|
1544
1666
|
],
|
|
1545
1667
|
isError: true,
|
|
1546
1668
|
internalToolStatus: toolStatus,
|
|
1547
|
-
};
|
|
1669
|
+
}, tool.name, callDiagnostics.failure_category, callDiagnostics.failure_http_status);
|
|
1548
1670
|
await storeTaskResultOrSkipIfExpired(this.taskStore, tool.name, taskId, 'failed', failedResult, mcpSessionId);
|
|
1549
1671
|
await emitTaskStatusNotification(taskId, mcpSessionId, this.taskStore, this.server);
|
|
1550
1672
|
finishTaskTracking(toolStatus, callDiagnostics, failedResult);
|
|
@@ -1651,4 +1773,5 @@ export class ActorsMcpServer {
|
|
|
1651
1773
|
await this.server.close();
|
|
1652
1774
|
}
|
|
1653
1775
|
}
|
|
1654
|
-
//# sourceMappingURL=server.js.map
|
|
1776
|
+
//# sourceMappingURL=server.js.map
|
|
1777
|
+
//# debugId=5bffa047-752e-51d3-a0f8-131d529ca2f9
|