@kadoa/mcp 0.3.5-rc.4 → 0.3.5-rc.6
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/dist/index.js +92 -16
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -49167,6 +49167,9 @@ var init_client2 = __esm(() => {
|
|
|
49167
49167
|
});
|
|
49168
49168
|
|
|
49169
49169
|
// src/tools.ts
|
|
49170
|
+
function strictSchema(shape) {
|
|
49171
|
+
return exports_external.object(shape).strict();
|
|
49172
|
+
}
|
|
49170
49173
|
function coerceArray(wrapPlainString = false) {
|
|
49171
49174
|
return (val) => {
|
|
49172
49175
|
if (typeof val === "string") {
|
|
@@ -49258,6 +49261,24 @@ function classifyError(error48) {
|
|
|
49258
49261
|
return "Unknown error";
|
|
49259
49262
|
}
|
|
49260
49263
|
function registerTools(server, ctx) {
|
|
49264
|
+
async function elicit(message, schema, required2) {
|
|
49265
|
+
try {
|
|
49266
|
+
const result = await server.server.elicitInput({
|
|
49267
|
+
message,
|
|
49268
|
+
requestedSchema: {
|
|
49269
|
+
type: "object",
|
|
49270
|
+
properties: schema,
|
|
49271
|
+
...required2 ? { required: required2 } : {}
|
|
49272
|
+
}
|
|
49273
|
+
});
|
|
49274
|
+
if (result.action === "accept" && result.content) {
|
|
49275
|
+
return result.content;
|
|
49276
|
+
}
|
|
49277
|
+
return null;
|
|
49278
|
+
} catch {
|
|
49279
|
+
return null;
|
|
49280
|
+
}
|
|
49281
|
+
}
|
|
49261
49282
|
function withErrorHandling(name, handler) {
|
|
49262
49283
|
return async (...args) => {
|
|
49263
49284
|
try {
|
|
@@ -49298,8 +49319,8 @@ function registerTools(server, ctx) {
|
|
|
49298
49319
|
` + `• REAL_TIME workflows are fundamentally different from regular extraction workflows. They route to a separate Observer service, run continuously, and cannot be triggered manually.
|
|
49299
49320
|
` + `• A regular workflow CANNOT be converted to real-time after creation (and vice versa). The interval=REAL_TIME flag MUST be set at creation time.
|
|
49300
49321
|
` + `• If the user wants to monitor a page for changes, set interval='REAL_TIME' here. Do NOT create a regular workflow and then try to change its interval to REAL_TIME via update_workflow — this will be rejected.
|
|
49301
|
-
` + "• When
|
|
49302
|
-
inputSchema: {
|
|
49322
|
+
` + "• When the user says 'monitor', 'track', 'watch', or 'alert me' — ASK if they want real-time continuous monitoring (interval='REAL_TIME') or scheduled extraction. Do NOT assume one or the other.",
|
|
49323
|
+
inputSchema: strictSchema({
|
|
49303
49324
|
prompt: exports_external.string().describe('Natural language description of what to extract (e.g., "Extract product prices and names")'),
|
|
49304
49325
|
url: exports_external.string().optional().describe("Single URL — prefer using 'urls' instead. If both are provided, 'urls' takes precedence."),
|
|
49305
49326
|
urls: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string()).min(1)).optional().describe("Starting URLs for the workflow (array of strings). Also accepts a single URL string."),
|
|
@@ -49336,16 +49357,17 @@ function registerTools(server, ctx) {
|
|
|
49336
49357
|
}).optional().describe("Send notifications to a Slack channel"),
|
|
49337
49358
|
websocket: exports_external.boolean().optional().describe("Enable WebSocket notifications for programmatic real-time consumption")
|
|
49338
49359
|
}).optional().describe("Notification channels to alert when data changes. At least one channel is required for REAL_TIME workflows.")
|
|
49339
|
-
},
|
|
49360
|
+
}),
|
|
49340
49361
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
|
|
49341
49362
|
}, withErrorHandling("create_workflow", async (args) => {
|
|
49342
49363
|
const urls = args.urls ?? (args.url ? [args.url] : []);
|
|
49343
49364
|
if (urls.length === 0) {
|
|
49344
49365
|
return errorResult("At least one URL is required. Provide 'urls' (array) or 'url' (string).");
|
|
49345
49366
|
}
|
|
49367
|
+
const interval = args.interval;
|
|
49346
49368
|
const n = args.notifications;
|
|
49347
49369
|
const hasNotifications = n && (n.email || n.webhook || n.slack || n.websocket);
|
|
49348
|
-
if (
|
|
49370
|
+
if (interval === "REAL_TIME" && !hasNotifications) {
|
|
49349
49371
|
return errorResult("Real-time workflows require at least one notification channel so you get alerted when data changes. " + "Add a notifications object with one or more channels: email, webhook, slack, or websocket.");
|
|
49350
49372
|
}
|
|
49351
49373
|
const extraction = args.schema ? (builder2) => {
|
|
@@ -49355,14 +49377,14 @@ function registerTools(server, ctx) {
|
|
|
49355
49377
|
}
|
|
49356
49378
|
return entity;
|
|
49357
49379
|
} : undefined;
|
|
49358
|
-
const isRealTime =
|
|
49380
|
+
const isRealTime = interval === "REAL_TIME";
|
|
49359
49381
|
let builder = ctx.client.extract({
|
|
49360
49382
|
urls,
|
|
49361
49383
|
name: args.name || "Untitled Workflow",
|
|
49362
49384
|
...isRealTime ? {} : { navigationMode: "agentic-navigation" },
|
|
49363
49385
|
userPrompt: args.prompt,
|
|
49364
49386
|
extraction,
|
|
49365
|
-
interval
|
|
49387
|
+
interval
|
|
49366
49388
|
});
|
|
49367
49389
|
if (hasNotifications) {
|
|
49368
49390
|
const channels = {};
|
|
@@ -49401,7 +49423,7 @@ function registerTools(server, ctx) {
|
|
|
49401
49423
|
if (n?.websocket)
|
|
49402
49424
|
enabledChannels.push("websocket");
|
|
49403
49425
|
let message = "Workflow created successfully.";
|
|
49404
|
-
if (
|
|
49426
|
+
if (interval === "REAL_TIME") {
|
|
49405
49427
|
message += " This real-time workflow monitors continuously — no manual runs needed.";
|
|
49406
49428
|
} else {
|
|
49407
49429
|
message += " The AI agent will start extracting data automatically.";
|
|
@@ -49516,16 +49538,40 @@ function registerTools(server, ctx) {
|
|
|
49516
49538
|
});
|
|
49517
49539
|
}));
|
|
49518
49540
|
server.registerTool("delete_workflow", {
|
|
49519
|
-
description: "Delete a workflow permanently",
|
|
49541
|
+
description: "Delete a workflow permanently. This is irreversible. " + "You MUST first call this tool without confirmed=true to preview what will be deleted, " + "then show the user the workflow name and ask for confirmation, " + "then call again with confirmed=true only after the user explicitly agrees.",
|
|
49520
49542
|
inputSchema: {
|
|
49521
|
-
workflowId: exports_external.string().describe("The workflow ID to delete")
|
|
49543
|
+
workflowId: exports_external.string().describe("The workflow ID to delete"),
|
|
49544
|
+
confirmed: exports_external.boolean().optional().describe("Set to true only after the user has explicitly confirmed deletion. Omit or set to false for the initial preview call.")
|
|
49522
49545
|
},
|
|
49523
49546
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true }
|
|
49524
49547
|
}, withErrorHandling("delete_workflow", async (args) => {
|
|
49548
|
+
let workflowName = args.workflowId;
|
|
49549
|
+
let workflowStatus = "unknown";
|
|
49550
|
+
try {
|
|
49551
|
+
const workflow = await ctx.client.workflow.get(args.workflowId);
|
|
49552
|
+
workflowName = workflow.name || args.workflowId;
|
|
49553
|
+
workflowStatus = workflow.displayState ?? workflow.state ?? "unknown";
|
|
49554
|
+
} catch {}
|
|
49555
|
+
if (!args.confirmed) {
|
|
49556
|
+
const confirmation = await elicit(`Are you sure you want to permanently delete "${workflowName}" (${workflowStatus})? This cannot be undone.`, { confirm: { type: "boolean", description: "Confirm deletion" } }, ["confirm"]);
|
|
49557
|
+
if (confirmation !== null) {
|
|
49558
|
+
if (!confirmation.confirm) {
|
|
49559
|
+
return jsonResult({ success: false, message: "Deletion cancelled by user." });
|
|
49560
|
+
}
|
|
49561
|
+
} else {
|
|
49562
|
+
return jsonResult({
|
|
49563
|
+
pending: true,
|
|
49564
|
+
workflowId: args.workflowId,
|
|
49565
|
+
workflowName,
|
|
49566
|
+
status: workflowStatus,
|
|
49567
|
+
message: `⚠️ You are about to permanently delete "${workflowName}" (${workflowStatus}). This cannot be undone. Ask the user to confirm, then call delete_workflow again with confirmed=true.`
|
|
49568
|
+
});
|
|
49569
|
+
}
|
|
49570
|
+
}
|
|
49525
49571
|
await ctx.client.workflow.delete(args.workflowId);
|
|
49526
49572
|
return jsonResult({
|
|
49527
49573
|
success: true,
|
|
49528
|
-
message:
|
|
49574
|
+
message: `Workflow "${workflowName}" deleted successfully`
|
|
49529
49575
|
});
|
|
49530
49576
|
}));
|
|
49531
49577
|
server.registerTool("approve_workflow", {
|
|
@@ -49546,7 +49592,7 @@ function registerTools(server, ctx) {
|
|
|
49546
49592
|
description: `Update a workflow's configuration. All fields are optional — only provided fields will be updated. Use this to change the name, URLs, extraction schema, entity, prompt, schedule, or other metadata.
|
|
49547
49593
|
|
|
49548
49594
|
` + "IMPORTANT: You cannot change a workflow's interval to or from REAL_TIME. Real-time workflows are architecturally different (Observer service vs Scraper) and must be created as real-time from the start using create_workflow with interval='REAL_TIME'. To switch a workflow between real-time and regular, delete it and create a new one with the correct interval.",
|
|
49549
|
-
inputSchema: {
|
|
49595
|
+
inputSchema: strictSchema({
|
|
49550
49596
|
workflowId: exports_external.string().describe("The workflow ID to update"),
|
|
49551
49597
|
name: exports_external.string().optional().describe("New name for the workflow"),
|
|
49552
49598
|
urls: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string()).min(1)).optional().describe("New target URLs for the workflow (array of strings). Also accepts a single URL string."),
|
|
@@ -49576,7 +49622,7 @@ function registerTools(server, ctx) {
|
|
|
49576
49622
|
]).optional().describe("How often the workflow should run. CUSTOM requires schedules. Note: REAL_TIME is NOT allowed here — real-time workflows must be created via create_workflow."),
|
|
49577
49623
|
schedules: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string())).optional().describe("Cron expressions for CUSTOM update interval"),
|
|
49578
49624
|
limit: exports_external.number().optional().describe("Maximum number of records to extract per run")
|
|
49579
|
-
},
|
|
49625
|
+
}),
|
|
49580
49626
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true }
|
|
49581
49627
|
}, withErrorHandling("update_workflow", async (args) => {
|
|
49582
49628
|
const { workflowId, ...updates } = args;
|
|
@@ -49649,12 +49695,27 @@ function registerTools(server, ctx) {
|
|
|
49649
49695
|
});
|
|
49650
49696
|
}));
|
|
49651
49697
|
server.registerTool("delete_notification_channel", {
|
|
49652
|
-
description: "Delete a notification channel by ID",
|
|
49698
|
+
description: "Delete a notification channel by ID. " + "Call first without confirmed=true to preview, then with confirmed=true after user agrees.",
|
|
49653
49699
|
inputSchema: {
|
|
49654
|
-
channelId: exports_external.string().describe("The channel ID to delete")
|
|
49700
|
+
channelId: exports_external.string().describe("The channel ID to delete"),
|
|
49701
|
+
confirmed: exports_external.boolean().optional().describe("Set to true only after the user has explicitly confirmed deletion.")
|
|
49655
49702
|
},
|
|
49656
49703
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true }
|
|
49657
49704
|
}, withErrorHandling("delete_notification_channel", async (args) => {
|
|
49705
|
+
if (!args.confirmed) {
|
|
49706
|
+
const confirmation = await elicit(`Are you sure you want to delete notification channel "${args.channelId}"?`, { confirm: { type: "boolean", description: "Confirm deletion" } }, ["confirm"]);
|
|
49707
|
+
if (confirmation !== null) {
|
|
49708
|
+
if (!confirmation.confirm) {
|
|
49709
|
+
return jsonResult({ success: false, message: "Deletion cancelled by user." });
|
|
49710
|
+
}
|
|
49711
|
+
} else {
|
|
49712
|
+
return jsonResult({
|
|
49713
|
+
pending: true,
|
|
49714
|
+
channelId: args.channelId,
|
|
49715
|
+
message: `⚠️ About to delete notification channel "${args.channelId}". Ask the user to confirm, then call again with confirmed=true.`
|
|
49716
|
+
});
|
|
49717
|
+
}
|
|
49718
|
+
}
|
|
49658
49719
|
await ctx.client.notification.channels.deleteChannel(args.channelId);
|
|
49659
49720
|
return jsonResult({
|
|
49660
49721
|
success: true,
|
|
@@ -49765,12 +49826,27 @@ function registerTools(server, ctx) {
|
|
|
49765
49826
|
});
|
|
49766
49827
|
}));
|
|
49767
49828
|
server.registerTool("delete_notification_setting", {
|
|
49768
|
-
description: "Delete a notification setting by ID. This removes the event-to-channel mapping but does not delete the channels themselves.",
|
|
49829
|
+
description: "Delete a notification setting by ID. This removes the event-to-channel mapping but does not delete the channels themselves. " + "Call first without confirmed=true to preview, then with confirmed=true after user agrees.",
|
|
49769
49830
|
inputSchema: {
|
|
49770
|
-
settingsId: exports_external.string().describe("The notification settings ID to delete")
|
|
49831
|
+
settingsId: exports_external.string().describe("The notification settings ID to delete"),
|
|
49832
|
+
confirmed: exports_external.boolean().optional().describe("Set to true only after the user has explicitly confirmed deletion.")
|
|
49771
49833
|
},
|
|
49772
49834
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true }
|
|
49773
49835
|
}, withErrorHandling("delete_notification_setting", async (args) => {
|
|
49836
|
+
if (!args.confirmed) {
|
|
49837
|
+
const confirmation = await elicit(`Are you sure you want to delete notification setting "${args.settingsId}"?`, { confirm: { type: "boolean", description: "Confirm deletion" } }, ["confirm"]);
|
|
49838
|
+
if (confirmation !== null) {
|
|
49839
|
+
if (!confirmation.confirm) {
|
|
49840
|
+
return jsonResult({ success: false, message: "Deletion cancelled by user." });
|
|
49841
|
+
}
|
|
49842
|
+
} else {
|
|
49843
|
+
return jsonResult({
|
|
49844
|
+
pending: true,
|
|
49845
|
+
settingsId: args.settingsId,
|
|
49846
|
+
message: `⚠️ About to delete notification setting "${args.settingsId}". Ask the user to confirm, then call again with confirmed=true.`
|
|
49847
|
+
});
|
|
49848
|
+
}
|
|
49849
|
+
}
|
|
49774
49850
|
await ctx.client.notification.settings.deleteSettings(args.settingsId);
|
|
49775
49851
|
return jsonResult({
|
|
49776
49852
|
success: true,
|