@kadoa/mcp 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +543 -366
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -578,7 +578,7 @@ CLI and MCP both call **identical** SDK domain methods. A new feature added to t
578
578
  - Description string written **for the LLM**: when to call it, what inputs mean, what the response shape is.
579
579
  - Implementation calls `ctx.client.<thing>.<action>(...)` — never raw HTTP.
580
580
  - Add a unit test under `tests/unit/`.
581
- - If the change intentionally alters a tool's name, title, description, schema, outputSchema or annotations, regenerate the snapshot: `UPDATE_TOOLS_SNAPSHOT=1 BUN_TEST=1 bun test tests/unit/tools-list-snapshot.test.ts`, then `./node_modules/.bin/biome format --write tests/unit/fixtures/tools-list.snapshot.json`.
581
+ - If the change intentionally alters a tool's name, title, description, schema, outputSchema or annotations, regenerate the snapshot: `UPDATE_TOOLS_SNAPSHOT=1 BUN_TEST=1 bun test tests/unit/tools-list-snapshot.test.ts`, then `./node_modules/.bin/biome format --write tests/unit/fixtures/tools-list.snapshot.json`. Advertised input and output schemas are normalised by `src/tools/shared/portable-schema.ts` (no `$ref`/`$defs`, `const`, `oneOf`, `additionalProperties`, `$schema`, etc.), so strict clients such as Gemini accept them; zod validation is unchanged.
582
582
  - Use a Conventional Commit message (`feat(mcp): ...`, `fix(mcp): ...`). Release Please will cut the next version on merge to `main`.
583
583
 
