@kadoa/mcp 0.4.3-rc.1 → 0.4.3-rc.2

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.
Files changed (2) hide show
  1. package/dist/index.js +231 -299
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -44032,6 +44032,40 @@ function createCrawlerDomain(client) {
44032
44032
  session: new CrawlerSessionService(client)
44033
44033
  };
44034
44034
  }
44035
+ function getFieldName(field) {
44036
+ return "name" in field && typeof field.name === "string" ? field.name : undefined;
44037
+ }
44038
+ function buildAgenticPrompt(params) {
44039
+ if (params.userPrompt) {
44040
+ return params.userPrompt;
44041
+ }
44042
+ const fieldNames = params.fields.map((field) => getFieldName(field)).filter((name) => Boolean(name));
44043
+ if (fieldNames.length === 0) {
44044
+ return DEFAULT_AGENTIC_PROMPT;
44045
+ }
44046
+ const fieldList = fieldNames.join(", ");
44047
+ if (params.entity) {
44048
+ return `extract all ${params.entity} entities from this page and return these fields: ${fieldList}`;
44049
+ }
44050
+ return `extract all records from this page and return these fields: ${fieldList}`;
44051
+ }
44052
+ function getFieldName2(field) {
44053
+ return "name" in field && typeof field.name === "string" ? field.name : undefined;
44054
+ }
44055
+ function buildAgenticPrompt2(params) {
44056
+ if (params.userPrompt) {
44057
+ return params.userPrompt;
44058
+ }
44059
+ const fieldNames = params.fields.map((field) => getFieldName2(field)).filter((name) => Boolean(name));
44060
+ if (fieldNames.length === 0) {
44061
+ return DEFAULT_AGENTIC_PROMPT2;
44062
+ }
44063
+ const fieldList = fieldNames.join(", ");
44064
+ if (params.entity) {
44065
+ return `extract all ${params.entity} entities from this page and return these fields: ${fieldList}`;
44066
+ }
44067
+ return `extract all records from this page and return these fields: ${fieldList}`;
44068
+ }
44035
44069
  function validateEmailChannelConfig(config2) {
44036
44070
  const issues = [];
44037
44071
  if (!config2.recipients?.length) {
@@ -44194,7 +44228,6 @@ function createClientDomains(params) {
44194
44228
  const dataFetcherService = new DataFetcherService(client.apis.workflows);
44195
44229
  const channelsService = new NotificationChannelsService(client.apis.notifications, userService);
44196
44230
  const settingsService = new NotificationSettingsService(client.apis.notifications);
44197
- const entityResolverService = new EntityResolverService(client);
44198
44231
  const workflowsCoreService = new WorkflowsCoreService(client.apis.workflows);
44199
44232
  const schemasService = new SchemasService(client);
44200
44233
  const templatesService = new TemplatesService(client);
@@ -44202,8 +44235,8 @@ function createClientDomains(params) {
44202
44235
  const channelSetupService = new NotificationSetupService(channelsService, settingsService);
44203
44236
  const coreService = new ValidationCoreService(client);
44204
44237
  const rulesService = new ValidationRulesService(client);
44205
- const extractionService = new ExtractionService(workflowsCoreService, dataFetcherService, entityResolverService, channelSetupService, channelsService, settingsService);
44206
- const extractionBuilderService = new ExtractionBuilderService(workflowsCoreService, entityResolverService, dataFetcherService, channelSetupService);
44238
+ const extractionService = new ExtractionService(workflowsCoreService, dataFetcherService, channelSetupService, channelsService, settingsService);
44239
+ const extractionBuilderService = new ExtractionBuilderService(workflowsCoreService, dataFetcherService, channelSetupService);
44207
44240
  const notification = createNotificationDomain({
44208
44241
  notificationsApi: client.apis.notifications,
44209
44242
  channelsService,
@@ -47371,12 +47404,19 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
47371
47404
  yield page;
47372
47405
  }
47373
47406
  }
47374
- }, _SchemaBuilder = class _SchemaBuilder2 {
47407
+ }, RAW_HELPER_FIELD_CONFIG, _SchemaBuilder = class _SchemaBuilder2 {
47375
47408
  constructor() {
47376
47409
  this.fields = [];
47377
47410
  }
47378
- hasSchemaFields() {
47379
- return this.fields.some((field) => field.fieldType === "SCHEMA");
47411
+ isRawHelperField(field) {
47412
+ return field.fieldType === "SCHEMA" && field.name in {
47413
+ rawHtml: true,
47414
+ rawMarkdown: true,
47415
+ rawPageUrl: true
47416
+ };
47417
+ }
47418
+ hasEntityRequiringSchemaFields() {
47419
+ return this.fields.some((field) => field.fieldType === "SCHEMA" && !this.isRawHelperField(field));
47380
47420
  }
47381
47421
  entity(entityName) {
47382
47422
  this.entityName = entityName;
@@ -47412,20 +47452,23 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
47412
47452
  const names = Array.isArray(name) ? name : [name];
47413
47453
  for (const name2 of names) {
47414
47454
  const fieldName = `raw${upperFirst(camelCase(name2))}`;
47455
+ const rawKey = name2.toUpperCase();
47456
+ const config2 = RAW_HELPER_FIELD_CONFIG[rawKey];
47415
47457
  if (this.fields.some((field) => field.name === fieldName)) {
47416
47458
  continue;
47417
47459
  }
47418
47460
  this.fields.push({
47419
47461
  name: fieldName,
47420
- description: `Raw page content in ${name2.toUpperCase()} format`,
47421
- fieldType: "METADATA",
47422
- metadataKey: name2
47462
+ description: config2.description,
47463
+ fieldType: "SCHEMA",
47464
+ dataType: config2.dataType,
47465
+ example: config2.example
47423
47466
  });
47424
47467
  }
47425
47468
  return this;
47426
47469
  }
47427
47470
  build() {
47428
- if (this.hasSchemaFields() && !this.entityName) {
47471
+ if (this.hasEntityRequiringSchemaFields() && !this.entityName) {
47429
47472
  throw new KadoaSdkException("Entity name is required when schema fields are present", {
47430
47473
  code: "VALIDATION_ERROR",
47431
47474
  details: { entityName: this.entityName }
@@ -47530,88 +47573,10 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
47530
47573
  schemaId
47531
47574
  });
47532
47575
  }
47533
- }, ENTITY_API_ENDPOINT = "/v4/entity", EntityResolverService = class {
47534
- constructor(client) {
47535
- this.client = client;
47536
- this.schemasService = new SchemasService(client);
47537
- }
47538
- async resolveEntity(entityConfig, options) {
47539
- if (entityConfig === "ai-detection") {
47540
- if (!options?.link) {
47541
- throw new KadoaSdkException(ERROR_MESSAGES.LINK_REQUIRED, {
47542
- code: "VALIDATION_ERROR",
47543
- details: { entityConfig, options }
47544
- });
47545
- }
47546
- const entityPrediction = await this.fetchEntityFields({
47547
- link: options.link,
47548
- location: options.location,
47549
- navigationMode: options.navigationMode,
47550
- selectorMode: options.selectorMode ?? false
47551
- });
47552
- const entity = entityPrediction.entity;
47553
- return {
47554
- entity,
47555
- fields: entityPrediction.fields
47556
- };
47557
- } else if (entityConfig) {
47558
- if ("schemaId" in entityConfig) {
47559
- const schema = await this.schemasService.getSchema(entityConfig.schemaId);
47560
- return {
47561
- entity: schema.entity ?? undefined,
47562
- fields: schema.schema
47563
- };
47564
- } else if ("fields" in entityConfig) {
47565
- return {
47566
- entity: entityConfig.name,
47567
- fields: entityConfig.fields
47568
- };
47569
- }
47570
- }
47571
- throw new KadoaSdkException(ERROR_MESSAGES.ENTITY_INVARIANT_VIOLATION, {
47572
- details: {
47573
- entity: entityConfig
47574
- }
47575
- });
47576
- }
47577
- async fetchEntityFields(options) {
47578
- this.validateEntityOptions(options);
47579
- const url3 = `${this.client.baseUrl}${ENTITY_API_ENDPOINT}`;
47580
- const requestBody = options;
47581
- const response = await this.client.axiosInstance.post(url3, requestBody, {
47582
- headers: {
47583
- "Content-Type": "application/json",
47584
- Accept: "application/json",
47585
- "x-api-key": this.client.apiKey
47586
- }
47587
- });
47588
- const data = response.data;
47589
- if (!data.success || !data.entityPrediction || data.entityPrediction.length === 0) {
47590
- throw new KadoaSdkException(ERROR_MESSAGES.NO_PREDICTIONS, {
47591
- code: "NOT_FOUND",
47592
- details: {
47593
- success: data.success,
47594
- hasPredictions: !!data.entityPrediction,
47595
- predictionCount: data.entityPrediction?.length || 0,
47596
- link: options.link
47597
- }
47598
- });
47599
- }
47600
- return data.entityPrediction[0];
47601
- }
47602
- validateEntityOptions(options) {
47603
- if (!options.link) {
47604
- throw new KadoaSdkException(ERROR_MESSAGES.LINK_REQUIRED, {
47605
- code: "VALIDATION_ERROR",
47606
- details: { options }
47607
- });
47608
- }
47609
- }
47610
- }, debug3, SUCCESSFUL_RUN_STATES, DEFAULT_OPTIONS, ExtractionService = class {
47611
- constructor(workflowsCoreService, dataFetcherService, entityResolverService, notificationSetupService, notificationChannelsService, notificationSettingsService) {
47576
+ }, debug3, SUCCESSFUL_RUN_STATES, DEFAULT_AGENTIC_PROMPT = "extract all the data for the main entity of this page", DEFAULT_OPTIONS, ExtractionService = class {
47577
+ constructor(workflowsCoreService, dataFetcherService, notificationSetupService, notificationChannelsService, notificationSettingsService) {
47612
47578
  this.workflowsCoreService = workflowsCoreService;
47613
47579
  this.dataFetcherService = dataFetcherService;
47614
- this.entityResolverService = entityResolverService;
47615
47580
  this.notificationSetupService = notificationSetupService;
47616
47581
  this.notificationChannelsService = notificationChannelsService;
47617
47582
  this.notificationSettingsService = notificationSettingsService;
@@ -47646,7 +47611,9 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
47646
47611
  }
47647
47612
  async executeExtraction(options) {
47648
47613
  this.validateOptions(options);
47649
- const config2 = merge3(DEFAULT_OPTIONS, options);
47614
+ const config2 = {
47615
+ ...merge3(DEFAULT_OPTIONS, options)
47616
+ };
47650
47617
  const isRealTime = config2.interval === "REAL_TIME";
47651
47618
  if (isRealTime) {
47652
47619
  throw new KadoaSdkException("extraction.run()/submit() are not supported for real-time workflows. Use the builder API, call waitForReady(), and subscribe via client.realtime.onEvent(...).", {
@@ -47657,35 +47624,23 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
47657
47624
  });
47658
47625
  }
47659
47626
  let workflowId;
47660
- const isAgenticNavigation = config2.navigationMode === "agentic-navigation";
47661
- if (isAgenticNavigation) {
47662
- if (!config2.userPrompt) {
47663
- throw new KadoaSdkException("userPrompt is required when navigationMode is 'agentic-navigation'", {
47664
- code: "VALIDATION_ERROR",
47665
- details: { navigationMode: config2.navigationMode }
47666
- });
47667
- }
47668
- }
47669
- let resolvedEntity;
47670
- if (isAgenticNavigation) {
47671
- const entityConfig = options.entity || "ai-detection";
47672
- resolvedEntity = {
47673
- entity: typeof entityConfig === "object" && "name" in entityConfig ? entityConfig.name : undefined,
47674
- fields: typeof entityConfig === "object" && "fields" in entityConfig ? entityConfig.fields : []
47675
- };
47676
- } else {
47677
- resolvedEntity = await this.entityResolverService.resolveEntity(options.entity || "ai-detection", {
47678
- link: config2.urls[0],
47679
- location: config2.location,
47680
- navigationMode: config2.navigationMode
47681
- });
47682
- }
47627
+ const entityConfig = options.entity;
47628
+ const resolvedEntity = {
47629
+ entity: typeof entityConfig === "object" && "name" in entityConfig ? entityConfig.name : undefined,
47630
+ fields: typeof entityConfig === "object" && "fields" in entityConfig ? entityConfig.fields : []
47631
+ };
47683
47632
  const hasNotifications = !!config2.notifications;
47633
+ const userPrompt = buildAgenticPrompt({
47634
+ entity: resolvedEntity.entity,
47635
+ fields: resolvedEntity.fields,
47636
+ userPrompt: config2.userPrompt
47637
+ });
47684
47638
  const workflowRequest = {
47685
47639
  ...config2,
47640
+ navigationMode: "agentic-navigation",
47686
47641
  fields: resolvedEntity.fields,
47687
47642
  ...resolvedEntity.entity !== undefined ? { entity: resolvedEntity.entity } : {},
47688
- ...config2.userPrompt ? { userPrompt: config2.userPrompt } : {}
47643
+ userPrompt
47689
47644
  };
47690
47645
  const result = await this.workflowsCoreService.create(workflowRequest);
47691
47646
  workflowId = result.id;
@@ -47770,10 +47725,9 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
47770
47725
  isExtractionSuccessful(runState) {
47771
47726
  return runState ? SUCCESSFUL_RUN_STATES.has(runState.toUpperCase()) : false;
47772
47727
  }
47773
- }, debug4, ExtractionBuilderService = class {
47774
- constructor(workflowsCoreService, entityResolverService, dataFetcherService, notificationSetupService) {
47728
+ }, debug4, DEFAULT_AGENTIC_PROMPT2 = "extract all the data for the main entity of this page", BUILD_NOT_READY_ERROR = "No completed build. Build the project first.", TERMINAL_RUN_STATES, ExtractionBuilderService = class {
47729
+ constructor(workflowsCoreService, dataFetcherService, notificationSetupService) {
47775
47730
  this.workflowsCoreService = workflowsCoreService;
47776
- this.entityResolverService = entityResolverService;
47777
47731
  this.dataFetcherService = dataFetcherService;
47778
47732
  this.notificationSetupService = notificationSetupService;
47779
47733
  }
@@ -47799,7 +47753,6 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
47799
47753
  urls,
47800
47754
  name,
47801
47755
  description,
47802
- navigationMode,
47803
47756
  extraction,
47804
47757
  additionalData,
47805
47758
  bypassPreview,
@@ -47809,23 +47762,22 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
47809
47762
  location
47810
47763
  }) {
47811
47764
  let entity = "ai-detection";
47765
+ let builtFields = [];
47812
47766
  if (extraction) {
47813
47767
  const result = extraction(new SchemaBuilder);
47814
47768
  if ("schemaId" in result) {
47815
47769
  entity = { schemaId: result.schemaId };
47816
47770
  } else {
47817
47771
  const builtSchema = result.build();
47818
- entity = builtSchema.entityName ? { name: builtSchema.entityName, fields: builtSchema.fields } : { fields: builtSchema.fields };
47772
+ builtFields = builtSchema.fields;
47773
+ entity = builtSchema.entityName ? { name: builtSchema.entityName, fields: builtFields } : { fields: builtFields };
47819
47774
  }
47820
47775
  }
47821
- if (userPrompt) {
47822
- this._userPrompt = userPrompt;
47823
- }
47776
+ this._userPrompt = userPrompt;
47824
47777
  this._options = {
47825
47778
  urls,
47826
47779
  name,
47827
47780
  description,
47828
- navigationMode: navigationMode || "single-page",
47829
47781
  entity,
47830
47782
  bypassPreview: bypassPreview ?? false,
47831
47783
  additionalData,
@@ -47872,41 +47824,27 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
47872
47824
  }
47873
47825
  async create() {
47874
47826
  assert2(this._options, "Options are not set");
47875
- const { urls, name, description, navigationMode, entity } = this.options;
47876
- const isAgenticNavigation = navigationMode === "agentic-navigation";
47877
- if (isAgenticNavigation) {
47878
- if (!this._userPrompt) {
47879
- throw new KadoaSdkException("userPrompt is required when navigationMode is 'agentic-navigation'", {
47880
- code: "VALIDATION_ERROR",
47881
- details: { navigationMode }
47882
- });
47883
- }
47884
- }
47885
- const isRealTime = this._options.interval === "REAL_TIME";
47886
- const useSelectorMode = isRealTime && entity === "ai-detection";
47887
- let resolvedEntity;
47888
- if (isAgenticNavigation) {
47889
- resolvedEntity = {
47890
- entity: typeof entity === "object" && "name" in entity ? entity.name : undefined,
47891
- fields: typeof entity === "object" && "fields" in entity ? entity.fields : []
47892
- };
47893
- } else {
47894
- resolvedEntity = await this.entityResolverService.resolveEntity(entity, {
47895
- link: urls[0],
47896
- location: this._options.location,
47897
- navigationMode,
47898
- selectorMode: useSelectorMode
47899
- });
47900
- }
47901
- const hasSchemaId = typeof entity === "object" && "schemaId" in entity && entity.schemaId;
47827
+ const { urls, name, description, entity } = this.options;
47828
+ const resolvedEntity = {
47829
+ entity: typeof entity === "object" && "name" in entity ? entity.name : undefined,
47830
+ fields: typeof entity === "object" && "fields" in entity ? entity.fields : []
47831
+ };
47832
+ this._userPrompt = buildAgenticPrompt2({
47833
+ entity: resolvedEntity.entity,
47834
+ fields: resolvedEntity.fields,
47835
+ userPrompt: this._userPrompt
47836
+ });
47837
+ this._options.userPrompt = this._userPrompt;
47838
+ const schemaId = typeof entity === "object" && "schemaId" in entity ? entity.schemaId : undefined;
47839
+ const hasSchemaId = Boolean(schemaId);
47902
47840
  const workflow = await this.workflowsCoreService.create({
47903
47841
  urls,
47904
47842
  name,
47905
47843
  description,
47906
- navigationMode,
47844
+ navigationMode: "agentic-navigation",
47907
47845
  monitoring: this._monitoringOptions,
47908
47846
  ...hasSchemaId ? {
47909
- schemaId: entity.schemaId,
47847
+ schemaId,
47910
47848
  entity: resolvedEntity.entity,
47911
47849
  fields: resolvedEntity.fields
47912
47850
  } : { entity: resolvedEntity.entity, fields: resolvedEntity.fields },
@@ -47952,7 +47890,10 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
47952
47890
  }
47953
47891
  });
47954
47892
  }
47955
- const startedJob = await this.workflowsCoreService.runWorkflow(this._workflowId, { variables: options?.variables, limit: options?.limit });
47893
+ const startedJob = await this.startOrReuseWorkflowRun(this._workflowId, {
47894
+ variables: options?.variables,
47895
+ limit: options?.limit
47896
+ });
47956
47897
  assert2(startedJob.jobId, "Job ID is not set");
47957
47898
  debug4("Job started: %O", startedJob);
47958
47899
  this._jobId = startedJob.jobId;
@@ -47972,7 +47913,10 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
47972
47913
  }
47973
47914
  });
47974
47915
  }
47975
- const submittedJob = await this.workflowsCoreService.runWorkflow(this._workflowId, { variables: options?.variables, limit: options?.limit });
47916
+ const submittedJob = await this.startOrReuseWorkflowRun(this._workflowId, {
47917
+ variables: options?.variables,
47918
+ limit: options?.limit
47919
+ });
47976
47920
  assert2(submittedJob.jobId, "Job ID is not set");
47977
47921
  debug4("Job submitted: %O", submittedJob);
47978
47922
  this._jobId = submittedJob.jobId;
@@ -48001,6 +47945,60 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
48001
47945
  ...options
48002
47946
  });
48003
47947
  }
47948
+ async startOrReuseWorkflowRun(workflowId, input) {
47949
+ const currentJob = await this.findActiveWorkflowJob(workflowId);
47950
+ if (currentJob) {
47951
+ debug4("Reusing active workflow job: %O", currentJob);
47952
+ return currentJob;
47953
+ }
47954
+ try {
47955
+ return await this.workflowsCoreService.runWorkflow(workflowId, input);
47956
+ } catch (error48) {
47957
+ if (!this.isBuildNotReadyError(error48)) {
47958
+ throw error48;
47959
+ }
47960
+ const recoveredJob = await this.waitForWorkflowJob(workflowId);
47961
+ if (recoveredJob) {
47962
+ debug4("Recovered active workflow job after run rejection: %O", recoveredJob);
47963
+ return recoveredJob;
47964
+ }
47965
+ throw error48;
47966
+ }
47967
+ }
47968
+ async findActiveWorkflowJob(workflowId) {
47969
+ const workflow = await this.workflowsCoreService.get(workflowId);
47970
+ if (!workflow.jobId || !workflow.runState) {
47971
+ return;
47972
+ }
47973
+ if (TERMINAL_RUN_STATES.has(workflow.runState.toUpperCase())) {
47974
+ return;
47975
+ }
47976
+ return {
47977
+ jobId: workflow.jobId,
47978
+ status: workflow.runState,
47979
+ message: "Workflow already has an active run"
47980
+ };
47981
+ }
47982
+ async waitForWorkflowJob(workflowId) {
47983
+ for (let attempt2 = 0;attempt2 < 5; attempt2 += 1) {
47984
+ const currentJob = await this.findActiveWorkflowJob(workflowId);
47985
+ if (currentJob) {
47986
+ return currentJob;
47987
+ }
47988
+ await new Promise((resolve) => setTimeout(resolve, 1000));
47989
+ }
47990
+ return;
47991
+ }
47992
+ isBuildNotReadyError(error48) {
47993
+ if (!(error48 instanceof KadoaHttpException)) {
47994
+ return false;
47995
+ }
47996
+ const responseBody = error48.responseBody;
47997
+ if (responseBody && typeof responseBody === "object" && "error" in responseBody && typeof responseBody.error === "string") {
47998
+ return responseBody.error.includes(BUILD_NOT_READY_ERROR);
47999
+ }
48000
+ return typeof error48.message === "string" ? error48.message.includes(BUILD_NOT_READY_ERROR) : false;
48001
+ }
48004
48002
  }, NotificationChannelType, EMAIL_REGEX, _NotificationChannelsService = class _NotificationChannelsService2 {
48005
48003
  constructor(notificationsApi, userService) {
48006
48004
  this.api = notificationsApi;
@@ -48327,7 +48325,7 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
48327
48325
  }));
