@kadoa/mcp 0.5.19 → 0.5.21

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 +27 -7
  2. package/dist/index.js +372 -72
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -63,6 +63,7 @@ Point your client to `https://mcp.kadoa.com/mcp` with OAuth authentication.
63
63
  |------|-------------|
64
64
  | `scrape` | Immediately fetch one URL as markdown or raw HTML (shown only for enabled workspaces) |
65
65
  | `create_workflow` | Create an agentic navigation workflow from a prompt |
66
+ | `create_realtime_monitor` | Create an asynchronous Julie realtime monitor after persisting notification channels; returns workflow/session/thread/job IDs |
66
67
  | `list_workflows` | List all workflows with status |
67
68
  | `get_workflow` | Get canonical intent, Assistant/session, template ownership, run health, schedule, location, monitoring, and validation details |
68
69
  | `request_workflow_update` | Ask the workflow Assistant to change extraction intent, navigation, sourcing, pagination, or generated behavior without changing the workflow ID |
@@ -126,9 +127,10 @@ to retrieve the extracted records and display them as a table.
126
127
  > You: Use my "Product Scraper" template to scrape https://example-shop.com.
127
128
 
128
129
  Claude calls list_templates to find the matching template, then
129
- create_workflow with `templateId` and `urls` only the prompt and
130
- schema are inherited from the template version. Returns the workflow
131
- ID for follow-up with get_workflow or fetch_data.
130
+ create_workflow with `templateId` and `urls` only - never copy the prompt,
131
+ entity, or schema into standalone creation. Those values are inherited from
132
+ the template version. Returns the workflow ID for follow-up with get_workflow
133
+ or fetch_data.
132
134
  ```
133
135
 
134
136
  ### Update a workflow and re-run
@@ -147,8 +149,10 @@ changes, and shows the updated field list.
147
149
 
148
150
  > You: Run it again with the new schema.
149
151
 
150
- Claude calls run_workflow and waits for completion, then fetches
151
- the latest data with fetch_data so you can verify the changes.
152
+ Claude calls run_workflow and returns the workflow ID while the run proceeds
153
+ asynchronously. Check back later with get_workflow or fetch_data. If a
154
+ one-time or scheduled workflow fails, call run_workflow again with the same
155
+ workflow ID and configuration - never delete and recreate it just to retry.
152
156
  ```
153
157
 
154
158
  ### Update deterministic workflow settings
@@ -163,6 +167,20 @@ in `America/New_York`, monitoring fields/conditions, and `limit: null` for all
163
167
  rows.
164
168
  ```
165
169
 
170
+ ### Create a Julie realtime monitor
171
+
172
+ ```
173
+ > You: Watch https://example-shop.com/products for price changes and alert me by webhook.
174
+
175
+ Claude calls create_realtime_monitor with the URL, schema/change intent, and notification settings. Kadoa persists reusable notification channels first, then Julie asynchronously accepts creation and returns workflow, session, thread, job, and dashboard identifiers.
176
+
177
+ > You: Does Julie need anything from me?
178
+
179
+ Claude uses get_workflow_assistant and the other workflow Assistant tools for follow-up status, questions, interrupts, resumes, or stops. It does not poll or sleep-wait.
180
+ ```
181
+
182
+ Realtime monitors cannot be converted in place to or from scheduled workflows; create them with `create_realtime_monitor` from the start.
183
+
166
184
  ### Update an Assistant-built workflow without replacing it
167
185
 
168
186
  ```
@@ -232,14 +250,16 @@ Relevant files in `kadoa-backend`:
232
250
 
233
251
  1. Merge PRs to `main` using Conventional Commits (`feat:`, `fix:`, etc.). Release Please opens/maintains a `chore(main): release mcp x.y.z` PR.
234
252
  2. Merge the release PR. The [`release-please.yml`](.github/workflows/release-please.yml) workflow tags, drafts a GitHub Release, and publishes to npm (`latest` dist-tag).
235
- 3. In `kadoa-backend`, bump `infra/docker/mcp/package.json` `@kadoa/mcp` to the new version, run `bun install` to refresh `bun.lock`, open a PR.
236
- 4. Merge to `main`. CI (`main-build-deploy.yml`) builds and pushes `europe-west3-docker.pkg.dev/oceanic-base-310208/kadoa-artifacts/mcp-server:<IMAGE_TAG>` (tag shown in the build summary).
253
+ 3. After npm publication, `release-please.yml` opens or updates a deterministic PR in `kadoa-backend` that bumps `infra/docker/mcp/package.json` and refreshes `bun.lock`. The PR validates a frozen production install and Docker image build, and remains manually mergeable.
254
+ 4. Merge the backend PR to `main`. CI (`main-build-deploy.yml`) builds and pushes `europe-west3-docker.pkg.dev/oceanic-base-310208/kadoa-artifacts/mcp-server:<IMAGE_TAG>` (tag shown in the build summary).
237
255
  5. Trigger the **Deploy to Production** workflow ([`deploy-prod.yml`](https://github.com/kadoa-org/kadoa-backend/actions/workflows/deploy-prod.yml)) with:
238
256
  - **Target cluster:** `gcp`
239
257
  - **Deployment scope:** `mcp`
240
258
  - **Image tag:** the tag from step 4
241
259
  - **Method:** `kubectl`
242
260
 
261
+ The cross-repository bump requires the `BACKEND_REPO_TOKEN` secret in this repository. The selected authentication approach is a fine-grained PAT scoped only to `kadoa-org/kadoa-backend` with Contents read/write and Pull requests read/write permissions (Metadata read is automatic). If the secret is not provisioned, the release workflow skips the backend bump with a warning rather than failing npm releases; an administrator must provision it before relying on automatic hosted-server updates.
262
+
243
263
  ### RC / test release
244
264
 
245
265
  Use this when you want to validate a change end-to-end against real clients (Claude Desktop, Cursor, ChatGPT) before promoting to `latest` / prod. The flow mirrors the prod one, but every step targets `rc` channels.
package/dist/index.js CHANGED
@@ -45747,6 +45747,25 @@ var import_debug, __require2, FREEFORM_ASSISTANT_ANSWER_KEY = "_freeform", Kadoa
45747
45747
  constructor(agentApi) {
45748
45748
  this.agentApi = agentApi;
45749
45749
  }
45750
+ async createRealtimeWorkflow(input) {
45751
+ const response = await this.agentApi.v5AgentPrompt({
45752
+ agentPromptRequest: {
45753
+ prompt: input.instructions,
45754
+ productType: "realtime",
45755
+ notificationChannelIds: input.notificationChannelIds,
45756
+ ...input.tags != null && { tags: input.tags },
45757
+ ...input.newSessionId != null && { newSessionId: input.newSessionId }
45758
+ }
45759
+ });
45760
+ const data = response.data?.data;
45761
+ if (!data?.workflowId || !data.sessionId || !data.threadId || !Object.keys(data ?? {}).includes("jobId") || data.jobId === undefined) {
45762
+ throw new KadoaSdkException("Realtime Assistant creation response is missing required identifiers", {
45763
+ code: "INTERNAL_ERROR",
45764
+ details: { response: response.data }
45765
+ });
45766
+ }
45767
+ return data;
45768
+ }
45750
45769
  async requestWorkflowUpdate(workflowId, input) {
45751
45770
  const response = await this.agentApi.v5AgentWorkflowAssistantMessage({
45752
45771
  workflowId,
@@ -45927,6 +45946,28 @@ var import_debug, __require2, FREEFORM_ASSISTANT_ANSWER_KEY = "_freeform", Kadoa
45927
45946
  options: localVarRequestOptions
45928
45947
  };
45929
45948
  },
45949
+ v5AgentPrompt: async (agentPromptRequest, options = {}) => {
45950
+ assertParamExists("v5AgentPrompt", "agentPromptRequest", agentPromptRequest);
45951
+ const localVarPath = `/v5/agent`;
45952
+ const localVarUrlObj = new URL$1(localVarPath, DUMMY_BASE_URL);
45953
+ let baseOptions;
45954
+ if (configuration) {
45955
+ baseOptions = configuration.baseOptions;
45956
+ }
45957
+ const localVarRequestOptions = { method: "POST", ...baseOptions, ...options };
45958
+ const localVarHeaderParameter = {};
45959
+ const localVarQueryParameter = {};
45960
+ localVarHeaderParameter["Content-Type"] = "application/json";
45961
+ localVarHeaderParameter["Accept"] = "application/json";
45962
+ setSearchParams(localVarUrlObj, localVarQueryParameter);
45963
+ let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
45964
+ localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers };
45965
+ localVarRequestOptions.data = serializeDataIfNeeded(agentPromptRequest, localVarRequestOptions, configuration);
45966
+ return {
45967
+ url: toPathString(localVarUrlObj),
45968
+ options: localVarRequestOptions
45969
+ };
45970
+ },
45930
45971
  v5AgentResume: async (sessionId, options = {}) => {
45931
45972
  assertParamExists("v5AgentResume", "sessionId", sessionId);
45932
45973
  const localVarPath = `/v5/agent/{sessionId}/resume`.replace(`{${"sessionId"}}`, encodeURIComponent(String(sessionId)));
@@ -46032,6 +46073,12 @@ var import_debug, __require2, FREEFORM_ASSISTANT_ANSWER_KEY = "_freeform", Kadoa
46032
46073
  const localVarOperationServerBasePath = operationServerMap["AgentApi.v5AgentPauseState"]?.[localVarOperationServerIndex]?.url;
46033
46074
  return (axios2, basePath) => createRequestFunction(localVarAxiosArgs, axios_default, BASE_PATH, configuration)(axios2, localVarOperationServerBasePath || basePath);
46034
46075
  },
46076
+ async v5AgentPrompt(agentPromptRequest, options) {
46077
+ const localVarAxiosArgs = await localVarAxiosParamCreator.v5AgentPrompt(agentPromptRequest, options);
46078
+ const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
46079
+ const localVarOperationServerBasePath = operationServerMap["AgentApi.v5AgentPrompt"]?.[localVarOperationServerIndex]?.url;
46080
+ return (axios2, basePath) => createRequestFunction(localVarAxiosArgs, axios_default, BASE_PATH, configuration)(axios2, localVarOperationServerBasePath || basePath);
46081
+ },
46035
46082
  async v5AgentResume(sessionId, options) {
46036
46083
  const localVarAxiosArgs = await localVarAxiosParamCreator.v5AgentResume(sessionId, options);
46037
46084
  const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
@@ -47817,6 +47864,29 @@ var import_debug, __require2, FREEFORM_ASSISTANT_ANSWER_KEY = "_freeform", Kadoa
47817
47864
  options: localVarRequestOptions
47818
47865
  };
47819
47866
  },
47867
+ v4TemplatesTemplateIdDuplicatePost: async (templateId, duplicateTemplateBody, options = {}) => {
47868
+ assertParamExists("v4TemplatesTemplateIdDuplicatePost", "templateId", templateId);
47869
+ const localVarPath = `/v4/templates/{templateId}/duplicate`.replace(`{${"templateId"}}`, encodeURIComponent(String(templateId)));
47870
+ const localVarUrlObj = new URL$1(localVarPath, DUMMY_BASE_URL);
47871
+ let baseOptions;
47872
+ if (configuration) {
47873
+ baseOptions = configuration.baseOptions;
47874
+ }
47875
+ const localVarRequestOptions = { method: "POST", ...baseOptions, ...options };
47876
+ const localVarHeaderParameter = {};
47877
+ const localVarQueryParameter = {};
47878
+ await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration);
47879
+ localVarHeaderParameter["Content-Type"] = "application/json";
47880
+ localVarHeaderParameter["Accept"] = "application/json";
47881
+ setSearchParams(localVarUrlObj, localVarQueryParameter);
47882
+ let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
47883
+ localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers };
47884
+ localVarRequestOptions.data = serializeDataIfNeeded(duplicateTemplateBody, localVarRequestOptions, configuration);
47885
+ return {
47886
+ url: toPathString(localVarUrlObj),
47887
+ options: localVarRequestOptions
47888
+ };
47889
+ },
47820
47890
  v4TemplatesTemplateIdGet: async (templateId, options = {}) => {
47821
47891
  assertParamExists("v4TemplatesTemplateIdGet", "templateId", templateId);
47822
47892
  const localVarPath = `/v4/templates/{templateId}`.replace(`{${"templateId"}}`, encodeURIComponent(String(templateId)));
@@ -48030,6 +48100,12 @@ var import_debug, __require2, FREEFORM_ASSISTANT_ANSWER_KEY = "_freeform", Kadoa
48030
48100
  const localVarOperationServerBasePath = operationServerMap["TemplatesApi.v4TemplatesTemplateIdDelete"]?.[localVarOperationServerIndex]?.url;
48031
48101
  return (axios2, basePath) => createRequestFunction(localVarAxiosArgs, axios_default, BASE_PATH, configuration)(axios2, localVarOperationServerBasePath || basePath);
48032
48102
  },
