@kadoa/mcp 0.7.0-rc.2 → 0.7.0
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 +90 -43
- package/package.json +1 -1
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.0
|
|
20
|
+
version: "0.7.0",
|
|
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",
|
|
@@ -104,6 +104,11 @@ function createKadoaClient(auth) {
|
|
|
104
104
|
var MCP_SCRAPE_REQUEST_TIMEOUT_MS = 310000;
|
|
105
105
|
var init_client = () => {};
|
|
106
106
|
|
|
107
|
+
// src/default-product.ts
|
|
108
|
+
function parseDefaultProductType(value) {
|
|
109
|
+
return value === "realtime" || value === "workflow" ? value : undefined;
|
|
110
|
+
}
|
|
111
|
+
|
|
107
112
|
// src/error-summary.ts
|
|
108
113
|
function redactCredentialText(value) {
|
|
109
114
|
return value.replace(/Bearer\s+[^\s,;]+/gi, "Bearer [redacted]").replace(/\b(?:kcs_|tk-)[A-Za-z0-9._~-]+\b/g, "[redacted]").replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, "[redacted]");
|
|
@@ -1888,11 +1893,11 @@ function scrapeResult(response, format) {
|
|
|
1888
1893
|
};
|
|
1889
1894
|
return withExtraContent(textOnlyResult(meta), { type: "text", text });
|
|
1890
1895
|
}
|
|
1891
|
-
function registerScrapeTools(server, ctx, helpers) {
|
|
1896
|
+
function registerScrapeTools(server, ctx, helpers, defaultProductType) {
|
|
1892
1897
|
const { withErrorHandling } = helpers;
|
|
1893
1898
|
server.registerTool("scrape", {
|
|
1894
1899
|
title: "Scrape URL",
|
|
1895
|
-
description: "Immediately fetch one URL as LLM-friendly markdown or raw HTML without creating a workflow. " + "Markdown is the default. Routing is fully automatic: it starts cheap and escalates through stealth tiers on its own when a site blocks bots, and remembers the working method per host. " + "Use create_workflow instead for structured extraction, recurring runs, monitoring, or navigation-heavy jobs. " + "The result is a JSON metadata block followed by the page text as its own block, capped at 70 KB.",
|
|
1900
|
+
description: "Immediately fetch one URL as LLM-friendly markdown or raw HTML without creating a workflow. " + "Markdown is the default. Routing is fully automatic: it starts cheap and escalates through stealth tiers on its own when a site blocks bots, and remembers the working method per host. " + (defaultProductType === "realtime" ? "Use create_realtime_monitor for monitoring, and create_workflow for one-time or scheduled structured extraction or navigation-heavy jobs. " : "Use create_workflow instead for structured extraction, recurring runs, monitoring, or navigation-heavy jobs. ") + "The result is a JSON metadata block followed by the page text as its own block, capped at 70 KB.",
|
|
1896
1901
|
inputSchema: strictSchema({
|
|
1897
1902
|
url: z7.url().refine((value) => {
|
|
1898
1903
|
const protocol = new URL(value).protocol;
|
|
@@ -2161,24 +2166,31 @@ function registerWhoamiTool(server, ctx, helpers) {
|
|
|
2161
2166
|
const { withErrorHandling } = helpers;
|
|
2162
2167
|
server.registerTool("whoami", {
|
|
2163
2168
|
title: "Show Current User",
|
|
2164
|
-
description: "Show current user details: email, authentication method, and team
|
|
2169
|
+
description: "Show current user details: email, authentication method, team memberships, and the team's default product",
|
|
2165
2170
|
inputSchema: strictSchema({}),
|
|
2166
2171
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
2167
2172
|
}, withErrorHandling("whoami", async () => {
|
|
2173
|
+
const defaultProductTypePromise = ctx.client.user.getFeatures().then((features) => parseDefaultProductType(features.defaultProductType), () => {
|
|
2174
|
+
return;
|
|
2175
|
+
});
|
|
2168
2176
|
if (ctx.principal.connector) {
|
|
2169
|
-
const teams = await
|
|
2177
|
+
const [teams, defaultProductType] = await Promise.all([
|
|
2178
|
+
ctx.client.listTeams(),
|
|
2179
|
+
defaultProductTypePromise
|
|
2180
|
+
]);
|
|
2170
2181
|
const activeTeam = teams.find((team) => team.id === ctx.principal.teamId);
|
|
2171
2182
|
if (!activeTeam) {
|
|
2172
2183
|
throw new Error("The active team is unavailable for this credential. Reconnect and choose a team.");
|
|
2173
2184
|
}
|
|
2174
2185
|
return jsonResult({
|
|
2175
2186
|
authMethod: "Shared organization service account",
|
|
2176
|
-
team: { name: activeTeam.name }
|
|
2187
|
+
team: { name: activeTeam.name, ...defaultProductType ? { defaultProductType } : {} }
|
|
2177
2188
|
});
|
|
2178
2189
|
}
|
|
2179
|
-
const [user, teams] = await Promise.all([
|
|
2190
|
+
const [user, teams, defaultProductType] = await Promise.all([
|
|
2180
2191
|
ctx.client.user.getCurrentUser(),
|
|
2181
|
-
ctx.client.listTeams()
|
|
2192
|
+
ctx.client.listTeams(),
|
|
2193
|
+
defaultProductTypePromise
|
|
2182
2194
|
]);
|
|
2183
2195
|
const activeTeam = teams.find((team) => team.id === ctx.principal.teamId);
|
|
2184
2196
|
if (!activeTeam) {
|
|
@@ -2189,7 +2201,8 @@ function registerWhoamiTool(server, ctx, helpers) {
|
|
|
2189
2201
|
authMethod: ctx.principal.actor ? `Kadoa Assistant (delegated via ${ctx.principal.actor.sub})` : "Kadoa access token",
|
|
2190
2202
|
team: {
|
|
2191
2203
|
name: activeTeam.name,
|
|
2192
|
-
...activeTeam.memberRole ? { memberRole: activeTeam.memberRole } : {}
|
|
2204
|
+
...activeTeam.memberRole ? { memberRole: activeTeam.memberRole } : {},
|
|
2205
|
+
...defaultProductType ? { defaultProductType } : {}
|
|
2193
2206
|
},
|
|
2194
2207
|
scopes: ctx.principal.scopes
|
|
2195
2208
|
});
|
|
@@ -2789,7 +2802,7 @@ function summarizeWorkflowAuditEntry(entry) {
|
|
|
2789
2802
|
function historyEmptyHint(includeSnapshots) {
|
|
2790
2803
|
return includeSnapshots ? "A single entry is too large to return inline. Retry without includeSnapshots for the diff summary." : "A single entry is too large to return inline even without snapshots. Retry with a narrower page (e.g. limit=1) or fewer fields.";
|
|
2791
2804
|
}
|
|
2792
|
-
function registerWorkflowCoreTools(server, ctx, helpers) {
|
|
2805
|
+
function registerWorkflowCoreTools(server, ctx, helpers, defaultProductType) {
|
|
2793
2806
|
const { withErrorHandling } = helpers;
|
|
2794
2807
|
function resolveUrls(args) {
|
|
2795
2808
|
const urls = args.urls ?? (args.url ? [args.url] : []);
|
|
@@ -2897,7 +2910,7 @@ function registerWorkflowCoreTools(server, ctx, helpers) {
|
|
|
2897
2910
|
|
|
2898
2911
|
` + "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.
|
|
2899
2912
|
|
|
2900
|
-
` + "PREFER TEMPLATES: If the user's request matches an existing template, instantiate it via `templateId` instead of writing a fresh prompt/schema. " + "Use `list_templates` to discover available templates and `get_template` to inspect schemas before deciding. " + "When creating from a template, call this tool with the template's `templateId`, the source `urls`, optional `templateVersion`, and optional `prompt` containing only workflow-specific additional instructions. Never copy the template prompt, entity, or schema into the call, and never silently match or rewrite inline configuration.\n\n" + "NOTE: This tool is for one-time or scheduled extraction ONLY. " + "For continuous realtime monitoring that watches a page for data changes and sends alerts, use the create_realtime_monitor tool instead.",
|
|
2913
|
+
` + "PREFER TEMPLATES: If the user's request matches an existing template, instantiate it via `templateId` instead of writing a fresh prompt/schema. " + "Use `list_templates` to discover available templates and `get_template` to inspect schemas before deciding. " + "When creating from a template, call this tool with the template's `templateId`, the source `urls`, optional `templateVersion`, and optional `prompt` containing only workflow-specific additional instructions. Never copy the template prompt, entity, or schema into the call, and never silently match or rewrite inline configuration.\n\n" + "NOTE: This tool is for one-time or scheduled extraction ONLY. " + (defaultProductType === "realtime" ? `This team's default product is realtime monitoring. Do not call this tool unless the user explicitly asked for a one-time run (e.g. "once", "just the current list") or a schedule (e.g. "daily"). A request to get, extract, or scrape data that names no frequency is not explicit: call create_realtime_monitor instead.` : "For continuous realtime monitoring that watches a page for data changes and sends alerts, use the create_realtime_monitor tool instead."),
|
|
2901
2914
|
inputSchema: strictSchema({
|
|
2902
2915
|
prompt: extractionPromptInput.describe('Natural language description of what to extract (e.g., "Extract product prices and names"). Required unless templateId is provided. With templateId, use it only for workflow-specific instructions to add to the template prompt.'),
|
|
2903
2916
|
...extractionInputShape,
|
|
@@ -3050,7 +3063,7 @@ function registerWorkflowCoreTools(server, ctx, helpers) {
|
|
|
3050
3063
|
}
|
|
3051
3064
|
server.registerTool("create_realtime_monitor", {
|
|
3052
3065
|
title: "Create Realtime Monitor",
|
|
3053
|
-
description: "Create a realtime monitoring workflow that continuously watches a page for data changes and sends alerts. " + "Use this when the user wants to monitor, track, or watch a page in realtime. " + "At least one notification channel (email, webhook, slack, or websocket) is required; notification settings are persisted before Assistant creation is dispatched. " + `Realtime monitors CANNOT be converted to/from one-time or scheduled extraction workflows after creation.
|
|
3066
|
+
description: "Create a realtime monitoring workflow that continuously watches a page for data changes and sends alerts. " + (defaultProductType === "realtime" ? "This team's default product is realtime monitoring: use this tool whenever the user asks to monitor, track, or watch a page, or to get or extract data from it, unless they explicitly ask for a one-time run or a schedule. A request that names no frequency counts as realtime. " : "Use this when the user wants to monitor, track, or watch a page in realtime. ") + "At least one notification channel (email, webhook, slack, or websocket) is required; notification settings are persisted before Assistant creation is dispatched. " + `Realtime monitors CANNOT be converted to/from one-time or scheduled extraction workflows after creation.
|
|
3054
3067
|
|
|
3055
3068
|
` + "If entity and schema are provided, they guide what the Assistant should monitor; otherwise the Assistant determines what to watch. " + "Creation is asynchronous and the response includes workflow, session, thread, and job IDs plus a dashboard URL. " + "Use generic workflow Assistant tools (get_workflow_assistant, answer_workflow_assistant_question, interrupt_workflow_assistant, resume_workflow_assistant, stop_workflow_assistant) for follow-up status, questions, and controls. " + "Do NOT poll or sleep-wait for completion.",
|
|
3056
3069
|
inputSchema: strictSchema({
|
|
@@ -3727,9 +3740,9 @@ var init_workflows = __esm(() => {
|
|
|
3727
3740
|
function registerTools(server, ctx, capabilities) {
|
|
3728
3741
|
const helpers = { withErrorHandling: createWithErrorHandling(ctx) };
|
|
3729
3742
|
if (capabilities.scrape)
|
|
3730
|
-
registerScrapeTools(server, ctx, helpers);
|
|
3743
|
+
registerScrapeTools(server, ctx, helpers, capabilities.defaultProductType);
|
|
3731
3744
|
registerWhoamiTool(server, ctx, helpers);
|
|
3732
|
-
registerWorkflowCoreTools(server, ctx, helpers);
|
|
3745
|
+
registerWorkflowCoreTools(server, ctx, helpers, capabilities.defaultProductType);
|
|
3733
3746
|
registerAssistantTools(server, ctx, helpers);
|
|
3734
3747
|
registerWorkflowHistoryTools(server, ctx, helpers);
|
|
3735
3748
|
registerObservabilityTools(server, ctx, helpers);
|
|
@@ -4413,12 +4426,41 @@ var init_request_log = __esm(() => {
|
|
|
4413
4426
|
|
|
4414
4427
|
// src/server.ts
|
|
4415
4428
|
import { McpServer as McpServer2 } from "@modelcontextprotocol/server";
|
|
4429
|
+
function lifecycleInstruction2(defaultProductType) {
|
|
4430
|
+
return defaultProductType === "realtime" ? "For one-time or scheduled workflows, the lifecycle is create_workflow → get_workflow (check status) → fetch_data (get results). Workflows run asynchronously - never poll or sleep-wait." : "Workflow lifecycle: create_workflow → get_workflow (check status) → fetch_data (get results). Workflows run asynchronously - never poll or sleep-wait.";
|
|
4431
|
+
}
|
|
4432
|
+
function routingInstructions2(defaultProductType) {
|
|
4433
|
+
if (defaultProductType === "realtime") {
|
|
4434
|
+
return [
|
|
4435
|
+
`This team's default product is realtime monitoring. Use create_realtime_monitor whenever the user asks to monitor, track, or watch a page, or to get or extract data from it, unless they explicitly ask for a one-time run (e.g. "once") or a schedule (e.g. "daily"). A request that names no frequency is not explicit: default to realtime without asking the user to choose. It first persists notification channels, then asynchronously returns workflow/session/thread/job IDs; use the workflow Assistant tools for follow-up status, questions, and controls.`,
|
|
4436
|
+
"Use create_workflow only when the user explicitly asks for a one-time run or a schedule. Use scrape for an immediate raw HTML or markdown fetch from one URL."
|
|
4437
|
+
];
|
|
4438
|
+
}
|
|
4439
|
+
return [
|
|
4440
|
+
"Use create_realtime_monitor only when the user wants continuous realtime change detection with alerts. It first persists notification channels, then asynchronously returns workflow/session/thread/job IDs; use the workflow Assistant tools for follow-up status, questions, and controls.",
|
|
4441
|
+
"For one-time or scheduled extraction, use create_workflow. Use scrape for an immediate raw HTML or markdown fetch from one URL. Use create_workflow for structured extraction, recurring runs, monitoring, or navigation-heavy jobs."
|
|
4442
|
+
];
|
|
4443
|
+
}
|
|
4416
4444
|
async function createServer3(auth, options) {
|
|
4417
4445
|
const ctx = {
|
|
4418
4446
|
client: createKadoaClient(auth),
|
|
4419
4447
|
principal: auth.principal,
|
|
4420
4448
|
teamId: auth.principal.teamId
|
|
4421
4449
|
};
|
|
4450
|
+
let capabilities = options?.capabilities;
|
|
4451
|
+
if (!capabilities) {
|
|
4452
|
+
try {
|
|
4453
|
+
const response = await ctx.client.user.getFeatures();
|
|
4454
|
+
capabilities = {
|
|
4455
|
+
scrape: response.features.scrape,
|
|
4456
|
+
defaultProductType: parseDefaultProductType(response.defaultProductType)
|
|
4457
|
+
};
|
|
4458
|
+
} catch (error) {
|
|
4459
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
4460
|
+
console.error(`[CAPABILITIES] Failed to resolve team capabilities; gated tools will be hidden: ${message}`);
|
|
4461
|
+
capabilities = { scrape: false };
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
4422
4464
|
const server = new McpServer2({ name: "kadoa", version: package_default.version }, {
|
|
4423
4465
|
instructions: [
|
|
4424
4466
|
"IMPORTANT: One workflow = one source. A single workflow can extract many different fields, tables, and sections from the same URL(s) using a rich schema.",
|
|
@@ -4427,10 +4469,9 @@ async function createServer3(auth, options) {
|
|
|
4427
4469
|
"Kadoa workflows use agentic navigation: the AI agent can browse pages, click buttons, fill forms, select dropdowns, paginate, open detail pages, and handle multi-step interactions.",
|
|
4428
4470
|
"Describe the full navigation steps in the prompt (e.g., 'select year 2022 from the dropdown, click Search, extract the table, then repeat for 2023').",
|
|
4429
4471
|
"",
|
|
4430
|
-
|
|
4472
|
+
lifecycleInstruction2(capabilities.defaultProductType),
|
|
4431
4473
|
"",
|
|
4432
|
-
|
|
4433
|
-
"For one-time or scheduled extraction, use create_workflow. Use scrape for an immediate raw HTML or markdown fetch from one URL. Use create_workflow for structured extraction, recurring runs, monitoring, or navigation-heavy jobs.",
|
|
4474
|
+
...routingInstructions2(capabilities.defaultProductType),
|
|
4434
4475
|
"If a one-time or scheduled workflow fails, retry it with run_workflow using the existing workflow ID. Do NOT delete and recreate the workflow just to retry it - preserve its workflow ID and configuration. Realtime workflows cannot be manually run.",
|
|
4435
4476
|
"Use list_changes and get_change to retrieve detected diffs from realtime monitoring workflows.",
|
|
4436
4477
|
"List tools, plus fetch_data, get_workflow_history, and get_workflow_assistant_timeline, all return pagination {totalCount?, hasMore, next?}. When hasMore is true, call the same tool again with pagination.next as its arguments to get the next page. totalCount is present only when the total is known; never treat the page size as the total.",
|
|
@@ -4444,17 +4485,6 @@ async function createServer3(auth, options) {
|
|
|
4444
4485
|
].join(`
|
|
4445
4486
|
`)
|
|
4446
4487
|
});
|
|
4447
|
-
let capabilities = options?.capabilities;
|
|
4448
|
-
if (!capabilities) {
|
|
4449
|
-
try {
|
|
4450
|
-
const response = await ctx.client.user.getFeatures();
|
|
4451
|
-
capabilities = { scrape: response.features.scrape };
|
|
4452
|
-
} catch (error) {
|
|
4453
|
-
const message = error instanceof Error ? error.message : "unknown error";
|
|
4454
|
-
console.error(`[CAPABILITIES] Failed to resolve team capabilities; gated tools will be hidden: ${message}`);
|
|
4455
|
-
capabilities = { scrape: false };
|
|
4456
|
-
}
|
|
4457
|
-
}
|
|
4458
4488
|
registerTools(server, ctx, capabilities);
|
|
4459
4489
|
server.server.onerror = (error) => console.error("[MCP Error]", safeErrorSummary(error));
|
|
4460
4490
|
return server;
|
|
@@ -4534,7 +4564,7 @@ async function requestNeedsCapabilityLookup(request) {
|
|
|
4534
4564
|
return false;
|
|
4535
4565
|
}
|
|
4536
4566
|
}
|
|
4537
|
-
return method === "tools/list" || method === "tools/call" && name === "scrape";
|
|
4567
|
+
return method === "initialize" || method === "server/discover" || method === "tools/list" || method === "tools/call" && name === "scrape";
|
|
4538
4568
|
}
|
|
4539
4569
|
function isClientCausedHandlerError(error) {
|
|
4540
4570
|
if (error instanceof ProtocolError)
|
|
@@ -4874,12 +4904,41 @@ init_package();
|
|
|
4874
4904
|
init_client();
|
|
4875
4905
|
init_tools();
|
|
4876
4906
|
import { McpServer } from "@modelcontextprotocol/server";
|
|
4907
|
+
function lifecycleInstruction(defaultProductType) {
|
|
4908
|
+
return defaultProductType === "realtime" ? "For one-time or scheduled workflows, the lifecycle is create_workflow → get_workflow (check status) → fetch_data (get results). Workflows run asynchronously - never poll or sleep-wait." : "Workflow lifecycle: create_workflow → get_workflow (check status) → fetch_data (get results). Workflows run asynchronously - never poll or sleep-wait.";
|
|
4909
|
+
}
|
|
4910
|
+
function routingInstructions(defaultProductType) {
|
|
4911
|
+
if (defaultProductType === "realtime") {
|
|
4912
|
+
return [
|
|
4913
|
+
`This team's default product is realtime monitoring. Use create_realtime_monitor whenever the user asks to monitor, track, or watch a page, or to get or extract data from it, unless they explicitly ask for a one-time run (e.g. "once") or a schedule (e.g. "daily"). A request that names no frequency is not explicit: default to realtime without asking the user to choose. It first persists notification channels, then asynchronously returns workflow/session/thread/job IDs; use the workflow Assistant tools for follow-up status, questions, and controls.`,
|
|
4914
|
+
"Use create_workflow only when the user explicitly asks for a one-time run or a schedule. Use scrape for an immediate raw HTML or markdown fetch from one URL."
|
|
4915
|
+
];
|
|
4916
|
+
}
|
|
4917
|
+
return [
|
|
4918
|
+
"Use create_realtime_monitor only when the user wants continuous realtime change detection with alerts. It first persists notification channels, then asynchronously returns workflow/session/thread/job IDs; use the workflow Assistant tools for follow-up status, questions, and controls.",
|
|
4919
|
+
"For one-time or scheduled extraction, use create_workflow. Use scrape for an immediate raw HTML or markdown fetch from one URL. Use create_workflow for structured extraction, recurring runs, monitoring, or navigation-heavy jobs."
|
|
4920
|
+
];
|
|
4921
|
+
}
|
|
4877
4922
|
async function createServer(auth, options) {
|
|
4878
4923
|
const ctx = {
|
|
4879
4924
|
client: createKadoaClient(auth),
|
|
4880
4925
|
principal: auth.principal,
|
|
4881
4926
|
teamId: auth.principal.teamId
|
|
4882
4927
|
};
|
|
4928
|
+
let capabilities = options?.capabilities;
|
|
4929
|
+
if (!capabilities) {
|
|
4930
|
+
try {
|
|
4931
|
+
const response = await ctx.client.user.getFeatures();
|
|
4932
|
+
capabilities = {
|
|
4933
|
+
scrape: response.features.scrape,
|
|
4934
|
+
defaultProductType: parseDefaultProductType(response.defaultProductType)
|
|
4935
|
+
};
|
|
4936
|
+
} catch (error) {
|
|
4937
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
4938
|
+
console.error(`[CAPABILITIES] Failed to resolve team capabilities; gated tools will be hidden: ${message}`);
|
|
4939
|
+
capabilities = { scrape: false };
|
|
4940
|
+
}
|
|
4941
|
+
}
|
|
4883
4942
|
const server = new McpServer({ name: "kadoa", version: package_default.version }, {
|
|
4884
4943
|
instructions: [
|
|
4885
4944
|
"IMPORTANT: One workflow = one source. A single workflow can extract many different fields, tables, and sections from the same URL(s) using a rich schema.",
|
|
@@ -4888,10 +4947,9 @@ async function createServer(auth, options) {
|
|
|
4888
4947
|
"Kadoa workflows use agentic navigation: the AI agent can browse pages, click buttons, fill forms, select dropdowns, paginate, open detail pages, and handle multi-step interactions.",
|
|
4889
4948
|
"Describe the full navigation steps in the prompt (e.g., 'select year 2022 from the dropdown, click Search, extract the table, then repeat for 2023').",
|
|
4890
4949
|
"",
|
|
4891
|
-
|
|
4950
|
+
lifecycleInstruction(capabilities.defaultProductType),
|
|
4892
4951
|
"",
|
|
4893
|
-
|
|
4894
|
-
"For one-time or scheduled extraction, use create_workflow. Use scrape for an immediate raw HTML or markdown fetch from one URL. Use create_workflow for structured extraction, recurring runs, monitoring, or navigation-heavy jobs.",
|
|
4952
|
+
...routingInstructions(capabilities.defaultProductType),
|
|
4895
4953
|
"If a one-time or scheduled workflow fails, retry it with run_workflow using the existing workflow ID. Do NOT delete and recreate the workflow just to retry it - preserve its workflow ID and configuration. Realtime workflows cannot be manually run.",
|
|
4896
4954
|
"Use list_changes and get_change to retrieve detected diffs from realtime monitoring workflows.",
|
|
4897
4955
|
"List tools, plus fetch_data, get_workflow_history, and get_workflow_assistant_timeline, all return pagination {totalCount?, hasMore, next?}. When hasMore is true, call the same tool again with pagination.next as its arguments to get the next page. totalCount is present only when the total is known; never treat the page size as the total.",
|
|
@@ -4905,17 +4963,6 @@ async function createServer(auth, options) {
|
|
|
4905
4963
|
].join(`
|
|
4906
4964
|
`)
|
|
4907
4965
|
});
|
|
4908
|
-
let capabilities = options?.capabilities;
|
|
4909
|
-
if (!capabilities) {
|
|
4910
|
-
try {
|
|
4911
|
-
const response = await ctx.client.user.getFeatures();
|
|
4912
|
-
capabilities = { scrape: response.features.scrape };
|
|
4913
|
-
} catch (error) {
|
|
4914
|
-
const message = error instanceof Error ? error.message : "unknown error";
|
|
4915
|
-
console.error(`[CAPABILITIES] Failed to resolve team capabilities; gated tools will be hidden: ${message}`);
|
|
4916
|
-
capabilities = { scrape: false };
|
|
4917
|
-
}
|
|
4918
|
-
}
|
|
4919
4966
|
registerTools(server, ctx, capabilities);
|
|
4920
4967
|
server.server.onerror = (error) => console.error("[MCP Error]", safeErrorSummary(error));
|
|
4921
4968
|
return server;
|