48328
48326
  return channels;
48329
48327
  }
48330
- }, PUBLIC_API_URI, WSS_API_URI, REALTIME_API_URI, SDK_VERSION = "0.27.0", SDK_NAME = "kadoa-node-sdk", SDK_LANGUAGE = "node", debug6, isDrainControlMessage = (message) => message.type === "control.draining", isRealtimeEvent = (message) => message.type !== "heartbeat" && message.type !== "control.draining", _Realtime = class _Realtime2 {
48328
+ }, PUBLIC_API_URI, WSS_API_URI, REALTIME_API_URI, SDK_VERSION = "0.28.0", SDK_NAME = "kadoa-node-sdk", SDK_LANGUAGE = "node", debug6, isDrainControlMessage = (message) => message.type === "control.draining", isRealtimeEvent = (message) => message.type !== "heartbeat" && message.type !== "control.draining", _Realtime = class _Realtime2 {
48331
48329
  constructor(config2) {
48332
48330
  this.drainingSockets = /* @__PURE__ */ new Set;
48333
48331
  this.lastHeartbeat = Date.now();
@@ -48737,37 +48735,6 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
48737
48735
  });
48738
48736
  return response.data.data ?? [];
48739
48737
  }
48740
- async listWorkflows(templateId) {
48741
- debug7("Listing workflows for template: %s", templateId);
48742
- const response = await this.templatesApi.v4TemplatesTemplateIdWorkflowsGet({
48743
- templateId
48744
- });
48745
- return response.data.data ?? [];
48746
- }
48747
- async linkWorkflows(templateId, body) {
48748
- debug7("Linking %d workflows to template: %s", body.workflowIds.length, templateId);
48749
- const response = await this.templatesApi.v4TemplatesTemplateIdLinkPost({
48750
- templateId,
48751
- linkWorkflowsBody: body
48752
- });
48753
- return response.data;
48754
- }
48755
- async unlinkWorkflows(templateId, body) {
48756
- debug7("Unlinking %d workflows from template: %s", body.workflowIds.length, templateId);
48757
- const response = await this.templatesApi.v4TemplatesTemplateIdUnlinkPost({
48758
- templateId,
48759
- unlinkWorkflowsBody: body
48760
- });
48761
- return response.data;
48762
- }
48763
- async applyUpdate(templateId, body) {
48764
- debug7("Applying version %d to %d workflows for template: %s", body.targetVersion, body.workflowIds.length, templateId);
48765
- const response = await this.templatesApi.v4TemplatesTemplateIdApplyPost({
48766
- templateId,
48767
- applyTemplateUpdateBody: body
48768
- });
48769
- return response.data;
48770
- }
48771
48738
  async createFromWorkflow(body) {
48772
48739
  debug7("Creating template from workflow: %s", body.workflowId);
48773
48740
  const response = await this.templatesApi.v4TemplatesFromWorkflowPost({
@@ -49086,7 +49053,7 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
49086
49053
  variableId
49087
49054
  });
49088
49055
  }
