@kadoa/mcp 0.5.14 → 0.5.16

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 +1 -0
  2. package/dist/index.js +88 -21
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -66,6 +66,7 @@ Point your client to `https://mcp.kadoa.com/mcp` with OAuth authentication.
66
66
  | `list_workflows` | List all workflows with status |
67
67
  | `get_workflow` | Get detailed workflow information |
68
68
  | `get_workflow_history` | Get the workflow's configuration revision history (audit log) — who changed it, when, from which channel, and a `changedFields` summary per revision |
69
+ | `list_workflow_runs` | List a workflow's execution run history (status, start/finish, record count, errors); filter by outcome and paginate |
69
70
  | `run_workflow` | Execute a workflow |
70
71
  | `fetch_data` | Get extracted data from a workflow |
71
72
  | `delete_workflow` | Delete a workflow |
package/dist/index.js CHANGED
@@ -48467,7 +48467,7 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
48467
48467
  options: localVarRequestOptions
48468
48468
  };
48469
48469
  },
48470
- v4WorkflowsWorkflowIdHistoryGet: async (workflowId, options = {}) => {
48470
+ v4WorkflowsWorkflowIdHistoryGet: async (workflowId, page, limit, status, options = {}) => {
48471
48471
  assertParamExists("v4WorkflowsWorkflowIdHistoryGet", "workflowId", workflowId);
48472
48472
  const localVarPath = `/v4/workflows/{workflowId}/history`.replace(`{${"workflowId"}}`, encodeURIComponent(String(workflowId)));
48473
48473
  const localVarUrlObj = new URL$1(localVarPath, DUMMY_BASE_URL);
@@ -48479,6 +48479,15 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
48479
48479
  const localVarHeaderParameter = {};
48480
48480
  const localVarQueryParameter = {};
48481
48481
  await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration);
48482
+ if (page !== undefined) {
48483
+ localVarQueryParameter["page"] = page;
48484
+ }
48485
+ if (limit !== undefined) {
48486
+ localVarQueryParameter["limit"] = limit;
48487
+ }
48488
+ if (status !== undefined) {
48489
+ localVarQueryParameter["status"] = status;
48490
+ }
48482
48491
  localVarHeaderParameter["Accept"] = "application/json";
48483
48492
  setSearchParams(localVarUrlObj, localVarQueryParameter);
48484
48493
  let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
@@ -48842,8 +48851,8 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
48842
48851
  const localVarOperationServerBasePath = operationServerMap["WorkflowsApi.v4WorkflowsWorkflowIdGet"]?.[localVarOperationServerIndex]?.url;
48843
48852
  return (axios2, basePath) => createRequestFunction(localVarAxiosArgs, axios_default, BASE_PATH, configuration)(axios2, localVarOperationServerBasePath || basePath);
48844
48853
  },
48845
- async v4WorkflowsWorkflowIdHistoryGet(workflowId, options) {
48846
- const localVarAxiosArgs = await localVarAxiosParamCreator.v4WorkflowsWorkflowIdHistoryGet(workflowId, options);
48854
+ async v4WorkflowsWorkflowIdHistoryGet(workflowId, page, limit, status, options) {
48855
+ const localVarAxiosArgs = await localVarAxiosParamCreator.v4WorkflowsWorkflowIdHistoryGet(workflowId, page, limit, status, options);
48847
48856
  const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
48848
48857
  const localVarOperationServerBasePath = operationServerMap["WorkflowsApi.v4WorkflowsWorkflowIdHistoryGet"]?.[localVarOperationServerIndex]?.url;
48849
48858
  return (axios2, basePath) => createRequestFunction(localVarAxiosArgs, axios_default, BASE_PATH, configuration)(axios2, localVarOperationServerBasePath || basePath);
@@ -49308,7 +49317,7 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
49308
49317
  async executeExtraction(options) {
49309
49318
  this.validateOptions(options);
49310
49319
  const config2 = {
49311
- ...merge3(DEFAULT_OPTIONS, options)
49320
+ ...merge3(merge3({}, DEFAULT_OPTIONS), options)
49312
49321
  };
49313
49322
  const isRealTime = config2.interval === "REAL_TIME";
49314
49323
  if (isRealTime) {
@@ -49591,7 +49600,7 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
49591
49600
  assert2(startedJob.jobId, "Job ID is not set");
49592
49601
  debug4("Job started: %O", startedJob);
49593
49602
  this._jobId = startedJob.jobId;
49594
- const finishedJob = await this.workflowsCoreService.waitForJobCompletion(this._workflowId, startedJob.jobId);
49603
+ const finishedJob = await this.workflowsCoreService.waitForJobCompletion(this._workflowId, startedJob.jobId, { timeoutMs: 30 * 60 * 1000 });
49595
49604
  debug4("Job finished: %O", finishedJob);
49596
49605
  return this;
49597
49606
  }
@@ -50019,7 +50028,7 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
50019
50028
  }));
