@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.
Files changed (2) hide show
  1. package/dist/index.js +76 -89
  2. 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 team_list tool to check your role, or contact your team admin to request write access.`;
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
- const message = classifyError(error48);
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
- role: t.role,
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.object({
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().optional().describe("Slack incoming webhook URL (legacy)"),
49326
- slackChannelId: exports_external.string().optional().describe("Slack channel ID from OAuth integration (e.g. C01ABCDEF)"),
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
- if (typeof n.email === "object" && n.email.recipients?.length) {
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
- const webhookChannel = {
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
- const slackChannel = { name: "mcp-slack" };
49370
- if (n.slack.webhookUrl)
49371
- slackChannel.webhookUrl = n.slack.webhookUrl;
49372
- if (n.slack.slackChannelId)
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
- if (typeof n.email === "object" && n.email.recipients?.length) {
49386
- channels.push(`email (${n.email.recipients.join(", ")})`);
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, slack, or websocket). The channel can then be linked to notification settings for specific events. " + "For SLACK: use slackChannelId + slackChannelName for OAuth integration, or webhookUrl for legacy incoming webhooks. " + "For WEBHOOK: auth is optional and supports bearer, basic, or header authentication.",
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", "WEBSOCKET"]).describe("Type of notification channel"),
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, optional for SLACK)"),
49737
- httpMethod: exports_external.enum(["POST", "GET", "PUT", "PATCH"]).optional().describe("HTTP method for WEBHOOK channels (defaults to POST)"),
49738
- slackChannelId: exports_external.string().optional().describe("Slack channel ID from OAuth integration (for SLACK channels, e.g. C01ABCDEF)"),
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, workflow_validation_anomaly_change, workflow_export_completed, " + "workflow_health_degraded, workflow_recovered, " + "system_maintenance, service_degradation, credits_low, free_trial_ending"),
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().optional().describe("Slack incoming webhook URL (legacy)"),
49843
- slackChannelId: exports_external.string().optional().describe("Slack channel ID from OAuth integration (e.g. C01ABCDEF)"),
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
- const webhookChannel = {
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
- const slackChannel = { name: "mcp-slack" };
49875
- if (ch.slack.webhookUrl)
49876
- slackChannel.webhookUrl = ch.slack.webhookUrl;
49877
- if (ch.slack.slackChannelId)
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
- role: t.role,
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kadoa/mcp",
3
- "version": "0.3.6-rc.0",
3
+ "version": "0.3.6-rc.2",
4
4
  "description": "Kadoa MCP Server — manage workflows from Claude Desktop, Cursor, and other MCP clients",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",