@kadoa/mcp 0.3.5-rc.6 → 0.3.5
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 +153 -137
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -49261,24 +49261,6 @@ function classifyError(error48) {
|
|
|
49261
49261
|
return "Unknown error";
|
|
49262
49262
|
}
|
|
49263
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
|
-
}
|
|
49282
49264
|
function withErrorHandling(name, handler) {
|
|
49283
49265
|
return async (...args) => {
|
|
49284
49266
|
try {
|
|
@@ -49312,21 +49294,88 @@ function registerTools(server, ctx) {
|
|
|
49312
49294
|
}))
|
|
49313
49295
|
});
|
|
49314
49296
|
}));
|
|
49297
|
+
const urlInputShape = {
|
|
49298
|
+
url: exports_external.string().optional().describe("Single URL — prefer using 'urls' instead. If both are provided, 'urls' takes precedence."),
|
|
49299
|
+
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.")
|
|
49300
|
+
};
|
|
49301
|
+
const extractionInputShape = {
|
|
49302
|
+
prompt: exports_external.string().describe('Natural language description of what to extract (e.g., "Extract product prices and names")'),
|
|
49303
|
+
name: exports_external.string().optional().describe("Optional name for the workflow"),
|
|
49304
|
+
entity: exports_external.string().optional().describe("Entity name for extraction (e.g., 'Product', 'Job Posting')"),
|
|
49305
|
+
schema: exports_external.preprocess(coerceArray(), exports_external.array(exports_external.object(SchemaFieldShape))).optional().describe("Extraction schema fields. If omitted, the AI agent auto-detects the schema.")
|
|
49306
|
+
};
|
|
49307
|
+
const notificationsInputShape = {
|
|
49308
|
+
notifications: exports_external.object({
|
|
49309
|
+
email: exports_external.boolean().optional().describe("Send email notifications to the account email when data changes"),
|
|
49310
|
+
webhook: exports_external.object({
|
|
49311
|
+
url: exports_external.string().url().describe("Webhook endpoint URL"),
|
|
49312
|
+
httpMethod: exports_external.enum(["POST", "GET", "PUT", "PATCH"]).optional().describe("HTTP method (defaults to POST)")
|
|
49313
|
+
}).optional().describe("Send notifications via HTTP webhook"),
|
|
49314
|
+
slack: exports_external.object({
|
|
49315
|
+
webhookUrl: exports_external.string().url().describe("Slack incoming webhook URL")
|
|
49316
|
+
}).optional().describe("Send notifications to a Slack channel"),
|
|
49317
|
+
websocket: exports_external.boolean().optional().describe("Enable WebSocket notifications for programmatic real-time consumption")
|
|
49318
|
+
}).optional().describe("Notification channels to alert when data changes.")
|
|
49319
|
+
};
|
|
49320
|
+
function resolveUrls(args) {
|
|
49321
|
+
const urls = args.urls ?? (args.url ? [args.url] : []);
|
|
49322
|
+
return urls.length > 0 ? urls : null;
|
|
49323
|
+
}
|
|
49324
|
+
function buildExtraction(args) {
|
|
49325
|
+
if (!args.schema)
|
|
49326
|
+
return;
|
|
49327
|
+
return (builder) => {
|
|
49328
|
+
const entity = builder.entity(args.entity || "Data");
|
|
49329
|
+
for (const field of args.schema) {
|
|
49330
|
+
entity.field(field.name, field.description || field.name, field.dataType || "STRING", { example: field.example });
|
|
49331
|
+
}
|
|
49332
|
+
return entity;
|
|
49333
|
+
};
|
|
49334
|
+
}
|
|
49335
|
+
function buildNotificationChannels(n) {
|
|
49336
|
+
const channels = {};
|
|
49337
|
+
if (n.email)
|
|
49338
|
+
channels.EMAIL = true;
|
|
49339
|
+
if (n.websocket)
|
|
49340
|
+
channels.WEBSOCKET = true;
|
|
49341
|
+
if (n.webhook) {
|
|
49342
|
+
channels.WEBHOOK = {
|
|
49343
|
+
name: "mcp-webhook",
|
|
49344
|
+
webhookUrl: n.webhook.url,
|
|
49345
|
+
httpMethod: n.webhook.httpMethod || "POST"
|
|
49346
|
+
};
|
|
49347
|
+
}
|
|
49348
|
+
if (n.slack) {
|
|
49349
|
+
channels.SLACK = {
|
|
49350
|
+
name: "mcp-slack",
|
|
49351
|
+
webhookUrl: n.slack.webhookUrl
|
|
49352
|
+
};
|
|
49353
|
+
}
|
|
49354
|
+
return channels;
|
|
49355
|
+
}
|
|
49356
|
+
async function describeNotifications(n) {
|
|
49357
|
+
if (!n)
|
|
49358
|
+
return [];
|
|
49359
|
+
const channels = [];
|
|
49360
|
+
if (n.email) {
|
|
49361
|
+
const user = await ctx.client.user.getCurrentUser();
|
|
49362
|
+
channels.push(`email (${user.email})`);
|
|
49363
|
+
}
|
|
49364
|
+
if (n.slack)
|
|
49365
|
+
channels.push(`slack (${n.slack.webhookUrl})`);
|
|
49366
|
+
if (n.webhook)
|
|
49367
|
+
channels.push(`webhook (${n.webhook.url})`);
|
|
49368
|
+
if (n.websocket)
|
|
49369
|
+
channels.push("websocket");
|
|
49370
|
+
return channels;
|
|
49371
|
+
}
|
|
49315
49372
|
server.registerTool("create_workflow", {
|
|
49316
|
-
description:
|
|
49373
|
+
description: "Create a data extraction workflow using agentic navigation. Supports one-time or scheduled runs. " + "If entity and schema are provided, they guide the extraction; otherwise the AI agent auto-detects the schema from the page. " + "The workflow runs asynchronously and may take several minutes. Do NOT poll or sleep-wait for completion. " + `Return the workflow ID to the user and let them check back later with get_workflow or fetch_data.
|
|
49317
49374
|
|
|
49318
|
-
` +
|
|
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.
|
|
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.
|
|
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.
|
|
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.",
|
|
49375
|
+
` + "NOTE: This tool is for one-time or scheduled extraction ONLY. " + "For continuous real-time monitoring (watching a page for changes and alerting), use the create_realtime_monitor tool instead.",
|
|
49323
49376
|
inputSchema: strictSchema({
|
|
49324
|
-
|
|
49325
|
-
|
|
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."),
|
|
49327
|
-
name: exports_external.string().optional().describe("Optional name for the workflow"),
|
|
49328
|
-
entity: exports_external.string().optional().describe("Entity name for extraction (e.g., 'Product', 'Job Posting')"),
|
|
49329
|
-
schema: exports_external.preprocess(coerceArray(), exports_external.array(exports_external.object(SchemaFieldShape))).optional().describe("Extraction schema fields. If omitted, the AI agent auto-detects the schema."),
|
|
49377
|
+
...extractionInputShape,
|
|
49378
|
+
...urlInputShape,
|
|
49330
49379
|
interval: exports_external.enum([
|
|
49331
49380
|
"ONLY_ONCE",
|
|
49332
49381
|
"EVERY_10_MINUTES",
|
|
@@ -49343,91 +49392,35 @@ function registerTools(server, ctx) {
|
|
|
49343
49392
|
"BIWEEKLY",
|
|
49344
49393
|
"TRIWEEKLY",
|
|
49345
49394
|
"FOUR_WEEKS",
|
|
49346
|
-
"MONTHLY"
|
|
49347
|
-
|
|
49348
|
-
|
|
49349
|
-
notifications: exports_external.object({
|
|
49350
|
-
email: exports_external.boolean().optional().describe("Send email notifications to the account email when data changes"),
|
|
49351
|
-
webhook: exports_external.object({
|
|
49352
|
-
url: exports_external.string().url().describe("Webhook endpoint URL"),
|
|
49353
|
-
httpMethod: exports_external.enum(["POST", "GET", "PUT", "PATCH"]).optional().describe("HTTP method (defaults to POST)")
|
|
49354
|
-
}).optional().describe("Send notifications via HTTP webhook"),
|
|
49355
|
-
slack: exports_external.object({
|
|
49356
|
-
webhookUrl: exports_external.string().url().describe("Slack incoming webhook URL")
|
|
49357
|
-
}).optional().describe("Send notifications to a Slack channel"),
|
|
49358
|
-
websocket: exports_external.boolean().optional().describe("Enable WebSocket notifications for programmatic real-time consumption")
|
|
49359
|
-
}).optional().describe("Notification channels to alert when data changes. At least one channel is required for REAL_TIME workflows.")
|
|
49395
|
+
"MONTHLY"
|
|
49396
|
+
]).optional().describe("How often the workflow runs. Defaults to ONLY_ONCE."),
|
|
49397
|
+
...notificationsInputShape
|
|
49360
49398
|
}),
|
|
49361
49399
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
|
|
49362
49400
|
}, withErrorHandling("create_workflow", async (args) => {
|
|
49363
|
-
const urls =
|
|
49364
|
-
if (urls
|
|
49401
|
+
const urls = resolveUrls(args);
|
|
49402
|
+
if (!urls) {
|
|
49365
49403
|
return errorResult("At least one URL is required. Provide 'urls' (array) or 'url' (string).");
|
|
49366
49404
|
}
|
|
49367
|
-
const interval = args.interval;
|
|
49368
|
-
const n = args.notifications;
|
|
49369
|
-
const hasNotifications = n && (n.email || n.webhook || n.slack || n.websocket);
|
|
49370
|
-
if (interval === "REAL_TIME" && !hasNotifications) {
|
|
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.");
|
|
49372
|
-
}
|
|
49373
|
-
const extraction = args.schema ? (builder2) => {
|
|
49374
|
-
const entity = builder2.entity(args.entity || "Data");
|
|
49375
|
-
for (const field of args.schema) {
|
|
49376
|
-
entity.field(field.name, field.description || field.name, field.dataType || "STRING", { example: field.example });
|
|
49377
|
-
}
|
|
49378
|
-
return entity;
|
|
49379
|
-
} : undefined;
|
|
49380
|
-
const isRealTime = interval === "REAL_TIME";
|
|
49381
49405
|
let builder = ctx.client.extract({
|
|
49382
49406
|
urls,
|
|
49383
49407
|
name: args.name || "Untitled Workflow",
|
|
49384
|
-
|
|
49408
|
+
navigationMode: "agentic-navigation",
|
|
49385
49409
|
userPrompt: args.prompt,
|
|
49386
|
-
extraction,
|
|
49387
|
-
interval
|
|
49410
|
+
extraction: buildExtraction(args),
|
|
49411
|
+
interval: args.interval
|
|
49388
49412
|
});
|
|
49413
|
+
const n = args.notifications;
|
|
49414
|
+
const hasNotifications = n && (n.email || n.webhook || n.slack || n.websocket);
|
|
49389
49415
|
if (hasNotifications) {
|
|
49390
|
-
const channels = {};
|
|
49391
|
-
if (n.email)
|
|
49392
|
-
channels.EMAIL = true;
|
|
49393
|
-
if (n.websocket)
|
|
49394
|
-
channels.WEBSOCKET = true;
|
|
49395
|
-
if (n.webhook) {
|
|
49396
|
-
channels.WEBHOOK = {
|
|
49397
|
-
name: "mcp-webhook",
|
|
49398
|
-
webhookUrl: n.webhook.url,
|
|
49399
|
-
httpMethod: n.webhook.httpMethod || "POST"
|
|
49400
|
-
};
|
|
49401
|
-
}
|
|
49402
|
-
if (n.slack) {
|
|
49403
|
-
channels.SLACK = {
|
|
49404
|
-
name: "mcp-slack",
|
|
49405
|
-
webhookUrl: n.slack.webhookUrl
|
|
49406
|
-
};
|
|
49407
|
-
}
|
|
49408
49416
|
builder = builder.withNotifications({
|
|
49409
49417
|
events: ["workflow_data_change"],
|
|
49410
|
-
channels
|
|
49418
|
+
channels: buildNotificationChannels(n)
|
|
49411
49419
|
});
|
|
49412
49420
|
}
|
|
49413
49421
|
const workflow = await builder.create();
|
|
49414
|
-
const enabledChannels =
|
|
49415
|
-
|
|
49416
|
-
const user = await ctx.client.user.getCurrentUser();
|
|
49417
|
-
enabledChannels.push(`email (${user.email})`);
|
|
49418
|
-
}
|
|
49419
|
-
if (n?.slack)
|
|
49420
|
-
enabledChannels.push(`slack (${n.slack.webhookUrl})`);
|
|
49421
|
-
if (n?.webhook)
|
|
49422
|
-
enabledChannels.push(`webhook (${n.webhook.url})`);
|
|
49423
|
-
if (n?.websocket)
|
|
49424
|
-
enabledChannels.push("websocket");
|
|
49425
|
-
let message = "Workflow created successfully.";
|
|
49426
|
-
if (interval === "REAL_TIME") {
|
|
49427
|
-
message += " This real-time workflow monitors continuously — no manual runs needed.";
|
|
49428
|
-
} else {
|
|
49429
|
-
message += " The AI agent will start extracting data automatically.";
|
|
49430
|
-
}
|
|
49422
|
+
const enabledChannels = await describeNotifications(n);
|
|
49423
|
+
let message = "Workflow created successfully. The AI agent will start extracting data automatically.";
|
|
49431
49424
|
if (enabledChannels.length > 0) {
|
|
49432
49425
|
message += ` Notifications on data changes via: ${enabledChannels.join(", ")}.`;
|
|
49433
49426
|
}
|
|
@@ -49438,6 +49431,50 @@ function registerTools(server, ctx) {
|
|
|
49438
49431
|
message
|
|
49439
49432
|
});
|
|
49440
49433
|
}));
|
|
49434
|
+
server.registerTool("create_realtime_monitor", {
|
|
49435
|
+
description: "Create a real-time monitoring workflow that continuously watches a page for changes and sends alerts. " + "Use this when the user wants to monitor, track, or watch a page in real-time. " + "At least one notification channel (email, webhook, slack, or websocket) is required so the user gets alerted on changes. " + `Real-time monitors route to a separate Observer service and CANNOT be converted to/from regular extraction workflows after creation.
|
|
49436
|
+
|
|
49437
|
+
` + "If entity and schema are provided, they guide what to monitor; otherwise the AI agent auto-detects what to watch. " + "Do NOT poll or sleep-wait for completion. Return the workflow ID and let the user check back with get_workflow or fetch_data.",
|
|
49438
|
+
inputSchema: strictSchema({
|
|
49439
|
+
...extractionInputShape,
|
|
49440
|
+
...urlInputShape,
|
|
49441
|
+
...notificationsInputShape
|
|
49442
|
+
}),
|
|
49443
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
|
|
49444
|
+
}, withErrorHandling("create_realtime_monitor", async (args) => {
|
|
49445
|
+
const urls = resolveUrls(args);
|
|
49446
|
+
if (!urls) {
|
|
49447
|
+
return errorResult("At least one URL is required. Provide 'urls' (array) or 'url' (string).");
|
|
49448
|
+
}
|
|
49449
|
+
const n = args.notifications;
|
|
49450
|
+
const hasNotifications = n && (n.email || n.webhook || n.slack || n.websocket);
|
|
49451
|
+
if (!hasNotifications) {
|
|
49452
|
+
return errorResult("Real-time monitors require at least one notification channel so you get alerted when data changes. " + "Add a notifications object with one or more channels: email, webhook, slack, or websocket.");
|
|
49453
|
+
}
|
|
49454
|
+
let builder = ctx.client.extract({
|
|
49455
|
+
urls,
|
|
49456
|
+
name: args.name || "Untitled Monitor",
|
|
49457
|
+
userPrompt: args.prompt,
|
|
49458
|
+
extraction: buildExtraction(args),
|
|
49459
|
+
interval: "REAL_TIME"
|
|
49460
|
+
});
|
|
49461
|
+
builder = builder.withNotifications({
|
|
49462
|
+
events: ["workflow_data_change"],
|
|
49463
|
+
channels: buildNotificationChannels(n)
|
|
49464
|
+
});
|
|
49465
|
+
const workflow = await builder.create();
|
|
49466
|
+
const enabledChannels = await describeNotifications(n);
|
|
49467
|
+
let message = "Real-time monitor created successfully. It will continuously watch for changes — no manual runs needed.";
|
|
49468
|
+
if (enabledChannels.length > 0) {
|
|
49469
|
+
message += ` Notifications on data changes via: ${enabledChannels.join(", ")}.`;
|
|
49470
|
+
}
|
|
49471
|
+
message += " Use fetch_data to retrieve the latest data.";
|
|
49472
|
+
return jsonResult({
|
|
49473
|
+
success: true,
|
|
49474
|
+
workflowId: workflow.workflowId,
|
|
49475
|
+
message
|
|
49476
|
+
});
|
|
49477
|
+
}));
|
|
49441
49478
|
server.registerTool("list_workflows", {
|
|
49442
49479
|
description: "List all workflows with their current status",
|
|
49443
49480
|
inputSchema: {
|
|
@@ -49553,20 +49590,13 @@ function registerTools(server, ctx) {
|
|
|
49553
49590
|
workflowStatus = workflow.displayState ?? workflow.state ?? "unknown";
|
|
49554
49591
|
} catch {}
|
|
49555
49592
|
if (!args.confirmed) {
|
|
49556
|
-
|
|
49557
|
-
|
|
49558
|
-
|
|
49559
|
-
|
|
49560
|
-
|
|
49561
|
-
|
|
49562
|
-
|
|
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
|
-
}
|
|
49593
|
+
return jsonResult({
|
|
49594
|
+
pending: true,
|
|
49595
|
+
workflowId: args.workflowId,
|
|
49596
|
+
workflowName,
|
|
49597
|
+
status: workflowStatus,
|
|
49598
|
+
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.`
|
|
49599
|
+
});
|
|
49570
49600
|
}
|
|
49571
49601
|
await ctx.client.workflow.delete(args.workflowId);
|
|
49572
49602
|
return jsonResult({
|
|
@@ -49703,18 +49733,11 @@ function registerTools(server, ctx) {
|
|
|
49703
49733
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true }
|
|
49704
49734
|
}, withErrorHandling("delete_notification_channel", async (args) => {
|
|
49705
49735
|
if (!args.confirmed) {
|
|
49706
|
-
|
|
49707
|
-
|
|
49708
|
-
|
|
49709
|
-
|
|
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
|
-
}
|
|
49736
|
+
return jsonResult({
|
|
49737
|
+
pending: true,
|
|
49738
|
+
channelId: args.channelId,
|
|
49739
|
+
message: `⚠️ About to delete notification channel "${args.channelId}". Ask the user to confirm, then call again with confirmed=true.`
|
|
49740
|
+
});
|
|
49718
49741
|
}
|
|
49719
49742
|
await ctx.client.notification.channels.deleteChannel(args.channelId);
|
|
49720
49743
|
return jsonResult({
|
|
@@ -49834,18 +49857,11 @@ function registerTools(server, ctx) {
|
|
|
49834
49857
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true }
|
|
49835
49858
|
}, withErrorHandling("delete_notification_setting", async (args) => {
|
|
49836
49859
|
if (!args.confirmed) {
|
|
49837
|
-
|
|
49838
|
-
|
|
49839
|
-
|
|
49840
|
-
|
|
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
|
-
}
|
|
49860
|
+
return jsonResult({
|
|
49861
|
+
pending: true,
|
|
49862
|
+
settingsId: args.settingsId,
|
|
49863
|
+
message: `⚠️ About to delete notification setting "${args.settingsId}". Ask the user to confirm, then call again with confirmed=true.`
|
|
49864
|
+
});
|
|
49849
49865
|
}
|
|
49850
49866
|
await ctx.client.notification.settings.deleteSettings(args.settingsId);
|
|
49851
49867
|
return jsonResult({
|