48103
+ async v4TemplatesTemplateIdDuplicatePost(templateId, duplicateTemplateBody, options) {
48104
+ const localVarAxiosArgs = await localVarAxiosParamCreator.v4TemplatesTemplateIdDuplicatePost(templateId, duplicateTemplateBody, options);
48105
+ const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
48106
+ const localVarOperationServerBasePath = operationServerMap["TemplatesApi.v4TemplatesTemplateIdDuplicatePost"]?.[localVarOperationServerIndex]?.url;
48107
+ return (axios2, basePath) => createRequestFunction(localVarAxiosArgs, axios_default, BASE_PATH, configuration)(axios2, localVarOperationServerBasePath || basePath);
48108
+ },
48033
48109
  async v4TemplatesTemplateIdGet(templateId, options) {
48034
48110
  const localVarAxiosArgs = await localVarAxiosParamCreator.v4TemplatesTemplateIdGet(templateId, options);
48035
48111
  const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
@@ -48349,7 +48425,7 @@ var import_debug, __require2, FREEFORM_ASSISTANT_ANSWER_KEY = "_freeform", Kadoa
48349
48425
  options: localVarRequestOptions
48350
48426
  };
48351
48427
  },
48352
- v4WorkflowsGet: async (search, skip, limit, state, runState, displayState, inSupport, tags, userId, monitoring, updateInterval, scheduleType, includeDeleted, format, options = {}) => {
48428
+ v4WorkflowsGet: async (search, skip, limit, state, runState, displayState, inSupport, tags, statusFilters, userId, channelId, monitoring, updateInterval, scheduleType, templateId, includeDeleted, format, options = {}) => {
48353
48429
  const localVarPath = `/v4/workflows`;
48354
48430
  const localVarUrlObj = new URL$1(localVarPath, DUMMY_BASE_URL);
48355
48431
  let baseOptions;
@@ -48384,9 +48460,15 @@ var import_debug, __require2, FREEFORM_ASSISTANT_ANSWER_KEY = "_freeform", Kadoa
48384
48460
  if (tags) {
48385
48461
  localVarQueryParameter["tags"] = tags;
48386
48462
  }
48387
- if (userId !== undefined) {
48463
+ if (statusFilters) {
48464
+ localVarQueryParameter["statusFilters"] = statusFilters;
48465
+ }
48466
+ if (userId) {
48388
48467
  localVarQueryParameter["userId"] = userId;
48389
48468
  }
48469
+ if (channelId) {
48470
+ localVarQueryParameter["channelId"] = channelId;
48471
+ }
48390
48472
  if (monitoring !== undefined) {
48391
48473
  localVarQueryParameter["monitoring"] = monitoring;
48392
48474
  }
@@ -48396,6 +48478,9 @@ var import_debug, __require2, FREEFORM_ASSISTANT_ANSWER_KEY = "_freeform", Kadoa
48396
48478
  if (scheduleType !== undefined) {
48397
48479
  localVarQueryParameter["scheduleType"] = scheduleType;
48398
48480
  }
48481
+ if (templateId) {
48482
+ localVarQueryParameter["templateId"] = templateId;
48483
+ }
48399
48484
  if (includeDeleted !== undefined) {
48400
48485
  localVarQueryParameter["includeDeleted"] = includeDeleted;
48401
48486
  }
@@ -48745,6 +48830,30 @@ var import_debug, __require2, FREEFORM_ASSISTANT_ANSWER_KEY = "_freeform", Kadoa
48745
48830
  options: localVarRequestOptions
48746
48831
  };
48747
48832
  },
48833
+ v4WorkflowsWorkflowIdHistoryMetricsGet: async (workflowId, days, options = {}) => {
48834
+ assertParamExists("v4WorkflowsWorkflowIdHistoryMetricsGet", "workflowId", workflowId);
48835
+ const localVarPath = `/v4/workflows/{workflowId}/history/metrics`.replace(`{${"workflowId"}}`, encodeURIComponent(String(workflowId)));
48836
+ const localVarUrlObj = new URL$1(localVarPath, DUMMY_BASE_URL);
48837
+ let baseOptions;
48838
+ if (configuration) {
48839
+ baseOptions = configuration.baseOptions;
48840
+ }
48841
+ const localVarRequestOptions = { method: "GET", ...baseOptions, ...options };
48842
+ const localVarHeaderParameter = {};
48843
+ const localVarQueryParameter = {};
48844
+ await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration);
48845
+ if (days !== undefined) {
48846
+ localVarQueryParameter["days"] = days;
48847
+ }
48848
+ localVarHeaderParameter["Accept"] = "application/json";
48849
+ setSearchParams(localVarUrlObj, localVarQueryParameter);
48850
+ let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
48851
+ localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers };
48852
+ return {
48853
+ url: toPathString(localVarUrlObj),
48854
+ options: localVarRequestOptions
48855
+ };
48856
+ },
48748
48857
  v4WorkflowsWorkflowIdJobsJobIdGet: async (workflowId, jobId, options = {}) => {
48749
48858
  assertParamExists("v4WorkflowsWorkflowIdJobsJobIdGet", "workflowId", workflowId);
48750
48859
  assertParamExists("v4WorkflowsWorkflowIdJobsJobIdGet", "jobId", jobId);
@@ -48812,6 +48921,31 @@ var import_debug, __require2, FREEFORM_ASSISTANT_ANSWER_KEY = "_freeform", Kadoa
48812
48921
  options: localVarRequestOptions
48813
48922
  };
48814
48923
  },
