@kadoa/mcp 0.5.5 → 0.5.7

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 (3) hide show
  1. package/README.md +11 -0
  2. package/dist/index.js +149 -54
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -85,6 +85,17 @@ Claude checks the run status with get_workflow, then calls fetch_data
85
85
  to retrieve the extracted records and display them as a table.
86
86
  ```
87
87
 
88
+ ### Create a workflow from a template
89
+
90
+ ```
91
+ > You: Use my "Product Scraper" template to scrape https://example-shop.com.
92
+
93
+ Claude calls list_templates to find the matching template, then
94
+ create_workflow with `templateId` and `urls` only — the prompt and
95
+ schema are inherited from the template version. Returns the workflow
96
+ ID for follow-up with get_workflow or fetch_data.
97
+ ```
98
+
88
99
  ### Update a workflow and re-run
89
100
 
90
101
  ```
package/dist/index.js CHANGED
@@ -14552,7 +14552,7 @@ function finalize(ctx, schema) {
14552
14552
  result.$schema = "http://json-schema.org/draft-07/schema#";
14553
14553
  } else if (ctx.target === "draft-04") {
14554
14554
  result.$schema = "http://json-schema.org/draft-04/schema#";
14555
- } else if (ctx.target === "openapi-3.0") {} else {}
14555
+ } else if (ctx.target === "openapi-3.0") {}
14556
14556
  if (ctx.external?.uri) {
14557
14557
  const id = ctx.external.registry.get(schema)?.id;
14558
14558
  if (!id)
@@ -14817,7 +14817,7 @@ var formatMap, stringProcessor = (schema, ctx, _json, _params) => {
14817
14817
  if (val === undefined) {
14818
14818
  if (ctx.unrepresentable === "throw") {
14819
14819
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
14820
- } else {}
14820
+ }
14821
14821
  } else if (typeof val === "bigint") {
14822
14822
  if (ctx.unrepresentable === "throw") {
14823
14823
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -44498,9 +44498,9 @@ function createClientDomains(params) {
44498
44498
  const dataFetcherService = new DataFetcherService(client.apis.workflows);
44499
44499
  const channelsService = new NotificationChannelsService(client.apis.notifications, userService);
44500
44500
  const settingsService = new NotificationSettingsService(client.apis.notifications);
44501
- const workflowsCoreService = new WorkflowsCoreService(client.apis.workflows);
44502
44501
  const schemasService = new SchemasService(client);
44503
44502
  const templatesService = new TemplatesService(client);
44503
+ const workflowsCoreService = new WorkflowsCoreService(client.apis.workflows, templatesService);
44504
44504
  const variablesService = new VariablesService(client);
44505
44505
  const channelSetupService = new NotificationSetupService(channelsService, settingsService);
44506
44506
  const coreService = new ValidationCoreService(client);
@@ -48714,7 +48714,7 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
48714
48714
  }));
48715
48715
  return channels;
48716
48716
  }
48717
- }, PUBLIC_API_URI, WSS_API_URI, REALTIME_API_URI, SDK_VERSION = "0.32.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 {
48717
+ }, PUBLIC_API_URI, WSS_API_URI, REALTIME_API_URI, SDK_VERSION = "0.33.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 {
48718
48718
  constructor(config2) {
48719
48719
  this.drainingSockets = /* @__PURE__ */ new Set;
48720
48720
  this.lastHeartbeat = Date.now();
@@ -49443,36 +49443,82 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
49443
49443
  });
49444
49444
  }