50020
50029
  return channels;
50021
50030
  }
50022
- }, PUBLIC_API_URI, WSS_API_URI, REALTIME_API_URI, SDK_VERSION = "0.35.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 {
50031
+ }, PUBLIC_API_URI, WSS_API_URI, REALTIME_API_URI, SDK_VERSION = "0.36.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 {
50023
50032
  constructor(config2) {
50024
50033
  this.drainingSockets = /* @__PURE__ */ new Set;
50025
50034
  this.lastHeartbeat = Date.now();
@@ -50941,6 +50950,15 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
50941
50950
  });
50942
50951
  return response.data;
50943
50952
  }
50953
+ async listWorkflowRuns(id, options) {
50954
+ const response = await this.workflowsApi.v4WorkflowsWorkflowIdHistoryGet({
50955
+ workflowId: id,
50956
+ page: options?.page,
50957
+ limit: options?.limit,
50958
+ status: options?.status
50959
+ });
50960
+ return response.data;
50961
+ }
50944
50962
  async delete(id) {
50945
50963
  await this.workflowsApi.v4WorkflowsWorkflowIdDelete({
50946
50964
  workflowId: id
@@ -51703,7 +51721,7 @@ var init_dist2 = __esm(() => {
51703
51721
  return WorkflowsApiFp(this.configuration).v4WorkflowsWorkflowIdGet(requestParameters.workflowId, options).then((request) => request(this.axios, this.basePath));
51704
51722
  }
51705
51723
  v4WorkflowsWorkflowIdHistoryGet(requestParameters, options) {
51706
- return WorkflowsApiFp(this.configuration).v4WorkflowsWorkflowIdHistoryGet(requestParameters.workflowId, options).then((request) => request(this.axios, this.basePath));
51724
+ return WorkflowsApiFp(this.configuration).v4WorkflowsWorkflowIdHistoryGet(requestParameters.workflowId, requestParameters.page, requestParameters.limit, requestParameters.status, options).then((request) => request(this.axios, this.basePath));
51707
51725
  }
51708
51726
  v4WorkflowsWorkflowIdJobsJobIdGet(requestParameters, options) {
51709
51727
  return WorkflowsApiFp(this.configuration).v4WorkflowsWorkflowIdJobsJobIdGet(requestParameters.workflowId, requestParameters.jobId, options).then((request) => request(this.axios, this.basePath));
@@ -51799,7 +51817,7 @@ This is the main page content.`
51799
51817
  DEFAULT_OPTIONS = {
51800
51818
  mode: "run",
51801
51819
  pollingInterval: 5000,
51802
- maxWaitTime: 300000,
51820
+ maxWaitTime: 30 * 60 * 1000,
51803
51821
  location: { type: "auto" },
51804
51822
  bypassPreview: true,
51805
51823
  autoStart: true
@@ -52141,6 +52159,22 @@ function deriveStatusLabel(workflow) {
52141
52159
  return ds ?? "Unknown";
52142
52160
  }
52143
52161
  }
52162
+ function mapRunStatus(state) {
52163
+ switch (state) {
52164
+ case "FINISHED":
52165
+ return "success";
52166
+ case "IN_PROGRESS":
52167
+ case "VALIDATING":
52168
+ return "in_progress";
52169
+ case "FAILED":
52170
+ case "FAILED_INSUFFICIENT_FUNDS":
52171
+ case "NOT_SUPPORTED":
52172
+ return "failed";
52173
+ default:
52174
+ console.error(`[list_workflow_runs] unmapped run state, passing through verbatim: ${state}`);
52175
+ return state ?? "unknown";
52176
+ }
52177
+ }
52144
52178
  function workflowDashboardUrl(workflowId) {
52145
52179
  return `${DASHBOARD_BASE_URL}/workflow/${workflowId}`;
52146
52180
  }
@@ -52364,7 +52398,7 @@ function registerTools(server, ctx, capabilities) {
52364
52398
  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.'),
52365
52399
  name: exports_external.string().optional().describe("Optional name for the workflow"),
52366
52400
  entity: exports_external.string().optional().describe("Entity name for extraction (e.g., 'Product', 'Job Posting')"),
52367
- 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.")
52401
+ schema: exports_external.preprocess(coerceArray(), exports_external.array(exports_external.object(SchemaFieldShape).strict())).optional().describe("Extraction schema fields. If omitted, the AI agent auto-detects the schema. When you do supply fields, mark the one that identifies a record with isKey — change detection needs it.")
52368
52402
  };
52369
52403
  const webhookAuthShape = exports_external.object({
52370
52404
  type: exports_external.enum(["bearer", "basic", "header"]).describe("Authentication type"),
@@ -52735,6 +52769,35 @@ function registerTools(server, ctx, capabilities) {
52735
52769
  entries
52736
52770
  });
52737
52771
  }));
52772
+ server.registerTool("list_workflow_runs", {
52773
+ description: "List a workflow's execution run history — each run's status, start/finish time, " + "record count, and errors. Use to answer 'when did this last succeed?' " + "(status='success', limit=1) or 'recent success/failure pattern?'. Distinct from " + "get_workflow_history, which is the config audit log. `status` is normally " + "success | failed | in_progress; a run whose backend state this server does not " + "recognize reports that raw state verbatim, so treat any other value as unknown " + "rather than as a failure.",
52774
+ inputSchema: {
52775
+ workflowId: exports_external.string().describe("The workflow ID"),
52776
+ status: exports_external.enum(["success", "failed", "in_progress"]).optional().describe("Filter runs by outcome"),
52777
+ page: exports_external.preprocess(coerceNumber(), exports_external.number()).optional().describe("Page number, 1-based (default: 1)"),
52778
+ limit: exports_external.preprocess(coerceNumber(), exports_external.number()).optional().describe("Runs per page (default: 25)")
52779
+ },
52780
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true }
52781
+ }, withErrorHandling("list_workflow_runs", async (args) => {
52782
+ const response = await ctx.client.workflow.listWorkflowRuns(args.workflowId, {
52783
+ page: args.page,
52784
+ limit: args.limit,
52785
+ status: args.status
52786
+ });
52787
+ return jsonResult({
52788
+ workflowId: response.workflowId,
52789
+ runs: (response.workflowRuns ?? []).map((r) => ({
52790
+ id: r.id,
52791
+ status: mapRunStatus(r.state),
52792
+ startedAt: r.startedAt,
52793
+ finishedAt: r.finishedAt,
52794
+ records: r.records,
52795
+ errors: r.errors
52796
+ })),
52797
+ totalCount: response.totalCount,
52798
+ pagination: response.pagination
52799
+ });
52800
+ }));
52738
52801
  server.registerTool("run_workflow", {
52739
52802
  description: "Run a workflow to extract fresh data. The run is asynchronous and may take several minutes. Do NOT poll or sleep-wait for completion. Return the workflow ID to the user and let them check status with get_workflow or fetch results later with fetch_data.",
52740
52803
  inputSchema: {
@@ -52936,13 +52999,15 @@ function registerTools(server, ctx, capabilities) {
52936
52999
  server.registerTool("update_workflow", {
52937
53000
  description: `Update a workflow's configuration. All fields are optional — only provided fields will be updated. Use this to change the name, URLs, extraction schema, entity, prompt, schedule, or other metadata.
52938
53001
 
52939
- ` + "IMPORTANT: You cannot change a workflow's interval to or from REAL_TIME. Real-time workflows are architecturally different (Observer service vs Scraper) and must be created as real-time from the start using create_workflow with interval='REAL_TIME'. To switch a workflow between real-time and regular, delete it and create a new one with the correct interval.",
53002
+ ` + `IMPORTANT: You cannot change a workflow's interval to or from REAL_TIME. Real-time workflows are architecturally different (Observer service vs Scraper) and must be created as real-time from the start using create_workflow with interval='REAL_TIME'. To switch a workflow between real-time and regular, delete it and create a new one with the correct interval.
53003
+
53004
+ ` + "PROMPT EDITS: `userPrompt` works on most workflows and re-derives the extraction intent. Some agent-built (Shelly) scheduled workflows reject it with `SHELLY_INTENT_REQUIRES_EXTRACTION_SPEC` because their intent is owned elsewhere. " + "If that happens, tell the user to change the extraction intent in Lighthouse chat in the Kadoa dashboard. NEVER delete and recreate the workflow to work around it — that mints a new workflowId, breaking anything downstream keyed to the old one (warehouse tables, connectors) and discarding the workflow's history.",
52940
53005
  inputSchema: strictSchema({
52941
53006
  workflowId: exports_external.string().describe("The workflow ID to update"),
52942
53007
  name: exports_external.string().optional().describe("New name for the workflow"),
52943
53008
  urls: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string()).min(1)).optional().describe("New target URLs for the workflow (array of strings). Also accepts a single URL string."),
52944
53009
  entity: exports_external.string().optional().describe("Entity name for extraction (e.g., 'Product', 'Job Posting')"),
52945
- schema: exports_external.preprocess(coerceArray(), exports_external.array(exports_external.object(SchemaFieldShape))).optional().describe("New extraction schema fields"),
53010
+ schema: exports_external.preprocess(coerceArray(), exports_external.array(exports_external.object(SchemaFieldShape).strict())).optional().describe("New extraction schema fields. This REPLACES the whole schema — list every field you want to keep, each with its isKey flag, or the omitted ones and their flags are lost. Call get_workflow first and edit the schema it returns."),
52946
53011
  description: exports_external.string().max(500).optional().describe("Workflow description (max 500 characters)"),
52947
53012
  tags: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string())).optional().describe("Tags for organizing workflows"),
52948
53013
  userPrompt: exports_external.string().optional().describe("Navigation prompt for agentic-navigation mode (10-5000 characters)"),
@@ -53409,11 +53474,13 @@ function registerTools(server, ctx, capabilities) {
53409
53474
  });
53410
53475
  }));