48924
+ v4WorkflowsWorkflowIdQualityMetricsGet: async (workflowId, jobId, options = {}) => {
48925
+ assertParamExists("v4WorkflowsWorkflowIdQualityMetricsGet", "workflowId", workflowId);
48926
+ const localVarPath = `/v4/workflows/{workflowId}/quality-metrics`.replace(`{${"workflowId"}}`, encodeURIComponent(String(workflowId)));
48927
+ const localVarUrlObj = new URL$1(localVarPath, DUMMY_BASE_URL);
48928
+ let baseOptions;
48929
+ if (configuration) {
48930
+ baseOptions = configuration.baseOptions;
48931
+ }
48932
+ const localVarRequestOptions = { method: "GET", ...baseOptions, ...options };
48933
+ const localVarHeaderParameter = {};
48934
+ const localVarQueryParameter = {};
48935
+ await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration);
48936
+ await setBearerAuthToObject(localVarHeaderParameter, configuration);
48937
+ if (jobId !== undefined) {
48938
+ localVarQueryParameter["jobId"] = jobId;
48939
+ }
48940
+ localVarHeaderParameter["Accept"] = "application/json";
48941
+ setSearchParams(localVarUrlObj, localVarQueryParameter);
48942
+ let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
48943
+ localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers };
48944
+ return {
48945
+ url: toPathString(localVarUrlObj),
48946
+ options: localVarRequestOptions
48947
+ };
48948
+ },
48815
48949
  v4WorkflowsWorkflowIdResumePut: async (workflowId, options = {}) => {
48816
48950
  assertParamExists("v4WorkflowsWorkflowIdResumePut", "workflowId", workflowId);
48817
48951
  const localVarPath = `/v4/workflows/{workflowId}/resume`.replace(`{${"workflowId"}}`, encodeURIComponent(String(workflowId)));
@@ -49033,8 +49167,8 @@ var import_debug, __require2, FREEFORM_ASSISTANT_ANSWER_KEY = "_freeform", Kadoa
49033
49167
  const localVarOperationServerBasePath = operationServerMap["WorkflowsApi.v4WorkflowsBulkPost"]?.[localVarOperationServerIndex]?.url;
49034
49168
  return (axios2, basePath) => createRequestFunction(localVarAxiosArgs, axios_default, BASE_PATH, configuration)(axios2, localVarOperationServerBasePath || basePath);
49035
49169
  },
49036
- async v4WorkflowsGet(search, skip, limit, state, runState, displayState, inSupport, tags, userId, monitoring, updateInterval, scheduleType, includeDeleted, format, options) {
49037
- const localVarAxiosArgs = await localVarAxiosParamCreator.v4WorkflowsGet(search, skip, limit, state, runState, displayState, inSupport, tags, userId, monitoring, updateInterval, scheduleType, includeDeleted, format, options);
49170
+ async v4WorkflowsGet(search, skip, limit, state, runState, displayState, inSupport, tags, statusFilters, userId, channelId, monitoring, updateInterval, scheduleType, templateId, includeDeleted, format, options) {
49171
+ const localVarAxiosArgs = await localVarAxiosParamCreator.v4WorkflowsGet(search, skip, limit, state, runState, displayState, inSupport, tags, statusFilters, userId, channelId, monitoring, updateInterval, scheduleType, templateId, includeDeleted, format, options);
49038
49172
  const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
49039
49173
  const localVarOperationServerBasePath = operationServerMap["WorkflowsApi.v4WorkflowsGet"]?.[localVarOperationServerIndex]?.url;
49040
49174
  return (axios2, basePath) => createRequestFunction(localVarAxiosArgs, axios_default, BASE_PATH, configuration)(axios2, localVarOperationServerBasePath || basePath);
@@ -49105,6 +49239,12 @@ var import_debug, __require2, FREEFORM_ASSISTANT_ANSWER_KEY = "_freeform", Kadoa
49105
49239
  const localVarOperationServerBasePath = operationServerMap["WorkflowsApi.v4WorkflowsWorkflowIdHistoryGet"]?.[localVarOperationServerIndex]?.url;
49106
49240
  return (axios2, basePath) => createRequestFunction(localVarAxiosArgs, axios_default, BASE_PATH, configuration)(axios2, localVarOperationServerBasePath || basePath);
49107
49241
  },
49242
+ async v4WorkflowsWorkflowIdHistoryMetricsGet(workflowId, days, options) {
49243
+ const localVarAxiosArgs = await localVarAxiosParamCreator.v4WorkflowsWorkflowIdHistoryMetricsGet(workflowId, days, options);
49244
+ const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
49245
+ const localVarOperationServerBasePath = operationServerMap["WorkflowsApi.v4WorkflowsWorkflowIdHistoryMetricsGet"]?.[localVarOperationServerIndex]?.url;
49246
+ return (axios2, basePath) => createRequestFunction(localVarAxiosArgs, axios_default, BASE_PATH, configuration)(axios2, localVarOperationServerBasePath || basePath);
49247
+ },
49108
49248
  async v4WorkflowsWorkflowIdJobsJobIdGet(workflowId, jobId, options) {
49109
49249
  const localVarAxiosArgs = await localVarAxiosParamCreator.v4WorkflowsWorkflowIdJobsJobIdGet(workflowId, jobId, options);
49110
49250
  const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
@@ -49123,6 +49263,12 @@ var import_debug, __require2, FREEFORM_ASSISTANT_ANSWER_KEY = "_freeform", Kadoa
49123
49263
  const localVarOperationServerBasePath = operationServerMap["WorkflowsApi.v4WorkflowsWorkflowIdPausePut"]?.[localVarOperationServerIndex]?.url;
49124
49264
  return (axios2, basePath) => createRequestFunction(localVarAxiosArgs, axios_default, BASE_PATH, configuration)(axios2, localVarOperationServerBasePath || basePath);
49125
49265
  },
49266
+ async v4WorkflowsWorkflowIdQualityMetricsGet(workflowId, jobId, options) {
49267
+ const localVarAxiosArgs = await localVarAxiosParamCreator.v4WorkflowsWorkflowIdQualityMetricsGet(workflowId, jobId, options);
49268
+ const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
49269
+ const localVarOperationServerBasePath = operationServerMap["WorkflowsApi.v4WorkflowsWorkflowIdQualityMetricsGet"]?.[localVarOperationServerIndex]?.url;
49270
+ return (axios2, basePath) => createRequestFunction(localVarAxiosArgs, axios_default, BASE_PATH, configuration)(axios2, localVarOperationServerBasePath || basePath);
49271
+ },
49126
49272
  async v4WorkflowsWorkflowIdResumePut(workflowId, options) {
49127
49273
  const localVarAxiosArgs = await localVarAxiosParamCreator.v4WorkflowsWorkflowIdResumePut(workflowId, options);
49128
49274
  const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
@@ -50276,7 +50422,7 @@ var import_debug, __require2, FREEFORM_ASSISTANT_ANSWER_KEY = "_freeform", Kadoa
50276
50422
  }));
50277
50423
  return channels;
50278
50424
  }
50279
- }, PUBLIC_API_URI, WSS_API_URI, REALTIME_API_URI, SDK_VERSION = "0.37.1", 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 {
50425
+ }, PUBLIC_API_URI, WSS_API_URI, REALTIME_API_URI, SDK_VERSION = "0.38.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 {
50280
50426
  constructor(config2) {
50281
50427
  this.drainingSockets = /* @__PURE__ */ new Set;
50282
50428
  this.lastHeartbeat = Date.now();
@@ -51181,7 +51327,17 @@ var import_debug, __require2, FREEFORM_ASSISTANT_ANSWER_KEY = "_freeform", Kadoa
51181
51327
  return response.data;
51182
51328
  }
51183
51329
  async list(filters) {
51184
- const response = await this.workflowsApi.v4WorkflowsGet(filters);
51330
+ if (filters == null) {
51331
+ const response2 = await this.workflowsApi.v4WorkflowsGet();
51332
+ return response2.data?.workflows ?? [];
51333
+ }
51334
+ const { templateId, ...rest2 } = filters;
51335
+ const response = await this.workflowsApi.v4WorkflowsGet({
51336
+ ...rest2,
51337
+ ...templateId != null && {
51338
+ templateId: Array.isArray(templateId) ? templateId : [templateId]
51339
+ }
51340
+ });
51185
51341
  return response.data?.workflows ?? [];
51186
51342
  }