49445
49445
  }, JobStateEnum, TERMINAL_JOB_STATES, TERMINAL_RUN_STATES2, debug9, WorkflowsCoreService = class {
49446
- constructor(workflowsApi) {
49446
+ constructor(workflowsApi, templatesService) {
49447
49447
  this.workflowsApi = workflowsApi;
49448
+ this.templatesService = templatesService;
49448
49449
  }
49449
49450
  async create(input) {
49450
49451
  validateAdditionalData(input.additionalData);
49451
- if (!input.userPrompt) {
49452
+ const isFromTemplate = input.templateId != null;
49453
+ if (isFromTemplate) {
49454
+ const conflicting = [];
49455
+ if (input.userPrompt != null)
49456
+ conflicting.push("userPrompt");
49457
+ if (input.entity != null)
49458
+ conflicting.push("entity");
49459
+ if (input.fields != null)
49460
+ conflicting.push("fields");
49461
+ if (input.schemaId != null)
49462
+ conflicting.push("schemaId");
49463
+ if (input.monitoring != null)
49464
+ conflicting.push("monitoring");
49465
+ if (input.navigationMode != null)
49466
+ conflicting.push("navigationMode");
49467
+ if (conflicting.length > 0) {
49468
+ throw new KadoaSdkException(`Fields are defined by the template and cannot be supplied when creating from a template: ${conflicting.join(", ")}`, {
49469
+ code: "VALIDATION_ERROR",
49470
+ details: { conflicting }
49471
+ });
49472
+ }
49473
+ } else if (!input.userPrompt) {
49452
49474
  throw new KadoaSdkException("userPrompt is required to create a workflow", {
49453
49475
  code: "VALIDATION_ERROR",
49454
49476
  details: { urls: input.urls }
49455
49477
  });
49456
49478
  }
49457
49479
  const domainName = new URL(input.urls[0]).hostname;
49458
- const request = {
49459
- urls: input.urls,
49460
- name: input.name ?? domainName,
49461
- description: input.description,
49462
- userPrompt: input.userPrompt,
49463
- navigationMode: "agentic-navigation",
49464
- schemaId: input.schemaId,
49465
- ...input.entity != null && { entity: input.entity },
49466
- fields: input.fields,
49467
- bypassPreview: input.bypassPreview ?? true,
49468
- tags: input.tags,
49469
- interval: input.interval,
49470
- monitoring: input.monitoring,
49471
- location: input.location,
49472
- schedules: input.schedules,
49473
- additionalData: input.additionalData,
49474
- limit: input.limit
49475
- };
49480
+ let request;
49481
+ if (isFromTemplate) {
49482
+ const templateId = input.templateId;
49483
+ const templateVersion = input.templateVersion ?? await this.resolveLatestVersion(templateId);
49484
+ request = {
49485
+ urls: input.urls,
49486
+ templateId,
49487
+ templateVersion,
49488
+ ...input.name != null && { name: input.name },
49489
+ ...input.description != null && { description: input.description },
49490
+ ...input.tags != null && { tags: input.tags },
49491
+ ...input.interval != null && { interval: input.interval },
49492
+ ...input.schedules != null && { schedules: input.schedules },
49493
+ ...input.location != null && { location: input.location },
49494
+ ...input.bypassPreview != null && {
49495
+ bypassPreview: input.bypassPreview
49496
+ },
49497
+ ...input.additionalData != null && {
49498
+ additionalData: input.additionalData
49499
+ },
49500
+ ...input.limit != null && { limit: input.limit }
49501
+ };
49502
+ } else {
49503
+ request = {
49504
+ urls: input.urls,
49505
+ name: input.name ?? domainName,
49506
+ description: input.description,
49507
+ userPrompt: input.userPrompt,
49508
+ navigationMode: "agentic-navigation",
49509
+ schemaId: input.schemaId,
49510
+ ...input.entity != null && { entity: input.entity },
49511
+ fields: input.fields,
49512
+ bypassPreview: input.bypassPreview ?? true,
49513
+ tags: input.tags,
49514
+ interval: input.interval,
49515
+ monitoring: input.monitoring,
49516
+ location: input.location,
49517
+ schedules: input.schedules,
49518
+ additionalData: input.additionalData,
49519
+ limit: input.limit
49520
+ };
49521
+ }
49476
49522
  const response = await this.workflowsApi.v4WorkflowsPost({
49477
49523
  publicWorkflowCreateRequest: request
49478
49524
  });
@@ -49487,6 +49533,23 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
49487
49533
  }
49488
49534
  return { id: workflowId };
49489
49535
  }
49536
+ async resolveLatestVersion(templateId) {
49537
+ if (!this.templatesService) {
49538
+ throw new KadoaSdkException("TemplatesService is required to resolve a template's latest version. Pass `templateVersion` explicitly or construct WorkflowsCoreService with a TemplatesService.", {
49539
+ code: "INTERNAL_ERROR",
49540
+ details: { templateId }
49541
+ });
49542
+ }
49543
+ const template = await this.templatesService.get(templateId);
49544
+ const latest = template.latestVersion;
49545
+ if (latest == null) {
49546
+ throw new KadoaSdkException(`Template ${templateId} has no published versions; supply templateVersion explicitly or publish a version first.`, {
49547
+ code: "VALIDATION_ERROR",
49548
+ details: { templateId }
49549
+ });
49550
+ }
49551
+ return latest;
49552
+ }
49490
49553
  async get(id) {
49491
49554
  const response = await this.workflowsApi.v4WorkflowsWorkflowIdGet({
49492
49555
  workflowId: id
@@ -50848,7 +50911,7 @@ function registerTools(server, ctx) {
50848
50911
  urls: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string()).min(1)).optional().describe("Starting URLs for the workflow (array of strings). Also accepts a single URL string.")
50849
50912
  };
50850
50913
  const extractionInputShape = {
50851
- prompt: exports_external.string().describe('Natural language description of what to extract (e.g., "Extract product prices and names")'),
50914
+ prompt: exports_external.string().optional().describe('Natural language description of what to extract (e.g., "Extract product prices and names"). Required unless templateId is provided.'),
50852
50915
  name: exports_external.string().optional().describe("Optional name for the workflow"),
50853
50916
  entity: exports_external.string().optional().describe("Entity name for extraction (e.g., 'Product', 'Job Posting')"),
50854
50917
  schema: exports_external.preprocess(coerceArray(), exports_external.array(exports_external.object(SchemaFieldShape))).optional().describe("Extraction schema fields. If omitted, the AI agent auto-detects the schema.")
@@ -50953,10 +51016,12 @@ function registerTools(server, ctx) {
50953
51016
 
50954
51017
  ` + "Create a data extraction workflow using agentic navigation. Supports one-time or scheduled runs. " + "If entity and schema are provided, they guide the extraction; otherwise the AI agent auto-detects the schema from the page. " + "The workflow runs asynchronously and may take several minutes. Do NOT poll or sleep-wait for completion. " + `Return the workflow ID to the user and let them check back later with get_workflow or fetch_data.
50955
51018
 
50956
- ` + "NOTE: This tool is for one-time or scheduled extraction ONLY. " + "For continuous real-time monitoring (watching a page for changes and alerting), use the create_realtime_monitor tool instead.",
51019
+ ` + "PREFER TEMPLATES: If the user's request matches an existing template, instantiate it via `templateId` instead of writing a fresh prompt/schema. " + "Use `list_templates` to discover available templates and `get_template` to inspect schemas before deciding. " + "When `templateId` is set, only `urls` is required — `prompt`, `entity`, and `schema` must NOT be supplied; they are inherited from the template version.\n\n" + "NOTE: This tool is for one-time or scheduled extraction ONLY. " + "For continuous real-time monitoring (watching a page for changes and alerting), use the create_realtime_monitor tool instead.",
50957
51020
  inputSchema: strictSchema({
50958
51021
  ...extractionInputShape,
50959
51022
  ...urlInputShape,
51023
+ templateId: exports_external.string().optional().describe("Instantiate this workflow from a published template. When set, only 'urls' is required — prompt/entity/schema must NOT be supplied; they are inherited from the template version. Discover templates via list_templates."),
51024
+ templateVersion: exports_external.preprocess(coerceNumber(), exports_external.number()).optional().describe("Specific published template version (integer) to instantiate. Defaults to the latest published version when templateId is set."),
50960
51025
  description: exports_external.string().max(500).optional().describe("Description of what this workflow does (max 500 characters)"),
50961
51026
  tags: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string())).optional().describe("Tags for organizing workflows"),
50962
51027
  limit: exports_external.preprocess(coerceNumber(), exports_external.number()).optional().describe("Maximum number of records to extract per run. Useful for limiting scope or cost control."),
@@ -50991,32 +51056,62 @@ function registerTools(server, ctx) {
50991
51056
  if (args.updateInterval === "CUSTOM" && (!args.schedules || args.schedules.length === 0)) {
50992
51057
  return errorResult("updateInterval='CUSTOM' requires at least one cron expression in the 'schedules' field (e.g. '0 8 * * 2' for Tuesdays at 8am UTC).");
50993
51058
  }
50994
- let builder = ctx.client.extract({
50995
- urls,
50996
- name: args.name || "Untitled Workflow",
50997
- userPrompt: args.prompt,
50998
- extraction: buildExtraction(args),
50999
- interval: args.updateInterval,
51000
- description: args.description,
51001
- schedules: args.schedules
51002
- });
51003
51059
  const n = args.notifications;
51004
- const hasNotifications = n && (n.email || n.webhook || n.slack || n.websocket);
51005
- if (hasNotifications) {
51006
- builder = builder.withNotifications({
51007
- events: ["workflow_data_change"],
51008
- channels: buildNotificationChannels(n)
51060
+ const hasNotifications = !!(n && (n.email || n.webhook || n.slack || n.websocket));
51061
+ let workflowId;
51062
+ if (args.templateId) {
51063
+ if (args.prompt || args.entity || args.schema) {
51064
+ return errorResult("When 'templateId' is set, 'prompt', 'entity', and 'schema' must NOT be supplied — they are inherited from the template version.");
51065
+ }
51066
+ const { id } = await ctx.client.workflow.create({
51067
+ urls,
51068
+ name: args.name || "Untitled Workflow",
51069
+ description: args.description,
51070
+ interval: args.updateInterval,
51071
+ schedules: args.schedules,
51072
+ tags: args.tags && args.tags.length > 0 ? args.tags : undefined,
51073
+ limit: args.limit,
51074
+ templateId: args.templateId,
51075
+ templateVersion: args.templateVersion
51009
51076
  });
51010
- }
51011
- const workflow = await builder.create();
51012
- const needsUpdate = args.limit !== undefined || args.tags && args.tags.length > 0;
51013
- if (needsUpdate) {
51014
- const updates = {};
51015
- if (args.limit !== undefined)
51016
- updates.limit = args.limit;
51017
- if (args.tags && args.tags.length > 0)
51018
- updates.tags = args.tags;
51019
- await ctx.client.workflow.update(workflow.workflowId, updates);
51077
+ workflowId = id;
51078
+ if (hasNotifications) {
51079
+ await ctx.client.notification.configure({
51080
+ workflowId,
51081
+ events: ["workflow_data_change"],
51082
+ channels: buildNotificationChannels(n)
51083
+ });
51084
+ }
51085
+ } else {
51086
+ if (!args.prompt) {
51087
+ return errorResult("'prompt' is required unless 'templateId' is provided.");
51088
+ }
51089
+ let builder = ctx.client.extract({
51090
+ urls,
51091
+ name: args.name || "Untitled Workflow",
51092
+ userPrompt: args.prompt,
51093
+ extraction: buildExtraction(args),
51094
+ interval: args.updateInterval,
51095
+ description: args.description,
51096
+ schedules: args.schedules
51097
+ });
51098
+ if (hasNotifications) {
51099
+ builder = builder.withNotifications({
51100
+ events: ["workflow_data_change"],
51101
+ channels: buildNotificationChannels(n)
51102
+ });
51103
+ }
51104
+ const workflow = await builder.create();
51105
+ workflowId = workflow.workflowId;
51106
+ const needsUpdate = args.limit !== undefined || args.tags && args.tags.length > 0;
51107
+ if (needsUpdate) {
51108
+ const updates = {};
51109
+ if (args.limit !== undefined)
51110
+ updates.limit = args.limit;
51111
+ if (args.tags && args.tags.length > 0)
51112
+ updates.tags = args.tags;
51113
+ await ctx.client.workflow.update(workflowId, updates);
51114
+ }
51020
51115
  }
51021
51116
  const enabledChannels = await describeNotifications(n);
51022
51117
  let message = "Workflow created successfully. The AI agent will start extracting data automatically.";
@@ -51026,8 +51121,8 @@ function registerTools(server, ctx) {
51026
51121
  message += " Use fetch_data to retrieve the latest extracted results.";
51027
51122
  return jsonResult({
51028
51123
  success: true,
51029
- workflowId: workflow.workflowId,
51030
- dashboardUrl: workflowDashboardUrl(workflow.workflowId),
51124
+ workflowId,
51125
+ dashboardUrl: workflowDashboardUrl(workflowId),
51031
51126
  message
51032
51127
  });
51033
51128
  }));
@@ -51987,7 +52082,7 @@ var package_default;
51987
52082
  var init_package = __esm(() => {
51988
52083
  package_default = {
51989
52084
  name: "@kadoa/mcp",
51990
- version: "0.5.5",
52085
+ version: "0.5.7",
51991
52086
  description: "Kadoa MCP Server — manage workflows from Claude Desktop, Cursor, and other MCP clients",
51992
52087
  type: "module",
51993
52088
  main: "dist/index.js",
@@ -52011,7 +52106,7 @@ var init_package = __esm(() => {
52011
52106
  prepublishOnly: "bun run check-types && bun run test:unit && bun run build"
52012
52107
  },
52013
52108
  dependencies: {
52014
- "@kadoa/node-sdk": "^0.32.0",
52109
+ "@kadoa/node-sdk": "^0.33.0",
52015
52110
  "@modelcontextprotocol/sdk": "^1.26.0",
52016
52111
  express: "^5.2.1",
52017
52112
  ioredis: "^5.6.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kadoa/mcp",
3
- "version": "0.5.5",
3
+ "version": "0.5.7",
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.32.0",
27
+ "@kadoa/node-sdk": "^0.33.0",
28
28
  "@modelcontextprotocol/sdk": "^1.26.0",
29
29
  "express": "^5.2.1",
30
30
  "ioredis": "^5.6.1",