53411
53476
  server.registerTool("update_template", {
53412
- description: "Update a template's name or description. At least one field must be provided.",
53477
+ description: `Update a template's name or description ONLY. At least one of the two must be provided.
53478
+
53479
+ ` + "This tool CANNOT change what linked workflows inherit — prompt, schema, validation rules, notifications and frequency all live in template *versions*, which are immutable snapshots. " + "To change any of those, publish a new version with `create_template_version` (the version IS the edit), then roll it out to linked workflows with `apply_template_version`. " + "There is no in-place edit of a published version, and never report a prompt or schema change as impossible — versioning is the supported path.",
53413
53480
  inputSchema: strictSchema({
53414
53481
  templateId: exports_external.string().describe("The template ID to update"),
53415
- name: exports_external.preprocess(coerceNull(), exports_external.string().optional()).describe("New template name"),
53416
- description: exports_external.preprocess(coerceNull(), exports_external.string().optional()).describe("New template description")
53482
+ name: exports_external.preprocess(coerceNull(), exports_external.string().optional()).optional().describe("New template name"),
53483
+ description: exports_external.preprocess(coerceNull(), exports_external.string().optional()).optional().describe("New template description")
53417
53484
  }),
53418
53485
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true }
53419
53486
  }, withErrorHandling("update_template", async (args) => {
@@ -53465,12 +53532,14 @@ function registerTools(server, ctx, capabilities) {
53465
53532
  }))).optional().describe("Predefined categories for a CLASSIFICATION field ({title, definition}[]). Required for fieldType=CLASSIFICATION; omitted otherwise.")
