@kadoa/mcp 0.3.9-rc.2 → 0.3.9-rc.3

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 +80 -135
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -49167,37 +49167,13 @@ function createKadoaClient(auth) {
49167
49167
  });
49168
49168
  return client;
49169
49169
  }
49170
- var refreshRawMutex, ctxRefreshMutex;
49170
+ var ctxRefreshMutex;
49171
49171
  var init_client = __esm(() => {
49172
49172
  init_dist2();
49173
- refreshRawMutex = new Map;
49174
49173
  ctxRefreshMutex = new WeakMap;
49175
49174
  });
49176
49175
 
49177
49176
  // src/client.ts
49178
- var exports_client = {};
49179
- __export(exports_client, {
49180
- refreshSupabaseJwtRaw: () => refreshSupabaseJwtRaw,
49181
- refreshSupabaseJwt: () => refreshSupabaseJwt,
49182
- isJwtExpired: () => isJwtExpired,
49183
- getValidJwt: () => getValidJwt,
49184
- decodeJwtClaims: () => decodeJwtClaims,
49185
- createKadoaClient: () => createKadoaClient2,
49186
- SessionExpiredError: () => SessionExpiredError,
49187
- KadoaSdkException: () => KadoaSdkException,
49188
- KadoaClient: () => KadoaClient
49189
- });
49190
- function createKadoaClient2(auth) {
49191
- const client = new KadoaClient({ bearerToken: auth.jwt });
49192
- client.axiosInstance.interceptors.request.use((config2) => {
49193
- config2.headers["x-kadoa-source"] = "mcp";
49194
- if (auth.teamId) {
49195
- config2.headers["x-team-id"] = auth.teamId;
49196
- }
49197
- return config2;
49198
- });
49199
- return client;
49200
- }
49201
49177
  function decodeJwtClaims(jwt2) {
49202
49178
  try {
49203
49179
  const payload = JSON.parse(Buffer.from(jwt2.split(".")[1], "base64url").toString());
@@ -49219,57 +49195,36 @@ function isJwtExpired(jwt2) {
49219
49195
  return true;
49220
49196
  }
49221
49197
  }
49222
- async function refreshSupabaseJwtRaw(supabaseRefreshToken) {
49223
- const inflight = refreshRawMutex2.get(supabaseRefreshToken);
49224
- if (inflight) {
49225
- console.error(`[JWT_REFRESH] DEDUP: reusing in-flight raw refresh`);
49226
- return inflight;
49198
+ async function refreshSupabaseJwt(ctx) {
49199
+ if (!ctx.supabaseRefreshToken) {
49200
+ console.error("[JWT_REFRESH] No refresh token available, cannot refresh");
49201
+ return;
49227
49202
  }
49228
- const promise3 = _doRefreshRaw(supabaseRefreshToken).finally(() => {
49229
- refreshRawMutex2.delete(supabaseRefreshToken);
49230
- });
49231
- refreshRawMutex2.set(supabaseRefreshToken, promise3);
49232
- return promise3;
49233
- }
49234
- async function _doRefreshRaw(supabaseRefreshToken) {
49235
49203
  const supabaseUrl = process.env.SUPABASE_URL;
49236
49204
  if (!supabaseUrl) {
49237
49205
  console.error("[JWT_REFRESH] SUPABASE_URL not set, cannot refresh");
49238
- return null;
49239
- }
49240
- const res = await fetch(`${supabaseUrl}/auth/v1/token?grant_type=refresh_token`, {
49241
- method: "POST",
49242
- headers: {
49243
- "Content-Type": "application/json",
49244
- apikey: process.env.SUPABASE_ANON_KEY
49245
- },
49246
- body: JSON.stringify({ refresh_token: supabaseRefreshToken })
49247
- });
49248
- if (res.ok) {
49249
- const data = await res.json();
49250
- return { jwt: data.access_token, refreshToken: data.refresh_token };
49251
- }
49252
- const body = await res.text().catch(() => "");
49253
- console.error(`[JWT_REFRESH] FAIL: Supabase returned ${res.status} (refreshToken=${supabaseRefreshToken.slice(0, 12)}...): ${body}`);
49254
- if (body.includes("session_expired") || body.includes("refresh_token_not_found") || body.includes("refresh_token_already_used")) {
49255
- throw new SessionExpiredError("Your Kadoa session has expired due to inactivity. Please reconnect to re-authenticate.");
49256
- }
49257
- return null;
49258
- }
49259
- async function refreshSupabaseJwt(ctx) {
49260
- if (!ctx.supabaseRefreshToken) {
49261
- console.error("[JWT_REFRESH] No refresh token available, cannot refresh");
49262
49206
  return;
49263
49207
  }
49264
49208
  try {
49265
49209
  const refreshToken = ctx.supabaseRefreshToken;
49266
49210
  console.error(`[JWT_REFRESH] Refreshing Supabase JWT (refreshToken=${refreshToken.slice(0, 12)}..., team=${ctx.teamId ?? "unknown"})`);
49267
- const result = await refreshSupabaseJwtRaw(refreshToken);
49268
- if (!result)
49211
+ const res = await fetch(`${supabaseUrl}/auth/v1/token?grant_type=refresh_token`, {
49212
+ method: "POST",
49213
+ headers: {
49214
+ "Content-Type": "application/json",
49215
+ apikey: process.env.SUPABASE_ANON_KEY
49216
+ },
49217
+ body: JSON.stringify({ refresh_token: refreshToken })
49218
+ });
49219
+ if (!res.ok) {
49220
+ const body = await res.text().catch(() => "");
49221
+ console.error(`[JWT_REFRESH] FAIL: Supabase returned ${res.status} (refreshToken=${refreshToken.slice(0, 12)}...): ${body}`);
49269
49222
  return;
49270
- ctx.supabaseJwt = result.jwt;
49271
- ctx.supabaseRefreshToken = result.refreshToken;
49272
- ctx.client.setBearerToken(result.jwt);
49223
+ }
49224
+ const data = await res.json();
49225
+ ctx.supabaseJwt = data.access_token;
49226
+ ctx.supabaseRefreshToken = data.refresh_token;
49227
+ ctx.client.setBearerToken(data.access_token);
49273
49228
  try {
49274
49229
  await ctx.persist?.({
49275
49230
  supabaseJwt: ctx.supabaseJwt,
@@ -49279,11 +49234,9 @@ async function refreshSupabaseJwt(ctx) {
49279
49234
  } catch (e) {
49280
49235
  console.error("[JWT_REFRESH] WARN: persist failed, tokens updated in-memory only:", e);
49281
49236
  }
49282
- console.error(`[JWT_REFRESH] OK: token refreshed (team=${ctx.teamId ?? "unknown"}, newRefreshToken=${result.refreshToken.slice(0, 12)}...)`);
49283
- return result.jwt;
49237
+ console.error(`[JWT_REFRESH] OK: token refreshed (team=${ctx.teamId ?? "unknown"}, newRefreshToken=${data.refresh_token.slice(0, 12)}...)`);
49238
+ return data.access_token;
49284
49239
  } catch (error48) {
49285
- if (error48 instanceof SessionExpiredError)
49286
- throw error48;
49287
49240
  console.error("[JWT_REFRESH] FAIL: threw", error48);
49288
49241
  return;
49289
49242
  }
@@ -49304,16 +49257,9 @@ async function getValidJwt(ctx) {
49304
49257
  ctxRefreshMutex2.set(ctx, promise3);
49305
49258
  return promise3;
49306
49259
  }
49307
- var SessionExpiredError, refreshRawMutex2, ctxRefreshMutex2;
49260
+ var ctxRefreshMutex2;
49308
49261
  var init_client2 = __esm(() => {
49309
49262
  init_dist2();
49310
- SessionExpiredError = class SessionExpiredError extends Error {
49311
- constructor(message) {
49312
- super(message ?? "Supabase session expired. Please re-authenticate.");
49313
- this.name = "SessionExpiredError";
49314
- }
49315
- };
49316
- refreshRawMutex2 = new Map;
49317
49263
  ctxRefreshMutex2 = new WeakMap;
49318
49264
  });
49319
49265
 
@@ -49338,6 +49284,9 @@ function coerceArray(wrapPlainString = false) {
49338
49284
  function isRealTimeInterval(interval) {
49339
49285
  return interval?.toUpperCase().replace("-", "_") === "REAL_TIME";
49340
49286
  }
49287
+ function workflowDashboardUrl(workflowId) {
49288
+ return `${DASHBOARD_BASE_URL}/workflow/${workflowId}`;
49289
+ }
49341
49290
  function jsonResult(data) {
49342
49291
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
49343
49292
  }
@@ -49431,10 +49380,6 @@ function registerTools(server, ctx) {
49431
49380
  }
49432
49381
  return await handler(...args);
49433
49382
  } catch (error48) {
49434
- if (error48 instanceof SessionExpiredError) {
49435
- console.error(`[Tool Error] ${name}: session expired, user must re-authenticate`);
49436
- return errorResult("Your session has expired. Please reconnect the MCP server to re-authenticate.");
49437
- }
49438
49383
  let message = classifyError(error48);
49439
49384
  if (KadoaHttpException.isInstance(error48) && error48.httpStatus === 403) {
49440
49385
  try {
@@ -49636,6 +49581,7 @@ function registerTools(server, ctx) {
49636
49581
  return jsonResult({
49637
49582
  success: true,
49638
49583
  workflowId: workflow.workflowId,
49584
+ dashboardUrl: workflowDashboardUrl(workflow.workflowId),
49639
49585
  message
49640
49586
  });
49641
49587
  }));
@@ -49680,6 +49626,7 @@ function registerTools(server, ctx) {
49680
49626
  return jsonResult({
49681
49627
  success: true,
49682
49628
  workflowId: workflow.workflowId,
49629
+ dashboardUrl: workflowDashboardUrl(workflow.workflowId),
49683
49630
  message
49684
49631
  });
49685
49632
  }));
@@ -49699,6 +49646,7 @@ function registerTools(server, ctx) {
49699
49646
  workflows: workflows.map((w) => ({
49700
49647
  id: w.id,
49701
49648
  name: w.name,
49649
+ dashboardUrl: workflowDashboardUrl(w.id),
49702
49650
  status: w.displayState ?? w.state,
49703
49651
  urls: w.urls,
49704
49652
  totalRecords: w.totalRecords,
@@ -49721,6 +49669,7 @@ function registerTools(server, ctx) {
49721
49669
  return jsonResult({
49722
49670
  id: workflow.id,
49723
49671
  name: workflow.name,
49672
+ dashboardUrl: workflowDashboardUrl(workflow.id),
49724
49673
  description: workflow.description,
49725
49674
  status: workflow.displayState ?? workflow.state,
49726
49675
  urls: workflow.urls,
@@ -50148,7 +50097,7 @@ function registerTools(server, ctx) {
50148
50097
  });
50149
50098
  }));
50150
50099
  }
50151
- var SchemaFieldShape;
50100
+ var SchemaFieldShape, DASHBOARD_BASE_URL = "https://app.kadoa.com";
50152
50101
  var init_tools = __esm(() => {
50153
50102
  init_zod();
50154
50103
  init_dist2();
@@ -54159,6 +54108,45 @@ function generatePKCE() {
54159
54108
  const challenge = createHash2("sha256").update(verifier).digest("base64url");
54160
54109
  return { verifier, challenge };
54161
54110
  }
54111
+ async function refreshSupabaseToken(supabaseRefreshToken, context) {
54112
+ const inflight = supabaseRefreshMutex.get(supabaseRefreshToken);
54113
+ if (inflight) {
54114
+ console.error(`[AUTH] REFRESH_DEDUP: reusing in-flight refresh (${context})`);
54115
+ return inflight;
54116
+ }
54117
+ const promise3 = (async () => {
54118
+ const supabaseUrl = process.env.SUPABASE_URL;
54119
+ if (!supabaseUrl || !supabaseRefreshToken)
54120
+ return null;
54121
+ try {
54122
+ const res = await fetch(`${supabaseUrl}/auth/v1/token?grant_type=refresh_token`, {
54123
+ method: "POST",
54124
+ headers: {
54125
+ "Content-Type": "application/json",
54126
+ apikey: process.env.SUPABASE_ANON_KEY
54127
+ },
54128
+ body: JSON.stringify({ refresh_token: supabaseRefreshToken })
54129
+ });
54130
+ if (res.ok) {
54131
+ const data = await res.json();
54132
+ const newClaims = jwtClaims(data.access_token);
54133
+ console.log(`[AUTH] REFRESH_OK: Supabase JWT refreshed (${context}, newEmail=${newClaims.email})`);
54134
+ return { jwt: data.access_token, refreshToken: data.refresh_token };
54135
+ }
54136
+ const body = await res.text().catch(() => "");
54137
+ console.error(`[AUTH] REFRESH_FAIL: Supabase returned ${res.status} (${context}): ${body.slice(0, 200)}`);
54138
+ return null;
54139
+ } catch (err) {
54140
+ console.error(`[AUTH] REFRESH_FAIL: Supabase refresh threw (${context}):`, err);
54141
+ return null;
54142
+ }
54143
+ })();
54144
+ supabaseRefreshMutex.set(supabaseRefreshToken, promise3);
54145
+ promise3.finally(() => {
54146
+ supabaseRefreshMutex.delete(supabaseRefreshToken);
54147
+ });
54148
+ return promise3;
54149
+ }
54162
54150
  function jwtClaims(jwt2) {
54163
54151
  try {
54164
54152
  const payload = JSON.parse(Buffer.from(jwt2.split(".")[1], "base64url").toString());
@@ -54466,22 +54454,12 @@ class KadoaOAuthProvider {
54466
54454
  let { supabaseJwt, supabaseRefreshToken } = entry;
54467
54455
  const claims = jwtClaims(entry.supabaseJwt);
54468
54456
  const context = `email=${claims.email}, team=${entry.teamId}`;
54469
- try {
54470
- const { refreshSupabaseJwtRaw: refreshSupabaseJwtRaw2, SessionExpiredError: SessionExpiredError2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
54471
- const refreshed = await refreshSupabaseJwtRaw2(supabaseRefreshToken);
54472
- if (refreshed) {
54473
- supabaseJwt = refreshed.jwt;
54474
- supabaseRefreshToken = refreshed.refreshToken;
54475
- console.error(`[AUTH] REFRESH_OK: Supabase JWT refreshed (${context})`);
54476
- } else {
54477
- console.error(`[AUTH] REFRESH_WARN: using stale Supabase JWT as fallback (${context})`);
54478
- }
54479
- } catch (error48) {
54480
- if (error48 instanceof Error && error48.name === "SessionExpiredError") {
54481
- console.error(`[AUTH] REFRESH_DEAD: session permanently expired (${context}): ${error48.message}`);
54482
- throw new InvalidTokenError("Supabase session expired. Please re-authenticate.");
54483
- }
54484
- console.error(`[AUTH] REFRESH_WARN: unexpected error, using stale JWT (${context}):`, error48);
54457
+ const refreshed = await refreshSupabaseToken(supabaseRefreshToken, context);
54458
+ if (refreshed) {
54459
+ supabaseJwt = refreshed.jwt;
54460
+ supabaseRefreshToken = refreshed.refreshToken;
54461
+ } else {
54462
+ console.error(`[AUTH] REFRESH_WARN: using stale Supabase JWT as fallback (${context})`);
54485
54463
  }
54486
54464
  const freshClaims = jwtClaims(supabaseJwt);
54487
54465
  const teamId = freshClaims.activeTeamId ?? entry.teamId;
@@ -54608,7 +54586,7 @@ class KadoaOAuthProvider {
54608
54586
  codeChallenge: pending.params.codeChallenge,
54609
54587
  clientId: pending.client.client_id,
54610
54588
  redirectUri: pending.params.redirectUri,
54611
- expiresAt: Date.now() + 600000
54589
+ expiresAt: Date.now() + 10 * 60 * 1000
54612
54590
  }, 600);
54613
54591
  const redirectUrl = new URL(pending.params.redirectUri);
54614
54592
  redirectUrl.searchParams.set("code", mcpCode);
@@ -55037,11 +55015,12 @@ function renderLoginPage(state, error48) {
55037
55015
  function escapeHtml(str) {
55038
55016
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
55039
55017
  }
55040
- var TEAM_SELECTION_TTL, ACCESS_TOKEN_TTL;
55018
+ var TEAM_SELECTION_TTL, ACCESS_TOKEN_TTL, supabaseRefreshMutex;
55041
55019
  var init_auth2 = __esm(() => {
55042
55020
  init_errors4();
55043
55021
  TEAM_SELECTION_TTL = 10 * 60 * 1000;
55044
55022
  ACCESS_TOKEN_TTL = 7 * 24 * 3600;
55023
+ supabaseRefreshMutex = new Map;
55045
55024
  });
55046
55025
 
55047
55026
  // src/redis-store.ts
@@ -55234,39 +55213,6 @@ async function startHttpServer(options) {
55234
55213
  return;
55235
55214
  }
55236
55215
  const identity = `jwt:${auth.userId.slice(0, 8)}...:team=${auth.teamId.slice(0, 8)}...`;
55237
- if (isJwtExpired(auth.jwt)) {
55238
- try {
55239
- const refreshed = await refreshSupabaseJwtRaw(auth.refreshToken);
55240
- if (refreshed) {
55241
- auth.jwt = refreshed.jwt;
55242
- auth.refreshToken = refreshed.refreshToken;
55243
- const entry = await store.get("access_tokens", auth.mcpToken);
55244
- if (entry) {
55245
- const remainingMs = entry.expiresAt - Date.now();
55246
- if (remainingMs > 0) {
55247
- await store.set("access_tokens", auth.mcpToken, {
55248
- ...entry,
55249
- supabaseJwt: refreshed.jwt,
55250
- supabaseRefreshToken: refreshed.refreshToken
55251
- }, Math.ceil(remainingMs / 1000));
55252
- console.error(`[PROACTIVE_REFRESH] OK: JWT refreshed on ${method} (${identity})`);
55253
- }
55254
- }
55255
- }
55256
- } catch (error48) {
55257
- if (error48 instanceof SessionExpiredError) {
55258
- console.error(`[PROACTIVE_REFRESH] Session dead on ${method} (${identity}): ${error48.message}`);
55259
- await store.del("access_tokens", auth.mcpToken);
55260
- res.status(401).json({
55261
- jsonrpc: "2.0",
55262
- error: { code: -32001, message: error48.message },
55263
- id: req.body?.id ?? null
55264
- });
55265
- return;
55266
- }
55267
- console.error(`[PROACTIVE_REFRESH] WARN: refresh failed on ${method}, continuing with stale JWT`, error48);
55268
- }
55269
- }
55270
55216
  try {
55271
55217
  console.error(`[MCP] POST method=${method} auth=${identity}`);
55272
55218
  const transport = new StreamableHTTPServerTransport({
@@ -55344,7 +55290,6 @@ var init_http2 = __esm(async () => {
55344
55290
  init_bearerAuth();
55345
55291
  init_auth2();
55346
55292
  init_redis_store();
55347
- init_client2();
55348
55293
  await init_src();
55349
55294
  });
55350
55295
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kadoa/mcp",
3
- "version": "0.3.9-rc.2",
3
+ "version": "0.3.9-rc.3",
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",