584
584
  4. **CLI** (`kadoa-cli`), if the feature is user-facing on the command line: mirror the MCP step in `src/commands/<thing>.ts`. Same SDK call, different UX.
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ var package_default;
17
17
  var init_package = __esm(() => {
18
18
  package_default = {
19
19
  name: "@kadoa/mcp",
20
- version: "0.7.1",
20
+ version: "0.7.2",
21
21
  description: "Kadoa MCP Server — manage workflows from Claude Desktop, Cursor, and other MCP clients",
22
22
  type: "module",
23
23
  main: "dist/index.js",
@@ -212,257 +212,187 @@ function withVariableDashboardUrl(variable) {
212
212
  return { ...variable, dashboardUrl: variableSettingsDashboardUrl() };
213
213
  }
214
214
 
215
- // src/tools/shared/output-schemas.ts
216
- import { z } from "zod";
217
- var apiField, nullableString, PaginationOutput, workflowStatusFields, WorkflowRowOutput, ListWorkflowsOutput, GetWorkflowOutput, FetchDataOutput, ASSISTANT_ACTIVITIES, WorkflowAssistantOutput, AssistantTimelineOutput, AssistantAnswerOutput, AssistantControlOutput, WorkflowStrategyOutput;
218
- var init_output_schemas = __esm(() => {
219
- apiField = z.unknown().optional();
220
- nullableString = z.string().nullable();
221
- PaginationOutput = z.object({
222
- totalCount: z.number().optional(),
223
- hasMore: z.boolean(),
224
- limit: z.number().optional(),
225
- skip: z.number().optional(),
226
- offset: z.number().optional(),
227
- page: z.number().optional(),
228
- totalPages: z.number().optional(),
229
- nextCursor: nullableString.optional(),
230
- next: z.record(z.string(), z.unknown()).optional()
231
- }).loose();
232
- workflowStatusFields = {
233
- status: z.string(),
234
- badges: z.array(z.object({ kind: z.string(), status: z.string(), label: z.string() }).loose()),
235
- group: nullableString,
236
- reviewReason: z.object({ code: z.string(), explanation: z.string() }).loose().nullable(),
237
- execution: apiField,
238
- assistantState: apiField,
239
- workspaceActivity: apiField
240
- };
241
- WorkflowRowOutput = z.object({
242
- id: apiField,
243
- name: apiField,
244
- dashboardUrl: z.string(),
245
- ...workflowStatusFields,
246
- state: nullableString,
247
- displayState: nullableString,
248
- runState: nullableString,
249
- urls: apiField,
250
- totalRecords: apiField,
251
- startedAt: apiField,
252
- finishedAt: apiField,
253
- createdAt: apiField,
254
- assistant: z.object({
255
- eligible: z.boolean(),
256
- session: z.object({ id: z.string(), threadId: apiField, status: apiField }).loose().nullable()
257
- }).loose(),
258
- template: z.object({ id: z.string(), pinnedVersion: apiField, latestVersion: apiField, isOutdated: z.boolean() }).loose().nullable(),
259
- realtime: z.object({ enabled: z.boolean(), lastDataChangedAt: apiField, health: apiField }).loose(),
260
- awaitingUserInput: apiField
261
- }).loose();
262
- ListWorkflowsOutput = z.object({ workflows: z.array(WorkflowRowOutput), pagination: PaginationOutput }).loose();
263
- GetWorkflowOutput = z.object({
264
- id: z.string(),
265
- name: apiField,
266
- dashboardUrl: z.string(),
267
- description: apiField,
268
- ...workflowStatusFields,
269
- state: nullableString,
270
- displayState: nullableString,
271
- runState: nullableString,
272
- urls: apiField,
273
- prompt: nullableString,
274
- intentSource: z.enum(["extraction_spec", "legacy_prompt"]).nullable(),
275
- extractionSpec: apiField,
276
- entity: apiField,
277
- schema: apiField,
278
- navigationMode: apiField,
279
- assistantEligible: z.boolean(),
280
- assistantSessionId: apiField,
281
- assistantThreadId: apiField,
282
- assistantSessionStatus: apiField,
283
- assistant: z.object({ eligible: z.boolean(), sessionId: apiField, threadId: apiField, sessionStatus: apiField }).loose(),
284
- template: z.object({
285
- id: apiField,
286
- name: apiField,
287
- version: apiField,
288
- controlledParts: z.array(z.string()),
289
- hasSchemaValidationRules: apiField
290
- }).loose().nullable(),
291
- updateInterval: apiField,
292
- schedules: apiField,
293
- scheduleTimezone: apiField,
294
- nextInvocation: apiField,
295
- location: apiField,
296
- limit: apiField,
297
- tags: apiField,
298
- totalRecords: apiField,
299
- job: z.object({ id: apiField }).loose().nullable(),
300
- startedAt: apiField,
301
- finishedAt: apiField,
302
- estimatedFinishedAtUtc: apiField,
303
- errors: apiField,
304
- createdAt: apiField,
305
- notificationConfig: apiField,
306
- channelCounts: apiField,
307
- realtime: z.object({ lastDataChangedAt: apiField, observerHealth: apiField }).loose().nullable()
308
- }).loose();
309
- FetchDataOutput = z.object({
310
- data: z.array(z.record(z.string(), z.unknown())),
311
- pagination: PaginationOutput,
312
- totalCount: z.number().optional(),
313
- hint: z.string().optional()
314
- }).loose();
315
- ASSISTANT_ACTIVITIES = [
316
- "unsupported",
317
- "not_started",
318
- "working",
319
- "waiting_for_input",
320
- "idle",
321
- "interrupted",
322
- "failed",
323
- "closed",
324
- "unknown"
325
- ];
326
- WorkflowAssistantOutput = z.object({
327
- workflowId: z.string(),
328
- assistantEligible: z.boolean(),
329
- sessionId: nullableString,
330
- threadId: apiField,
331
- sessionStatus: apiField,
332
- activity: z.enum(ASSISTANT_ACTIVITIES),
333
- canAcceptUpdate: z.boolean(),
334
- isTerminal: z.boolean(),
335
- paused: apiField,
336
- pause: apiField,
337
- pendingQuestion: apiField
338
- }).loose();
339
- AssistantTimelineOutput = z.object({
340
- workflowId: z.string(),
341
- sessionId: apiField,
342
- items: z.array(z.unknown()),
343
- pagination: PaginationOutput,
344
- dashboardUrl: z.string()
345
- }).loose();
346
- AssistantAnswerOutput = z.object({
347
- success: z.literal(true),
348
- workflowId: z.string(),
349
- sessionId: z.string(),
350
- threadId: apiField,
351
- questionId: z.string(),
352
- message: z.string()
353
- }).loose();
354
- AssistantControlOutput = z.object({
355
- success: z.boolean(),
356
- status: z.literal("accepted"),
357
- action: z.enum(["interrupt", "resume", "stop"]),
358
- workflowId: z.string(),
359
- sessionId: z.string(),
360
- delivery: apiField,
361
- jobId: apiField,
362
- message: z.string()
363
- }).loose();
364
- WorkflowStrategyOutput = z.object({
365
- workflowId: z.string(),
366
- sessionId: nullableString,
367
- source: z.enum(["assistant_build", "workflow_metadata"]).nullable(),
368
- status: z.enum(["ready", "building", "unavailable"]),
369
- strategy: apiField,
370
- message: z.string().optional()
371
- }).loose();
372
- });
373
-
374
- // src/tools/shared/pagination.ts
375
- import { z as z2 } from "zod";
376
- function limitInput(options) {
377
- const range = `limit must be a whole number from 1 to ${options.max}`;
378
- const overMax = `${range}. ${options.overMaxHint ?? "Page with pagination.next."}`;
379
- return z2.preprocess(coerceNumber(), z2.number().int(range).min(1, range).max(options.max, overMax)).optional().describe(`${options.noun} per page, 1-${options.max} (default ${options.defaultLimit})`);
380
- }
381
- function offsetInput(param, noun) {
382
- const range = `${param} must be a whole number from 0 to ${MAX_OFFSET}`;
383
- return z2.preprocess(coerceNumber(), z2.number().int(range).min(0, range).max(MAX_OFFSET, range)).optional().describe(`Number of ${noun} to skip (default 0)`);
384
- }
385
- function pageInput() {
386
- const range = `page must be a whole number from 1 to ${MAX_PAGE}`;
387
- return z2.preprocess(coerceNumber(), z2.number().int(range).min(1, range).max(MAX_PAGE, range)).optional().describe("Page number, 1-based (default 1)");
388
- }
389
- function nextArgs(args, patch) {
390
- const next = {};
391
- for (const [key, value] of Object.entries({ ...args, ...patch })) {
392
- if (value !== undefined)
393
- next[key] = value;
215
+ // src/tools/shared/portable-schema.ts
216
+ function lookupRef(ref, defs) {
217
+ if (typeof ref !== "string")
218
+ return;
219
+ const match = /^#\/(?:\$defs|definitions)\/(.+)$/.exec(ref);
220
+ if (!match)
221
+ return;
222
+ const name = match[1].replace(/~1/g, "/").replace(/~0/g, "~");
223
+ const def = defs[name];
224
+ return isObject(def) ? { name, def } : undefined;
225
+ }
226
+ function mergeSchemas(base, extra) {
227
+ const merged = { ...extra, ...base };
228
+ if (isObject(base.properties) || isObject(extra.properties)) {
229
+ merged.properties = {
230
+ ...isObject(extra.properties) ? extra.properties : {},
231
+ ...isObject(base.properties) ? base.properties : {}
232
+ };
394
233
  }
395
- return next;
396
- }
397
- function withTotal(totalCount, hasMore) {
398
- return totalCount !== undefined ? { totalCount, hasMore } : { hasMore };
399
- }
400
- function offsetPage(options) {
401
- const nextOffset = options.offset + options.returned;
402
- const more = options.hasMore ?? (options.totalCount !== undefined ? nextOffset < options.totalCount : options.returned >= options.limit);
403
- const hasMore = options.returned > 0 && more;
404
- const meta = withTotal(options.totalCount, hasMore);
405
- meta.limit = options.limit;
406
- meta[options.param] = options.offset;
407
- if (hasMore)
408
- meta.next = nextArgs(options.args, { [options.param]: nextOffset, limit: options.limit });
409
- return meta;
410
- }
411
- function numberedPage(options) {
412
- const more = options.totalPages !== undefined ? options.page < options.totalPages : options.totalCount !== undefined ? options.page * options.limit < options.totalCount : options.returned >= options.limit;
413
- const hasMore = options.returned > 0 && more;
414
- const meta = withTotal(options.totalCount, hasMore);
415
- meta.limit = options.limit;
416
- meta.page = options.page;
417
- if (options.totalPages !== undefined)
418
- meta.totalPages = options.totalPages;
419
- if (hasMore)
420
- meta.next = nextArgs(options.args, { page: options.page + 1, limit: options.limit });
421
- return meta;
422
- }
423
- function cursorPage(options) {
424
- const cursor = typeof options.nextCursor === "string" && options.nextCursor.length > 0 ? options.nextCursor : null;
425
- const meta = { hasMore: options.hasMore && cursor !== null, nextCursor: cursor };
426
- if (meta.hasMore)
427
- meta.next = nextArgs(options.args, { cursor });
428
- return meta;
429
- }
430
- function completeList(totalCount) {
431
- return { totalCount, hasMore: false };
432
- }
433
- var MAX_OFFSET = 1e6, MAX_PAGE = 1e5;
434
- var init_pagination = () => {};
435
-
436
- // src/tools/shared/results.ts
437
- function asStructuredObject(data) {
438
- return data !== null && !Array.isArray(data) ? data : { result: data };
439
- }
440
- function structuredResult(data, extra = []) {
441
- const structuredContent = asStructuredObject(data);
442
- return {
443
- structuredContent,
444
- content: [{ type: "text", text: JSON.stringify(structuredContent) }, ...extra]
445
- };
446
- }
447
- function jsonResult(data) {
448
- return structuredResult(data);
449
- }
450
- function textOnlyResult(data, extra = []) {
451
- const structuredContent = asStructuredObject(data);
452
- return { content: [{ type: "text", text: JSON.stringify(structuredContent) }, ...extra] };
234
+ if (Array.isArray(base.required) || Array.isArray(extra.required)) {
235
+ const required = [
236
+ ...Array.isArray(extra.required) ? extra.required : [],
237
+ ...Array.isArray(base.required) ? base.required : []
238
+ ];
239
+ merged.required = [...new Set(required)];
240
+ }
241
+ return merged;
242
+ }
243
+ function walk(node, defs, refCounts, io) {
244
+ if (!isObject(node))
245
+ return node;
246
+ let current = { ...node };
247
+ let counts = refCounts;
248
+ while ("$ref" in current) {
249
+ const { $ref, ...site } = current;
250
+ const target = lookupRef($ref, defs);
251
+ if (!target) {
252
+ current = site;
253
+ break;
254
+ }
255
+ const seen = counts.get(target.name) ?? 0;
256
+ counts = new Map(counts).set(target.name, seen + 1);
257
+ if (seen >= MAX_REF_DEPTH) {
258
+ const description = typeof target.def.description === "string" ? target.def.description : STUB_DESCRIPTION;
259
+ const type = typeof target.def.type === "string" ? target.def.type : io === "input" ? "object" : undefined;
260
+ current = { ...type ? { type } : {}, description, ...site };
261
+ break;
262
+ }
263
+ current = { ...target.def, ...site };
264
+ }
265
+ for (const keyword of DROPPED_KEYWORDS)
266
+ delete current[keyword];
267
+ if ("const" in current) {
268
+ const { const: value, ...rest } = current;
269
+ current = typeof value === "string" ? { ...rest, enum: [value] } : rest;
270
+ if (current.type === undefined && ["string", "number", "boolean"].includes(typeof value)) {
271
+ current.type = typeof value;
272
+ }
273
+ }
274
+ if (typeof current.exclusiveMinimum === "number") {
275
+ const bound = current.exclusiveMinimum;
276
+ current.minimum = typeof current.minimum === "number" ? Math.max(current.minimum, bound) : bound;
277
+ }
278
+ if (typeof current.exclusiveMaximum === "number") {
279
+ const bound = current.exclusiveMaximum;
280
+ current.maximum = typeof current.maximum === "number" ? Math.min(current.maximum, bound) : bound;
281
+ }
282
+ delete current.exclusiveMinimum;
283
+ delete current.exclusiveMaximum;
284
+ if (io === "input" && Array.isArray(current.enum) && Array.isArray(current.type) && current.type.includes("null")) {
285
+ current.enum = current.enum.filter((member) => member !== null);
286
+ }
287
+ if (Array.isArray(current.enum) && current.enum.some((member) => typeof member !== "string")) {
288
+ delete current.enum;
289
+ }
290
+ if (Array.isArray(current.prefixItems)) {
291
+ const { prefixItems, ...rest } = current;
292
+ current = rest;
293
+ if (current.items === undefined && prefixItems.length > 0) {
294
+ current.items = prefixItems.length === 1 ? prefixItems[0] : { anyOf: prefixItems };
295
+ }
296
+ }
297
+ if (isObject(current.properties)) {
298
+ current.properties = Object.fromEntries(Object.entries(current.properties).map(([name, value]) => [name, walk(value, defs, counts, io)]));
299
+ }
300
+ if (isObject(current.items))
301
+ current.items = walk(current.items, defs, counts, io);
302
+ if (Array.isArray(current.oneOf)) {
303
+ current.anyOf = [...Array.isArray(current.anyOf) ? current.anyOf : [], ...current.oneOf];
304
+ delete current.oneOf;
305
+ }
306
+ if (Array.isArray(current.anyOf))
307
+ current.anyOf = current.anyOf.map((member) => walk(member, defs, counts, io));
308
+ if (Array.isArray(current.allOf)) {
309
+ const { allOf, ...rest } = current;
310
+ current = rest;
311
+ for (const member of allOf) {
312
+ const walked = walk(member, defs, counts, io);
313
+ if (isObject(walked))
314
+ current = mergeSchemas(current, walked);
315
+ }
316
+ }
317
+ if (Array.isArray(current.type)) {
318
+ const types = current.type.filter((entry) => entry !== "null");
319
+ const nullable = types.length !== current.type.length;
320
+ if (types.length === 0) {
321
+ current.type = "null";
322
+ } else if (types.length === 1 && !(nullable && io === "output")) {
323
+ current.type = types[0];
324
+ if (nullable)
325
+ current.nullable = true;
326
+ } else {
327
+ const members = current.type.map((type) => ({ type }));
328
+ delete current.type;
329
+ current.anyOf = members;
330
+ }
331
+ }
332
+ if (Array.isArray(current.anyOf) && current.anyOf.some(isNullSchema)) {
333
+ const members = current.anyOf.filter((member) => !isNullSchema(member));
334
+ const { anyOf: _anyOf, ...rest } = current;
335
+ if (members.length === 0) {
336
+ current = { ...rest, type: "null" };
337
+ } else if (io === "input" && members.length === 1 && isObject(members[0])) {
338
+ const merged = mergeSchemas(rest, members[0]);
339
+ if (typeof merged.type === "string")
340
+ current = { ...merged, nullable: true };
341
+ }
342
+ }
343
+ return current;
453
344
  }
454
- function errorResult(message, details = {}) {
455
- return {
456
- content: [{ type: "text", text: JSON.stringify({ error: message, ...details }) }],
457
- isError: true
345
+ function toPortableJsonSchema(schema, options = {}) {
346
+ const io = options.io ?? "input";
347
+ const defs = {
348
+ ...isObject(schema.definitions) ? schema.definitions : {},
349
+ ...isObject(schema.$defs) ? schema.$defs : {}
458
350
  };
459
- }
460
- function withExtraContent(result, ...blocks) {
461
- return { ...result, content: [...result.content, ...blocks] };
462
- }
351
+ return walk(schema, defs, new Map, io);
352
+ }
353
+ var NON_PORTABLE_KEYWORDS, DROPPED_KEYWORDS, MAX_REF_DEPTH = 3, STUB_DESCRIPTION = "Nested value of the same shape", isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isNullSchema = (schema) => isObject(schema) && schema.type === "null";
354
+ var init_portable_schema = __esm(() => {
355
+ NON_PORTABLE_KEYWORDS = new Set([
356
+ "$ref",
357
+ "$defs",
358
+ "definitions",
359
+ "const",
360
+ "oneOf",
361
+ "allOf",
362
+ "exclusiveMinimum",
363
+ "exclusiveMaximum",
364
+ "prefixItems",
365
+ "$schema",
366
+ "additionalProperties",
367
+ "propertyNames",
368
+ "multipleOf",
369
+ "uniqueItems",
370
+ "id",
371
+ "$id",
372
+ "contentEncoding",
373
+ "contentMediaType",
374
+ "dependentRequired",
375
+ "patternProperties"
376
+ ]);
377
+ DROPPED_KEYWORDS = [
378
+ "$schema",
379
+ "$id",
380
+ "id",
381
+ "$defs",
382
+ "definitions",
383
+ "additionalProperties",
384
+ "propertyNames",
385
+ "patternProperties",
386
+ "multipleOf",
387
+ "uniqueItems",
388
+ "contentEncoding",
389
+ "contentMediaType",
390
+ "dependentRequired"
391
+ ];
392
+ });
463
393
 
464
394
  // src/tools/shared/schemas.ts
465
- import { z as z3 } from "zod";
395
+ import { z } from "zod";
466
396
  function isPlainObject(value) {
467
397
  return value !== null && typeof value === "object" && !Array.isArray(value);
468
398
  }
@@ -524,18 +454,14 @@ function stampSchemaValidationRules(rules, attribution, previous) {
524
454
  return Object.fromEntries(Object.entries(rules).map(([name, field]) => [name, stampField(field, previousFields[name])]));
525
455
  }
526
456
  function strictSchema(shape) {
527
- return z3.object(shape).strict();
457
+ return portable(z.object(shape).strict());
528
458
  }
529
- function withoutDefIds(schema) {
459
+ function portable(schema) {
530
460
  const jsonSchema = schema["~standard"].jsonSchema;
531
461
  const emitInput = jsonSchema.input;
532
- jsonSchema.input = (params) => {
533
- const result = emitInput(params);
534
- const defs = result.$defs;
535
- for (const def of Object.values(defs ?? {}))
536
- delete def.id;
537
- return result;
538
- };
462
+ const emitOutput = jsonSchema.output;
463
+ jsonSchema.input = (params) => toPortableJsonSchema(emitInput(params), { io: "input" });
464
+ jsonSchema.output = (params) => toPortableJsonSchema(emitOutput(params), { io: "output" });
539
465
  return schema;
540
466
  }
541
467
  function normalizeDataFilters(val, ctx) {
@@ -560,7 +486,7 @@ function normalizeDataFilters(val, ctx) {
560
486
  code: "custom",
561
487
  message: "Grouped filters ({logicalOperator, conditions}) are not supported. Send a flat array of " + "{field, operator, value} filters instead; multiple filters are combined with OR."
562
488
  });
563
- return z3.NEVER;
489
+ return z.NEVER;
564
490
  }
565
491
  return [v];
566
492
  }
@@ -568,7 +494,8 @@ function normalizeDataFilters(val, ctx) {
568
494
  }
569
495
  var UpdateIntervalSchema, NOTIFICATION_EVENT_TYPES, NotificationEventTypeSchema, SchemaFieldDataTypeSchema, SchemaFieldShape, SchemaValidationPresenceRule, SchemaValidationUniquenessRule, SchemaValidationBoundRule, SchemaValidationStringFormatRule, SchemaValidationFieldRulesSchema, SchemaValidationRulesSchema, SCHEMA_VALIDATION_ATTRIBUTION_KEYS, LocationSchema, MonitoringValueOperators, MonitoringValuelessOperators, MonitoringConditionOperatorSchema, MonitoringConditionSchema, MonitoringSchema, urlInputShape, extractionPromptInput, extractionInputShape, webhookAuthShape, notificationChannelsSchema, notificationsInputShape, requiredNotificationsInputShape, DATA_FILTER_OPERATORS, DATA_FILTER_VALUELESS_OPERATORS, DATA_FILTER_LIST_OPERATORS, DataFilterSchema, dataFiltersInput;
570
496
  var init_schemas = __esm(() => {
571
- UpdateIntervalSchema = z3.enum([
497
+ init_portable_schema();
498
+ UpdateIntervalSchema = z.enum([
572
499
  "ONLY_ONCE",
573
500
  "EVERY_10_MINUTES",
574
501
  "HALF_HOURLY",
@@ -602,8 +529,8 @@ var init_schemas = __esm(() => {
602
529
  "credits_low",
603
530
  "free_trial_ending"
604
531
  ];
605
- NotificationEventTypeSchema = z3.enum(NOTIFICATION_EVENT_TYPES);
606
- SchemaFieldDataTypeSchema = z3.enum([
532
+ NotificationEventTypeSchema = z.enum(NOTIFICATION_EVENT_TYPES);
533
+ SchemaFieldDataTypeSchema = z.enum([
607
534
  "STRING",
608
535
  "NUMBER",
609
536
  "BOOLEAN",
@@ -616,61 +543,61 @@ var init_schemas = __esm(() => {
616
543
  "ARRAY"
617
544
  ]);
618
545
  SchemaFieldShape = {
619
- name: z3.string().describe("Field name"),
620
- description: z3.string().optional().describe("What this field contains"),
621
- example: z3.string().describe("Example value"),
546
+ name: z.string().describe("Field name"),
547
+ description: z.string().optional().describe("What this field contains"),
548
+ example: z.string().describe("Example value"),
622
549
  dataType: SchemaFieldDataTypeSchema.optional().describe("Data type for the field"),
623
- isKey: z3.preprocess(coerceBoolean(), z3.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).")
550
+ isKey: z.preprocess(coerceBoolean(), z.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).")
624
551
  };
625
- SchemaValidationPresenceRule = z3.object({
626
- target: z3.number().int().min(0).max(100).step(20).describe("Expected percentage of rows with a value")
552
+ SchemaValidationPresenceRule = z.object({
553
+ target: z.number().int().min(0).max(100).step(20).describe("Expected percentage of rows with a value")
627
554
  }).strict().meta({ id: "PresenceRule" });
628
- SchemaValidationUniquenessRule = z3.object({
629
- target: z3.number().int().min(0).max(100).step(20).describe("Expected percentage of rows with a unique value")
555
+ SchemaValidationUniquenessRule = z.object({
556
+ target: z.number().int().min(0).max(100).step(20).describe("Expected percentage of rows with a unique value")
630
557
  }).strict().meta({ id: "UniquenessRule" });
631
- SchemaValidationBoundRule = z3.object({
632
- value: z3.number().int().min(0).describe("Length or count bound")
558
+ SchemaValidationBoundRule = z.object({
559
+ value: z.number().int().min(0).describe("Length or count bound")
633
560
  }).strict().meta({ id: "BoundRule" });
634
- SchemaValidationStringFormatRule = z3.discriminatedUnion("kind", [
635
- z3.object({
636
- kind: z3.literal("FREE_TEXT"),
637
- charset: z3.discriminatedUnion("kind", [
638
- z3.object({
639
- kind: z3.literal("PRESET"),
640
- preset: z3.enum(["natural_language", "alphanumeric", "alpha"])
561
+ SchemaValidationStringFormatRule = z.discriminatedUnion("kind", [
562
+ z.object({
563
+ kind: z.literal("FREE_TEXT"),
564
+ charset: z.discriminatedUnion("kind", [
565
+ z.object({
566
+ kind: z.literal("PRESET"),
567
+ preset: z.enum(["natural_language", "alphanumeric", "alpha"])
641
568
  }).strict()
642
569
  ])
643
570
  }).strict(),
644
- z3.object({
645
- kind: z3.literal("FORMAT"),
646
- source: z3.discriminatedUnion("kind", [
647
- z3.object({
648
- kind: z3.literal("PRESET"),
649
- preset: z3.enum(["url", "email", "phone", "date", "datetime", "time", "uuid", "slug"])
571
+ z.object({
572
+ kind: z.literal("FORMAT"),
573
+ source: z.discriminatedUnion("kind", [
574
+ z.object({
575
+ kind: z.literal("PRESET"),
576
+ preset: z.enum(["url", "email", "phone", "date", "datetime", "time", "uuid", "slug"])
650
577
  }).strict(),
651
- z3.object({
652
- kind: z3.literal("CUSTOM"),
653
- pattern: z3.string().min(1).describe("Regular expression pattern")
578
+ z.object({
579
+ kind: z.literal("CUSTOM"),
580
+ pattern: z.string().min(1).describe("Regular expression pattern")
654
581
  }).strict()
655
582
  ])
656
583
  }).strict(),
657
- z3.object({
658
- kind: z3.literal("LIST"),
659
- source: z3.discriminatedUnion("kind", [
660
- z3.object({
661
- kind: z3.literal("PRESET"),
662
- preset: z3.enum(["language2", "country2", "country3", "currency3", "month3", "usState2"])
584
+ z.object({
585
+ kind: z.literal("LIST"),
586
+ source: z.discriminatedUnion("kind", [
587
+ z.object({
588
+ kind: z.literal("PRESET"),
589
+ preset: z.enum(["language2", "country2", "country3", "currency3", "month3", "usState2"])
663
590
  }).strict(),
664
- z3.object({
665
- kind: z3.literal("CUSTOM"),
666
- values: z3.array(z3.string().min(1)).min(1).describe("Allowed string values")
591
+ z.object({
592
+ kind: z.literal("CUSTOM"),
593
+ values: z.array(z.string().min(1)).min(1).describe("Allowed string values")
667
594
  }).strict()
668
595
  ])
669
596
  }).strict()
670
597
  ]);
671
- SchemaValidationFieldRulesSchema = z3.lazy(() => z3.discriminatedUnion("kind", [
672
- z3.object({
673
- kind: z3.literal("STRING"),
598
+ SchemaValidationFieldRulesSchema = z.lazy(() => z.discriminatedUnion("kind", [
599
+ z.object({
600
+ kind: z.literal("STRING"),
674
601
  presence: SchemaValidationPresenceRule.optional(),
675
602
  uniqueness: SchemaValidationUniquenessRule.optional(),
676
603
  minLength: SchemaValidationBoundRule.optional(),
@@ -679,27 +606,27 @@ var init_schemas = __esm(() => {
679
606
  maxHtmlElements: SchemaValidationBoundRule.optional(),
680
607
  format: SchemaValidationStringFormatRule.optional()
681
608
  }).strict(),
682
- z3.object({
683
- kind: z3.literal("NUMBER"),
609
+ z.object({
610
+ kind: z.literal("NUMBER"),
684
611
  presence: SchemaValidationPresenceRule.optional(),
685
612
  uniqueness: SchemaValidationUniquenessRule.optional(),
686
- minimum: z3.object({ value: z3.number().describe("Minimum numeric value") }).strict().optional(),
687
- maximum: z3.object({ value: z3.number().describe("Maximum numeric value") }).strict().optional(),
688
- maxDecimalPlaces: z3.object({ value: z3.number().int().min(0).max(16) }).strict().optional()
613
+ minimum: z.object({ value: z.number().describe("Minimum numeric value") }).strict().optional(),
614
+ maximum: z.object({ value: z.number().describe("Maximum numeric value") }).strict().optional(),
615
+ maxDecimalPlaces: z.object({ value: z.number().int().min(0).max(16) }).strict().optional()
689
616
  }).strict(),
690
- z3.object({
691
- kind: z3.literal("OTHER"),
617
+ z.object({
618
+ kind: z.literal("OTHER"),
692
619
  presence: SchemaValidationPresenceRule.optional(),
693
620
  uniqueness: SchemaValidationUniquenessRule.optional()
694
621
  }).strict(),
695
- z3.object({
696
- kind: z3.literal("OBJECT"),
622
+ z.object({
623
+ kind: z.literal("OBJECT"),
697
624
  presence: SchemaValidationPresenceRule.optional(),
698
625
  uniqueness: SchemaValidationUniquenessRule.optional(),
699
- properties: z3.record(z3.string(), SchemaValidationFieldRulesSchema)
626
+ properties: z.record(z.string(), SchemaValidationFieldRulesSchema)
700
627
  }).strict(),
701
- z3.object({
702
- kind: z3.literal("ARRAY"),
628
+ z.object({
629
+ kind: z.literal("ARRAY"),
703
630
  presence: SchemaValidationPresenceRule.optional(),
704
631
  uniqueness: SchemaValidationUniquenessRule.optional(),
705
632
  minItems: SchemaValidationBoundRule.optional(),
@@ -707,21 +634,21 @@ var init_schemas = __esm(() => {
707
634
  items: SchemaValidationFieldRulesSchema.optional()
708
635
  }).strict()
709
636
  ]));
710
- SchemaValidationRulesSchema = z3.record(z3.string(), SchemaValidationFieldRulesSchema);
637
+ SchemaValidationRulesSchema = z.record(z.string(), SchemaValidationFieldRulesSchema);
711
638
  SCHEMA_VALIDATION_ATTRIBUTION_KEYS = new Set(["editedBy", "editedByLabel", "editedAt"]);
712
- LocationSchema = z3.object({
713
- type: z3.enum(["auto", "manual"]),
714
- isoCode: z3.string().trim().min(2).optional()
639
+ LocationSchema = z.object({
640
+ type: z.enum(["auto", "manual"]),
641
+ isoCode: z.string().trim().min(2).optional()
715
642
  }).strict().superRefine((location, ctx) => {
716
643
  if (location.type === "manual" && !location.isoCode) {
717
644
  ctx.addIssue({
718
- code: z3.ZodIssueCode.custom,
645
+ code: z.ZodIssueCode.custom,
719
646
  path: ["isoCode"],
720
647
  message: "isoCode is required for manual location"
721
648
  });
722
649
  }
723
650
  if (location.type === "auto" && location.isoCode) {
724
- ctx.addIssue({ code: z3.ZodIssueCode.custom, path: ["isoCode"], message: "omit isoCode for auto location" });
651
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["isoCode"], message: "omit isoCode for auto location" });
725
652
  }
726
653
  });
727
654
  MonitoringValueOperators = [
@@ -743,81 +670,81 @@ var init_schemas = __esm(() => {
743
670
  "WITHIN_LAST_DAYS"
744
671
  ];
745
672
  MonitoringValuelessOperators = ["IS_NULL", "IS_NOT_NULL", "IS_EMPTY", "IS_NOT_EMPTY"];
746
- MonitoringConditionOperatorSchema = z3.enum([...MonitoringValueOperators, ...MonitoringValuelessOperators]);
747
- MonitoringConditionSchema = z3.object({
748
- type: z3.enum(["SINGLE", "ANY"]),
749
- field: z3.string().trim().min(1).optional(),
673
+ MonitoringConditionOperatorSchema = z.enum([...MonitoringValueOperators, ...MonitoringValuelessOperators]);
674
+ MonitoringConditionSchema = z.object({
675
+ type: z.enum(["SINGLE", "ANY"]),
676
+ field: z.string().trim().min(1).optional(),
750
677
  operator: MonitoringConditionOperatorSchema,
751
- value: z3.union([z3.string(), z3.number(), z3.boolean()]).optional()
678
+ value: z.union([z.string(), z.number(), z.boolean()]).optional()
752
679
  }).strict().superRefine((condition, ctx) => {
753
680
  if (condition.type === "SINGLE" && !condition.field) {
754
681
  ctx.addIssue({
755
- code: z3.ZodIssueCode.custom,
682
+ code: z.ZodIssueCode.custom,
756
683
  path: ["field"],
757
684
  message: "field is required for SINGLE conditions"
758
685
  });
759
686
  }
760
687
  const needsValue = MonitoringValueOperators.includes(condition.operator);
761
688
  if (needsValue && condition.value === undefined) {
762
- ctx.addIssue({ code: z3.ZodIssueCode.custom, path: ["value"], message: `${condition.operator} requires a value` });
689
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["value"], message: `${condition.operator} requires a value` });
763
690
  }
764
691
  if (!needsValue && condition.value !== undefined) {
765
692
  ctx.addIssue({
766
- code: z3.ZodIssueCode.custom,
693
+ code: z.ZodIssueCode.custom,
767
694
  path: ["value"],
768
695
  message: `${condition.operator} does not accept a value`
769
696
  });
770
697
  }
771
698
  });
772
- MonitoringSchema = z3.object({
773
- fields: z3.preprocess(coerceArray(), z3.array(z3.object({
774
- fieldName: z3.string().trim().min(1),
775
- operator: z3.enum(["changed", "added", "removed"]),
776
- isKeyField: z3.preprocess(coerceBoolean(), z3.boolean()).optional()
699
+ MonitoringSchema = z.object({
700
+ fields: z.preprocess(coerceArray(), z.array(z.object({
701
+ fieldName: z.string().trim().min(1),
702
+ operator: z.enum(["changed", "added", "removed"]),
703
+ isKeyField: z.preprocess(coerceBoolean(), z.boolean()).optional()
777
704
  }).strict()).min(1)),
778
- conditions: z3.object({
779
- logicalOperator: z3.enum(["AND", "OR"]),
780
- conditions: z3.preprocess(coerceArray(), z3.array(MonitoringConditionSchema).min(1))
705
+ conditions: z.object({
706
+ logicalOperator: z.enum(["AND", "OR"]),
707
+ conditions: z.preprocess(coerceArray(), z.array(MonitoringConditionSchema).min(1))
781
708
  }).strict().optional()
782
709
  }).strict();
783
710
  urlInputShape = {
784
- url: z3.string().optional().describe("Single URL - prefer using 'urls' instead. If both are provided, 'urls' takes precedence."),
785
- urls: z3.preprocess(coerceArray(true), z3.array(z3.string()).min(1)).optional().describe("Starting URLs for the workflow (array of strings). Also accepts a single URL string.")
711
+ url: z.string().optional().describe("Single URL - prefer using 'urls' instead. If both are provided, 'urls' takes precedence."),
712
+ urls: z.preprocess(coerceArray(true), z.array(z.string()).min(1)).optional().describe("Starting URLs for the workflow (array of strings). Also accepts a single URL string.")
786
713
  };
787
- extractionPromptInput = z3.string().optional();
714
+ extractionPromptInput = z.string().optional();
788
715
  extractionInputShape = {
789
- name: z3.string().optional().describe("Optional name for the workflow"),
790
- entity: z3.string().optional().describe("Entity name for extraction (e.g., 'Product', 'Job Posting')"),
791
- schema: z3.preprocess(coerceArray(), z3.array(z3.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.")
716
+ name: z.string().optional().describe("Optional name for the workflow"),
717
+ entity: z.string().optional().describe("Entity name for extraction (e.g., 'Product', 'Job Posting')"),
718
+ schema: z.preprocess(coerceArray(), z.array(z.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.")
792
719
  };
793
- webhookAuthShape = z3.object({
794
- type: z3.enum(["bearer", "basic", "header"]).describe("Authentication type"),
795
- token: z3.string().optional().describe("Bearer token (required for 'bearer' type)"),
796
- username: z3.string().optional().describe("Username (required for 'basic' type)"),
797
- password: z3.string().optional().describe("Password (required for 'basic' type)"),
798
- headers: z3.object({}).passthrough().optional().describe("Custom headers as key-value pairs (required for 'header' type)")
720
+ webhookAuthShape = z.object({
721
+ type: z.enum(["bearer", "basic", "header"]).describe("Authentication type"),
722
+ token: z.string().optional().describe("Bearer token (required for 'bearer' type)"),
723
+ username: z.string().optional().describe("Username (required for 'basic' type)"),
724
+ password: z.string().optional().describe("Password (required for 'basic' type)"),
725
+ headers: z.object({}).passthrough().optional().describe("Custom headers as key-value pairs (required for 'header' type)")
799
726
  }).optional().describe("Authentication configuration for the webhook endpoint");
800
- notificationChannelsSchema = z3.object({
801
- email: z3.object({
802
- recipients: z3.array(z3.string().email()).optional().describe("Email addresses to notify. Omit to use account default email.")
727
+ notificationChannelsSchema = z.object({
728
+ email: z.object({
729
+ recipients: z.array(z.string().email()).optional().describe("Email addresses to notify. Omit to use account default email.")
803
730
  }).optional().describe("Send email notifications. Pass {} for account default email, or {recipients: [...]} for custom addresses."),
804
- webhook: z3.object({
805
- url: z3.string().url().describe("Webhook endpoint URL"),
806
- httpMethod: z3.enum(["POST", "GET", "PUT", "PATCH"]).optional().describe("HTTP method (defaults to POST)"),
731
+ webhook: z.object({
732
+ url: z.string().url().describe("Webhook endpoint URL"),
733
+ httpMethod: z.enum(["POST", "GET", "PUT", "PATCH"]).optional().describe("HTTP method (defaults to POST)"),
807
734
  auth: webhookAuthShape
808
735
  }).optional().describe("Send notifications via HTTP webhook"),
809
- slack: z3.object({
810
- webhookUrl: z3.string().url().optional().describe("Slack incoming webhook URL (legacy)"),
811
- slackChannelId: z3.string().optional().describe("Slack channel ID from OAuth integration (e.g. C01ABCDEF)"),
812
- slackChannelName: z3.string().optional().describe("Slack channel name (e.g. #alerts)")
736
+ slack: z.object({
737
+ webhookUrl: z.string().url().optional().describe("Slack incoming webhook URL (legacy)"),
738
+ slackChannelId: z.string().optional().describe("Slack channel ID from OAuth integration (e.g. C01ABCDEF)"),
739
+ slackChannelName: z.string().optional().describe("Slack channel name (e.g. #alerts)")
813
740
  }).optional().describe("Send notifications to a Slack channel. Use slackChannelId/slackChannelName for OAuth integration, or webhookUrl for legacy incoming webhooks."),
814
- websocket: z3.boolean().optional().describe("Enable WebSocket notifications for programmatic real-time consumption")
741
+ websocket: z.boolean().optional().describe("Enable WebSocket notifications for programmatic real-time consumption")
815
742
  });
816
743
  notificationsInputShape = {
817
- notifications: z3.preprocess(coerceJson(), notificationChannelsSchema.optional()).optional().describe("Optional notification channels to alert when data changes.")
744
+ notifications: z.preprocess(coerceJson(), notificationChannelsSchema.optional()).optional().describe("Optional notification channels to alert when data changes.")
818
745
  };
819
746
  requiredNotificationsInputShape = {
820
- notifications: z3.preprocess(coerceJson(), notificationChannelsSchema).describe("Notification channels to alert when data changes.")
747
+ notifications: z.preprocess(coerceJson(), notificationChannelsSchema).describe("Notification channels to alert when data changes.")
821
748
  };
822
749
  DATA_FILTER_OPERATORS = [
823
750
  "EQUALS",
@@ -842,10 +769,10 @@ var init_schemas = __esm(() => {
842
769
  ];
843
770
  DATA_FILTER_VALUELESS_OPERATORS = new Set(["IS_NULL", "IS_NOT_NULL", "IS_EMPTY", "IS_NOT_EMPTY"]);
844
771
  DATA_FILTER_LIST_OPERATORS = new Set(["IN", "NOT_IN"]);
845
- DataFilterSchema = z3.object({
846
- field: z3.string().trim().min(1).describe("Field name from the workflow schema; one nested level is allowed, e.g. classification.match"),
847
- operator: z3.preprocess((val) => typeof val === "string" ? val.trim().toUpperCase() : val, z3.enum(DATA_FILTER_OPERATORS)),
848
- value: z3.union([z3.string(), z3.number(), z3.boolean(), z3.array(z3.union([z3.string(), z3.number()]))]).optional().describe("Omit for IS_NULL, IS_NOT_NULL, IS_EMPTY, IS_NOT_EMPTY; an array for IN/NOT_IN; days for WITHIN_LAST_DAYS")
772
+ DataFilterSchema = z.object({
773
+ field: z.string().trim().min(1).describe("Field name from the workflow schema; one nested level is allowed, e.g. classification.match"),
774
+ operator: z.preprocess((val) => typeof val === "string" ? val.trim().toUpperCase() : val, z.enum(DATA_FILTER_OPERATORS)),
775
+ value: z.union([z.string(), z.number(), z.boolean(), z.array(z.union([z.string(), z.number()]))]).optional().describe("Omit for IS_NULL, IS_NOT_NULL, IS_EMPTY, IS_NOT_EMPTY; an array for IN/NOT_IN; days for WITHIN_LAST_DAYS")
849
776
  }).strict().superRefine((filter, ctx) => {
850
777
  if (DATA_FILTER_VALUELESS_OPERATORS.has(filter.operator)) {
851
778
  if (filter.value !== undefined) {
@@ -870,9 +797,259 @@ var init_schemas = __esm(() => {
870
797
  });
871
798
  }
872
799
  });
873
- dataFiltersInput = z3.preprocess(normalizeDataFilters, z3.array(DataFilterSchema).optional()).optional().describe('Row filters, e.g. [{"field":"title","operator":"CONTAINS","value":"Manager"}]. Multiple filters are combined with OR. BEFORE, AFTER and WITHIN_LAST_DAYS only apply to DATE/DATETIME fields.');
800
+ dataFiltersInput = z.preprocess(normalizeDataFilters, z.array(DataFilterSchema).optional()).optional().describe('Row filters, e.g. [{"field":"title","operator":"CONTAINS","value":"Manager"}]. Multiple filters are combined with OR. BEFORE, AFTER and WITHIN_LAST_DAYS only apply to DATE/DATETIME fields.');
801
+ });
802
+
803
+ // src/tools/shared/output-schemas.ts
804
+ import { z as z2 } from "zod";
805
+ var apiField, nullableString, PaginationOutput, workflowStatusFields, WorkflowRowOutput, ListWorkflowsOutput, GetWorkflowOutput, FetchDataOutput, ASSISTANT_ACTIVITIES, WorkflowAssistantOutput, AssistantTimelineOutput, AssistantAnswerOutput, AssistantControlOutput, WorkflowStrategyOutput;
806
+ var init_output_schemas = __esm(() => {
807
+ init_schemas();
808
+ apiField = z2.unknown().optional();
809
+ nullableString = z2.string().nullable();
810
+ PaginationOutput = portable(z2.object({
811
+ totalCount: z2.number().optional(),
812
+ hasMore: z2.boolean(),
813
+ limit: z2.number().optional(),
814
+ skip: z2.number().optional(),
815
+ offset: z2.number().optional(),
816
+ page: z2.number().optional(),
817
+ totalPages: z2.number().optional(),
818
+ nextCursor: nullableString.optional(),
819
+ next: z2.record(z2.string(), z2.unknown()).optional()
820
+ }).loose());
821
+ workflowStatusFields = {
822
+ status: z2.string(),
823
+ badges: z2.array(z2.object({ kind: z2.string(), status: z2.string(), label: z2.string() }).loose()),
824
+ group: nullableString,
825
+ reviewReason: z2.object({ code: z2.string(), explanation: z2.string() }).loose().nullable(),
826
+ execution: apiField,
827
+ assistantState: apiField,
828
+ workspaceActivity: apiField
829
+ };
830
+ WorkflowRowOutput = z2.object({
831
+ id: apiField,
832
+ name: apiField,
833
+ dashboardUrl: z2.string(),
834
+ ...workflowStatusFields,
835
+ state: nullableString,
836
+ displayState: nullableString,
837
+ runState: nullableString,
838
+ urls: apiField,
839
+ totalRecords: apiField,
840
+ startedAt: apiField,
841
+ finishedAt: apiField,
842
+ createdAt: apiField,
843
+ assistant: z2.object({
844
+ eligible: z2.boolean(),
845
+ session: z2.object({ id: z2.string(), threadId: apiField, status: apiField }).loose().nullable()
846
+ }).loose(),
847
+ template: z2.object({ id: z2.string(), pinnedVersion: apiField, latestVersion: apiField, isOutdated: z2.boolean() }).loose().nullable(),
848
+ realtime: z2.object({ enabled: z2.boolean(), lastDataChangedAt: apiField, health: apiField }).loose(),
849
+ awaitingUserInput: apiField
850
+ }).loose();
851
+ ListWorkflowsOutput = portable(z2.object({ workflows: z2.array(WorkflowRowOutput), pagination: PaginationOutput }).loose());
852
+ GetWorkflowOutput = portable(z2.object({
853
+ id: z2.string(),
854
+ name: apiField,
855
+ dashboardUrl: z2.string(),
856
+ description: apiField,
857
+ ...workflowStatusFields,
858
+ state: nullableString,
859
+ displayState: nullableString,
860
+ runState: nullableString,
861
+ urls: apiField,
862
+ prompt: nullableString,
863
+ intentSource: z2.enum(["extraction_spec", "legacy_prompt"]).nullable(),
864
+ extractionSpec: apiField,
865
+ entity: apiField,
866
+ schema: apiField,
867
+ navigationMode: apiField,
868
+ assistantEligible: z2.boolean(),
869
+ assistantSessionId: apiField,
870
+ assistantThreadId: apiField,
871
+ assistantSessionStatus: apiField,
872
+ assistant: z2.object({ eligible: z2.boolean(), sessionId: apiField, threadId: apiField, sessionStatus: apiField }).loose(),
873
+ template: z2.object({
874
+ id: apiField,
875
+ name: apiField,
876
+ version: apiField,
877
+ controlledParts: z2.array(z2.string()),
878
+ hasSchemaValidationRules: apiField
879
+ }).loose().nullable(),
880
+ updateInterval: apiField,
881
+ schedules: apiField,
882
+ scheduleTimezone: apiField,
883
+ nextInvocation: apiField,
884
+ location: apiField,
885
+ limit: apiField,
886
+ tags: apiField,
887
+ totalRecords: apiField,
888
+ job: z2.object({ id: apiField }).loose().nullable(),
889
+ startedAt: apiField,
890
+ finishedAt: apiField,
891
+ estimatedFinishedAtUtc: apiField,
892
+ errors: apiField,
893
+ createdAt: apiField,
894
+ notificationConfig: apiField,
895
+ channelCounts: apiField,
896
+ realtime: z2.object({ lastDataChangedAt: apiField, observerHealth: apiField }).loose().nullable()
897
+ }).loose());
898
+ FetchDataOutput = portable(z2.object({
899
+ data: z2.array(z2.record(z2.string(), z2.unknown())),
900
+ pagination: PaginationOutput,
901
+ totalCount: z2.number().optional(),
902
+ hint: z2.string().optional()
903
+ }).loose());
904
+ ASSISTANT_ACTIVITIES = [
905
+ "unsupported",
906
+ "not_started",
907
+ "working",
908
+ "waiting_for_input",
909
+ "idle",
910
+ "interrupted",
911
+ "failed",
912
+ "closed",
913
+ "unknown"
914
+ ];
915
+ WorkflowAssistantOutput = portable(z2.object({
916
+ workflowId: z2.string(),
917
+ assistantEligible: z2.boolean(),
918
+ sessionId: nullableString,
919
+ threadId: apiField,
920
+ sessionStatus: apiField,
921
+ activity: z2.enum(ASSISTANT_ACTIVITIES),
922
+ canAcceptUpdate: z2.boolean(),
923
+ isTerminal: z2.boolean(),
924
+ paused: apiField,
925
+ pause: apiField,
926
+ pendingQuestion: apiField
927
+ }).loose());
928
+ AssistantTimelineOutput = portable(z2.object({
929
+ workflowId: z2.string(),
930
+ sessionId: apiField,
931
+ items: z2.array(z2.unknown()),
932
+ pagination: PaginationOutput,
933
+ dashboardUrl: z2.string()
934
+ }).loose());
935
+ AssistantAnswerOutput = portable(z2.object({
936
+ success: z2.literal(true),
937
+ workflowId: z2.string(),
938
+ sessionId: z2.string(),
939
+ threadId: apiField,
940
+ questionId: z2.string(),
941
+ message: z2.string()
942
+ }).loose());
943
+ AssistantControlOutput = portable(z2.object({
944
+ success: z2.boolean(),
945
+ status: z2.literal("accepted"),
946
+ action: z2.enum(["interrupt", "resume", "stop"]),
947
+ workflowId: z2.string(),
948
+ sessionId: z2.string(),
949
+ delivery: apiField,
950
+ jobId: apiField,
951
+ message: z2.string()
952
+ }).loose());
953
+ WorkflowStrategyOutput = portable(z2.object({
954
+ workflowId: z2.string(),
955
+ sessionId: nullableString,
956
+ source: z2.enum(["assistant_build", "workflow_metadata"]).nullable(),
957
+ status: z2.enum(["ready", "building", "unavailable"]),
958
+ strategy: apiField,
959
+ message: z2.string().optional()
960
+ }).loose());
874
961
  });
875
962
 
963
+ // src/tools/shared/pagination.ts
964
+ import { z as z3 } from "zod";
965
+ function limitInput(options) {
966
+ const range = `limit must be a whole number from 1 to ${options.max}`;
967
+ const overMax = `${range}. ${options.overMaxHint ?? "Page with pagination.next."}`;
968
+ return z3.preprocess(coerceNumber(), z3.number().int(range).min(1, range).max(options.max, overMax)).optional().describe(`${options.noun} per page, 1-${options.max} (default ${options.defaultLimit})`);
969
+ }
970
+ function offsetInput(param, noun) {
971
+ const range = `${param} must be a whole number from 0 to ${MAX_OFFSET}`;
972
+ return z3.preprocess(coerceNumber(), z3.number().int(range).min(0, range).max(MAX_OFFSET, range)).optional().describe(`Number of ${noun} to skip (default 0)`);
973
+ }
974
+ function pageInput() {
975
+ const range = `page must be a whole number from 1 to ${MAX_PAGE}`;
976
+ return z3.preprocess(coerceNumber(), z3.number().int(range).min(1, range).max(MAX_PAGE, range)).optional().describe("Page number, 1-based (default 1)");
977
+ }
978
+ function nextArgs(args, patch) {
979
+ const next = {};
980
+ for (const [key, value] of Object.entries({ ...args, ...patch })) {
981
+ if (value !== undefined)
982
+ next[key] = value;
983
+ }
984
+ return next;
985
+ }
986
+ function withTotal(totalCount, hasMore) {
987
+ return totalCount !== undefined ? { totalCount, hasMore } : { hasMore };
988
+ }
989
+ function offsetPage(options) {
990
+ const nextOffset = options.offset + options.returned;
991
+ const more = options.hasMore ?? (options.totalCount !== undefined ? nextOffset < options.totalCount : options.returned >= options.limit);
992
+ const hasMore = options.returned > 0 && more;
993
+ const meta = withTotal(options.totalCount, hasMore);
994
+ meta.limit = options.limit;
995
+ meta[options.param] = options.offset;
996
+ if (hasMore)
997
+ meta.next = nextArgs(options.args, { [options.param]: nextOffset, limit: options.limit });
998
+ return meta;
999
+ }
1000
+ function numberedPage(options) {
1001
+ const more = options.totalPages !== undefined ? options.page < options.totalPages : options.totalCount !== undefined ? options.page * options.limit < options.totalCount : options.returned >= options.limit;
1002
+ const hasMore = options.returned > 0 && more;
1003
+ const meta = withTotal(options.totalCount, hasMore);
1004
+ meta.limit = options.limit;
1005
+ meta.page = options.page;
1006
+ if (options.totalPages !== undefined)
1007
+ meta.totalPages = options.totalPages;
1008
+ if (hasMore)
1009
+ meta.next = nextArgs(options.args, { page: options.page + 1, limit: options.limit });
1010
+ return meta;
1011
+ }
1012
+ function cursorPage(options) {
1013
+ const cursor = typeof options.nextCursor === "string" && options.nextCursor.length > 0 ? options.nextCursor : null;
1014
+ const meta = { hasMore: options.hasMore && cursor !== null, nextCursor: cursor };
1015
+ if (meta.hasMore)
1016
+ meta.next = nextArgs(options.args, { cursor });
1017
+ return meta;
1018
+ }
1019
+ function completeList(totalCount) {
1020
+ return { totalCount, hasMore: false };
1021
+ }
1022
+ var MAX_OFFSET = 1e6, MAX_PAGE = 1e5;
1023
+ var init_pagination = () => {};
1024
+
1025
+ // src/tools/shared/results.ts
1026
+ function asStructuredObject(data) {
1027
+ return data !== null && !Array.isArray(data) ? data : { result: data };
1028
+ }
1029
+ function structuredResult(data, extra = []) {
1030
+ const structuredContent = asStructuredObject(data);
1031
+ return {
1032
+ structuredContent,
1033
+ content: [{ type: "text", text: JSON.stringify(structuredContent) }, ...extra]
1034
+ };
1035
+ }
1036
+ function jsonResult(data) {
1037
+ return structuredResult(data);
1038
+ }
1039
+ function textOnlyResult(data, extra = []) {
1040
+ const structuredContent = asStructuredObject(data);
1041
+ return { content: [{ type: "text", text: JSON.stringify(structuredContent) }, ...extra] };
1042
+ }
1043
+ function errorResult(message, details = {}) {
1044
+ return {
1045
+ content: [{ type: "text", text: JSON.stringify({ error: message, ...details }) }],
1046
+ isError: true
1047
+ };
1048
+ }
1049
+ function withExtraContent(result, ...blocks) {
1050
+ return { ...result, content: [...result.content, ...blocks] };
1051
+ }
1052
+
876
1053
  // src/tools/assistant.ts
877
1054
  import { FREEFORM_ASSISTANT_ANSWER_KEY } from "@kadoa/node-sdk";
878
1055
  import { z as z4 } from "zod";
@@ -2395,13 +2572,13 @@ function registerTemplateTools(server, ctx, helpers) {
2395
2572
  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.
2396
2573
 
2397
2574
  ` + "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.",
2398
- inputSchema: withoutDefIds(strictSchema({
2575
+ inputSchema: strictSchema({
2399
2576
  templateId: z10.string().min(1).describe("The template ID to publish a version for"),
2400
2577
  prompt: z10.string().optional().describe("User prompt to copy into workflow config"),
2401
2578
  schemaId: z10.string().optional().describe("Existing schema ID to reference (mutually exclusive with schemaFields)"),
2402
2579
  schemaFields: z10.preprocess(coerceArray(), z10.array(z10.preprocess(normalizeTemplateSchemaField, z10.object(TemplateSchemaFieldShape).strict()))).optional().describe("Inline schema fields to create a new schema (mutually exclusive with schemaId)"),
2403
2580
  schemaEntity: z10.string().optional().describe("Entity name for the inline schema"),
2404
- schemaValidationRules: z10.preprocess((val) => stripSchemaValidationAttribution(coerceJson()(val)), 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. Edit attribution is stamped server-side, so send only rule values. Example: { "price": { "kind": "NUMBER", "minimum": { "value": 0 } } }. Not inherited from prior versions - omitting this on a new version drops any rules the previous version had.'),
2581
+ schemaValidationRules: z10.preprocess((val) => stripSchemaValidationAttribution(coerceJson()(val)), SchemaValidationRulesSchema).optional().describe("Per-field schema validation rules keyed by field name. Each value is an object with a required `kind` and optional rules for that kind; unknown keys are rejected. " + "Every kind accepts presence and uniqueness, each { target: integer 0-100 in steps of 20 } (expected percentage of rows with a value / a unique value). " + "STRING: minLength, maxLength, minHtmlElements, maxHtmlElements, each { value: integer >= 0 }; format, one of " + '{ kind: "FORMAT", source: { kind: "PRESET", preset: url | email | phone | date | datetime | time | uuid | slug } or { kind: "CUSTOM", pattern: regex string } }, ' + '{ kind: "FREE_TEXT", charset: { kind: "PRESET", preset: natural_language | alphanumeric | alpha } }, or ' + '{ kind: "LIST", source: { kind: "PRESET", preset: language2 | country2 | country3 | currency3 | month3 | usState2 } or { kind: "CUSTOM", values: [non-empty strings] } }. ' + "NUMBER: minimum and maximum, each { value: number }; maxDecimalPlaces { value: integer 0-16 }. " + "OTHER: only presence and uniqueness. " + "OBJECT: properties (required), a map of nested field name to rule. " + "ARRAY: minItems, maxItems, each { value: integer >= 0 }; items, the rule for each element. " + 'Examples: { "price": { "kind": "NUMBER", "minimum": { "value": 0 } } }; { "email": { "kind": "STRING", "format": { "kind": "FORMAT", "source": { "kind": "PRESET", "preset": "email" } } } }. ' + "Edit attribution is stamped server-side, so send only rule values. Not inherited from prior versions - omitting this on a new version drops any rules the previous version had."),
2405
2582
  notifications: z10.preprocess(coerceArray(), z10.array(z10.object({
2406
2583
  eventType: z10.string().describe("Notification event type"),
2407
2584
  eventConfiguration: z10.preprocess(coerceJson(), z10.record(z10.string(), z10.unknown())).optional(),
@@ -2417,7 +2594,7 @@ function registerTemplateTools(server, ctx, helpers) {
2417
2594
  expressions: z10.preprocess(coerceArray(true), z10.array(z10.string())).optional().describe("Cron expressions when type='cron' (e.g. '0 8 * * 2')")
2418
2595
  }))).optional().describe("Schedule specs. Required when interval='CUSTOM'.")
2419
2596
  })).optional().describe("Template-controlled scheduling for linked workflows ({interval, schedules}). Linked workflows inherit and cannot override this when set.")
2420
- })),
2597
+ }),
2421
2598
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }
2422
2599
  }, withErrorHandling("create_template_version", async (args) => {
2423
2600
  const { templateId, schemaValidationRules, ...rest } = args;
@@ -3747,7 +3924,7 @@ function registerWorkflowRunTools(server, ctx, helpers) {
3747
3924
  timezone: z12.string().min(1).optional().describe("IANA timezone for cron schedules, for example America/New_York. A timezone-only update preserves existing cron expressions."),
3748
3925
  location: z12.preprocess(coerceJson(), LocationSchema).optional().describe("Scraping location: {type:'auto'} or {type:'manual', isoCode:'US'}"),
3749
3926
  monitoring: z12.preprocess(coerceJson(), MonitoringSchema).optional().describe("Complete monitoring replacement: fields to monitor and optional AND/OR conditions. Call get_workflow first because this replaces the existing monitoring configuration."),
3750
- limit: z12.preprocess(coerceNumber(), z12.number().positive().nullable()).optional().describe("Maximum records per run. Pass null to clear the limit and extract all rows.")
3927
+ limit: z12.preprocess(coerceNumber(), z12.number().positive().nullable()).optional().describe("Maximum records per run; must be positive (greater than 0). Pass null to clear the limit and extract all rows.")
3751
3928
  }),
3752
3929
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }
3753
3930
  }, withErrorHandling("update_workflow", async (args) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kadoa/mcp",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Kadoa MCP Server — manage workflows from Claude Desktop, Cursor, and other MCP clients",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",