@happyvertical/smrt-core 0.40.61 → 0.40.62

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.
@@ -53,6 +53,29 @@ function resolveCustomActionMethod(methods, toolAction) {
53
53
  for (const [methodName, method] of methods) if (methodName.toLowerCase() === toolAction.toLowerCase()) return [methodName, method];
54
54
  return [toolAction, void 0];
55
55
  }
56
+ /** Preserve a declared method's case when a runtime-only class has no manifest. */
57
+ function resolveRuntimeMethodName(classConstructor, action) {
58
+ let prototype = classConstructor?.prototype;
59
+ while (prototype && prototype !== Object.prototype) {
60
+ const match = Object.getOwnPropertyNames(prototype).find((name) => name.toLowerCase() === action.toLowerCase());
61
+ if (match) return match;
62
+ prototype = Object.getPrototypeOf(prototype);
63
+ }
64
+ return action;
65
+ }
66
+ /**
67
+ * Background task methods receive `JobExecutionContext` from TaskRunner, not
68
+ * from untrusted MCP arguments. Keep that conventional trailing parameter out
69
+ * of the persisted positional call so the runner can append its live context.
70
+ */
71
+ function buildTaskActionInvocationArgs(metadata, args) {
72
+ const parameters = metadata.parameters;
73
+ if (parameters?.at(-1)?.name === "context") return buildCustomActionInvocationArgs({
74
+ ...metadata,
75
+ parameters: parameters.slice(0, -1)
76
+ }, args);
77
+ return buildCustomActionInvocationArgs(metadata, args);
78
+ }
56
79
  /**
57
80
  * Generate MCP server from smrt objects
58
81
  */
@@ -459,6 +482,63 @@ var MCPGenerator = class {
459
482
  };
460
483
  }
461
484
  }
485
+ /** Whether a visible tool has explicitly opted into durable task execution. */
486
+ async supportsTaskTool(name) {
487
+ return await this.resolveTaskAction(name, { id: "__mcp_task_probe__" }) !== null;
488
+ }
489
+ /**
490
+ * Create a durable MCP task for an explicitly enabled item custom action.
491
+ * The caller is responsible for checking the client's extension capability
492
+ * before exposing this result on the wire.
493
+ */
494
+ async createTask(request) {
495
+ if (!this.context.taskStore) throw new Error("MCP Tasks is enabled but no durable task store is configured");
496
+ const resolved = await this.resolveTaskAction(request.params.name, request.params.arguments);
497
+ if (!resolved) throw new Error(`MCP task execution is not enabled for tool: ${request.params.name}`);
498
+ return {
499
+ content: [],
500
+ structuredContent: {},
501
+ resultType: "task",
502
+ ...await this.context.taskStore.createTask({
503
+ objectType: resolved.objectType,
504
+ objectId: resolved.objectId,
505
+ method: resolved.methodName,
506
+ invocationArgs: resolved.invocationArgs,
507
+ tenantId: this.context.tenantId ?? null
508
+ })
509
+ };
510
+ }
511
+ async resolveTaskAction(toolName, args) {
512
+ const separator = toolName.indexOf("_");
513
+ if (separator <= 0) return null;
514
+ const objectPrefix = toolName.slice(0, separator);
515
+ const action = toolName.slice(separator + 1);
516
+ if ([
517
+ "list",
518
+ "get",
519
+ "create",
520
+ "update",
521
+ "delete"
522
+ ].includes(action)) return null;
523
+ const classEntry = Array.from(ObjectRegistry.getAllClasses().entries()).find(([key, info]) => (info.name || key).toLowerCase() === objectPrefix.toLowerCase());
524
+ if (!classEntry) return null;
525
+ const [key, classInfo] = classEntry;
526
+ const objectName = classInfo.name || key;
527
+ const mcpConfig = ObjectRegistry.getConfig(objectName).mcp;
528
+ const configuredTasks = typeof mcpConfig === "object" ? mcpConfig.tasks : void 0;
529
+ if (configuredTasks !== true && (!Array.isArray(configuredTasks) || !configuredTasks.some((method) => method.toLowerCase() === action.toLowerCase()))) return null;
530
+ if (!(await this.generateTools()).some((tool) => tool.name === toolName)) return null;
531
+ const [resolvedMethodName, method] = resolveCustomActionMethod(await ObjectRegistry.getAllMethods(objectName), action);
532
+ const methodName = method ? resolvedMethodName : resolveRuntimeMethodName(classInfo.constructor, action);
533
+ const metadata = this.resolveCustomActionMetadata(objectName, methodName, method, this.hasCollectionReceiver(classInfo));
534
+ if (!metadata.idRequired || metadata.isStatic || typeof args.id !== "string") return null;
535
+ return {
536
+ objectType: classInfo.qualifiedName || objectName,
537
+ objectId: args.id,
538
+ methodName,
539
+ invocationArgs: buildTaskActionInvocationArgs(metadata, args)
540
+ };
541
+ }
462
542
  /** Convert runtime values to the JSON values MCP structuredContent permits. */
463
543
  toJsonValue(value) {
464
544
  const serialized = JSON.stringify(value);
@@ -820,6 +900,7 @@ var MCPGenerator = class {
820
900
  debug,
821
901
  tools,
822
902
  customActions: await this.runtimeCustomActions(tools),
903
+ taskActions: await this.runtimeTaskActions(tools),
823
904
  tenantScopedObjects,
824
905
  stiTargets: this.runtimeStiTargets(tools),
825
906
  toolListCacheHint: resolveMCPToolListCacheHint(this.config.cache?.toolsList, hasTenantScopedTools)
@@ -877,6 +958,26 @@ var MCPGenerator = class {
877
958
  }
878
959
  return metadata;
879
960
  }
961
+ /** Emit only task-enabled item custom actions for the generated runtime. */
962
+ async runtimeTaskActions(tools) {
963
+ const actions = {};
964
+ const classes = ObjectRegistry.getAllClasses();
965
+ for (const tool of tools) {
966
+ if (!await this.supportsTaskTool(tool.name)) continue;
967
+ const separator = tool.name.indexOf("_");
968
+ if (separator <= 0) continue;
969
+ const objectPrefix = tool.name.slice(0, separator).toLowerCase();
970
+ const matched = Array.from(classes.entries()).find(([key, info]) => (info.name || key).toLowerCase() === objectPrefix);
971
+ if (!matched) continue;
972
+ const [key, classInfo] = matched;
973
+ const objectName = classInfo.name || key;
974
+ actions[tool.name] = {
975
+ objectName,
976
+ objectType: classInfo.qualifiedName || objectName
977
+ };
978
+ }
979
+ return actions;
980
+ }
880
981
  /**
881
982
  * Emit only the STI discriminator targets advertised by create-tool schemas.
882
983
  * Generated processes start with an empty registry, so resolving the
@@ -1147,6 +1248,7 @@ const TENANT_SCOPED = new Set(${JSON.stringify(tenantScopedSet)});
1147
1248
  const MCP_TENANT_ID = process.env.SMRT_MCP_TENANT_ID || undefined;
1148
1249
  const MCP_ALLOW_CROSS_TENANT = process.env.SMRT_MCP_ALLOW_CROSS_TENANT === 'true';
1149
1250
  ` : ""}
1251
+
1150
1252
  const PUBLIC_JSON_OPTIONS = {
1151
1253
  permissions: (process.env.SMRT_MCP_PERMISSIONS || '')
1152
1254
  .split(',')