53466
53533
  };
53467
53534
  server.registerTool("create_template_version", {
53468
- description: "Publish a new version of a template. Versions capture the full workflow config: prompt, schema, and notifications. All fields are optional — include only what this version should set.",
53535
+ description: `Publish a new version of a template. Versions capture the full workflow config: prompt, schema, and notifications. All fields are optional — include only what this version should set.
53536
+
53537
+ ` + "THIS IS HOW YOU EDIT A TEMPLATE'S PROMPT OR SCHEMA. Published versions are immutable, so 'changing the template prompt' means publishing a new version here and then calling `apply_template_version` to push it onto linked workflows. " + "`update_template` only renames a template; it cannot touch prompt or schema.",
53469
53538
  inputSchema: strictSchema({
53470
53539
  templateId: exports_external.string().describe("The template ID to publish a version for"),
53471
53540
  prompt: exports_external.string().optional().describe("User prompt to copy into workflow config"),
53472
53541
  schemaId: exports_external.string().optional().describe("Existing schema ID to reference (mutually exclusive with schemaFields)"),
53473
- schemaFields: exports_external.preprocess(coerceArray(), exports_external.array(exports_external.object(TemplateSchemaFieldShape))).optional().describe("Inline schema fields to create a new schema (mutually exclusive with schemaId)"),
53542
+ schemaFields: exports_external.preprocess(coerceArray(), exports_external.array(exports_external.object(TemplateSchemaFieldShape).strict())).optional().describe("Inline schema fields to create a new schema (mutually exclusive with schemaId)"),
53474
53543
  schemaEntity: exports_external.string().optional().describe("Entity name for the inline schema"),
53475
53544
  schemaValidationRules: exports_external.preprocess(coerceJson(), exports_external.record(exports_external.string(), exports_external.record(exports_external.string(), exports_external.unknown()))).optional().describe("Per-field schema validation rules, keyed by field name. Not inherited from prior versions — omitting this on a new version drops any rules the previous version had."),
53476
53545
  notifications: exports_external.preprocess(coerceArray(), exports_external.array(exports_external.object({
@@ -53640,9 +53709,7 @@ var init_tools = __esm(() => {
53640
53709
  description: exports_external.string().optional().describe("What this field contains"),
53641
53710
  example: exports_external.string().describe("Example value"),
53642
53711
  dataType: exports_external.enum(["STRING", "NUMBER", "BOOLEAN", "DATE", "DATETIME", "MONEY", "IMAGE", "LINK", "OBJECT", "ARRAY"]).optional().describe("Data type for the field"),
53643
- isPrimaryKey: exports_external.boolean().optional(),
53644
- isRequired: exports_external.boolean().optional(),
53645
- isUnique: exports_external.boolean().optional()
53712
+ isKey: exports_external.preprocess(coerceBoolean(), exports_external.boolean()).optional().describe("Marks this field as a key field — the stable identity used to match records across runs. Change detection diffs records by their key fields, so a monitored or real-time workflow without one cannot tell an updated record from a new one. Set it on whatever uniquely identifies a row (a detail URL, an ID, a ticker).")
53646
53713
  };
53647
53714
  WORKFLOW_AUDIT_WATCHED_KEYS = [
53648
53715
  "name",
@@ -53662,7 +53729,7 @@ var package_default;
53662
53729
  var init_package = __esm(() => {
53663
53730
  package_default = {
53664
53731
  name: "@kadoa/mcp",
53665
- version: "0.5.14",
53732
+ version: "0.5.16",
53666
53733
  description: "Kadoa MCP Server — manage workflows from Claude Desktop, Cursor, and other MCP clients",
53667
53734
  type: "module",
53668
53735
  main: "dist/index.js",
@@ -53686,7 +53753,7 @@ var init_package = __esm(() => {
53686
53753
  prepublishOnly: "bun run check-types && bun run test:unit && bun run build"
53687
53754
  },
53688
53755
  dependencies: {
53689
- "@kadoa/node-sdk": "^0.35.0",
53756
+ "@kadoa/node-sdk": "^0.36.0",
53690
53757
  "@modelcontextprotocol/sdk": "^1.26.0",
53691
53758
  express: "^5.2.1",
53692
53759
  "express-rate-limit": "^8.2.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kadoa/mcp",
3
- "version": "0.5.14",
3
+ "version": "0.5.16",
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.35.0",
27
+ "@kadoa/node-sdk": "^0.36.0",
28
28
  "@modelcontextprotocol/sdk": "^1.26.0",
29
29
  "express": "^5.2.1",
30
30
  "express-rate-limit": "^8.2.1",