@kadoa/mcp 0.3.6-rc.0 → 0.3.6-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +76 -89
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -49219,7 +49219,7 @@ function classifyError(error48) {
|
|
|
49219
49219
|
switch (code) {
|
|
49220
49220
|
case "AUTH_ERROR":
|
|
49221
49221
|
if (httpError.httpStatus === 403) {
|
|
49222
|
-
return `Access denied${status}. Your current team role may not have permission for this action. Use the
|
|
49222
|
+
return `Access denied${status}. Your current team role may not have permission for this action. Use the whoami tool to check your role, or contact your team admin to request elevated access.`;
|
|
49223
49223
|
}
|
|
49224
49224
|
return `Authentication failed${status}. Your Kadoa API key may be invalid or expired. Please check your KADOA_API_KEY or re-authenticate.`;
|
|
49225
49225
|
case "NOT_FOUND":
|
|
@@ -49267,7 +49267,19 @@ function registerTools(server, ctx) {
|
|
|
49267
49267
|
await getValidJwt(ctx);
|
|
49268
49268
|
return await handler(...args);
|
|
49269
49269
|
} catch (error48) {
|
|
49270
|
-
|
|
49270
|
+
let message = classifyError(error48);
|
|
49271
|
+
if (KadoaHttpException.isInstance(error48) && error48.httpStatus === 403) {
|
|
49272
|
+
try {
|
|
49273
|
+
const jwt2 = ctx.supabaseJwt;
|
|
49274
|
+
const teams = await ctx.client.listTeams(jwt2 ? { bearerToken: jwt2 } : undefined);
|
|
49275
|
+
const config2 = loadConfig2();
|
|
49276
|
+
const activeTeamId = ctx.teamId ?? config2.teamId ?? teams[0]?.id;
|
|
49277
|
+
const activeTeam = teams.find((t) => t.id === activeTeamId);
|
|
49278
|
+
if (activeTeam?.adminEmail) {
|
|
49279
|
+
message += ` Your team admin is ${activeTeam.adminEmail}.`;
|
|
49280
|
+
}
|
|
49281
|
+
} catch {}
|
|
49282
|
+
}
|
|
49271
49283
|
console.error(`[Tool Error] ${name}:`, error48);
|
|
49272
49284
|
return errorResult(message);
|
|
49273
49285
|
}
|
|
@@ -49289,7 +49301,8 @@ function registerTools(server, ctx) {
|
|
|
49289
49301
|
authMethod,
|
|
49290
49302
|
teams: teams.map((t) => ({
|
|
49291
49303
|
name: t.name,
|
|
49292
|
-
|
|
49304
|
+
memberRole: t.memberRole,
|
|
49305
|
+
...t.adminEmail ? { adminEmail: t.adminEmail } : {},
|
|
49293
49306
|
...t.id === activeTeamId ? { active: true } : {}
|
|
49294
49307
|
}))
|
|
49295
49308
|
});
|
|
@@ -49304,28 +49317,16 @@ function registerTools(server, ctx) {
|
|
|
49304
49317
|
entity: exports_external.string().optional().describe("Entity name for extraction (e.g., 'Product', 'Job Posting')"),
|
|
49305
49318
|
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
49319
|
};
|
|
49307
|
-
const webhookAuthShape = exports_external.object({
|
|
49308
|
-
type: exports_external.enum(["bearer", "basic", "header"]).describe("Authentication type"),
|
|
49309
|
-
token: exports_external.string().optional().describe("Bearer token (required for 'bearer' type)"),
|
|
49310
|
-
username: exports_external.string().optional().describe("Username (required for 'basic' type)"),
|
|
49311
|
-
password: exports_external.string().optional().describe("Password (required for 'basic' type)"),
|
|
49312
|
-
headers: exports_external.object({}).passthrough().optional().describe("Custom headers as key-value pairs (required for 'header' type)")
|
|
49313
|
-
}).optional().describe("Authentication configuration for the webhook endpoint");
|
|
49314
49320
|
const notificationsInputShape = {
|
|
49315
49321
|
notifications: exports_external.object({
|
|
49316
|
-
email: exports_external.
|
|
49317
|
-
recipients: exports_external.array(exports_external.string().email()).optional().describe("Email addresses to notify. Omit to use account default email.")
|
|
49318
|
-
}).optional().describe("Send email notifications. Pass {} for account default email, or {recipients: [...]} for custom addresses."),
|
|
49322
|
+
email: exports_external.boolean().optional().describe("Send email notifications to the account email when data changes"),
|
|
49319
49323
|
webhook: exports_external.object({
|
|
49320
49324
|
url: exports_external.string().url().describe("Webhook endpoint URL"),
|
|
49321
|
-
httpMethod: exports_external.enum(["POST", "GET", "PUT", "PATCH"]).optional().describe("HTTP method (defaults to POST)")
|
|
49322
|
-
auth: webhookAuthShape
|
|
49325
|
+
httpMethod: exports_external.enum(["POST", "GET", "PUT", "PATCH"]).optional().describe("HTTP method (defaults to POST)")
|
|
49323
49326
|
}).optional().describe("Send notifications via HTTP webhook"),
|
|
49324
49327
|
slack: exports_external.object({
|
|
49325
|
-
webhookUrl: exports_external.string().url().
|
|
49326
|
-
|
|
49327
|
-
slackChannelName: exports_external.string().optional().describe("Slack channel name (e.g. #alerts)")
|
|
49328
|
-
}).optional().describe("Send notifications to a Slack channel. Use slackChannelId/slackChannelName for OAuth integration, or webhookUrl for legacy incoming webhooks."),
|
|
49328
|
+
webhookUrl: exports_external.string().url().describe("Slack incoming webhook URL")
|
|
49329
|
+
}).optional().describe("Send notifications to a Slack channel"),
|
|
49329
49330
|
websocket: exports_external.boolean().optional().describe("Enable WebSocket notifications for programmatic real-time consumption")
|
|
49330
49331
|
}).optional().describe("Notification channels to alert when data changes.")
|
|
49331
49332
|
};
|
|
@@ -49346,34 +49347,22 @@ function registerTools(server, ctx) {
|
|
|
49346
49347
|
}
|
|
49347
49348
|
function buildNotificationChannels(n) {
|
|
49348
49349
|
const channels = {};
|
|
49349
|
-
if (n.email)
|
|
49350
|
-
|
|
49351
|
-
channels.EMAIL = { name: "mcp-email", recipients: n.email.recipients };
|
|
49352
|
-
} else {
|
|
49353
|
-
channels.EMAIL = true;
|
|
49354
|
-
}
|
|
49355
|
-
}
|
|
49350
|
+
if (n.email)
|
|
49351
|
+
channels.EMAIL = true;
|
|
49356
49352
|
if (n.websocket)
|
|
49357
49353
|
channels.WEBSOCKET = true;
|
|
49358
49354
|
if (n.webhook) {
|
|
49359
|
-
|
|
49355
|
+
channels.WEBHOOK = {
|
|
49360
49356
|
name: "mcp-webhook",
|
|
49361
49357
|
webhookUrl: n.webhook.url,
|
|
49362
49358
|
httpMethod: n.webhook.httpMethod || "POST"
|
|
49363
49359
|
};
|
|
49364
|
-
if (n.webhook.auth)
|
|
49365
|
-
webhookChannel.auth = n.webhook.auth;
|
|
49366
|
-
channels.WEBHOOK = webhookChannel;
|
|
49367
49360
|
}
|
|
49368
49361
|
if (n.slack) {
|
|
49369
|
-
|
|
49370
|
-
|
|
49371
|
-
|
|
49372
|
-
|
|
49373
|
-
slackChannel.slackChannelId = n.slack.slackChannelId;
|
|
49374
|
-
if (n.slack.slackChannelName)
|
|
49375
|
-
slackChannel.slackChannelName = n.slack.slackChannelName;
|
|
49376
|
-
channels.SLACK = slackChannel;
|
|
49362
|
+
channels.SLACK = {
|
|
49363
|
+
name: "mcp-slack",
|
|
49364
|
+
webhookUrl: n.slack.webhookUrl
|
|
49365
|
+
};
|
|
49377
49366
|
}
|
|
49378
49367
|
return channels;
|
|
49379
49368
|
}
|
|
@@ -49382,17 +49371,11 @@ function registerTools(server, ctx) {
|
|
|
49382
49371
|
return [];
|
|
49383
49372
|
const channels = [];
|
|
49384
49373
|
if (n.email) {
|
|
49385
|
-
|
|
49386
|
-
|
|
49387
|
-
} else {
|
|
49388
|
-
const user = await ctx.client.user.getCurrentUser();
|
|
49389
|
-
channels.push(`email (${user.email})`);
|
|
49390
|
-
}
|
|
49391
|
-
}
|
|
49392
|
-
if (n.slack) {
|
|
49393
|
-
const slackDesc = n.slack.slackChannelName || n.slack.slackChannelId || n.slack.webhookUrl || "default";
|
|
49394
|
-
channels.push(`slack (${slackDesc})`);
|
|
49374
|
+
const user = await ctx.client.user.getCurrentUser();
|
|
49375
|
+
channels.push(`email (${user.email})`);
|
|
49395
49376
|
}
|
|
49377
|
+
if (n.slack)
|
|
49378
|
+
channels.push(`slack (${n.slack.webhookUrl})`);
|
|
49396
49379
|
if (n.webhook)
|
|
49397
49380
|
channels.push(`webhook (${n.webhook.url})`);
|
|
49398
49381
|
if (n.websocket)
|
|
@@ -49727,24 +49710,15 @@ function registerTools(server, ctx) {
|
|
|
49727
49710
|
});
|
|
49728
49711
|
}));
|
|
49729
49712
|
server.registerTool("create_notification_channel", {
|
|
49730
|
-
description: "Create a notification channel (email, webhook,
|
|
49713
|
+
description: "Create a notification channel (email, webhook, or slack). The channel can then be linked to notification settings for specific events.",
|
|
49731
49714
|
inputSchema: {
|
|
49732
|
-
channelType: exports_external.enum(["EMAIL", "WEBHOOK", "SLACK"
|
|
49715
|
+
channelType: exports_external.enum(["EMAIL", "WEBHOOK", "SLACK"]).describe("Type of notification channel"),
|
|
49733
49716
|
name: exports_external.string().optional().describe("Name for the channel (defaults to 'default')"),
|
|
49734
49717
|
config: exports_external.object({
|
|
49735
49718
|
recipients: exports_external.array(exports_external.string()).optional().describe("Email recipients (required for EMAIL channels)"),
|
|
49736
|
-
webhookUrl: exports_external.string().optional().describe("Webhook/Slack endpoint URL (required for WEBHOOK
|
|
49737
|
-
httpMethod: exports_external.enum(["POST", "GET", "PUT", "PATCH"]).optional().describe("HTTP method for WEBHOOK channels (defaults to POST)")
|
|
49738
|
-
|
|
49739
|
-
slackChannelName: exports_external.string().optional().describe("Slack channel name (for SLACK channels, e.g. #alerts)"),
|
|
49740
|
-
auth: exports_external.object({
|
|
49741
|
-
type: exports_external.enum(["bearer", "basic", "header"]).describe("Authentication type"),
|
|
49742
|
-
token: exports_external.string().optional().describe("Bearer token (for 'bearer' type)"),
|
|
49743
|
-
username: exports_external.string().optional().describe("Username (for 'basic' type)"),
|
|
49744
|
-
password: exports_external.string().optional().describe("Password (for 'basic' type)"),
|
|
49745
|
-
headers: exports_external.object({}).passthrough().optional().describe("Custom headers as key-value pairs (for 'header' type)")
|
|
49746
|
-
}).optional().describe("Authentication for WEBHOOK channels")
|
|
49747
|
-
}).optional().describe("Channel-specific configuration. " + "EMAIL: provide recipients. " + "WEBHOOK: provide webhookUrl, httpMethod, and optionally auth. " + "SLACK: provide slackChannelId + slackChannelName (OAuth) or webhookUrl (legacy). " + "WEBSOCKET: no config needed.")
|
|
49719
|
+
webhookUrl: exports_external.string().optional().describe("Webhook/Slack endpoint URL (required for WEBHOOK and SLACK channels)"),
|
|
49720
|
+
httpMethod: exports_external.enum(["POST", "GET", "PUT", "PATCH"]).optional().describe("HTTP method for WEBHOOK channels (defaults to POST)")
|
|
49721
|
+
}).optional().describe("Channel-specific configuration. For EMAIL: provide recipients. For WEBHOOK: provide webhookUrl. For SLACK: provide webhookUrl.")
|
|
49748
49722
|
},
|
|
49749
49723
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
|
|
49750
49724
|
}, withErrorHandling("create_notification_channel", async (args) => {
|
|
@@ -49794,10 +49768,6 @@ function registerTools(server, ctx) {
|
|
|
49794
49768
|
"workflow_failed",
|
|
49795
49769
|
"workflow_sample_finished",
|
|
49796
49770
|
"workflow_data_change",
|
|
49797
|
-
"workflow_validation_anomaly_change",
|
|
49798
|
-
"workflow_export_completed",
|
|
49799
|
-
"workflow_health_degraded",
|
|
49800
|
-
"workflow_recovered",
|
|
49801
49771
|
"system_maintenance",
|
|
49802
49772
|
"service_degradation",
|
|
49803
49773
|
"credits_low",
|
|
@@ -49828,21 +49798,18 @@ function registerTools(server, ctx) {
|
|
|
49828
49798
|
description: "Set up notifications for a workflow or workspace. Creates channels and settings in one step. " + "Specify which events to listen for and which channels to deliver to (email, webhook, slack, websocket).",
|
|
49829
49799
|
inputSchema: {
|
|
49830
49800
|
workflowId: exports_external.string().optional().describe("Workflow ID to configure notifications for. Omit for workspace-level notifications."),
|
|
49831
|
-
events: exports_external.array(exports_external.string()).describe('Event types to notify on. Pass ["all"] for all events, or pick from: ' + "workflow_started, workflow_finished, workflow_failed, workflow_sample_finished, " + "workflow_data_change,
|
|
49801
|
+
events: exports_external.array(exports_external.string()).describe('Event types to notify on. Pass ["all"] for all events, or pick from: ' + "workflow_started, workflow_finished, workflow_failed, workflow_sample_finished, " + "workflow_data_change, system_maintenance, service_degradation, credits_low, free_trial_ending"),
|
|
49832
49802
|
channels: exports_external.object({
|
|
49833
49803
|
email: exports_external.object({
|
|
49834
49804
|
recipients: exports_external.array(exports_external.string().email()).optional().describe("Email addresses to notify. Omit to use the account default email.")
|
|
49835
49805
|
}).optional().describe("Send email notifications. Pass {} for default email or {recipients: [...]} for custom addresses."),
|
|
49836
49806
|
webhook: exports_external.object({
|
|
49837
49807
|
url: exports_external.string().url().describe("Webhook endpoint URL"),
|
|
49838
|
-
httpMethod: exports_external.enum(["POST", "GET", "PUT", "PATCH"]).optional().describe("HTTP method (defaults to POST)")
|
|
49839
|
-
auth: webhookAuthShape
|
|
49808
|
+
httpMethod: exports_external.enum(["POST", "GET", "PUT", "PATCH"]).optional().describe("HTTP method (defaults to POST)")
|
|
49840
49809
|
}).optional().describe("Send via HTTP webhook"),
|
|
49841
49810
|
slack: exports_external.object({
|
|
49842
|
-
webhookUrl: exports_external.string().url().
|
|
49843
|
-
|
|
49844
|
-
slackChannelName: exports_external.string().optional().describe("Slack channel name (e.g. #alerts)")
|
|
49845
|
-
}).optional().describe("Send to Slack channel. Use slackChannelId/slackChannelName for OAuth integration, or webhookUrl for legacy incoming webhooks."),
|
|
49811
|
+
webhookUrl: exports_external.string().url().describe("Slack incoming webhook URL")
|
|
49812
|
+
}).optional().describe("Send to Slack channel"),
|
|
49846
49813
|
websocket: exports_external.boolean().optional().describe("Enable WebSocket notifications")
|
|
49847
49814
|
}).describe("At least one channel must be specified")
|
|
49848
49815
|
},
|
|
@@ -49861,24 +49828,17 @@ function registerTools(server, ctx) {
|
|
|
49861
49828
|
if (ch.websocket)
|
|
49862
49829
|
channelSetup.WEBSOCKET = true;
|
|
49863
49830
|
if (ch.webhook) {
|
|
49864
|
-
|
|
49831
|
+
channelSetup.WEBHOOK = {
|
|
49865
49832
|
name: "mcp-webhook",
|
|
49866
49833
|
webhookUrl: ch.webhook.url,
|
|
49867
49834
|
httpMethod: ch.webhook.httpMethod || "POST"
|
|
49868
49835
|
};
|
|
49869
|
-
if (ch.webhook.auth)
|
|
49870
|
-
webhookChannel.auth = ch.webhook.auth;
|
|
49871
|
-
channelSetup.WEBHOOK = webhookChannel;
|
|
49872
49836
|
}
|
|
49873
49837
|
if (ch.slack) {
|
|
49874
|
-
|
|
49875
|
-
|
|
49876
|
-
|
|
49877
|
-
|
|
49878
|
-
slackChannel.slackChannelId = ch.slack.slackChannelId;
|
|
49879
|
-
if (ch.slack.slackChannelName)
|
|
49880
|
-
slackChannel.slackChannelName = ch.slack.slackChannelName;
|
|
49881
|
-
channelSetup.SLACK = slackChannel;
|
|
49838
|
+
channelSetup.SLACK = {
|
|
49839
|
+
name: "mcp-slack",
|
|
49840
|
+
webhookUrl: ch.slack.webhookUrl
|
|
49841
|
+
};
|
|
49882
49842
|
}
|
|
49883
49843
|
if (Object.keys(channelSetup).length === 0) {
|
|
49884
49844
|
return errorResult("At least one notification channel must be specified (email, webhook, slack, or websocket).");
|
|
@@ -49935,7 +49895,8 @@ function registerTools(server, ctx) {
|
|
|
49935
49895
|
teams: teams.map((t) => ({
|
|
49936
49896
|
id: t.id,
|
|
49937
49897
|
name: t.name,
|
|
49938
|
-
|
|
49898
|
+
memberRole: t.memberRole,
|
|
49899
|
+
...t.adminEmail ? { adminEmail: t.adminEmail } : {},
|
|
49939
49900
|
active: t.id === activeTeamId
|
|
49940
49901
|
})),
|
|
49941
49902
|
activeTeamId
|
|
@@ -53987,6 +53948,14 @@ function generatePKCE() {
|
|
|
53987
53948
|
const challenge = createHash2("sha256").update(verifier).digest("base64url");
|
|
53988
53949
|
return { verifier, challenge };
|
|
53989
53950
|
}
|
|
53951
|
+
function jwtClaims(jwt2) {
|
|
53952
|
+
try {
|
|
53953
|
+
const payload = JSON.parse(Buffer.from(jwt2.split(".")[1], "base64url").toString());
|
|
53954
|
+
return { email: payload.email, sub: payload.sub };
|
|
53955
|
+
} catch {
|
|
53956
|
+
return {};
|
|
53957
|
+
}
|
|
53958
|
+
}
|
|
53990
53959
|
async function exchangeSupabaseCode(code, codeVerifier) {
|
|
53991
53960
|
const supabaseUrl = process.env.SUPABASE_URL;
|
|
53992
53961
|
if (!supabaseUrl)
|
|
@@ -54242,6 +54211,8 @@ class KadoaOAuthProvider {
|
|
|
54242
54211
|
clientId: entry.clientId
|
|
54243
54212
|
});
|
|
54244
54213
|
authCodes.delete(authorizationCode);
|
|
54214
|
+
const claims = jwtClaims(entry.supabaseJwt);
|
|
54215
|
+
console.log(`[AUTH] LOGIN: tokens issued (email=${claims.email}, team=${entry.teamId}, token=${accessToken.slice(0, 12)}..., ttl=${ACCESS_TOKEN_TTL}s, active_sessions=${accessTokens.size})`);
|
|
54245
54216
|
return {
|
|
54246
54217
|
access_token: accessToken,
|
|
54247
54218
|
token_type: "bearer",
|
|
@@ -54251,8 +54222,10 @@ class KadoaOAuthProvider {
|
|
|
54251
54222
|
}
|
|
54252
54223
|
async exchangeRefreshToken(_client, refreshToken) {
|
|
54253
54224
|
const entry = refreshTokens.get(refreshToken);
|
|
54254
|
-
if (!entry)
|
|
54225
|
+
if (!entry) {
|
|
54226
|
+
console.error(`[AUTH] REFRESH_FAIL: unknown refresh token (token=${refreshToken.slice(0, 12)}..., active_sessions=${refreshTokens.size})`);
|
|
54255
54227
|
throw new Error("Unknown refresh token");
|
|
54228
|
+
}
|
|
54256
54229
|
refreshTokens.delete(refreshToken);
|
|
54257
54230
|
let { supabaseJwt, supabaseRefreshToken } = entry;
|
|
54258
54231
|
try {
|
|
@@ -54270,9 +54243,18 @@ class KadoaOAuthProvider {
|
|
|
54270
54243
|
const data = await res.json();
|
|
54271
54244
|
supabaseJwt = data.access_token;
|
|
54272
54245
|
supabaseRefreshToken = data.refresh_token;
|
|
54246
|
+
const newClaims = jwtClaims(supabaseJwt);
|
|
54247
|
+
console.log(`[AUTH] REFRESH_OK: Supabase JWT refreshed (email=${newClaims.email}, team=${entry.teamId})`);
|
|
54248
|
+
} else {
|
|
54249
|
+
const body = await res.text().catch(() => "");
|
|
54250
|
+
const claims = jwtClaims(entry.supabaseJwt);
|
|
54251
|
+
console.error(`[AUTH] REFRESH_WARN: Supabase refresh failed HTTP ${res.status} (email=${claims.email}, team=${entry.teamId}): ${body.slice(0, 200)}`);
|
|
54273
54252
|
}
|
|
54274
54253
|
}
|
|
54275
|
-
} catch {
|
|
54254
|
+
} catch (err) {
|
|
54255
|
+
const claims = jwtClaims(entry.supabaseJwt);
|
|
54256
|
+
console.error(`[AUTH] REFRESH_WARN: Supabase refresh threw (email=${claims.email}, team=${entry.teamId}):`, err);
|
|
54257
|
+
}
|
|
54276
54258
|
const newAccessToken = randomToken();
|
|
54277
54259
|
const newRefreshToken = randomToken();
|
|
54278
54260
|
const expiresAt = Date.now() + ACCESS_TOKEN_TTL * 1000;
|
|
@@ -54307,9 +54289,14 @@ class KadoaOAuthProvider {
|
|
|
54307
54289
|
};
|
|
54308
54290
|
}
|
|
54309
54291
|
const entry = accessTokens.get(token);
|
|
54310
|
-
if (!entry)
|
|
54292
|
+
if (!entry) {
|
|
54293
|
+
console.error(`[AUTH] VERIFY_FAIL: unknown token (token=${token.slice(0, 12)}..., active_sessions=${accessTokens.size})`);
|
|
54311
54294
|
throw new Error("Unknown access token");
|
|
54295
|
+
}
|
|
54312
54296
|
if (entry.expiresAt < Date.now()) {
|
|
54297
|
+
const expiredAgo = Math.round((Date.now() - entry.expiresAt) / 1000);
|
|
54298
|
+
const claims = jwtClaims(entry.supabaseJwt);
|
|
54299
|
+
console.error(`[AUTH] VERIFY_FAIL: token expired ${expiredAgo}s ago (email=${claims.email}, team=${entry.teamId}, token=${token.slice(0, 12)}...)`);
|
|
54313
54300
|
accessTokens.delete(token);
|
|
54314
54301
|
throw new Error("Access token expired");
|
|
54315
54302
|
}
|