49089
- }, JobStateEnum, TERMINAL_JOB_STATES, TERMINAL_RUN_STATES, debug9, WorkflowsCoreService = class {
49056
+ }, JobStateEnum, TERMINAL_JOB_STATES, TERMINAL_RUN_STATES2, debug9, WorkflowsCoreService = class {
49090
49057
  constructor(workflowsApi) {
49091
49058
  this.workflowsApi = workflowsApi;
49092
49059
  }
@@ -49208,7 +49175,7 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
49208
49175
  if (options?.targetState && current.state === options.targetState) {
49209
49176
  return true;
49210
49177
  }
49211
- if (current.runState && TERMINAL_RUN_STATES.has(current.runState.toUpperCase()) && current.state !== "QUEUED") {
49178
+ if (current.runState && TERMINAL_RUN_STATES2.has(current.runState.toUpperCase()) && current.state !== "QUEUED") {
49212
49179
  return true;
49213
49180
  }
49214
49181
  return false;
@@ -49963,6 +49930,25 @@ var init_dist2 = __esm(() => {
49963
49930
  Text: DataFieldDataTypeEnum.String,
49964
49931
  Url: DataFieldDataTypeEnum.Link
49965
49932
  };
49933
+ RAW_HELPER_FIELD_CONFIG = {
49934
+ HTML: {
49935
+ dataType: "STRING",
49936
+ description: "Full page HTML as a string. Return the raw HTML content.",
49937
+ example: "<html><body><h1>Example page</h1></body></html>"
49938
+ },
49939
+ MARKDOWN: {
49940
+ dataType: "STRING",
49941
+ description: "Main page content rendered as markdown. Return the markdown as a string.",
49942
+ example: `# Example page
49943
+
49944
+ This is the main page content.`
49945
+ },
49946
+ PAGE_URL: {
49947
+ dataType: "LINK",
49948
+ description: "Canonical page URL for the extracted page. Return the resolved page URL.",
49949
+ example: "https://example.com/page"
49950
+ }
49951
+ };
49966
49952
  _SchemaBuilder.FIELD_NAME_PATTERN = /^[A-Za-z0-9]+$/;
49967
49953
  _SchemaBuilder.TYPES_REQUIRING_EXAMPLE = [
49968
49954
  "STRING",
@@ -49979,12 +49965,19 @@ var init_dist2 = __esm(() => {
49979
49965
  mode: "run",
49980
49966
  pollingInterval: 5000,
49981
49967
  maxWaitTime: 300000,
49982
- navigationMode: "single-page",
49983
49968
  location: { type: "auto" },
49984
49969
  bypassPreview: true,
49985
49970
  autoStart: true
49986
49971
  };
49987
49972
  debug4 = logger.extraction;
49973
+ TERMINAL_RUN_STATES = /* @__PURE__ */ new Set([
49974
+ "FINISHED",
49975
+ "SUCCESS",
49976
+ "FAILED",
49977
+ "ERROR",
49978
+ "STOPPED",
49979
+ "CANCELLED"
49980
+ ]);
49988
49981
  NotificationChannelType = {
49989
49982
  EMAIL: "EMAIL",
49990
49983
  SLACK: "SLACK",
@@ -50028,7 +50021,7 @@ var init_dist2 = __esm(() => {
50028
50021
  JobStateEnum.NotSupported,
50029
50022
  JobStateEnum.FailedInsufficientFunds
50030
50023
  ]);
50031
- TERMINAL_RUN_STATES = /* @__PURE__ */ new Set([
50024
+ TERMINAL_RUN_STATES2 = /* @__PURE__ */ new Set([
50032
50025
  "FINISHED",
50033
50026
  "SUCCESS",
50034
50027
  "FAILED",
@@ -50233,6 +50226,9 @@ function coerceBoolean() {
50233
50226
  return val;
50234
50227
  };
50235
50228
  }
50229
+ function coerceNull() {
50230
+ return (val) => val === "null" ? undefined : val;
50231
+ }
50236
50232
  function coerceJson() {
50237
50233
  return (val) => {
50238
50234
  if (typeof val === "string") {
@@ -50264,17 +50260,33 @@ function extractApiMessage(responseBody) {
50264
50260
  const body = responseBody;
50265
50261
  if (!body || typeof body !== "object")
50266
50262
  return;
50267
- const msg = typeof body.error === "string" && body.error || typeof body.message === "string" && body.message || undefined;
50268
- if (!msg)
50269
- return;
50263
+ let msg = typeof body.error === "string" && body.error || typeof body.message === "string" && body.message || undefined;
50270
50264
  if (body.validationErrors && typeof body.validationErrors === "object" && body.validationErrors !== null) {
50271
50265
  const details = Object.entries(body.validationErrors).map(([field, err]) => `${field}: "${err}"`).join(", ");
50272
- if (details)
50273
- return `${msg} — Details: ${details}`;
50266
+ if (details) {
50267
+ msg = msg ? `${msg} — Details: ${details}` : `Validation failed — Details: ${details}`;
50268
+ }
50274
50269
  }
50275
50270
  return msg;
50276
50271
  }
50277
- function classifyError(error48) {
50272
+ function inferResourceType(toolName) {
50273
+ if (!toolName)
50274
+ return "resource";
50275
+ if (toolName.includes("template") || toolName.includes("version"))
50276
+ return "template";
50277
+ if (toolName.includes("variable"))
50278
+ return "variable";
50279
+ if (toolName.includes("workflow") || toolName.includes("monitor"))
50280
+ return "workflow";
50281
+ if (toolName.includes("channel") || toolName.includes("notification"))
50282
+ return "notification channel";
50283
+ if (toolName.includes("schema"))
50284
+ return "schema";
50285
+ if (toolName.includes("change"))
50286
+ return "change record";
50287
+ return "resource";
50288
+ }
50289
+ function classifyError(error48, toolName) {
50278
50290
  if (KadoaSdkException.isInstance(error48)) {
50279
50291
  const code = error48.code;
50280
50292
  if (KadoaHttpException.isInstance(error48)) {
@@ -50286,8 +50298,10 @@ function classifyError(error48) {
50286
50298
  return `Access denied${status}. Your current team role may not have permission for this action. Use the whoami tool to check your role, or contact your team admin to request elevated access.`;
50287
50299
  }
50288
50300
  return `Authentication failed${status}. Please re-authenticate via OAuth.`;
50289
- case "NOT_FOUND":
50290
- return `Not found${status}. The workflow may have been deleted or the ID is incorrect.`;
50301
+ case "NOT_FOUND": {
50302
+ const resource = inferResourceType(toolName);
50303
+ return `Not found${status}. The ${resource} may have been deleted or the ID is incorrect.`;
50304
+ }
50291
50305
  case "RATE_LIMITED":
50292
50306
  return `Rate limit exceeded${status}. Please wait a moment and try again.`;
50293
50307
  case "NETWORK_ERROR":
@@ -50304,8 +50318,10 @@ function classifyError(error48) {
50304
50318
  switch (code) {
50305
50319
  case "AUTH_ERROR":
50306
50320
  return "Authentication failed. Please re-authenticate via OAuth.";
50307
- case "NOT_FOUND":
50308
- return "Not found. The workflow may have been deleted or the ID is incorrect.";
50321
+ case "NOT_FOUND": {
50322
+ const resource = inferResourceType(toolName);
50323
+ return `Not found. The ${resource} may have been deleted or the ID is incorrect.`;
50324
+ }
50309
50325
  case "RATE_LIMITED":
50310
50326
  return "Rate limit exceeded. Please wait a moment and try again.";
50311
50327
  case "NETWORK_ERROR":
@@ -50348,7 +50364,7 @@ function registerTools(server, ctx) {
50348
50364
  console.error(`[Tool Error] ${name}: session expired, user must re-authenticate`);
50349
50365
  return errorResult("Your session has expired. Please reconnect the MCP server to re-authenticate.");
50350
50366
  }
50351
- let message = classifyError(error48);
50367
+ let message = classifyError(error48, name);
50352
50368
  if (KadoaHttpException.isInstance(error48) && error48.httpStatus === 403) {
50353
50369
  try {
50354
50370
  const jwt2 = ctx.supabaseJwt;
@@ -50536,7 +50552,6 @@ function registerTools(server, ctx) {
50536
50552
  let builder = ctx.client.extract({
50537
50553
  urls,
50538
50554
  name: args.name || "Untitled Workflow",
50539
- navigationMode: "agentic-navigation",
50540
50555
  userPrompt: args.prompt,
50541
50556
  extraction: buildExtraction(args),
50542
50557
  interval: args.updateInterval,
@@ -51251,7 +51266,7 @@ function registerTools(server, ctx) {
51251
51266
  });
51252
51267
  }));
51253
51268
  server.registerTool("list_templates", {
51254
- description: "List all templates in the current team. Templates define reusable configurations (prompt, schema, notifications) that can be linked to multiple workflows.",
51269
+ description: "List all templates in the current team. Templates define reusable configurations (prompt, schema, notifications).",
51255
51270
  inputSchema: strictSchema({}),
51256
51271
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true }
51257
51272
  }, withErrorHandling("list_templates", async () => {
@@ -51269,7 +51284,7 @@ function registerTools(server, ctx) {
51269
51284
  return jsonResult({ template });
51270
51285
  }));
51271
51286
  server.registerTool("create_template", {
51272
- description: "Create a new template. Templates define reusable configurations that can be linked to workflows. After creation, use create_template_version to publish a version with prompt, schema, and notifications.",
51287
+ description: "Create a new template. After creation, use create_template_version to publish a version with prompt, schema, and notifications.",
51273
51288
  inputSchema: strictSchema({
51274
51289
  name: exports_external.string().describe("Template name (1-255 chars)"),
51275
51290
  description: exports_external.string().optional().describe("Template description (max 2000 chars)")
@@ -51290,8 +51305,8 @@ function registerTools(server, ctx) {
51290
51305
  description: "Update a template's name or description. At least one field must be provided.",
51291
51306
  inputSchema: strictSchema({
51292
51307
  templateId: exports_external.string().describe("The template ID to update"),
51293
- name: exports_external.string().optional().describe("New template name"),
51294
- description: exports_external.string().optional().describe("New template description")
51308
+ name: exports_external.preprocess(coerceNull(), exports_external.string().optional()).describe("New template name"),
51309
+ description: exports_external.preprocess(coerceNull(), exports_external.string().optional()).describe("New template description")
51295
51310
  }),
51296
51311
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true }
51297
51312
  }, withErrorHandling("update_template", async (args) => {
@@ -51304,7 +51319,7 @@ function registerTools(server, ctx) {
51304
51319
  });
51305
51320
  }));
51306
51321
  server.registerTool("delete_template", {
51307
- description: "Delete (archive) a template. Existing linked workflows keep their current config but are unlinked. " + "You MUST first call this tool without confirmed=true to preview what will be deleted, " + "then show the user the template name and ask for confirmation, " + "then call again with confirmed=true only after the user explicitly agrees.",
51322
+ description: "Delete (archive) a template. " + "You MUST first call this tool without confirmed=true to preview what will be deleted, " + "then show the user the template name and ask for confirmation, " + "then call again with confirmed=true only after the user explicitly agrees.",
51308
51323
  inputSchema: strictSchema({
51309
51324
  templateId: exports_external.string().describe("The template ID to delete"),
51310
51325
  confirmed: exports_external.preprocess(coerceBoolean(), exports_external.boolean()).optional().describe("Set to true only after the user has explicitly confirmed deletion. Omit or set to false for the initial preview call.")
@@ -51321,7 +51336,7 @@ function registerTools(server, ctx) {
51321
51336
  pending: true,
51322
51337
  templateId: args.templateId,
51323
51338
  templateName,
51324
- message: `⚠️ You are about to delete template "${templateName}". Linked workflows will keep their current config but will be unlinked. Ask the user to confirm, then call delete_template again with confirmed=true.`
51339
+ message: `⚠️ You are about to delete template "${templateName}". Ask the user to confirm, then call delete_template again with confirmed=true.`
51325
51340
  });
51326
51341
  }
51327
51342
  await ctx.client.template.delete(args.templateId);
@@ -51364,98 +51379,14 @@ function registerTools(server, ctx) {
51364
51379
  message: `Version ${version2.version} published for template ${templateId}.`
51365
51380
  });
51366
51381
  }));
51367
- server.registerTool("list_template_workflows", {
51368
- description: "List all workflows linked to a template.",
51369
- inputSchema: strictSchema({
51370
- templateId: exports_external.string().describe("The template ID")
51371
- }),
51372
- annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true }
51373
- }, withErrorHandling("list_template_workflows", async (args) => {
51374
- const workflows = await ctx.client.template.listWorkflows(args.templateId);
51375
- return jsonResult({ workflows, count: workflows.length });
51376
- }));
51377
- server.registerTool("link_workflows_to_template", {
51378
- description: "Link workflows to a template. Linking applies the template's latest version config to the workflows. " + "Without force, fails if any workflow is already linked to a different template. With force=true, relinks them. " + "You MUST first call without confirmed=true to preview, then ask the user for confirmation, " + "then call again with confirmed=true.",
51379
- inputSchema: strictSchema({
51380
- templateId: exports_external.string().describe("The template ID to link workflows to"),
51381
- workflowIds: exports_external.preprocess(coerceArray(), exports_external.array(exports_external.string())).describe("Array of workflow IDs to link"),
51382
- force: exports_external.preprocess(coerceBoolean(), exports_external.boolean()).optional().describe("If true, relink workflows already linked to another template"),
51383
- confirmed: exports_external.preprocess(coerceBoolean(), exports_external.boolean()).optional().describe("Set to true only after user confirms. Omit for preview.")
51384
- }),
51385
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
51386
- }, withErrorHandling("link_workflows_to_template", async (args) => {
51387
- if (!args.confirmed) {
51388
- const forceNote = args.force ? " Workflows already linked to other templates will be relinked (force=true)." : " Will fail if any workflow is already linked to a different template.";
51389
- return jsonResult({
51390
- pending: true,
51391
- templateId: args.templateId,
51392
- workflowIds: args.workflowIds,
51393
- force: !!args.force,
51394
- message: `⚠️ You are about to link ${args.workflowIds.length} workflow(s) to template ${args.templateId}. This will apply the template's latest version config to these workflows.${forceNote} Ask the user to confirm, then call again with confirmed=true.`
51395
- });
51396
- }
51397
- const result = await ctx.client.template.linkWorkflows(args.templateId, {
51398
- workflowIds: args.workflowIds,
51399
- ...args.force != null && { force: args.force }
51400
- });
51401
- return jsonResult({
51402
- success: true,
51403
- result,
51404
- message: `Linked ${args.workflowIds.length} workflow(s) to template ${args.templateId}.`
51405
- });
51406
- }));
51407
- server.registerTool("unlink_workflows_from_template", {
51408
- description: "Unlink workflows from a template. Workflows keep their current config but are no longer associated with the template. " + "You MUST first call without confirmed=true to preview, then ask the user for confirmation, " + "then call again with confirmed=true.",
51409
- inputSchema: strictSchema({
51410
- templateId: exports_external.string().describe("The template ID to unlink workflows from"),
51411
- workflowIds: exports_external.preprocess(coerceArray(), exports_external.array(exports_external.string())).describe("Array of workflow IDs to unlink"),
51412
- confirmed: exports_external.preprocess(coerceBoolean(), exports_external.boolean()).optional().describe("Set to true only after user confirms. Omit for preview.")
51413
- }),
51414
- annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false }
51415
- }, withErrorHandling("unlink_workflows_from_template", async (args) => {
51416
- if (!args.confirmed) {
51417
- return jsonResult({
51418
- pending: true,
51419
- templateId: args.templateId,
51420
- workflowIds: args.workflowIds,
51421
- message: `⚠️ You are about to unlink ${args.workflowIds.length} workflow(s) from template ${args.templateId}. Workflows will keep their current config. Ask the user to confirm, then call again with confirmed=true.`
51422
- });
51423
- }
51424
- const result = await ctx.client.template.unlinkWorkflows(args.templateId, {
51425
- workflowIds: args.workflowIds
51426
- });
51427
- return jsonResult({
51428
- success: true,
51429
- result,
51430
- message: `Unlinked ${args.workflowIds.length} workflow(s) from template ${args.templateId}.`
51431
- });
51432
- }));
51433
- server.registerTool("apply_template_update", {
51434
- description: "Apply a specific template version to workflows. Updates the workflows' config (prompt, schema, notifications) to match the specified version.",
51435
- inputSchema: strictSchema({
51436
- templateId: exports_external.string().describe("The template ID"),
51437
- targetVersion: exports_external.preprocess(coerceNumber(), exports_external.number().int().min(1)).describe("Version number to apply"),
51438
- workflowIds: exports_external.preprocess(coerceArray(), exports_external.array(exports_external.string())).describe("Array of workflow IDs to update (max 100)")
51439
- }),
51440
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
51441
- }, withErrorHandling("apply_template_update", async (args) => {
51442
- const result = await ctx.client.template.applyUpdate(args.templateId, {
51443
- targetVersion: args.targetVersion,
51444
- workflowIds: args.workflowIds
51445
- });
51446
- return jsonResult({
51447
- success: true,
51448
- result,
51449
- message: `Applied version ${args.targetVersion} to ${args.workflowIds.length} workflow(s).`
51450
- });
51451
- }));
51452
51382
  server.registerTool("save_workflow_as_template", {
51453
- description: "Create a template from an existing workflow's configuration (prompt, schema, notifications). " + "Provide 'name' to create a new template, or 'templateId' to add a new version to an existing template.",
51383
+ description: "Create a template from an existing workflow's configuration (prompt, schema, notifications). " + "Provide 'name' to create a new template, or 'templateId' to add a new version to an existing template. " + "Use 'includeParts' to save only specific parts of the configuration.",
51454
51384
  inputSchema: strictSchema({
51455
51385
  workflowId: exports_external.string().describe("Source workflow ID to extract config from"),
51456
51386
  name: exports_external.string().optional().describe("Name for the new template (required if templateId is not set)"),
51457
51387
  description: exports_external.string().optional().describe("Description for the new template"),
51458
- templateId: exports_external.string().optional().describe("Existing template ID to add a new version to (mutually exclusive with name)")
51388
+ templateId: exports_external.string().optional().describe("Existing template ID to add a new version to (mutually exclusive with name)"),
51389
+ includeParts: exports_external.preprocess(coerceArray(), exports_external.array(exports_external.enum(["prompt", "schema", "notifications"])).optional()).describe("Which parts of the workflow config to include. Omit to include all. At least 1 required if provided.")
51459
51390
  }),
51460
51391
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
51461
51392
  }, withErrorHandling("save_workflow_as_template", async (args) => {
@@ -51463,7 +51394,8 @@ function registerTools(server, ctx) {
51463
51394
  workflowId: args.workflowId,
51464
51395
  ...args.name != null && { name: args.name },
51465
51396
  ...args.description != null && { description: args.description },
51466
- ...args.templateId != null && { templateId: args.templateId }
51397
+ ...args.templateId != null && { templateId: args.templateId },
51398
+ ...args.includeParts != null && { includeParts: args.includeParts }
51467
51399
  });
51468
51400
  return jsonResult({
51469
51401
  success: true,
@@ -51503,7 +51435,7 @@ var package_default;
51503
51435
  var init_package = __esm(() => {
51504
51436
  package_default = {
51505
51437
  name: "@kadoa/mcp",
51506
- version: "0.4.3-rc.1",
51438
+ version: "0.4.3-rc.2",
51507
51439
  description: "Kadoa MCP Server — manage workflows from Claude Desktop, Cursor, and other MCP clients",
51508
51440
  type: "module",
51509
51441
  main: "dist/index.js",
@@ -51527,7 +51459,7 @@ var init_package = __esm(() => {
51527
51459
  prepublishOnly: "bun run check-types && bun run test:unit && bun run build"
51528
51460
  },
51529
51461
  dependencies: {
51530
- "@kadoa/node-sdk": "^0.27.0",
51462
+ "@kadoa/node-sdk": "^0.28.0",
51531
51463
  "@modelcontextprotocol/sdk": "^1.26.0",
51532
51464
  express: "^5.2.1",
51533
51465
  ioredis: "^5.6.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kadoa/mcp",
3
- "version": "0.4.3-rc.1",
3
+ "version": "0.4.3-rc.2",
4
4
  "description": "Kadoa MCP Server — manage workflows from Claude Desktop, Cursor, and other MCP clients",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,7 +24,7 @@
24
24
  "prepublishOnly": "bun run check-types && bun run test:unit && bun run build"
25
25
  },
26
26
  "dependencies": {
27
- "@kadoa/node-sdk": "^0.27.0",
27
+ "@kadoa/node-sdk": "^0.28.0",
28
28
  "@modelcontextprotocol/sdk": "^1.26.0",
29
29
  "express": "^5.2.1",
30
30
  "ioredis": "^5.6.1",