@apify/actors-mcp-server 0.11.6-beta.1 → 0.11.6-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/README.md +4 -0
- package/dist/const.d.ts +10 -0
- package/dist/const.d.ts.map +1 -1
- package/dist/const.js +10 -0
- package/dist/const.js.map +1 -1
- package/dist/dev_server.d.ts.map +1 -1
- package/dist/dev_server.js +4 -5
- package/dist/dev_server.js.map +1 -1
- package/dist/index_internals.d.ts +1 -0
- package/dist/index_internals.d.ts.map +1 -1
- package/dist/mcp/actors.d.ts +0 -4
- package/dist/mcp/actors.d.ts.map +1 -1
- package/dist/mcp/actors.js +1 -7
- package/dist/mcp/actors.js.map +1 -1
- package/dist/mcp/server.d.ts +63 -10
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +160 -82
- package/dist/mcp/server.js.map +1 -1
- package/dist/resources/resource_service.d.ts.map +1 -1
- package/dist/resources/resource_service.js +2 -2
- package/dist/resources/resource_service.js.map +1 -1
- package/dist/resources/widgets.d.ts.map +1 -1
- package/dist/resources/widgets.js +5 -5
- package/dist/resources/widgets.js.map +1 -1
- package/dist/stdio.js +2 -7
- 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 +41 -0
- package/dist/telemetry.js.map +1 -1
- package/dist/tools/actors/actor_run_response.d.ts +1 -1
- package/dist/tools/actors/actor_run_response.d.ts.map +1 -1
- package/dist/tools/actors/call_actor.d.ts +4 -4
- package/dist/tools/actors/call_actor.d.ts.map +1 -1
- package/dist/tools/actors/call_actor.js +6 -6
- package/dist/tools/actors/call_actor.js.map +1 -1
- package/dist/tools/actors/search_actors.d.ts +11 -0
- package/dist/tools/actors/search_actors.d.ts.map +1 -1
- package/dist/tools/actors/search_actors.js +29 -21
- 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 +112 -0
- package/dist/tools/dev/report_problem.js.map +1 -0
- package/dist/tools/registry.d.ts +1 -1
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +2 -1
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/runs/get_actor_run.d.ts.map +1 -1
- package/dist/tools/runs/get_actor_run.js +2 -2
- package/dist/tools/runs/get_actor_run.js.map +1 -1
- package/dist/tools/runs/get_actor_run_list.d.ts.map +1 -1
- package/dist/tools/runs/get_actor_run_list.js +5 -2
- package/dist/tools/runs/get_actor_run_list.js.map +1 -1
- package/dist/tools/runs/get_actor_run_log.d.ts.map +1 -1
- package/dist/tools/runs/get_actor_run_log.js +2 -1
- package/dist/tools/runs/get_actor_run_log.js.map +1 -1
- package/dist/tools/storage/get_dataset_items.d.ts.map +1 -1
- package/dist/tools/storage/get_dataset_items.js +5 -17
- package/dist/tools/storage/get_dataset_items.js.map +1 -1
- package/dist/tools/storage/get_dataset_list.d.ts.map +1 -1
- package/dist/tools/storage/get_dataset_list.js +0 -1
- package/dist/tools/storage/get_dataset_list.js.map +1 -1
- package/dist/tools/storage/get_dataset_schema.d.ts.map +1 -1
- package/dist/tools/storage/get_dataset_schema.js +3 -14
- package/dist/tools/storage/get_dataset_schema.js.map +1 -1
- package/dist/tools/storage/get_key_value_store_keys.d.ts.map +1 -1
- package/dist/tools/storage/get_key_value_store_keys.js +4 -14
- package/dist/tools/storage/get_key_value_store_keys.js.map +1 -1
- package/dist/tools/storage/get_key_value_store_list.d.ts.map +1 -1
- package/dist/tools/storage/get_key_value_store_list.js +0 -1
- package/dist/tools/storage/get_key_value_store_list.js.map +1 -1
- package/dist/tools/storage/get_key_value_store_record.js +9 -9
- package/dist/tools/storage/get_key_value_store_record.js.map +1 -1
- package/dist/tools/storage/storage_helpers.d.ts +14 -17
- package/dist/tools/storage/storage_helpers.d.ts.map +1 -1
- package/dist/tools/storage/storage_helpers.js +27 -11
- package/dist/tools/storage/storage_helpers.js.map +1 -1
- package/dist/tools/widgets/get_actor_run_widget.d.ts.map +1 -1
- package/dist/tools/widgets/get_actor_run_widget.js +2 -1
- package/dist/tools/widgets/get_actor_run_widget.js.map +1 -1
- package/dist/tools/widgets/search_actors_widget.d.ts.map +1 -1
- package/dist/tools/widgets/search_actors_widget.js +2 -6
- package/dist/tools/widgets/search_actors_widget.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +13 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/actor.d.ts +1 -15
- package/dist/utils/actor.d.ts.map +1 -1
- package/dist/utils/actor.js +0 -62
- package/dist/utils/actor.js.map +1 -1
- package/dist/utils/encode_text.d.ts +0 -30
- package/dist/utils/encode_text.d.ts.map +1 -1
- package/dist/utils/encode_text.js +1 -79
- package/dist/utils/encode_text.js.map +1 -1
- package/dist/utils/generic.d.ts +0 -17
- package/dist/utils/generic.d.ts.map +1 -1
- package/dist/utils/generic.js +0 -49
- package/dist/utils/generic.js.map +1 -1
- package/dist/utils/mcp.d.ts +40 -24
- package/dist/utils/mcp.d.ts.map +1 -1
- package/dist/utils/mcp.js +31 -0
- package/dist/utils/mcp.js.map +1 -1
- package/dist/utils/mcp_clients.d.ts +6 -2
- package/dist/utils/mcp_clients.d.ts.map +1 -1
- package/dist/utils/mcp_clients.js +9 -16
- package/dist/utils/mcp_clients.js.map +1 -1
- package/dist/utils/payment_errors.d.ts +1 -10
- package/dist/utils/payment_errors.d.ts.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 +10 -3
- package/dist/utils/server-instructions/index.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 +10 -0
- package/dist/utils/tool_status.js.map +1 -1
- package/dist/utils/tools.d.ts +0 -10
- package/dist/utils/tools.d.ts.map +1 -1
- package/dist/utils/tools.js +0 -35
- package/dist/utils/tools.js.map +1 -1
- package/dist/utils/tools_loader.d.ts.map +1 -1
- package/dist/utils/tools_loader.js +6 -0
- package/dist/utils/tools_loader.js.map +1 -1
- package/dist/web/dist/actor-detail-widget.js +171 -462
- package/dist/web/dist/actor-run-widget.js +189 -480
- package/dist/web/dist/search-actors-widget.js +168 -459
- package/package.json +1 -3
package/dist/mcp/server.js
CHANGED
|
@@ -19,21 +19,23 @@ import { prompts } from '../prompts/index.js';
|
|
|
19
19
|
import { createResourceService } from '../resources/resource_service.js';
|
|
20
20
|
import { resolveAvailableWidgets } from '../resources/widgets.js';
|
|
21
21
|
import { getServerInfo } from '../server_card.js';
|
|
22
|
-
import { getTelemetryEnv, trackToolCall } from '../telemetry.js';
|
|
22
|
+
import { buildReportedProblemProperties, getTelemetryEnv, trackReportedProblem, trackToolCall } from '../telemetry.js';
|
|
23
23
|
import { decodeDotPropertyNames } from '../tools/actor_input_schema.js';
|
|
24
24
|
import { legacyToolNameToNew } from '../tools/actor_tool_naming.js';
|
|
25
25
|
import { actorExecutor } from '../tools/actors/actor_executor.js';
|
|
26
26
|
import { buildPermissionApprovalResponse, checkPaymentProviderStandbyConflict } from '../tools/actors/call_actor.js';
|
|
27
|
+
import { appendReportProblemNudge } from '../tools/dev/report_problem.js';
|
|
27
28
|
import { getActorsAsTools } from '../tools/index.js';
|
|
28
29
|
import { SERVER_MODE, TOOL_TYPE } from '../types.js';
|
|
29
30
|
import { isPermissionApprovalError, remoteMcpFailureDetail } from '../utils/apify_errors.js';
|
|
30
31
|
import { getHttpStatusCode, isMcpClientFaultMessage, logHttpError, sanitizeMezmoMessage } from '../utils/logging.js';
|
|
31
32
|
import { computeToolResponseBytes, getToolCallErrorUserText, respondErrorNoTelemetry, respondOk, } from '../utils/mcp.js';
|
|
33
|
+
import { isReportProblemBlockedForClient } from '../utils/mcp_clients.js';
|
|
32
34
|
import { buildPaymentRequiredResponse, isX402PaymentRequiredError } from '../utils/payment_errors.js';
|
|
33
35
|
import { createProgressTracker } from '../utils/progress.js';
|
|
34
36
|
import { getServerInstructions } from '../utils/server-instructions/index.js';
|
|
35
37
|
import { parseServerMode, resolveServerMode } from '../utils/server_mode.js';
|
|
36
|
-
import { classifyFailureCategory, deriveResourceIds, extractAjvErrorDetails,
|
|
38
|
+
import { applyToolTelemetry, classifyFailureCategory, deriveResourceIds, extractAjvErrorDetails, getToolStatusFromError, } from '../utils/tool_status.js';
|
|
37
39
|
import { buildActorFields, extractActorId, extractActorName, getToolFullName, getToolPublicFieldOnly, } from '../utils/tools.js';
|
|
38
40
|
import { getActors, getToolsForServerMode, toolNamesToInput } from '../utils/tools_loader.js';
|
|
39
41
|
import { getUserInfoCached } from '../utils/userid_cache.js';
|
|
@@ -156,7 +158,10 @@ export class ActorsMcpServer {
|
|
|
156
158
|
writable: true,
|
|
157
159
|
value: void 0
|
|
158
160
|
});
|
|
159
|
-
/** True once mode is final
|
|
161
|
+
/** True once the server mode is final: at construction for explicit `default`/`apps`, or after
|
|
162
|
+
* the initialize handler resolves `'auto'`. Composing before this in `'auto'` mode would use
|
|
163
|
+
* the preliminary DEFAULT mode and produce the wrong (non-widget) tool variants, so composition
|
|
164
|
+
* waits for it. Distinct from {@link clientKnown}, which only withholds client-gated tools. */
|
|
160
165
|
Object.defineProperty(this, "serverModeResolved", {
|
|
161
166
|
enumerable: true,
|
|
162
167
|
configurable: true,
|
|
@@ -164,12 +169,13 @@ export class ActorsMcpServer {
|
|
|
164
169
|
value: void 0
|
|
165
170
|
});
|
|
166
171
|
/**
|
|
167
|
-
* Tool
|
|
168
|
-
* (
|
|
169
|
-
*
|
|
170
|
-
*
|
|
172
|
+
* Tool sources queued until composition is possible. Enqueued when the mode is not yet resolved
|
|
173
|
+
* (`'auto'` before initialize), and re-composed by the initialize flush — which is also when the
|
|
174
|
+
* client becomes known, so any client-gated tools withheld by an eager compose are added then.
|
|
175
|
+
* We capture the exact actor-tool slice fetched for each request so the flush composes every
|
|
176
|
+
* entry against *its own* actor list rather than the accumulated union across unrelated requests.
|
|
171
177
|
*/
|
|
172
|
-
Object.defineProperty(this, "
|
|
178
|
+
Object.defineProperty(this, "pendingToolsUntilClientKnown", {
|
|
173
179
|
enumerable: true,
|
|
174
180
|
configurable: true,
|
|
175
181
|
writable: true,
|
|
@@ -314,6 +320,8 @@ export class ActorsMcpServer {
|
|
|
314
320
|
}
|
|
315
321
|
this.serverModeResolved = true;
|
|
316
322
|
}
|
|
323
|
+
// Setting this makes `clientKnown` true, so the queued compose below (and any later
|
|
324
|
+
// load) resolves the tool set for this client and applies the per-client blocklist.
|
|
317
325
|
this.options.initializeRequestData = request;
|
|
318
326
|
log.info('Resolved server mode for client capabilities', {
|
|
319
327
|
serverMode: this.serverMode,
|
|
@@ -321,23 +329,74 @@ export class ActorsMcpServer {
|
|
|
321
329
|
clientSupportsUi: this.clientSupportsUi,
|
|
322
330
|
capabilities: (_a = request === null || request === void 0 ? void 0 : request.params) === null || _a === void 0 ? void 0 : _a.capabilities,
|
|
323
331
|
});
|
|
324
|
-
this.
|
|
332
|
+
this.composePendingToolsForClient();
|
|
325
333
|
await this.resolveWidgets();
|
|
326
334
|
const result = await sdkInitHandler(request);
|
|
327
|
-
|
|
335
|
+
// Tools are final here (composePendingToolsForClient ran above, applying the per-client
|
|
336
|
+
// blocklist), so tool presence is the ground truth for whether to advertise
|
|
337
|
+
// report-problem in the instructions.
|
|
338
|
+
result.instructions = getServerInstructions(this.serverMode, this.tools.has(HELPER_TOOLS.PROBLEM_REPORT));
|
|
328
339
|
return result;
|
|
329
340
|
});
|
|
330
341
|
}
|
|
331
|
-
|
|
332
|
-
|
|
342
|
+
/** True once the connecting client is known (set in the initialize handler, or hydrated by a
|
|
343
|
+
* recovery path). Only client-gated tools wait for this so the per-client blocklist can be
|
|
344
|
+
* applied; client-agnostic tools compose regardless. */
|
|
345
|
+
get clientKnown() {
|
|
346
|
+
return this.options.initializeRequestData != null;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Compose the tool list for the current connection: resolve mode-specific tools, then drop
|
|
350
|
+
* report-problem unless it is currently servable (see {@link isReportProblemServable}).
|
|
351
|
+
* report-problem is a default-injected tool (via tools_loader) rather than a category member,
|
|
352
|
+
* gated here by servability. Every other tool composes eagerly, so a recovery/rehydration load
|
|
353
|
+
* without an initialize still restores them. report-problem is withheld until the client is known
|
|
354
|
+
* and re-added by the initialize flush. Used by every input-driven load path and the flush.
|
|
355
|
+
* (loadActorsAsTools upserts actor tools directly; actor tools are never gated, so they need no
|
|
356
|
+
* filtering.)
|
|
357
|
+
*/
|
|
358
|
+
composeToolsForClient(input, actorTools) {
|
|
359
|
+
const tools = getToolsForServerMode(input, actorTools, this.serverMode);
|
|
360
|
+
if (this.isReportProblemServable())
|
|
361
|
+
return tools;
|
|
362
|
+
return tools.filter((tool) => tool.name !== HELPER_TOOLS.PROBLEM_REPORT);
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Whether report-problem may be served on this connection right now:
|
|
366
|
+
* - Its only function is forwarding submissions via telemetry, so it is never servable when
|
|
367
|
+
* telemetry is disabled (it would just fake an acknowledgement into the void).
|
|
368
|
+
* - It cannot be judged until the connecting client is known, so it is withheld until then;
|
|
369
|
+
* the initialize flush re-composes and adds it if the client allows.
|
|
370
|
+
* Every other tool is unconditionally servable, so recovery loads compose them eagerly and they
|
|
371
|
+
* survive a load that never sees an initialize.
|
|
372
|
+
*/
|
|
373
|
+
isReportProblemServable() {
|
|
374
|
+
return (!!this.telemetryEnabled &&
|
|
375
|
+
this.clientKnown &&
|
|
376
|
+
!isReportProblemBlockedForClient(this.options.initializeRequestData));
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Append the report-problem nudge to a failed tool result. Thin wrapper over
|
|
380
|
+
* {@link appendReportProblemNudge} that fills in whether report-problem is served on this
|
|
381
|
+
* connection; suppression by category/402 is handled inside the helper.
|
|
382
|
+
*/
|
|
383
|
+
withReportProblemNudge(result, failingToolName, failureCategory, failureHttpStatus) {
|
|
384
|
+
return appendReportProblemNudge(result, {
|
|
385
|
+
failingToolName,
|
|
386
|
+
available: this.tools.has(HELPER_TOOLS.PROBLEM_REPORT),
|
|
387
|
+
failureCategory,
|
|
388
|
+
failureHttpStatus,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
composePendingToolsForClient() {
|
|
392
|
+
if (this.pendingToolsUntilClientKnown.length === 0)
|
|
333
393
|
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.
|
|
394
|
+
const tools = this.pendingToolsUntilClientKnown.flatMap(({ input, actorTools }) => this.composeToolsForClient(input, actorTools));
|
|
395
|
+
this.pendingToolsUntilClientKnown = [];
|
|
396
|
+
// Notify after the flush so shared-state handlers (e.g. Redis recovery) see the final tool
|
|
397
|
+
// set. Load paths already upserted the client-agnostic tools pre-init; re-upserting is
|
|
398
|
+
// idempotent, and this pass adds the client-gated tools (e.g. report-problem) now that the
|
|
399
|
+
// client is known, reconciling shared state to the complete set.
|
|
341
400
|
if (tools.length > 0)
|
|
342
401
|
this.upsertTools(tools, true);
|
|
343
402
|
}
|
|
@@ -408,6 +467,31 @@ export class ActorsMcpServer {
|
|
|
408
467
|
listAllToolNames() {
|
|
409
468
|
return [...this.listInternalToolNames(), ...this.listActorToolNames(), ...this.listActorMcpServerToolIds()];
|
|
410
469
|
}
|
|
470
|
+
/**
|
|
471
|
+
* Buffer-or-compose gate shared by the actor-tools loaders. If the server mode isn't resolved
|
|
472
|
+
* yet ('auto' before initialize), queue the whole source for `composePendingToolsForClient` and
|
|
473
|
+
* (if non-empty) upsert the mode-agnostic actor tools immediately with the given `shouldNotify`.
|
|
474
|
+
* Once the mode is resolved, compose the client-specific set via `composeToolsForClient` (which
|
|
475
|
+
* withholds report-problem until the client is known) and upsert it; if the client still isn't
|
|
476
|
+
* known, queue the source so the initialize flush re-composes and adds the client-gated tools.
|
|
477
|
+
*
|
|
478
|
+
* Callers pass different `shouldNotify` values: `loadToolsByName` forwards `actorTools.length > 0`
|
|
479
|
+
* (notify only when actor tools were fetched), while `loadToolsFromUrl` and `loadToolsFromInput`
|
|
480
|
+
* pass `false` and defer to the post-initialize reconcile. See `composePendingToolsForClient`.
|
|
481
|
+
*/
|
|
482
|
+
registerFetchedActorTools(input, actorTools, shouldNotify) {
|
|
483
|
+
if (!this.serverModeResolved) {
|
|
484
|
+
this.pendingToolsUntilClientKnown.push({ input, actorTools });
|
|
485
|
+
if (actorTools.length > 0)
|
|
486
|
+
this.upsertTools(actorTools, shouldNotify);
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
const tools = this.composeToolsForClient(input, actorTools);
|
|
490
|
+
if (tools.length > 0)
|
|
491
|
+
this.upsertTools(tools, shouldNotify);
|
|
492
|
+
if (!this.clientKnown)
|
|
493
|
+
this.pendingToolsUntilClientKnown.push({ input, actorTools });
|
|
494
|
+
}
|
|
411
495
|
/**
|
|
412
496
|
* Loads missing toolNames from a provided list of tool names.
|
|
413
497
|
* Skips toolNames that are already loaded and loads only the missing ones.
|
|
@@ -424,16 +508,7 @@ export class ActorsMcpServer {
|
|
|
424
508
|
actorStore: this.actorStore,
|
|
425
509
|
paymentProvider: this.options.paymentProvider,
|
|
426
510
|
});
|
|
427
|
-
|
|
428
|
-
this.pendingToolsAfterModeResolved.push({ input: restoreInput, actorTools });
|
|
429
|
-
if (actorTools.length > 0)
|
|
430
|
-
this.upsertTools(actorTools, true);
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
const toolsToLoad = getToolsForServerMode(restoreInput, actorTools, this.serverMode);
|
|
434
|
-
if (toolsToLoad.length > 0) {
|
|
435
|
-
this.upsertTools(toolsToLoad, actorTools.length > 0);
|
|
436
|
-
}
|
|
511
|
+
this.registerFetchedActorTools(restoreInput, actorTools, actorTools.length > 0);
|
|
437
512
|
}
|
|
438
513
|
/**
|
|
439
514
|
* Load Actors as tools, upsert successes into the server, and return both the tool
|
|
@@ -458,24 +533,14 @@ export class ActorsMcpServer {
|
|
|
458
533
|
actorStore: this.actorStore,
|
|
459
534
|
paymentProvider: this.options.paymentProvider,
|
|
460
535
|
});
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
if (actorTools.length > 0) {
|
|
464
|
-
log.debug('Loading actor tools from query parameters before mode resolution');
|
|
465
|
-
this.upsertTools(actorTools, false);
|
|
466
|
-
}
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
const tools = getToolsForServerMode(input, actorTools, this.serverMode);
|
|
470
|
-
if (tools.length > 0) {
|
|
471
|
-
log.debug('Loading tools from query parameters');
|
|
472
|
-
this.upsertTools(tools, false);
|
|
473
|
-
}
|
|
536
|
+
log.debug('Loading tools from query parameters');
|
|
537
|
+
this.registerFetchedActorTools(input, actorTools, false);
|
|
474
538
|
}
|
|
475
539
|
/**
|
|
476
|
-
* Two-phase: getActors (async,
|
|
477
|
-
* (sync
|
|
478
|
-
* the
|
|
540
|
+
* Two-phase: getActors (async, client-agnostic Apify fetch) then composeToolsForClient
|
|
541
|
+
* (sync compose + servability filter). If the mode isn't resolved yet ('auto' before initialize)
|
|
542
|
+
* the whole source is queued for the flush. Otherwise tools compose immediately; client-gated
|
|
543
|
+
* tools are withheld until the client is known, and the source is queued so the flush adds them.
|
|
479
544
|
*
|
|
480
545
|
* Don't move the getActors await into the initialize handler — clients time out
|
|
481
546
|
* waiting for InitializeResult. The queue buffers already-fetched data, not network
|
|
@@ -486,15 +551,7 @@ export class ActorsMcpServer {
|
|
|
486
551
|
actorStore: this.actorStore,
|
|
487
552
|
paymentProvider: this.options.paymentProvider,
|
|
488
553
|
});
|
|
489
|
-
|
|
490
|
-
this.pendingToolsAfterModeResolved.push({ input, actorTools });
|
|
491
|
-
if (actorTools.length > 0)
|
|
492
|
-
this.upsertTools(actorTools);
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
const tools = getToolsForServerMode(input, actorTools, this.serverMode);
|
|
496
|
-
if (tools.length > 0)
|
|
497
|
-
this.upsertTools(tools);
|
|
554
|
+
this.registerFetchedActorTools(input, actorTools, false);
|
|
498
555
|
}
|
|
499
556
|
/** Delete tools from the server and notify the handler.
|
|
500
557
|
*/
|
|
@@ -518,6 +575,9 @@ export class ActorsMcpServer {
|
|
|
518
575
|
* @returns Array of added/updated tool wrappers
|
|
519
576
|
*/
|
|
520
577
|
upsertTools(tools, shouldNotifyToolsChangedHandler = false) {
|
|
578
|
+
// Client gating (e.g. hiding report-problem from Anthropic surfaces) is applied earlier, in
|
|
579
|
+
// composeToolsForClient — the single compose choke point where the client is known. Do not
|
|
580
|
+
// filter here: this is a low-level commit point reached before the client is known too.
|
|
521
581
|
for (const tool of tools) {
|
|
522
582
|
const stored = this.options.paymentProvider ? this.options.paymentProvider.decorateToolSchema(tool) : tool;
|
|
523
583
|
this.tools.set(stored.name, stored);
|
|
@@ -642,6 +702,18 @@ export class ActorsMcpServer {
|
|
|
642
702
|
};
|
|
643
703
|
});
|
|
644
704
|
}
|
|
705
|
+
/**
|
|
706
|
+
* Fetches a task by ID, softFail-logging and throwing a client-facing McpError if it doesn't exist.
|
|
707
|
+
*/
|
|
708
|
+
async getTaskOrThrow(taskId, mcpSessionId, logTag) {
|
|
709
|
+
const task = await this.taskStore.getTask(taskId, mcpSessionId);
|
|
710
|
+
if (!task) {
|
|
711
|
+
// Client error (invalid/unknown taskId) — softFail to avoid polluting error logs.
|
|
712
|
+
log.softFail(`[${logTag}] Task not found`, { taskId, mcpSessionId, statusCode: 404 });
|
|
713
|
+
throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found`);
|
|
714
|
+
}
|
|
715
|
+
return task;
|
|
716
|
+
}
|
|
645
717
|
/**
|
|
646
718
|
* Sets up MCP request handlers for long-running tasks.
|
|
647
719
|
*/
|
|
@@ -665,12 +737,7 @@ export class ActorsMcpServer {
|
|
|
665
737
|
const { taskId } = params;
|
|
666
738
|
const mcpSessionId = (_a = params._meta) === null || _a === void 0 ? void 0 : _a.mcpSessionId;
|
|
667
739
|
log.debug('[GetTaskRequestSchema] Getting task status', { taskId, mcpSessionId });
|
|
668
|
-
|
|
669
|
-
if (task)
|
|
670
|
-
return task;
|
|
671
|
-
// Client error (invalid/unknown taskId) — softFail to avoid polluting error logs.
|
|
672
|
-
log.softFail('[GetTaskRequestSchema] Task not found', { taskId, mcpSessionId, statusCode: 404 });
|
|
673
|
-
throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found`);
|
|
740
|
+
return await this.getTaskOrThrow(taskId, mcpSessionId, 'GetTaskRequestSchema');
|
|
674
741
|
});
|
|
675
742
|
// Get task result payload
|
|
676
743
|
this.server.setRequestHandler(GetTaskPayloadRequestSchema, async (request) => {
|
|
@@ -680,12 +747,7 @@ export class ActorsMcpServer {
|
|
|
680
747
|
const { taskId } = params;
|
|
681
748
|
const mcpSessionId = (_a = params._meta) === null || _a === void 0 ? void 0 : _a.mcpSessionId;
|
|
682
749
|
log.debug('[GetTaskPayloadRequestSchema] Getting task result', { taskId, mcpSessionId });
|
|
683
|
-
const task = await this.
|
|
684
|
-
if (!task) {
|
|
685
|
-
// Client error (invalid/unknown taskId) — softFail to avoid polluting error logs.
|
|
686
|
-
log.softFail('[GetTaskPayloadRequestSchema] Task not found', { taskId, mcpSessionId, statusCode: 404 });
|
|
687
|
-
throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found`);
|
|
688
|
-
}
|
|
750
|
+
const task = await this.getTaskOrThrow(taskId, mcpSessionId, 'GetTaskPayloadRequestSchema');
|
|
689
751
|
if (task.status !== 'completed' && task.status !== 'failed') {
|
|
690
752
|
throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" is not completed yet. Current status: ${task.status}`);
|
|
691
753
|
}
|
|
@@ -707,12 +769,7 @@ export class ActorsMcpServer {
|
|
|
707
769
|
const { taskId } = params;
|
|
708
770
|
const mcpSessionId = (_a = params._meta) === null || _a === void 0 ? void 0 : _a.mcpSessionId;
|
|
709
771
|
log.debug('[CancelTaskRequestSchema] Cancelling task', { taskId, mcpSessionId });
|
|
710
|
-
const task = await this.
|
|
711
|
-
if (!task) {
|
|
712
|
-
// Client error (invalid/unknown taskId) — softFail to avoid polluting error logs.
|
|
713
|
-
log.softFail('[CancelTaskRequestSchema] Task not found', { taskId, mcpSessionId, statusCode: 404 });
|
|
714
|
-
throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found`);
|
|
715
|
-
}
|
|
772
|
+
const task = await this.getTaskOrThrow(taskId, mcpSessionId, 'CancelTaskRequestSchema');
|
|
716
773
|
if (task.status === 'completed' || task.status === 'failed' || task.status === 'cancelled') {
|
|
717
774
|
// Client error (cancel on terminal task) — softFail to avoid polluting error logs.
|
|
718
775
|
log.softFail('[CancelTaskRequestSchema] Task already in terminal state', {
|
|
@@ -775,8 +832,12 @@ export class ActorsMcpServer {
|
|
|
775
832
|
// Captured by `captureResult` so the `finally` block can measure response size for telemetry.
|
|
776
833
|
let toolResult = null;
|
|
777
834
|
const captureResult = (r) => {
|
|
778
|
-
|
|
779
|
-
|
|
835
|
+
// On a failed result, nudge the agent to report the blocker via report-problem at the
|
|
836
|
+
// moment it decides what to do next. Gated on the tool actually being served (see
|
|
837
|
+
// isReportProblemServable), so clients where it is blocklisted or telemetry is off never see it.
|
|
838
|
+
const augmented = this.withReportProblemNudge(r, resolvedToolName, callDiagnostics.failure_category, callDiagnostics.failure_http_status);
|
|
839
|
+
toolResult = augmented;
|
|
840
|
+
return augmented;
|
|
780
841
|
};
|
|
781
842
|
const failInvalidParams = async (message, details, logFields) => {
|
|
782
843
|
toolStatus = TOOL_STATUS.SOFT_FAIL;
|
|
@@ -984,9 +1045,7 @@ export class ActorsMcpServer {
|
|
|
984
1045
|
mcpSessionId,
|
|
985
1046
|
}));
|
|
986
1047
|
// Extract diagnostics and strip internal fields from res before returning to client.
|
|
987
|
-
|
|
988
|
-
toolStatus = diag.toolStatus;
|
|
989
|
-
callDiagnostics = { ...callDiagnostics, ...diag.callDiagnostics };
|
|
1048
|
+
({ toolStatus, callDiagnostics } = applyToolTelemetry(res, actorName, actorId, callDiagnostics));
|
|
990
1049
|
return captureResult(res);
|
|
991
1050
|
}
|
|
992
1051
|
finally {
|
|
@@ -1097,6 +1156,10 @@ export class ActorsMcpServer {
|
|
|
1097
1156
|
// https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/cancellation#behavior-requirements
|
|
1098
1157
|
return captureResult({});
|
|
1099
1158
|
}
|
|
1159
|
+
// Mirror the INTERNAL branch: read the telemetry the executor embedded on error
|
|
1160
|
+
// results (e.g. respondUserError → SOFT_FAIL/INVALID_INPUT), strip it from the wire,
|
|
1161
|
+
// and set failure_category so the report-problem nudge picks the softer variant.
|
|
1162
|
+
({ toolStatus, callDiagnostics } = applyToolTelemetry(executorResult, actorName, actorId, callDiagnostics));
|
|
1100
1163
|
return captureResult(executorResult);
|
|
1101
1164
|
}
|
|
1102
1165
|
finally {
|
|
@@ -1243,6 +1306,13 @@ export class ActorsMcpServer {
|
|
|
1243
1306
|
...deriveResourceIds(params.args, params.result),
|
|
1244
1307
|
};
|
|
1245
1308
|
trackToolCall(params.userId, this.telemetryEnv, finalizedTelemetryData);
|
|
1309
|
+
// A successful report-problem call also emits a dedicated feedback event carrying the
|
|
1310
|
+
// submission. A downstream Segment destination fans it out to Slack/GitHub.
|
|
1311
|
+
if (params.toolName === HELPER_TOOLS.PROBLEM_REPORT &&
|
|
1312
|
+
params.toolStatus === TOOL_STATUS.SUCCEEDED &&
|
|
1313
|
+
params.args) {
|
|
1314
|
+
trackReportedProblem(params.userId, this.telemetryEnv, buildReportedProblemProperties(finalizedTelemetryData, params.args));
|
|
1315
|
+
}
|
|
1246
1316
|
}
|
|
1247
1317
|
}
|
|
1248
1318
|
// TODO: this function quite duplicates the main tool call login the CallToolRequestSchema handler, we should refactor
|
|
@@ -1378,9 +1448,7 @@ export class ActorsMcpServer {
|
|
|
1378
1448
|
mcpSessionId,
|
|
1379
1449
|
taskMode: true,
|
|
1380
1450
|
}));
|
|
1381
|
-
|
|
1382
|
-
toolStatus = diag.toolStatus;
|
|
1383
|
-
callDiagnostics = { ...callDiagnostics, ...diag.callDiagnostics };
|
|
1451
|
+
({ toolStatus, callDiagnostics } = applyToolTelemetry(res, actorName, actorId, callDiagnostics));
|
|
1384
1452
|
result = res;
|
|
1385
1453
|
}
|
|
1386
1454
|
finally {
|
|
@@ -1418,6 +1486,9 @@ export class ActorsMcpServer {
|
|
|
1418
1486
|
result = {};
|
|
1419
1487
|
}
|
|
1420
1488
|
else {
|
|
1489
|
+
// Mirror the INTERNAL task branch: extract embedded telemetry so failure_category
|
|
1490
|
+
// drives the success-path nudge below and toolTelemetry is stripped from the wire.
|
|
1491
|
+
({ toolStatus, callDiagnostics } = applyToolTelemetry(executorResult, actorName, actorId, callDiagnostics));
|
|
1421
1492
|
result = executorResult;
|
|
1422
1493
|
}
|
|
1423
1494
|
}
|
|
@@ -1430,6 +1501,9 @@ export class ActorsMcpServer {
|
|
|
1430
1501
|
// Check if task was cancelled before storing result
|
|
1431
1502
|
if (await skipIfTaskCancelled(', skipping result storage', toolStatus))
|
|
1432
1503
|
return;
|
|
1504
|
+
// On a failed result, nudge the agent to report the blocker via report-problem (mirrors the
|
|
1505
|
+
// synchronous CallTool path, which task-mode calls like call-actor bypass).
|
|
1506
|
+
result = this.withReportProblemNudge(result, tool.name, callDiagnostics.failure_category, callDiagnostics.failure_http_status);
|
|
1433
1507
|
// Store the result in the task store
|
|
1434
1508
|
log.debug('[executeToolAndUpdateTask] Storing completed result', {
|
|
1435
1509
|
taskId,
|
|
@@ -1542,7 +1616,11 @@ export class ActorsMcpServer {
|
|
|
1542
1616
|
taskId,
|
|
1543
1617
|
mcpSessionId,
|
|
1544
1618
|
});
|
|
1545
|
-
|
|
1619
|
+
// Nudge on a genuinely-failed task result. INTERNAL_ERROR and an unknown category get the
|
|
1620
|
+
// full nudge; a genuine INVALID_INPUT gets the softer nudge; payment (402) is suppressed via
|
|
1621
|
+
// the HTTP status and AUTH / PERMISSION_APPROVAL_REQUIRED via NON_NUDGE_FAILURE_CATEGORIES.
|
|
1622
|
+
// The 402 branch above returns before reaching here, so this only sees non-payment failures.
|
|
1623
|
+
const failedResult = this.withReportProblemNudge({
|
|
1546
1624
|
content: [
|
|
1547
1625
|
{
|
|
1548
1626
|
type: 'text',
|
|
@@ -1551,7 +1629,7 @@ export class ActorsMcpServer {
|
|
|
1551
1629
|
],
|
|
1552
1630
|
isError: true,
|
|
1553
1631
|
internalToolStatus: toolStatus,
|
|
1554
|
-
};
|
|
1632
|
+
}, tool.name, callDiagnostics.failure_category, callDiagnostics.failure_http_status);
|
|
1555
1633
|
await storeTaskResultOrSkipIfExpired(this.taskStore, tool.name, taskId, 'failed', failedResult, mcpSessionId);
|
|
1556
1634
|
await emitTaskStatusNotification(taskId, mcpSessionId, this.taskStore, this.server);
|
|
1557
1635
|
finishTaskTracking(toolStatus, callDiagnostics, failedResult);
|