@kadoa/mcp 0.7.1 → 0.7.3
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.
- package/README.md +4 -7
- package/dist/index.js +576 -366
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -163,6 +163,7 @@ Point your client to `https://mcp.kadoa.com/mcp` with OAuth authentication.
|
|
|
163
163
|
|------|-------------|
|
|
164
164
|
| `whoami` | Show current user details, auth method, and team memberships |
|
|
165
165
|
| `team_list` | List all teams you belong to and see which is active |
|
|
166
|
+
| `list_compliance_rules` | Read the compliance rules applied to your team (robots.txt, terms of service, blocked sources, PII, and similar), with each rule's enabled status and configuration. Read-only |
|
|
166
167
|
|
|
167
168
|
Team switching (a team_switch tool) exists in the code but is currently
|
|
168
169
|
disabled and hidden from every client's tool list — a credential is fixed to
|
|
@@ -544,15 +545,13 @@ A typical feature touches three repos — **`kadoa-backend` → `kadoa-sdks` →
|
|
|
544
545
|
| Backend endpoint | `kadoa-backend/` | The HTTP API. Source of truth; the OpenAPI spec is generated from here. |
|
|
545
546
|
| SDK low-level | `kadoa-sdks/sdks/node/src/generated/api/*` | Auto-generated axios client. Raw HTTP, typed from the OpenAPI spec. **Do not hand-edit.** |
|
|
546
547
|
| SDK domain (high-level) | `kadoa-sdks/sdks/node/src/domains/<thing>/<thing>.service.ts` | Thin domain wrapper over the generated client. `client.<thing>.<action>(...)`. This is what callers use. |
|
|
547
|
-
| CLI | `kadoa-cli/src/commands/<thing>.ts` | Commander action: flag parsing, spinner, table/json output. Calls SDK domain. |
|
|
548
548
|
| MCP (this repo) | `kadoa-mcp/src/tools/<domain>.ts` | Zod input schema + tool description tuned for LLMs. Calls SDK domain. |
|
|
549
549
|
|
|
550
550
|
Mental model:
|
|
551
551
|
- **SDK** = pure function over HTTP.
|
|
552
|
-
- **CLI** = SDK + human UX (flags, table output).
|
|
553
552
|
- **MCP** = SDK + LLM UX (zod schema, prose description).
|
|
554
553
|
|
|
555
|
-
|
|
554
|
+
MCP calls SDK domain methods, never raw HTTP. Any other SDK consumer gets new features for free — as long as we keep the layering honest.
|
|
556
555
|
|
|
557
556
|
### Adding a new feature
|
|
558
557
|
|
|
@@ -560,7 +559,7 @@ CLI and MCP both call **identical** SDK domain methods. A new feature added to t
|
|
|
560
559
|
- Add or modify the HTTP endpoint.
|
|
561
560
|
- Make sure the OpenAPI spec reflects the change (paths, request/response schemas).
|
|
562
561
|
|
|
563
|
-
2. **SDK** (`kadoa-sdks`) — always do this **before** MCP
|
|
562
|
+
2. **SDK** (`kadoa-sdks`) — always do this **before** MCP work (the SDK-first rule). No bandaid `axios` calls in MCP.
|
|
564
563
|
- Regenerate the low-level client:
|
|
565
564
|
```bash
|
|
566
565
|
bun kadoa-codegen fetch-spec -e https://api.kadoa.com/openapi -f
|
|
@@ -578,11 +577,9 @@ CLI and MCP both call **identical** SDK domain methods. A new feature added to t
|
|
|
578
577
|
- Description string written **for the LLM**: when to call it, what inputs mean, what the response shape is.
|
|
579
578
|
- Implementation calls `ctx.client.<thing>.<action>(...)` — never raw HTTP.
|
|
580
579
|
- 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`.
|
|
580
|
+
- 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
581
|
- Use a Conventional Commit message (`feat(mcp): ...`, `fix(mcp): ...`). Release Please will cut the next version on merge to `main`.
|
|
583
582
|
|
|
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.
|
|
585
|
-
|
|
586
583
|
### Anti-patterns
|
|
587
584
|
|
|
588
585
|
- ❌ Raw `axios` calls or hand-rolled HTTP in `src/tools/`. Always go through `ctx.client.<thing>`.
|
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.
|
|
20
|
+
version: "0.7.3",
|
|
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/
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
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
|
-
|
|
461
|
-
|
|
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
|
|
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
|
|
457
|
+
return portable(z.object(shape).strict());
|
|
528
458
|
}
|
|
529
|
-
function
|
|
459
|
+
function portable(schema) {
|
|
530
460
|
const jsonSchema = schema["~standard"].jsonSchema;
|
|
531
461
|
const emitInput = jsonSchema.input;
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
606
|
-
SchemaFieldDataTypeSchema =
|
|
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:
|
|
620
|
-
description:
|
|
621
|
-
example:
|
|
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:
|
|
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 =
|
|
626
|
-
target:
|
|
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 =
|
|
629
|
-
target:
|
|
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 =
|
|
632
|
-
value:
|
|
558
|
+
SchemaValidationBoundRule = z.object({
|
|
559
|
+
value: z.number().int().min(0).describe("Length or count bound")
|
|
633
560
|
}).strict().meta({ id: "BoundRule" });
|
|
634
|
-
SchemaValidationStringFormatRule =
|
|
635
|
-
|
|
636
|
-
kind:
|
|
637
|
-
charset:
|
|
638
|
-
|
|
639
|
-
kind:
|
|
640
|
-
preset:
|
|
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
|
-
|
|
645
|
-
kind:
|
|
646
|
-
source:
|
|
647
|
-
|
|
648
|
-
kind:
|
|
649
|
-
preset:
|
|
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
|
-
|
|
652
|
-
kind:
|
|
653
|
-
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
|
-
|
|
658
|
-
kind:
|
|
659
|
-
source:
|
|
660
|
-
|
|
661
|
-
kind:
|
|
662
|
-
preset:
|
|
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
|
-
|
|
665
|
-
kind:
|
|
666
|
-
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 =
|
|
672
|
-
|
|
673
|
-
kind:
|
|
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
|
-
|
|
683
|
-
kind:
|
|
609
|
+
z.object({
|
|
610
|
+
kind: z.literal("NUMBER"),
|
|
684
611
|
presence: SchemaValidationPresenceRule.optional(),
|
|
685
612
|
uniqueness: SchemaValidationUniquenessRule.optional(),
|
|
686
|
-
minimum:
|
|
687
|
-
maximum:
|
|
688
|
-
maxDecimalPlaces:
|
|
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
|
-
|
|
691
|
-
kind:
|
|
617
|
+
z.object({
|
|
618
|
+
kind: z.literal("OTHER"),
|
|
692
619
|
presence: SchemaValidationPresenceRule.optional(),
|
|
693
620
|
uniqueness: SchemaValidationUniquenessRule.optional()
|
|
694
621
|
}).strict(),
|
|
695
|
-
|
|
696
|
-
kind:
|
|
622
|
+
z.object({
|
|
623
|
+
kind: z.literal("OBJECT"),
|
|
697
624
|
presence: SchemaValidationPresenceRule.optional(),
|
|
698
625
|
uniqueness: SchemaValidationUniquenessRule.optional(),
|
|
699
|
-
properties:
|
|
626
|
+
properties: z.record(z.string(), SchemaValidationFieldRulesSchema)
|
|
700
627
|
}).strict(),
|
|
701
|
-
|
|
702
|
-
kind:
|
|
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 =
|
|
637
|
+
SchemaValidationRulesSchema = z.record(z.string(), SchemaValidationFieldRulesSchema);
|
|
711
638
|
SCHEMA_VALIDATION_ATTRIBUTION_KEYS = new Set(["editedBy", "editedByLabel", "editedAt"]);
|
|
712
|
-
LocationSchema =
|
|
713
|
-
type:
|
|
714
|
-
isoCode:
|
|
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:
|
|
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:
|
|
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 =
|
|
747
|
-
MonitoringConditionSchema =
|
|
748
|
-
type:
|
|
749
|
-
field:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
773
|
-
fields:
|
|
774
|
-
fieldName:
|
|
775
|
-
operator:
|
|
776
|
-
isKeyField:
|
|
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:
|
|
779
|
-
logicalOperator:
|
|
780
|
-
conditions:
|
|
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:
|
|
785
|
-
urls:
|
|
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 =
|
|
714
|
+
extractionPromptInput = z.string().optional();
|
|
788
715
|
extractionInputShape = {
|
|
789
|
-
name:
|
|
790
|
-
entity:
|
|
791
|
-
schema:
|
|
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 =
|
|
794
|
-
type:
|
|
795
|
-
token:
|
|
796
|
-
username:
|
|
797
|
-
password:
|
|
798
|
-
headers:
|
|
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 =
|
|
801
|
-
email:
|
|
802
|
-
recipients:
|
|
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:
|
|
805
|
-
url:
|
|
806
|
-
httpMethod:
|
|
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:
|
|
810
|
-
webhookUrl:
|
|
811
|
-
slackChannelId:
|
|
812
|
-
slackChannelName:
|
|
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:
|
|
741
|
+
websocket: z.boolean().optional().describe("Enable WebSocket notifications for programmatic real-time consumption")
|
|
815
742
|
});
|
|
816
743
|
notificationsInputShape = {
|
|
817
|
-
notifications:
|
|
744
|
+
notifications: z.preprocess(coerceJson(), notificationChannelsSchema.optional()).optional().describe("Optional notification channels to alert when data changes.")
|
|
818
745
|
};
|
|
819
746
|
requiredNotificationsInputShape = {
|
|
820
|
-
notifications:
|
|
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 =
|
|
846
|
-
field:
|
|
847
|
-
operator:
|
|
848
|
-
value:
|
|
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 =
|
|
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";
|
|
@@ -1200,6 +1377,37 @@ var init_assistant = __esm(() => {
|
|
|
1200
1377
|
CLOSED_ASSISTANT_STATUSES = new Set(["failed", "errored", "aborted", "timed_out"]);
|
|
1201
1378
|
});
|
|
1202
1379
|
|
|
1380
|
+
// src/tools/compliance.ts
|
|
1381
|
+
function registerComplianceTools(server, ctx, helpers) {
|
|
1382
|
+
const { withErrorHandling } = helpers;
|
|
1383
|
+
server.registerTool("list_compliance_rules", {
|
|
1384
|
+
title: "List Compliance Rules",
|
|
1385
|
+
description: "List the compliance rules that apply to your team: rule type, name, description, whether it is enabled, and its configuration. " + "Rules are set at the organization level and apply to every team in it. " + "Use this for questions about robots.txt, terms of service, blocked sources or countries, PII handling, captchas, or why a workflow went to compliance review. " + "Read-only: it cannot change rules or grant exceptions; for that, contact Kadoa support.",
|
|
1386
|
+
inputSchema: strictSchema({}),
|
|
1387
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
1388
|
+
}, withErrorHandling("list_compliance_rules", async () => {
|
|
1389
|
+
const response = await ctx.client.axiosInstance.get(`/v4/team/${encodeURIComponent(ctx.teamId)}/compliance-rules`, { baseURL: ctx.client.baseUrl });
|
|
1390
|
+
const rules = response.data?.complianceRules;
|
|
1391
|
+
if (!Array.isArray(rules))
|
|
1392
|
+
throw new Error("Unexpected response from the compliance rules API.");
|
|
1393
|
+
return jsonResult({
|
|
1394
|
+
complianceRules: rules.map((rule) => ({
|
|
1395
|
+
ruleType: rule.ruleType,
|
|
1396
|
+
ruleName: rule.ruleName,
|
|
1397
|
+
description: rule.description ?? "",
|
|
1398
|
+
isEnabled: rule.isEnabled,
|
|
1399
|
+
configuration: rule.configuration ?? {},
|
|
1400
|
+
...rule.updatedAt ? { updatedAt: rule.updatedAt } : {}
|
|
1401
|
+
})),
|
|
1402
|
+
note: rules.length === 0 ? `No compliance rules are configured for your team. ${NO_CHANGES_NOTE}` : NO_CHANGES_NOTE
|
|
1403
|
+
});
|
|
1404
|
+
}));
|
|
1405
|
+
}
|
|
1406
|
+
var NO_CHANGES_NOTE = "Rules are set by Kadoa for your organization and are read-only here. To change a rule or request an exception, contact Kadoa support.";
|
|
1407
|
+
var init_compliance = __esm(() => {
|
|
1408
|
+
init_schemas();
|
|
1409
|
+
});
|
|
1410
|
+
|
|
1203
1411
|
// src/notification-channels.ts
|
|
1204
1412
|
import { createHash } from "node:crypto";
|
|
1205
1413
|
function recipientKey(recipients) {
|
|
@@ -2395,13 +2603,13 @@ function registerTemplateTools(server, ctx, helpers) {
|
|
|
2395
2603
|
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
2604
|
|
|
2397
2605
|
` + "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:
|
|
2606
|
+
inputSchema: strictSchema({
|
|
2399
2607
|
templateId: z10.string().min(1).describe("The template ID to publish a version for"),
|
|
2400
2608
|
prompt: z10.string().optional().describe("User prompt to copy into workflow config"),
|
|
2401
2609
|
schemaId: z10.string().optional().describe("Existing schema ID to reference (mutually exclusive with schemaFields)"),
|
|
2402
2610
|
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
2611
|
schemaEntity: z10.string().optional().describe("Entity name for the inline schema"),
|
|
2404
|
-
schemaValidationRules: z10.preprocess((val) => stripSchemaValidationAttribution(coerceJson()(val)), SchemaValidationRulesSchema).optional().describe(
|
|
2612
|
+
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
2613
|
notifications: z10.preprocess(coerceArray(), z10.array(z10.object({
|
|
2406
2614
|
eventType: z10.string().describe("Notification event type"),
|
|
2407
2615
|
eventConfiguration: z10.preprocess(coerceJson(), z10.record(z10.string(), z10.unknown())).optional(),
|
|
@@ -2417,7 +2625,7 @@ function registerTemplateTools(server, ctx, helpers) {
|
|
|
2417
2625
|
expressions: z10.preprocess(coerceArray(true), z10.array(z10.string())).optional().describe("Cron expressions when type='cron' (e.g. '0 8 * * 2')")
|
|
2418
2626
|
}))).optional().describe("Schedule specs. Required when interval='CUSTOM'.")
|
|
2419
2627
|
})).optional().describe("Template-controlled scheduling for linked workflows ({interval, schedules}). Linked workflows inherit and cannot override this when set.")
|
|
2420
|
-
})
|
|
2628
|
+
}),
|
|
2421
2629
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }
|
|
2422
2630
|
}, withErrorHandling("create_template_version", async (args) => {
|
|
2423
2631
|
const { templateId, schemaValidationRules, ...rest } = args;
|
|
@@ -3747,7 +3955,7 @@ function registerWorkflowRunTools(server, ctx, helpers) {
|
|
|
3747
3955
|
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
3956
|
location: z12.preprocess(coerceJson(), LocationSchema).optional().describe("Scraping location: {type:'auto'} or {type:'manual', isoCode:'US'}"),
|
|
3749
3957
|
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.")
|
|
3958
|
+
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
3959
|
}),
|
|
3752
3960
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }
|
|
3753
3961
|
}, withErrorHandling("update_workflow", async (args) => {
|
|
@@ -3820,11 +4028,13 @@ function registerTools(server, ctx, capabilities) {
|
|
|
3820
4028
|
registerSupportTools(server, ctx, helpers);
|
|
3821
4029
|
registerNotificationSettingsTools(server, ctx, helpers);
|
|
3822
4030
|
registerTeamTools(server, ctx, helpers);
|
|
4031
|
+
registerComplianceTools(server, ctx, helpers);
|
|
3823
4032
|
registerVariableTools(server, ctx, helpers);
|
|
3824
4033
|
registerTemplateTools(server, ctx, helpers);
|
|
3825
4034
|
}
|
|
3826
4035
|
var init_tools = __esm(() => {
|
|
3827
4036
|
init_assistant();
|
|
4037
|
+
init_compliance();
|
|
3828
4038
|
init_notifications();
|
|
3829
4039
|
init_observability();
|
|
3830
4040
|
init_scrape();
|