51187
51343
  async getByName(name) {
@@ -51693,6 +51849,9 @@ var init_dist2 = __esm(() => {
51693
51849
  v5AgentPauseState(requestParameters, options) {
51694
51850
  return AgentApiFp(this.configuration).v5AgentPauseState(requestParameters.sessionId, options).then((request) => request(this.axios, this.basePath));
51695
51851
  }
51852
+ v5AgentPrompt(requestParameters, options) {
51853
+ return AgentApiFp(this.configuration).v5AgentPrompt(requestParameters.agentPromptRequest, options).then((request) => request(this.axios, this.basePath));
51854
+ }
51696
51855
  v5AgentResume(requestParameters, options) {
51697
51856
  return AgentApiFp(this.configuration).v5AgentResume(requestParameters.sessionId, options).then((request) => request(this.axios, this.basePath));
51698
51857
  }
@@ -51907,6 +52066,9 @@ var init_dist2 = __esm(() => {
51907
52066
  v4TemplatesTemplateIdDelete(requestParameters, options) {
51908
52067
  return TemplatesApiFp(this.configuration).v4TemplatesTemplateIdDelete(requestParameters.templateId, options).then((request) => request(this.axios, this.basePath));
51909
52068
  }
52069
+ v4TemplatesTemplateIdDuplicatePost(requestParameters, options) {
52070
+ return TemplatesApiFp(this.configuration).v4TemplatesTemplateIdDuplicatePost(requestParameters.templateId, requestParameters.duplicateTemplateBody, options).then((request) => request(this.axios, this.basePath));
52071
+ }
51910
52072
  v4TemplatesTemplateIdGet(requestParameters, options) {
51911
52073
  return TemplatesApiFp(this.configuration).v4TemplatesTemplateIdGet(requestParameters.templateId, options).then((request) => request(this.axios, this.basePath));
51912
52074
  }
@@ -51963,7 +52125,7 @@ var init_dist2 = __esm(() => {
51963
52125
  return WorkflowsApiFp(this.configuration).v4WorkflowsBulkPost(requestParameters.v4WorkflowsBulkPostRequest, options).then((request) => request(this.axios, this.basePath));
51964
52126
  }
51965
52127
  v4WorkflowsGet(requestParameters = {}, options) {
51966
- return WorkflowsApiFp(this.configuration).v4WorkflowsGet(requestParameters.search, requestParameters.skip, requestParameters.limit, requestParameters.state, requestParameters.runState, requestParameters.displayState, requestParameters.inSupport, requestParameters.tags, requestParameters.userId, requestParameters.monitoring, requestParameters.updateInterval, requestParameters.scheduleType, requestParameters.includeDeleted, requestParameters.format, options).then((request) => request(this.axios, this.basePath));
52128
+ return WorkflowsApiFp(this.configuration).v4WorkflowsGet(requestParameters.search, requestParameters.skip, requestParameters.limit, requestParameters.state, requestParameters.runState, requestParameters.displayState, requestParameters.inSupport, requestParameters.tags, requestParameters.statusFilters, requestParameters.userId, requestParameters.channelId, requestParameters.monitoring, requestParameters.updateInterval, requestParameters.scheduleType, requestParameters.templateId, requestParameters.includeDeleted, requestParameters.format, options).then((request) => request(this.axios, this.basePath));
51967
52129
  }
51968
52130
  v4WorkflowsPost(requestParameters = {}, options) {
51969
52131
  return WorkflowsApiFp(this.configuration).v4WorkflowsPost(requestParameters.publicWorkflowCreateRequest, options).then((request) => request(this.axios, this.basePath));
@@ -51998,6 +52160,9 @@ var init_dist2 = __esm(() => {
51998
52160
  v4WorkflowsWorkflowIdHistoryGet(requestParameters, options) {
51999
52161
  return WorkflowsApiFp(this.configuration).v4WorkflowsWorkflowIdHistoryGet(requestParameters.workflowId, requestParameters.page, requestParameters.limit, requestParameters.status, options).then((request) => request(this.axios, this.basePath));
52000
52162
  }
52163
+ v4WorkflowsWorkflowIdHistoryMetricsGet(requestParameters, options) {
52164
+ return WorkflowsApiFp(this.configuration).v4WorkflowsWorkflowIdHistoryMetricsGet(requestParameters.workflowId, requestParameters.days, options).then((request) => request(this.axios, this.basePath));
52165
+ }
52001
52166
  v4WorkflowsWorkflowIdJobsJobIdGet(requestParameters, options) {
52002
52167
  return WorkflowsApiFp(this.configuration).v4WorkflowsWorkflowIdJobsJobIdGet(requestParameters.workflowId, requestParameters.jobId, options).then((request) => request(this.axios, this.basePath));
52003
52168
  }
@@ -52007,6 +52172,9 @@ var init_dist2 = __esm(() => {
52007
52172
  v4WorkflowsWorkflowIdPausePut(requestParameters, options) {
52008
52173
  return WorkflowsApiFp(this.configuration).v4WorkflowsWorkflowIdPausePut(requestParameters.workflowId, options).then((request) => request(this.axios, this.basePath));
52009
52174
  }
52175
+ v4WorkflowsWorkflowIdQualityMetricsGet(requestParameters, options) {
52176
+ return WorkflowsApiFp(this.configuration).v4WorkflowsWorkflowIdQualityMetricsGet(requestParameters.workflowId, requestParameters.jobId, options).then((request) => request(this.axios, this.basePath));
52177
+ }
52010
52178
  v4WorkflowsWorkflowIdResumePut(requestParameters, options) {
52011
52179
  return WorkflowsApiFp(this.configuration).v4WorkflowsWorkflowIdResumePut(requestParameters.workflowId, options).then((request) => request(this.axios, this.basePath));
52012
52180
  }
@@ -52531,7 +52699,7 @@ function extractApiMessage(responseBody) {
52531
52699
  if (body.validationErrors && typeof body.validationErrors === "object" && body.validationErrors !== null) {
52532
52700
  const details = Object.entries(body.validationErrors).map(([field, err]) => `${field}: "${err}"`).join(", ");
52533
52701
  if (details) {
52534
- msg = msg ? `${msg} Details: ${details}` : `Validation failed Details: ${details}`;
52702
+ msg = msg ? `${msg} - Details: ${details}` : `Validation failed - Details: ${details}`;
52535
52703
  }
52536
52704
  }
52537
52705
  if (!msg && Array.isArray(body.issues)) {
@@ -52541,7 +52709,7 @@ function extractApiMessage(responseBody) {
52541
52709
  return path ? `${path}: "${message}"` : `"${message}"`;
52542
52710
  }).join(", ");
52543
52711
  if (details)
52544
- msg = `Validation failed Details: ${details}`;
52712
+ msg = `Validation failed - Details: ${details}`;
52545
52713
  }
52546
52714
  return msg;
52547
52715
  }
@@ -52727,14 +52895,14 @@ function registerTools(server, ctx, capabilities) {
52727
52895
  });
52728
52896
  }));
52729
52897
  const urlInputShape = {
52730
- url: exports_external.string().optional().describe("Single URL prefer using 'urls' instead. If both are provided, 'urls' takes precedence."),
52898
+ url: exports_external.string().optional().describe("Single URL - prefer using 'urls' instead. If both are provided, 'urls' takes precedence."),
52731
52899
  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.")
52732
52900
  };
52733
52901
  const extractionInputShape = {
52734
52902
  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.'),
52735
52903
  name: exports_external.string().optional().describe("Optional name for the workflow"),
52736
52904
  entity: exports_external.string().optional().describe("Entity name for extraction (e.g., 'Product', 'Job Posting')"),
52737
- 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.")
52905
+ 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.")
52738
52906
  };
52739
52907
  const webhookAuthShape = exports_external.object({
52740
52908
  type: exports_external.enum(["bearer", "basic", "header"]).describe("Authentication type"),
@@ -52765,6 +52933,33 @@ function registerTools(server, ctx, capabilities) {
52765
52933
  const urls = args.urls ?? (args.url ? [args.url] : []);
52766
52934
  return urls.length > 0 ? urls : null;
52767
52935
  }
52936
+ function buildRealtimeAssistantInstructions(args, urls) {
52937
+ const lines = ["Create a realtime monitoring workflow with Julie.", "", "Source URLs:"];
52938
+ for (const url3 of urls)
52939
+ lines.push(`- ${url3}`);
52940
+ if (args.prompt)
52941
+ lines.push("", "User prompt:", args.prompt);
52942
+ if (args.name)
52943
+ lines.push("", `Requested workflow name: ${args.name}`);
52944
+ if (args.description)
52945
+ lines.push(`Requested workflow description: ${args.description}`);
52946
+ if (args.entity)
52947
+ lines.push("", `Entity to monitor: ${args.entity}`);
52948
+ if (args.schema?.length) {
52949
+ lines.push("", "Schema fields to preserve:");
52950
+ for (const field of args.schema) {
52951
+ lines.push(`- ${field.name}`);
52952
+ lines.push(` Data type: ${field.dataType || "STRING"}`);
52953
+ lines.push(` Key field: ${field.isKey ? "yes" : "no"}`);
52954
+ if (field.description)
52955
+ lines.push(` Description: ${field.description}`);
52956
+ lines.push(` Example: ${field.example}`);
52957
+ }
52958
+ }
52959
+ lines.push("", "Monitor continuously for data changes and preserve these requirements while building the workflow.");
52960
+ return lines.join(`
52961
+ `);
52962
+ }
52768
52963
  function buildExtraction(args) {
52769
52964
  if (!args.schema)
52770
52965
  return;
@@ -52835,15 +53030,15 @@ function registerTools(server, ctx, capabilities) {
52835
53030
  return channels;
52836
53031
  }
52837
53032
  server.registerTool("create_workflow", {
52838
- description: "IMPORTANT: One workflow = one source. A single workflow can extract many different fields, tables, and sections from the same URL(s) using a rich schema. " + `Do NOT create separate workflows for different data points on the same page instead, define one workflow with a multi-field schema covering everything needed.
53033
+ description: "IMPORTANT: One workflow = one source. A single workflow can extract many different fields, tables, and sections from the same URL(s) using a rich schema. " + `Do NOT create separate workflows for different data points on the same page - instead, define one workflow with a multi-field schema covering everything needed.
52839
53034
 
52840
53035
  ` + "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.
52841
53036
 
52842
- ` + "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.",
53037
+ ` + "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 creating from a template, call this tool with the template's `templateId`, the source `urls`, and optional `templateVersion` only. Never copy the template prompt, entity, or schema into standalone creation, and never silently match or rewrite inline configuration.\n\n" + "NOTE: This tool is for one-time or scheduled extraction ONLY. " + "For continuous Julie realtime monitoring (watching a page for data changes and alerting), use the create_realtime_monitor tool instead.",
52843
53038
  inputSchema: strictSchema({
52844
53039
  ...extractionInputShape,
52845
53040
  ...urlInputShape,
52846
- 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, and the workflow's output conforms to the template's declared schema (field names are enforced, not drifted). Discover templates via list_templates."),
53041
+ templateId: exports_external.string().optional().describe("Instantiate this workflow from a published template. Pass the templateId, source urls, and optional templateVersion. Do not copy prompt/entity/schema from the template into this call - they are inherited from the published template version, and the workflow's output conforms to its declared schema. Discover templates via list_templates and get_template."),
52847
53042
  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."),
52848
53043
  description: exports_external.string().max(500).optional().describe("Description of what this workflow does (max 500 characters)"),
52849
53044
  tags: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string())).optional().describe("Tags for organizing workflows"),
@@ -52884,7 +53079,7 @@ function registerTools(server, ctx, capabilities) {
52884
53079
  let workflowId;
52885
53080
  if (args.templateId) {
52886
53081
  if (args.prompt || args.entity || args.schema) {
52887
- return errorResult("When 'templateId' is set, 'prompt', 'entity', and 'schema' must NOT be supplied they are inherited from the template version.");
53082
+ return errorResult("When 'templateId' is set, 'prompt', 'entity', and 'schema' must NOT be supplied - they are inherited from the template version.");
52888
53083
  }
52889
53084
  const { id } = await ctx.client.workflow.create({
52890
53085
  urls,
@@ -52950,9 +53145,9 @@ function registerTools(server, ctx, capabilities) {
52950
53145
  });
52951
53146
  }));
52952
53147
  server.registerTool("create_realtime_monitor", {
52953
- description: "Create a real-time monitoring workflow that continuously watches a page for changes and sends alerts. " + "Use this when the user wants to monitor, track, or watch a page in real-time. " + "At least one notification channel (email, webhook, slack, or websocket) is required so the user gets alerted on changes. " + `Real-time monitors route to a separate Observer service and CANNOT be converted to/from regular extraction workflows after creation.
53148
+ description: "Create a Julie realtime monitoring workflow that continuously watches a page for data changes and sends alerts. " + "Use this when the user wants to monitor, track, or watch a page in realtime. " + "At least one notification channel (email, webhook, slack, or websocket) is required; notification settings are persisted before Julie is dispatched. " + `Realtime monitors CANNOT be converted to/from regular extraction workflows after creation.
52954
53149
 
52955
- ` + "If entity and schema are provided, they guide what to monitor; otherwise the AI agent auto-detects what to watch. " + "Do NOT poll or sleep-wait for completion. Return the workflow ID and let the user check back with get_workflow or fetch_data.",
53150
+ ` + "If entity and schema are provided, they guide what Julie should monitor; otherwise Julie auto-detects what to watch. " + "Creation is asynchronous and the response includes workflow, session, thread, and job IDs plus a dashboard URL. " + "Use generic workflow Assistant tools (get_workflow_assistant, answer_workflow_assistant_question, interrupt_workflow_assistant, resume_workflow_assistant, stop_workflow_assistant) for follow-up status, questions, and controls. " + "Do NOT poll or sleep-wait for completion.",
52956
53151
  inputSchema: strictSchema({
52957
53152
  ...extractionInputShape,
52958
53153
  ...urlInputShape,
@@ -52971,33 +53166,33 @@ function registerTools(server, ctx, capabilities) {
52971
53166
  if (!hasNotifications) {
52972
53167
  return errorResult("Real-time monitors require at least one notification channel so you get alerted when data changes. " + "Add a notifications object with one or more channels: email, webhook, slack, or websocket.");
52973
53168
  }
52974
- let builder = ctx.client.extract({
52975
- urls,
52976
- name: args.name || "Untitled Monitor",
52977
- userPrompt: args.prompt,
52978
- extraction: buildExtraction(args),
52979
- interval: "REAL_TIME",
52980
- description: args.description
52981
- });
52982
- builder = builder.withNotifications({
52983
- events: ["workflow_data_change"],
53169
+ const channels = await ctx.client.notification.setup.setupChannels({
52984
53170
  channels: buildNotificationChannels(n)
52985
53171
  });
52986
- const workflow = await builder.create();
52987
- if (args.tags && args.tags.length > 0) {
52988
- await ctx.client.workflow.update(workflow.workflowId, { tags: args.tags });
53172
+ const notificationChannelIds = channels.map((channel) => channel.id).filter((id) => typeof id === "string" && id.length > 0);
53173
+ if (notificationChannelIds.length === 0) {
53174
+ return errorResult("No usable notification channels were configured for the realtime monitor.");
52989
53175
  }
52990
- const enabledChannels = await describeNotifications(n);
52991
- let message = "Real-time monitor created successfully. It will continuously watch for changes — no manual runs needed.";
52992
- if (enabledChannels.length > 0) {
52993
- message += ` Notifications on data changes via: ${enabledChannels.join(", ")}.`;
53176
+ const result = await ctx.client.assistant.createRealtimeWorkflow({
53177
+ instructions: buildRealtimeAssistantInstructions(args, urls),
53178
+ notificationChannelIds,
53179
+ ...args.tags?.length ? { tags: args.tags } : {}
53180
+ });
53181
+ if (!result || typeof result.workflowId !== "string" || result.workflowId.length === 0 || typeof result.sessionId !== "string" || result.sessionId.length === 0 || typeof result.threadId !== "string" || result.threadId.length === 0 || typeof result.jobId !== "string" || result.jobId.length === 0) {
53182
+ throw new KadoaSdkException("Realtime Assistant creation response is missing required identifiers", {
53183
+ code: "INTERNAL_ERROR",
53184
+ details: { response: result }
53185
+ });
52994
53186
  }
52995
- message += " Use fetch_data to retrieve the latest data.";
52996
53187
  return jsonResult({
52997
53188
  success: true,
52998
- workflowId: workflow.workflowId,
52999
- dashboardUrl: workflowDashboardUrl(workflow.workflowId),
53000
- message
53189
+ status: "accepted",
53190
+ workflowId: result.workflowId,
53191
+ sessionId: result.sessionId,
53192
+ threadId: result.threadId,
53193
+ jobId: result.jobId,
53194
+ dashboardUrl: workflowDashboardUrl(result.workflowId),
53195
+ message: "Julie realtime workflow creation accepted. Notification settings were persisted before Julie dispatch. Creation is asynchronous; use get_workflow_assistant for status, questions, or controls, and do not poll or sleep-wait."
53001
53196
  });
53002
53197
  }));
53003
53198
  server.registerTool("list_workflows", {
@@ -53040,7 +53235,7 @@ function registerTools(server, ctx, capabilities) {
53040
53235
  });
53041
53236
  }));
53042
53237
  server.registerTool("get_workflow", {
53043
- description: "Get canonical workflow details, including Assistant/session linkage, extraction intent, template-controlled parts, stale-data state, job/run state, schedule timezone, location, monitoring, validation, and realtime health. For Assistant-built workflows, extractionSpecnot the legacy promptis authoritative.",
53238
+ description: "Get canonical workflow details, including Assistant/session linkage, extraction intent, template-controlled parts, stale-data state, job/run state, schedule timezone, location, monitoring, validation, and realtime health. For Assistant-built workflows, extractionSpec - not the legacy prompt - is authoritative.",
53044
53239
  inputSchema: {
53045
53240
  workflowId: exports_external.string().describe("The workflow ID")
53046
53241
  },
@@ -53404,7 +53599,7 @@ function registerTools(server, ctx, capabilities) {
53404
53599
  });
53405
53600
  }));
53406
53601
  server.registerTool("list_workflow_runs", {
53407
- 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.",
53602
+ 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.",
53408
53603
  inputSchema: {
53409
53604
  workflowId: exports_external.string().describe("The workflow ID"),
53410
53605
  status: exports_external.enum(["success", "failed", "in_progress"]).optional().describe("Filter runs by outcome"),
@@ -53433,7 +53628,7 @@ function registerTools(server, ctx, capabilities) {
53433
53628
  });
53434
53629
  }));
53435
53630
  server.registerTool("run_workflow", {
53436
- 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.",
53631
+ description: "Run a workflow to extract fresh data, including retrying a failed one-time or scheduled workflow. Preserve the existing workflow ID and configuration - do not delete and recreate the workflow solely to retry it. 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. Realtime workflows cannot be manually run.",
53437
53632
  inputSchema: {
53438
53633
  workflowId: exports_external.string().describe("The workflow ID to run"),
53439
53634
  limit: exports_external.preprocess(coerceNumber(), exports_external.number()).optional().describe("Maximum number of records to extract (default: 1000)")
@@ -53456,7 +53651,7 @@ function registerTools(server, ctx, capabilities) {
53456
53651
  const FETCH_DATA_DEFAULT_LIMIT = 50;
53457
53652
  const FETCH_DATA_MAX_LIMIT = 500;
53458
53653
  server.registerTool("fetch_data", {
53459
- description: "Get a PAGE of extracted data from a workflow. Use ONLY for previews, sorted/filtered slices, or explicit 'first N rows' / 'top N' queries (capped at 500 rows per call). Do NOT use this to retrieve a full dataset, 'all rows', or anything the user wants to analyze in Excel / pandas / duckdb use export_data for those. Data is only available after the workflow run has completed (status is no longer 'Running' or 'Validating'). Do NOT poll or sleep-wait for completion.",
53654
+ description: "Get a PAGE of extracted data from a workflow. Use ONLY for previews, sorted/filtered slices, or explicit 'first N rows' / 'top N' queries (capped at 500 rows per call). Do NOT use this to retrieve a full dataset, 'all rows', or anything the user wants to analyze in Excel / pandas / duckdb - use export_data for those. Data is only available after the workflow run has completed (status is no longer 'Running' or 'Validating'). Do NOT poll or sleep-wait for completion.",
53460
53655
  inputSchema: {
53461
53656
  workflowId: exports_external.string().describe("The workflow ID"),
53462
53657
  limit: exports_external.preprocess(coerceNumber(), exports_external.number()).optional().describe(`Maximum number of records to return. Default ${FETCH_DATA_DEFAULT_LIMIT}, max ${FETCH_DATA_MAX_LIMIT}.`),
@@ -53491,7 +53686,7 @@ function registerTools(server, ctx, capabilities) {
53491
53686
  return jsonResult(result);
53492
53687
  }));
53493
53688
  server.registerTool("export_data", {
53494
- description: "PREFERRED tool for retrieving a workflow's FULL dataset. Materializes the data to object storage and returns a signed download URL. Use this whenever the user wants 'all rows', 'the full dataset', 'everything', an export, or anything destined for Excel / pandas / duckdb / a CSV file even for small workflows. The URL is self-authenticating (open with `fetch(url)`, no Authorization header). Use fetch_data ONLY when the user explicitly asks for a small preview slice (e.g., 'first 10', 'top N sorted by X').",
53689
+ description: "PREFERRED tool for retrieving a workflow's FULL dataset. Materializes the data to object storage and returns a signed download URL. Use this whenever the user wants 'all rows', 'the full dataset', 'everything', an export, or anything destined for Excel / pandas / duckdb / a CSV file - even for small workflows. The URL is self-authenticating (open with `fetch(url)`, no Authorization header). Use fetch_data ONLY when the user explicitly asks for a small preview slice (e.g., 'first 10', 'top N sorted by X').",
53495
53690
  inputSchema: {
53496
53691
  workflowId: exports_external.string().describe("The workflow ID"),
53497
53692
  format: exports_external.enum(["csv", "json"]).optional().describe("Export format. Default 'csv'."),
@@ -53523,7 +53718,7 @@ function registerTools(server, ctx, capabilities) {
53523
53718
  });
53524
53719
  }));
53525
53720
  server.registerTool("list_changes", {
53526
- description: "List detected data changes across one or more real-time monitoring workflows. " + "Returns structured diffs showing added, removed, and changed records. " + "Only works for workflows with real-time monitoring enabled.",
53721
+ description: "List detected data changes across one or more Julie realtime monitoring workflows. " + "Returns structured diffs showing added, removed, and changed records. " + "Only works for workflows with realtime monitoring enabled.",
53527
53722
  inputSchema: {
53528
53723
  workflowIds: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string())).optional().describe("Workflow IDs to filter by. If omitted, returns changes for all ACTIVE monitoring workflows."),
53529
53724
  startDate: exports_external.string().optional().describe("Start date filter (ISO format, e.g. 2025-01-01)"),
@@ -53617,7 +53812,7 @@ function registerTools(server, ctx, capabilities) {
53617
53812
  });
53618
53813
  }));
53619
53814
  server.registerTool("pause_workflow", {
53620
- description: "Pause an ACTIVE workflow so it stops running on its schedule. Requires the workflow to be in ACTIVE state pausing a workflow that is currently running, already paused, or in PREVIEW will return an error. Use approve_workflow to resume a paused workflow.",
53815
+ description: "Pause an ACTIVE workflow so it stops running on its schedule. Requires the workflow to be in ACTIVE state - pausing a workflow that is currently running, already paused, or in PREVIEW will return an error. Use approve_workflow to resume a paused workflow.",
53621
53816
  inputSchema: {
53622
53817
  workflowId: exports_external.string().min(1).describe("The workflow ID to pause")
53623
53818
  },
@@ -53631,17 +53826,17 @@ function registerTools(server, ctx, capabilities) {
53631
53826
  });
53632
53827
  }));
53633
53828
  server.registerTool("update_workflow", {
53634
- 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.
53829
+ 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.
53635
53830
 
53636
- ` + `IMPORTANT: You cannot change a workflow's interval to or from REAL_TIME. Real-time workflows are architecturally different from scheduled workflows and must be created with create_realtime_monitor from the start. Existing workflows cannot be converted between these modes in place; do not delete and recreate a workflow as an update workaround.
53831
+ ` + `IMPORTANT: You cannot change a workflow's interval to or from REAL_TIME. Julie realtime workflows are architecturally different from scheduled workflows and must be created with create_realtime_monitor from the start. Existing workflows cannot be converted between these modes in place; do not delete and recreate a workflow as an update workaround.
53637
53832
 
53638
- ` + "ASSISTANT-OWNED INTENT: Use request_workflow_updatenot `userPrompt`—for agent-built workflow changes to extraction intent, navigation, pagination, data sourcing, repair, or generated scripts. `userPrompt` may be rejected with `SHELLY_INTENT_REQUIRES_EXTRACTION_SPEC` because those workflows' canonical intent is owned by the Assistant. " + "Call get_workflow first. If its template.controlledParts contains the setting, create and apply a template version instead of overriding the workflow directly. NEVER delete and recreate a workflow to work around an update limitationthat changes workflowId, breaks downstream tables/connectors, and discards history.",
53833
+ ` + "ASSISTANT-OWNED INTENT: Use request_workflow_update - not `userPrompt` - for agent-built workflow changes to extraction intent, navigation, pagination, data sourcing, repair, or generated scripts. `userPrompt` may be rejected with `SHELLY_INTENT_REQUIRES_EXTRACTION_SPEC` because those workflows' canonical intent is owned by the Assistant. " + "Call get_workflow first. If its template.controlledParts contains the setting, create and apply a template version instead of overriding the workflow directly. NEVER delete and recreate a workflow to work around an update limitation - that changes workflowId, breaks downstream tables/connectors, and discards history.",
53639
53834
  inputSchema: strictSchema({
53640
53835
  workflowId: exports_external.string().describe("The workflow ID to update"),
53641
53836
  name: exports_external.string().optional().describe("New name for the workflow"),
53642
53837
  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."),
53643
53838
  entity: exports_external.string().optional().describe("Entity name for extraction (e.g., 'Product', 'Job Posting')"),
53644
- 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."),
53839
+ 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."),
53645
53840
  description: exports_external.string().max(500).optional().describe("Workflow description (max 500 characters)"),
53646
53841
  tags: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string())).optional().describe("Tags for organizing workflows"),
53647
53842
  userPrompt: exports_external.string().optional().describe("Navigation prompt for agentic-navigation mode (10-5000 characters)"),
@@ -53663,7 +53858,7 @@ function registerTools(server, ctx, capabilities) {
53663
53858
  "FOUR_WEEKS",
53664
53859
  "MONTHLY",
53665
53860
  "CUSTOM"
53666
- ]).optional().describe("How often the workflow should run. CUSTOM requires schedules. Note: REAL_TIME is NOT allowed here — real-time workflows must be created via create_workflow."),
53861
+ ]).optional().describe("How often the workflow should run. CUSTOM requires schedules. Note: REAL_TIME is NOT allowed here - realtime workflows must be created via create_realtime_monitor."),
53667
53862
  schedules: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string())).optional().describe("Cron expressions for CUSTOM update interval"),
53668
53863
  timezone: exports_external.string().min(1).optional().describe("IANA timezone for cron schedules, for example America/New_York. A timezone-only update preserves existing cron expressions."),
53669
53864
  location: exports_external.preprocess(coerceJson(), LocationSchema).optional().describe("Scraping location: {type:'auto'} or {type:'manual', isoCode:'US'}"),
@@ -53678,10 +53873,10 @@ function registerTools(server, ctx, capabilities) {
53678
53873
  const isCurrentlyRealTime = isRealTimeInterval(workflow.updateInterval);
53679
53874
  const isRequestingRealTime = isRealTimeInterval(updates.updateInterval);
53680
53875
  if (isRequestingRealTime && !isCurrentlyRealTime) {
53681
- return errorResult("Cannot change a regular workflow's interval to REAL_TIME. " + "Real-time workflows use a different service and cannot replace this workflow in place. " + "The existing workflow was left unchanged.");
53876
+ return errorResult("Cannot change a regular workflow's interval to REAL_TIME. " + "Julie realtime workflows cannot replace this workflow in place. " + "The existing workflow was left unchanged.");
53682
53877
  }
53683
53878
  if (!isRequestingRealTime && isCurrentlyRealTime) {
53684
- return errorResult("Cannot change a real-time workflow's interval to a scheduled interval. " + "Real-time workflows are architecturally different and cannot be converted in place. " + "The existing workflow was left unchanged.");
53879
+ return errorResult("Cannot change a Julie realtime workflow's interval to a scheduled interval. " + "Julie realtime workflows are architecturally different and cannot be converted in place. " + "The existing workflow was left unchanged.");
53685
53880
  }
53686
53881
  }
53687
53882
  if (updates.updateInterval === "CUSTOM" && (!updates.schedules || updates.schedules.length === 0)) {
@@ -54082,22 +54277,29 @@ function registerTools(server, ctx, capabilities) {
54082
54277
  });
54083
54278
  }));
54084
54279
  server.registerTool("list_templates", {
54085
- description: "List all templates in the current team. Templates define reusable configurations (prompt, schema, notifications).",
54280
+ description: "List all templates in the current team. Templates define reusable configurations (prompt, schema, validation rules, notifications, and frequency). Use this to find a matching template before creating a workflow. To instantiate a template, call create_workflow with its templateId, source URLs, and optional templateVersion - never copy the template prompt, entity, or schema into standalone creation.",
54086
54281
  inputSchema: strictSchema({}),
54087
54282
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true }
54088
54283
  }, withErrorHandling("list_templates", async () => {
54089
54284
  const templates = await ctx.client.template.list();
54090
- return jsonResult({ templates, count: templates.length });
54285
+ return jsonResult({
54286
+ templates,
54287
+ count: templates.length,
54288
+ instructions: "To instantiate a listed template, call create_workflow with the matching templateId, source URLs, and optional templateVersion. Do not copy the template prompt, entity, or schema into standalone creation."
54289
+ });
54091
54290
  }));
54092
54291
  server.registerTool("get_template", {
54093
- description: "Get a template by ID, including all published versions.",
54292
+ description: "Get a template by ID, including all published versions and their schemas. After inspecting it, instantiate the template with create_workflow using templateId, source URLs, and optional templateVersion. Never copy the returned prompt, entity, or schema into standalone creation - those values are inherited when templateId is passed.",
54094
54293
  inputSchema: strictSchema({
54095
54294
  templateId: exports_external.string().describe("The template ID")
54096
54295
  }),
54097
54296
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true }
54098
54297
  }, withErrorHandling("get_template", async (args) => {
54099
54298
  const template = await ctx.client.template.get(args.templateId);
54100
- return jsonResult({ template });
54299
+ return jsonResult({
54300
+ template,
54301
+ instructions: `To instantiate this template, call create_workflow with templateId "${args.templateId}", source URLs, and optional templateVersion. Do not copy this template's prompt, entity, or schema into standalone creation.`
54302
+ });
54101
54303
  }));
54102
54304
  server.registerTool("create_template", {
54103
54305
  description: "Create a new template. After creation, use create_template_version to publish a version with prompt, schema, and notifications.",
@@ -54120,7 +54322,7 @@ function registerTools(server, ctx, capabilities) {
54120
54322
  server.registerTool("update_template", {
54121
54323
  description: `Update a template's name or description ONLY. At least one of the two must be provided.
54122
54324
 
54123
- ` + "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.",
54325
+ ` + "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.",
54124
54326
  inputSchema: strictSchema({
54125
54327
  templateId: exports_external.string().describe("The template ID to update"),
54126
54328
  name: exports_external.preprocess(coerceNull(), exports_external.string().optional()).optional().describe("New template name"),
@@ -54176,7 +54378,7 @@ function registerTools(server, ctx, capabilities) {
54176
54378
  }))).optional().describe("Predefined categories for a CLASSIFICATION field ({title, definition}[]). Required for fieldType=CLASSIFICATION; omitted otherwise.")
54177
54379
  };
54178
54380
  server.registerTool("create_template_version", {
54179
- 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.
54381
+ 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.
54180
54382
 
54181
54383
  ` + "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.",
54182
54384
  inputSchema: strictSchema({
@@ -54185,7 +54387,7 @@ function registerTools(server, ctx, capabilities) {
54185
54387
  schemaId: exports_external.string().optional().describe("Existing schema ID to reference (mutually exclusive with schemaFields)"),
54186
54388
  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)"),
54187
54389
  schemaEntity: exports_external.string().optional().describe("Entity name for the inline schema"),
54188
- 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."),
54390
+ schemaValidationRules: exports_external.preprocess(coerceJson(), SchemaValidationRulesSchema).optional().describe('Per-field schema validation rules keyed by field name. Each rule uses kind STRING, NUMBER, OTHER, OBJECT, or ARRAY and nested rules are supported. Rule metadata requires editedBy (default|llm|agent|ops|user) and an ISO-8601 editedAt timestamp. Example: { "price": { "kind": "NUMBER", "minimum": { "value": 0, "editedBy": "user", "editedAt": "2026-01-01T00:00:00.000Z" } } }. Not inherited from prior versions - omitting this on a new version drops any rules the previous version had.'),
54189
54391
  notifications: exports_external.preprocess(coerceArray(), exports_external.array(exports_external.object({
54190
54392
  eventType: exports_external.string().describe("Notification event type"),
54191
54393
  eventConfiguration: exports_external.preprocess(coerceJson(), exports_external.record(exports_external.string(), exports_external.unknown())).optional(),
@@ -54266,7 +54468,7 @@ function registerTools(server, ctx, capabilities) {
54266
54468
  return jsonResult({ schemas: schemas4, count: schemas4.length });
54267
54469
  }));
54268
54470
  server.registerTool("link_template_to_workflows", {
54269
- description: "Link one or more EXISTING workflows to a template in a single call. " + "This is the bulk equivalent of creating a workflow with `templateId`: linked workflows adopt the template's configuration (prompt, schema, notifications) and stay in sync with it. " + "The template ENFORCES its schema on linked workflows their extracted output conforms to the template's declared field names, so this is the way to make many workflows produce a consistent, canonical schema. " + "Use `list_templates`/`get_template` to find the template, and `list_workflows` to find the workflow IDs. " + "Set `force: true` to relink workflows already linked to a different template.",
54471
+ description: "Link one or more EXISTING workflows to a template in a single call. " + "This is the bulk equivalent of creating a workflow with `templateId`: linked workflows adopt the template's configuration (prompt, schema, notifications) and stay in sync with it. " + "The template ENFORCES its schema on linked workflows - their extracted output conforms to the template's declared field names, so this is the way to make many workflows produce a consistent, canonical schema. " + "Use `list_templates`/`get_template` to find the template, and `list_workflows` to find the workflow IDs. " + "Set `force: true` to relink workflows already linked to a different template.",
54270
54472
  inputSchema: strictSchema({
54271
54473
  templateId: exports_external.string().describe("The template ID to link workflows to"),
54272
54474
  workflowIds: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string()).min(1)).describe("Workflow IDs to link to the template (array of strings). Also accepts a single ID string."),
@@ -54283,7 +54485,7 @@ function registerTools(server, ctx, capabilities) {
54283
54485
  return jsonResult({
54284
54486
  success: false,
54285
54487
  conflicts: result.conflicts,
54286
- message: `Cannot link ${result.conflicts.length} workflow(s) are already linked to another template: ${summary}. ` + "Re-run with force: true to move them to this template."
54488
+ message: `Cannot link - ${result.conflicts.length} workflow(s) are already linked to another template: ${summary}. ` + "Re-run with force: true to move them to this template."
54287
54489
  });
54288
54490
  }
54289
54491
  return jsonResult({
@@ -54343,7 +54545,7 @@ function registerTools(server, ctx, capabilities) {
54343
54545
  });
54344
54546
  }));
54345
54547
  }
54346
- var SchemaFieldShape, LocationSchema, MonitoringValueOperators, MonitoringValuelessOperators, MonitoringConditionOperatorSchema, MonitoringConditionSchema, MonitoringSchema, RESUMABLE_ASSISTANT_STATUSES, ACTIVE_ASSISTANT_STATUSES, IDLE_ASSISTANT_STATUSES, CLOSED_ASSISTANT_STATUSES, DASHBOARD_BASE_URL = "https://www.kadoa.com", WORKFLOW_AUDIT_WATCHED_KEYS;
54548
+ var SchemaFieldShape, SchemaValidationAttributionShape, SchemaValidationPresenceRule, SchemaValidationUniquenessRule, SchemaValidationStringLengthRule, SchemaValidationStringFormatRule, SchemaValidationFieldRulesSchema, SchemaValidationRulesSchema, LocationSchema, MonitoringValueOperators, MonitoringValuelessOperators, MonitoringConditionOperatorSchema, MonitoringConditionSchema, MonitoringSchema, RESUMABLE_ASSISTANT_STATUSES, ACTIVE_ASSISTANT_STATUSES, IDLE_ASSISTANT_STATUSES, CLOSED_ASSISTANT_STATUSES, DASHBOARD_BASE_URL = "https://www.kadoa.com", WORKFLOW_AUDIT_WATCHED_KEYS;
54347
54549
  var init_tools = __esm(() => {
54348
54550
  init_dist2();
54349
54551
  init_zod();
@@ -54353,8 +54555,105 @@ var init_tools = __esm(() => {
54353
54555
  description: exports_external.string().optional().describe("What this field contains"),
54354
54556
  example: exports_external.string().describe("Example value"),
54355
54557
  dataType: exports_external.enum(["STRING", "NUMBER", "BOOLEAN", "DATE", "DATETIME", "MONEY", "IMAGE", "LINK", "OBJECT", "ARRAY"]).optional().describe("Data type for the field"),
54356
- 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).")
54558
+ 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).")
54559
+ };
54560
+ SchemaValidationAttributionShape = {
54561
+ editedBy: exports_external.enum(["default", "llm", "agent", "ops", "user"]).describe("Who last edited this rule: default, llm, agent, ops, or user"),
54562
+ editedByLabel: exports_external.string().optional().describe("Optional editor identifier"),
54563
+ editedAt: exports_external.string().datetime().describe("ISO-8601 timestamp when this rule was last edited")
54357
54564
  };
54565
+ SchemaValidationPresenceRule = exports_external.object({
54566
+ target: exports_external.number().int().min(0).max(100).step(20).describe("Expected percentage of rows with a value"),
54567
+ ...SchemaValidationAttributionShape
54568
+ }).strict();
54569
+ SchemaValidationUniquenessRule = exports_external.object({
54570
+ target: exports_external.number().int().min(0).max(100).step(20).describe("Expected percentage of rows with a unique value"),
54571
+ ...SchemaValidationAttributionShape
54572
+ }).strict();
54573
+ SchemaValidationStringLengthRule = exports_external.object({
54574
+ value: exports_external.number().int().min(0).describe("String length bound"),
54575
+ ...SchemaValidationAttributionShape
54576
+ }).strict();
54577
+ SchemaValidationStringFormatRule = exports_external.discriminatedUnion("kind", [
54578
+ exports_external.object({
54579
+ kind: exports_external.literal("FREE_TEXT"),
54580
+ charset: exports_external.discriminatedUnion("kind", [
54581
+ exports_external.object({
54582
+ kind: exports_external.literal("PRESET"),
54583
+ preset: exports_external.enum(["natural_language", "alphanumeric", "alpha"])
54584
+ }).strict()
54585
+ ]),
54586
+ ...SchemaValidationAttributionShape
54587
+ }).strict(),
54588
+ exports_external.object({
54589
+ kind: exports_external.literal("FORMAT"),
54590
+ source: exports_external.discriminatedUnion("kind", [
54591
+ exports_external.object({
54592
+ kind: exports_external.literal("PRESET"),
54593
+ preset: exports_external.enum(["url", "email", "phone", "date", "datetime", "time", "uuid", "slug"])
54594
+ }).strict(),
54595
+ exports_external.object({
54596
+ kind: exports_external.literal("CUSTOM"),
54597
+ pattern: exports_external.string().min(1).describe("Regular expression pattern")
54598
+ }).strict()
54599
+ ]),
54600
+ ...SchemaValidationAttributionShape
54601
+ }).strict(),
54602
+ exports_external.object({
54603
+ kind: exports_external.literal("LIST"),
54604
+ source: exports_external.discriminatedUnion("kind", [
54605
+ exports_external.object({
54606
+ kind: exports_external.literal("PRESET"),
54607
+ preset: exports_external.enum(["language2", "country2", "country3", "currency3", "month3", "usState2"])
54608
+ }).strict(),
54609
+ exports_external.object({
54610
+ kind: exports_external.literal("CUSTOM"),
54611
+ values: exports_external.array(exports_external.string().min(1)).min(1).describe("Allowed string values")
54612
+ }).strict()
54613
+ ]),
54614
+ ...SchemaValidationAttributionShape
54615
+ }).strict()
54616
+ ]);
54617
+ SchemaValidationFieldRulesSchema = exports_external.lazy(() => exports_external.discriminatedUnion("kind", [
54618
+ exports_external.object({
54619
+ kind: exports_external.literal("STRING"),
54620
+ presence: SchemaValidationPresenceRule.optional(),
54621
+ uniqueness: SchemaValidationUniquenessRule.optional(),
54622
+ minLength: SchemaValidationStringLengthRule.optional(),
54623
+ maxLength: SchemaValidationStringLengthRule.optional(),
54624
+ minHtmlElements: SchemaValidationStringLengthRule.optional(),
54625
+ maxHtmlElements: SchemaValidationStringLengthRule.optional(),
54626
+ format: SchemaValidationStringFormatRule.optional()
54627
+ }).strict(),
54628
+ exports_external.object({
54629
+ kind: exports_external.literal("NUMBER"),
54630
+ presence: SchemaValidationPresenceRule.optional(),
54631
+ uniqueness: SchemaValidationUniquenessRule.optional(),
54632
+ minimum: exports_external.object({ value: exports_external.number().describe("Minimum numeric value"), ...SchemaValidationAttributionShape }).strict().optional(),
54633
+ maximum: exports_external.object({ value: exports_external.number().describe("Maximum numeric value"), ...SchemaValidationAttributionShape }).strict().optional(),
54634
+ maxDecimalPlaces: exports_external.object({ value: exports_external.number().int().min(0).max(16), ...SchemaValidationAttributionShape }).strict().optional()
54635
+ }).strict(),
54636
+ exports_external.object({
54637
+ kind: exports_external.literal("OTHER"),
54638
+ presence: SchemaValidationPresenceRule.optional(),
54639
+ uniqueness: SchemaValidationUniquenessRule.optional()
54640
+ }).strict(),
54641
+ exports_external.object({
54642
+ kind: exports_external.literal("OBJECT"),
54643
+ presence: SchemaValidationPresenceRule.optional(),
54644
+ uniqueness: SchemaValidationUniquenessRule.optional(),
54645
+ properties: exports_external.record(exports_external.string(), SchemaValidationFieldRulesSchema)
54646
+ }).strict(),
54647
+ exports_external.object({
54648
+ kind: exports_external.literal("ARRAY"),
54649
+ presence: SchemaValidationPresenceRule.optional(),
54650
+ uniqueness: SchemaValidationUniquenessRule.optional(),
54651
+ minItems: exports_external.object({ value: exports_external.number().int().min(0), ...SchemaValidationAttributionShape }).strict().optional(),
54652
+ maxItems: exports_external.object({ value: exports_external.number().int().min(0), ...SchemaValidationAttributionShape }).strict().optional(),
54653
+ items: SchemaValidationFieldRulesSchema.optional()
54654
+ }).strict()
54655
+ ]));
54656
+ SchemaValidationRulesSchema = exports_external.record(exports_external.string(), SchemaValidationFieldRulesSchema);
54358
54657
  LocationSchema = exports_external.object({
54359
54658
  type: exports_external.enum(["auto", "manual"]),
54360
54659
  isoCode: exports_external.string().trim().min(2).optional()
@@ -54443,7 +54742,7 @@ var package_default;
54443
54742
  var init_package = __esm(() => {
54444
54743
  package_default = {
54445
54744
  name: "@kadoa/mcp",
54446
- version: "0.5.19",
54745
+ version: "0.5.21",
54447
54746
  description: "Kadoa MCP Server — manage workflows from Claude Desktop, Cursor, and other MCP clients",
54448
54747
  type: "module",
54449
54748
  main: "dist/index.js",
@@ -54467,7 +54766,7 @@ var init_package = __esm(() => {
54467
54766
  prepublishOnly: "bun run check-types && bun run test:unit && bun run build"
54468
54767
  },
54469
54768
  dependencies: {
54470
- "@kadoa/node-sdk": "^0.37.1",
54769
+ "@kadoa/node-sdk": "^0.38.0",
54471
54770
  "@modelcontextprotocol/sdk": "^1.26.0",
54472
54771
  express: "^5.2.1",
54473
54772
  "express-rate-limit": "^8.2.1",
@@ -60046,21 +60345,22 @@ async function createServer(auth, options) {
60046
60345
  const server = new McpServer({ name: "kadoa", version: package_default.version }, {
60047
60346
  instructions: [
60048
60347
  "IMPORTANT: One workflow = one source. A single workflow can extract many different fields, tables, and sections from the same URL(s) using a rich schema.",
60049
- "Do NOT create multiple workflows for different data points on the same page \u2014 instead, define one workflow with a multi-field schema covering everything needed.",
60348
+ "Do NOT create multiple workflows for different data points on the same page - instead, define one workflow with a multi-field schema covering everything needed.",
60050
60349
  "",
60051
60350
  "Kadoa workflows use agentic navigation: the AI agent can browse pages, click buttons, fill forms, select dropdowns, paginate, open detail pages, and handle multi-step interactions.",
60052
60351
  "Describe the full navigation steps in the prompt (e.g., 'select year 2022 from the dropdown, click Search, extract the table, then repeat for 2023').",
60053
60352
  "",
60054
- "Workflow lifecycle: create_workflow \u2192 get_workflow (check status) \u2192 fetch_data (get results). Workflows run asynchronously \u2014 never poll or sleep-wait.",
60353
+ "Workflow lifecycle: create_workflow \u2192 get_workflow (check status) \u2192 fetch_data (get results). Workflows run asynchronously - never poll or sleep-wait.",
60055
60354
  "",
60056
- "Use create_realtime_monitor only when the user wants continuous change detection with alerts. For one-time or scheduled extraction, use create_workflow.",
60057
- "Use scrape for an immediate raw HTML or markdown fetch from one URL. Use create_workflow for structured extraction, recurring runs, monitoring, or navigation-heavy jobs.",
60058
- "Use list_changes and get_change to retrieve detected diffs from real-time monitoring workflows.",
60355
+ "Use create_realtime_monitor only when the user wants continuous Julie change detection with alerts. It first persists notification channels, then asynchronously returns workflow/session/thread/job IDs; use the workflow Assistant tools for follow-up status, questions, and controls.",
60356
+ "For one-time or scheduled extraction, use create_workflow. Use scrape for an immediate raw HTML or markdown fetch from one URL. Use create_workflow for structured extraction, recurring runs, monitoring, or navigation-heavy jobs.",
60357
+ "If a one-time or scheduled workflow fails, retry it with run_workflow using the existing workflow ID. Do NOT delete and recreate the workflow just to retry it - preserve its workflow ID and configuration. Realtime workflows cannot be manually run.",
60358
+ "Use list_changes and get_change to retrieve detected diffs from realtime monitoring workflows.",
60059
60359
  "",
60060
60360
  "Schema tips: Use descriptive field names and examples. Group related data under one entity.",
60061
- "The AI agent uses the schema + prompt to understand what to extract \u2014 a detailed prompt with a comprehensive schema produces better results than multiple simple workflows.",
60361
+ "The AI agent uses the schema + prompt to understand what to extract - a detailed prompt with a comprehensive schema produces better results than multiple simple workflows.",
60062
60362
  "",
60063
- "Templates enforce their schema: a workflow created from a template (create_workflow with templateId) or linked to one (link_template_to_workflows) produces output whose field names conform to the template's declared schema. Use templates when you need many workflows to return a consistent, canonical set of fields \u2014 do NOT assume the extractor will drift field names away from a template's schema."
60363
+ "Templates enforce their schema: a workflow created from a template (create_workflow with templateId) or linked to one (link_template_to_workflows) produces output whose field names conform to the template's declared schema. Use templates when you need many workflows to return a consistent, canonical set of fields - do NOT assume the extractor will drift field names away from a template's schema."
60064
60364
  ].join(`
60065
60365
  `)
60066
60366
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kadoa/mcp",
3
- "version": "0.5.19",
3
+ "version": "0.5.21",
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.37.1",
27
+ "@kadoa/node-sdk": "^0.38.0",
28
28
  "@modelcontextprotocol/sdk": "^1.26.0",
29
29
  "express": "^5.2.1",
30
30
  "express-rate-limit": "^8.2.1",