@kadoa/mcp 0.3.9 → 0.3.10-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 +168 -78
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -49167,13 +49167,37 @@ function createKadoaClient(auth) {
|
|
|
49167
49167
|
});
|
|
49168
49168
|
return client;
|
|
49169
49169
|
}
|
|
49170
|
-
var ctxRefreshMutex;
|
|
49170
|
+
var refreshRawMutex, ctxRefreshMutex;
|
|
49171
49171
|
var init_client = __esm(() => {
|
|
49172
49172
|
init_dist2();
|
|
49173
|
+
refreshRawMutex = new Map;
|
|
49173
49174
|
ctxRefreshMutex = new WeakMap;
|
|
49174
49175
|
});
|
|
49175
49176
|
|
|
49176
49177
|
// 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
|
+
}
|
|
49177
49201
|
function decodeJwtClaims(jwt2) {
|
|
49178
49202
|
try {
|
|
49179
49203
|
const payload = JSON.parse(Buffer.from(jwt2.split(".")[1], "base64url").toString());
|
|
@@ -49195,36 +49219,57 @@ function isJwtExpired(jwt2) {
|
|
|
49195
49219
|
return true;
|
|
49196
49220
|
}
|
|
49197
49221
|
}
|
|
49198
|
-
async function
|
|
49199
|
-
|
|
49200
|
-
|
|
49201
|
-
|
|
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;
|
|
49202
49227
|
}
|
|
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) {
|
|
49203
49235
|
const supabaseUrl = process.env.SUPABASE_URL;
|
|
49204
49236
|
if (!supabaseUrl) {
|
|
49205
49237
|
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");
|
|
49206
49262
|
return;
|
|
49207
49263
|
}
|
|
49208
49264
|
try {
|
|
49209
49265
|
const refreshToken = ctx.supabaseRefreshToken;
|
|
49210
49266
|
console.error(`[JWT_REFRESH] Refreshing Supabase JWT (refreshToken=${refreshToken.slice(0, 12)}..., team=${ctx.teamId ?? "unknown"})`);
|
|
49211
|
-
const
|
|
49212
|
-
|
|
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}`);
|
|
49267
|
+
const result = await refreshSupabaseJwtRaw(refreshToken);
|
|
49268
|
+
if (!result)
|
|
49222
49269
|
return;
|
|
49223
|
-
|
|
49224
|
-
|
|
49225
|
-
ctx.
|
|
49226
|
-
ctx.supabaseRefreshToken = data.refresh_token;
|
|
49227
|
-
ctx.client.setBearerToken(data.access_token);
|
|
49270
|
+
ctx.supabaseJwt = result.jwt;
|
|
49271
|
+
ctx.supabaseRefreshToken = result.refreshToken;
|
|
49272
|
+
ctx.client.setBearerToken(result.jwt);
|
|
49228
49273
|
try {
|
|
49229
49274
|
await ctx.persist?.({
|
|
49230
49275
|
supabaseJwt: ctx.supabaseJwt,
|
|
@@ -49234,9 +49279,11 @@ async function refreshSupabaseJwt(ctx) {
|
|
|
49234
49279
|
} catch (e) {
|
|
49235
49280
|
console.error("[JWT_REFRESH] WARN: persist failed, tokens updated in-memory only:", e);
|
|
49236
49281
|
}
|
|
49237
|
-
console.error(`[JWT_REFRESH] OK: token refreshed (team=${ctx.teamId ?? "unknown"}, newRefreshToken=${
|
|
49238
|
-
return
|
|
49282
|
+
console.error(`[JWT_REFRESH] OK: token refreshed (team=${ctx.teamId ?? "unknown"}, newRefreshToken=${result.refreshToken.slice(0, 12)}...)`);
|
|
49283
|
+
return result.jwt;
|
|
49239
49284
|
} catch (error48) {
|
|
49285
|
+
if (error48 instanceof SessionExpiredError)
|
|
49286
|
+
throw error48;
|
|
49240
49287
|
console.error("[JWT_REFRESH] FAIL: threw", error48);
|
|
49241
49288
|
return;
|
|
49242
49289
|
}
|
|
@@ -49257,9 +49304,16 @@ async function getValidJwt(ctx) {
|
|
|
49257
49304
|
ctxRefreshMutex2.set(ctx, promise3);
|
|
49258
49305
|
return promise3;
|
|
49259
49306
|
}
|
|
49260
|
-
var ctxRefreshMutex2;
|
|
49307
|
+
var SessionExpiredError, refreshRawMutex2, ctxRefreshMutex2;
|
|
49261
49308
|
var init_client2 = __esm(() => {
|
|
49262
49309
|
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;
|
|
49263
49317
|
ctxRefreshMutex2 = new WeakMap;
|
|
49264
49318
|
});
|
|
49265
49319
|
|
|
@@ -49380,6 +49434,10 @@ function registerTools(server, ctx) {
|
|
|
49380
49434
|
}
|
|
49381
49435
|
return await handler(...args);
|
|
49382
49436
|
} catch (error48) {
|
|
49437
|
+
if (error48 instanceof SessionExpiredError) {
|
|
49438
|
+
console.error(`[Tool Error] ${name}: session expired, user must re-authenticate`);
|
|
49439
|
+
return errorResult("Your session has expired. Please reconnect the MCP server to re-authenticate.");
|
|
49440
|
+
}
|
|
49383
49441
|
let message = classifyError(error48);
|
|
49384
49442
|
if (KadoaHttpException.isInstance(error48) && error48.httpStatus === 403) {
|
|
49385
49443
|
try {
|
|
@@ -49529,7 +49587,10 @@ function registerTools(server, ctx) {
|
|
|
49529
49587
|
inputSchema: strictSchema({
|
|
49530
49588
|
...extractionInputShape,
|
|
49531
49589
|
...urlInputShape,
|
|
49532
|
-
|
|
49590
|
+
description: exports_external.string().max(500).optional().describe("Description of what this workflow does (max 500 characters)"),
|
|
49591
|
+
tags: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string())).optional().describe("Tags for organizing workflows"),
|
|
49592
|
+
limit: exports_external.number().optional().describe("Maximum number of records to extract per run. Useful for limiting scope or cost control."),
|
|
49593
|
+
updateInterval: exports_external.enum([
|
|
49533
49594
|
"ONLY_ONCE",
|
|
49534
49595
|
"EVERY_10_MINUTES",
|
|
49535
49596
|
"HALF_HOURLY",
|
|
@@ -49545,8 +49606,10 @@ function registerTools(server, ctx) {
|
|
|
49545
49606
|
"BIWEEKLY",
|
|
49546
49607
|
"TRIWEEKLY",
|
|
49547
49608
|
"FOUR_WEEKS",
|
|
49548
|
-
"MONTHLY"
|
|
49549
|
-
|
|
49609
|
+
"MONTHLY",
|
|
49610
|
+
"CUSTOM"
|
|
49611
|
+
]).optional().describe("How often the workflow runs. Defaults to ONLY_ONCE. Use CUSTOM with the 'schedules' field for cron-based scheduling."),
|
|
49612
|
+
schedules: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string())).optional().describe("Cron expressions for CUSTOM updateInterval (e.g. '0 8 * * 2' for Tuesdays at 8am UTC). Requires updateInterval='CUSTOM'."),
|
|
49550
49613
|
...notificationsInputShape
|
|
49551
49614
|
}),
|
|
49552
49615
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
|
|
@@ -49555,13 +49618,18 @@ function registerTools(server, ctx) {
|
|
|
49555
49618
|
if (!urls) {
|
|
49556
49619
|
return errorResult("At least one URL is required. Provide 'urls' (array) or 'url' (string).");
|
|
49557
49620
|
}
|
|
49621
|
+
if (args.updateInterval === "CUSTOM" && (!args.schedules || args.schedules.length === 0)) {
|
|
49622
|
+
return errorResult("updateInterval='CUSTOM' requires at least one cron expression in the 'schedules' field (e.g. '0 8 * * 2' for Tuesdays at 8am UTC).");
|
|
49623
|
+
}
|
|
49558
49624
|
let builder = ctx.client.extract({
|
|
49559
49625
|
urls,
|
|
49560
49626
|
name: args.name || "Untitled Workflow",
|
|
49561
49627
|
navigationMode: "agentic-navigation",
|
|
49562
49628
|
userPrompt: args.prompt,
|
|
49563
49629
|
extraction: buildExtraction(args),
|
|
49564
|
-
interval: args.
|
|
49630
|
+
interval: args.updateInterval,
|
|
49631
|
+
description: args.description,
|
|
49632
|
+
schedules: args.schedules
|
|
49565
49633
|
});
|
|
49566
49634
|
const n = args.notifications;
|
|
49567
49635
|
const hasNotifications = n && (n.email || n.webhook || n.slack || n.websocket);
|
|
@@ -49572,6 +49640,15 @@ function registerTools(server, ctx) {
|
|
|
49572
49640
|
});
|
|
49573
49641
|
}
|
|
49574
49642
|
const workflow = await builder.create();
|
|
49643
|
+
const needsUpdate = args.limit !== undefined || args.tags && args.tags.length > 0;
|
|
49644
|
+
if (needsUpdate) {
|
|
49645
|
+
const updates = {};
|
|
49646
|
+
if (args.limit !== undefined)
|
|
49647
|
+
updates.limit = args.limit;
|
|
49648
|
+
if (args.tags && args.tags.length > 0)
|
|
49649
|
+
updates.tags = args.tags;
|
|
49650
|
+
await ctx.client.workflow.update(workflow.workflowId, updates);
|
|
49651
|
+
}
|
|
49575
49652
|
const enabledChannels = await describeNotifications(n);
|
|
49576
49653
|
let message = "Workflow created successfully. The AI agent will start extracting data automatically.";
|
|
49577
49654
|
if (enabledChannels.length > 0) {
|
|
@@ -49592,6 +49669,8 @@ function registerTools(server, ctx) {
|
|
|
49592
49669
|
inputSchema: strictSchema({
|
|
49593
49670
|
...extractionInputShape,
|
|
49594
49671
|
...urlInputShape,
|
|
49672
|
+
description: exports_external.string().max(500).optional().describe("Description of what this monitor watches (max 500 characters)"),
|
|
49673
|
+
tags: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string())).optional().describe("Tags for organizing monitors"),
|
|
49595
49674
|
...notificationsInputShape
|
|
49596
49675
|
}),
|
|
49597
49676
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false }
|
|
@@ -49610,13 +49689,17 @@ function registerTools(server, ctx) {
|
|
|
49610
49689
|
name: args.name || "Untitled Monitor",
|
|
49611
49690
|
userPrompt: args.prompt,
|
|
49612
49691
|
extraction: buildExtraction(args),
|
|
49613
|
-
interval: "REAL_TIME"
|
|
49692
|
+
interval: "REAL_TIME",
|
|
49693
|
+
description: args.description
|
|
49614
49694
|
});
|
|
49615
49695
|
builder = builder.withNotifications({
|
|
49616
49696
|
events: ["workflow_data_change"],
|
|
49617
49697
|
channels: buildNotificationChannels(n)
|
|
49618
49698
|
});
|
|
49619
49699
|
const workflow = await builder.create();
|
|
49700
|
+
if (args.tags && args.tags.length > 0) {
|
|
49701
|
+
await ctx.client.workflow.update(workflow.workflowId, { tags: args.tags });
|
|
49702
|
+
}
|
|
49620
49703
|
const enabledChannels = await describeNotifications(n);
|
|
49621
49704
|
let message = "Real-time monitor created successfully. It will continuously watch for changes — no manual runs needed.";
|
|
49622
49705
|
if (enabledChannels.length > 0) {
|
|
@@ -49785,7 +49868,7 @@ function registerTools(server, ctx) {
|
|
|
49785
49868
|
urls: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string()).min(1)).optional().describe("New target URLs for the workflow (array of strings). Also accepts a single URL string."),
|
|
49786
49869
|
entity: exports_external.string().optional().describe("Entity name for extraction (e.g., 'Product', 'Job Posting')"),
|
|
49787
49870
|
schema: exports_external.preprocess(coerceArray(), exports_external.array(exports_external.object(SchemaFieldShape))).optional().describe("New extraction schema fields"),
|
|
49788
|
-
description: exports_external.string().optional().describe("Workflow description"),
|
|
49871
|
+
description: exports_external.string().max(500).optional().describe("Workflow description (max 500 characters)"),
|
|
49789
49872
|
tags: exports_external.preprocess(coerceArray(true), exports_external.array(exports_external.string())).optional().describe("Tags for organizing workflows"),
|
|
49790
49873
|
userPrompt: exports_external.string().optional().describe("Navigation prompt for agentic-navigation mode (10-5000 characters)"),
|
|
49791
49874
|
updateInterval: exports_external.enum([
|
|
@@ -49824,6 +49907,9 @@ function registerTools(server, ctx) {
|
|
|
49824
49907
|
return errorResult("Cannot change a real-time workflow's interval to a scheduled interval. " + "Real-time workflows are architecturally different and cannot be converted to regular workflows. " + "Delete this workflow and create a new one with the desired interval using create_workflow.");
|
|
49825
49908
|
}
|
|
49826
49909
|
}
|
|
49910
|
+
if (updates.updateInterval === "CUSTOM" && (!updates.schedules || updates.schedules.length === 0)) {
|
|
49911
|
+
return errorResult("updateInterval='CUSTOM' requires at least one cron expression in the 'schedules' field (e.g. '0 8 * * 2' for Tuesdays at 8am UTC).");
|
|
49912
|
+
}
|
|
49827
49913
|
const result = await ctx.client.workflow.update(workflowId, updates);
|
|
49828
49914
|
return jsonResult({
|
|
49829
49915
|
success: true,
|
|
@@ -54108,45 +54194,6 @@ function generatePKCE() {
|
|
|
54108
54194
|
const challenge = createHash2("sha256").update(verifier).digest("base64url");
|
|
54109
54195
|
return { verifier, challenge };
|
|
54110
54196
|
}
|
|
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
|
-
}
|
|
54150
54197
|
function jwtClaims(jwt2) {
|
|
54151
54198
|
try {
|
|
54152
54199
|
const payload = JSON.parse(Buffer.from(jwt2.split(".")[1], "base64url").toString());
|
|
@@ -54454,12 +54501,22 @@ class KadoaOAuthProvider {
|
|
|
54454
54501
|
let { supabaseJwt, supabaseRefreshToken } = entry;
|
|
54455
54502
|
const claims = jwtClaims(entry.supabaseJwt);
|
|
54456
54503
|
const context = `email=${claims.email}, team=${entry.teamId}`;
|
|
54457
|
-
|
|
54458
|
-
|
|
54459
|
-
|
|
54460
|
-
|
|
54461
|
-
|
|
54462
|
-
|
|
54504
|
+
try {
|
|
54505
|
+
const { refreshSupabaseJwtRaw: refreshSupabaseJwtRaw2, SessionExpiredError: SessionExpiredError2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
|
|
54506
|
+
const refreshed = await refreshSupabaseJwtRaw2(supabaseRefreshToken);
|
|
54507
|
+
if (refreshed) {
|
|
54508
|
+
supabaseJwt = refreshed.jwt;
|
|
54509
|
+
supabaseRefreshToken = refreshed.refreshToken;
|
|
54510
|
+
console.error(`[AUTH] REFRESH_OK: Supabase JWT refreshed (${context})`);
|
|
54511
|
+
} else {
|
|
54512
|
+
console.error(`[AUTH] REFRESH_WARN: using stale Supabase JWT as fallback (${context})`);
|
|
54513
|
+
}
|
|
54514
|
+
} catch (error48) {
|
|
54515
|
+
if (error48 instanceof Error && error48.name === "SessionExpiredError") {
|
|
54516
|
+
console.error(`[AUTH] REFRESH_DEAD: session permanently expired (${context}): ${error48.message}`);
|
|
54517
|
+
throw new InvalidTokenError("Supabase session expired. Please re-authenticate.");
|
|
54518
|
+
}
|
|
54519
|
+
console.error(`[AUTH] REFRESH_WARN: unexpected error, using stale JWT (${context}):`, error48);
|
|
54463
54520
|
}
|
|
54464
54521
|
const freshClaims = jwtClaims(supabaseJwt);
|
|
54465
54522
|
const teamId = freshClaims.activeTeamId ?? entry.teamId;
|
|
@@ -54586,7 +54643,7 @@ class KadoaOAuthProvider {
|
|
|
54586
54643
|
codeChallenge: pending.params.codeChallenge,
|
|
54587
54644
|
clientId: pending.client.client_id,
|
|
54588
54645
|
redirectUri: pending.params.redirectUri,
|
|
54589
|
-
expiresAt: Date.now() +
|
|
54646
|
+
expiresAt: Date.now() + 600000
|
|
54590
54647
|
}, 600);
|
|
54591
54648
|
const redirectUrl = new URL(pending.params.redirectUri);
|
|
54592
54649
|
redirectUrl.searchParams.set("code", mcpCode);
|
|
@@ -55015,12 +55072,11 @@ function renderLoginPage(state, error48) {
|
|
|
55015
55072
|
function escapeHtml(str) {
|
|
55016
55073
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
55017
55074
|
}
|
|
55018
|
-
var TEAM_SELECTION_TTL, ACCESS_TOKEN_TTL
|
|
55075
|
+
var TEAM_SELECTION_TTL, ACCESS_TOKEN_TTL;
|
|
55019
55076
|
var init_auth2 = __esm(() => {
|
|
55020
55077
|
init_errors4();
|
|
55021
55078
|
TEAM_SELECTION_TTL = 10 * 60 * 1000;
|
|
55022
55079
|
ACCESS_TOKEN_TTL = 7 * 24 * 3600;
|
|
55023
|
-
supabaseRefreshMutex = new Map;
|
|
55024
55080
|
});
|
|
55025
55081
|
|
|
55026
55082
|
// src/redis-store.ts
|
|
@@ -55213,6 +55269,39 @@ async function startHttpServer(options) {
|
|
|
55213
55269
|
return;
|
|
55214
55270
|
}
|
|
55215
55271
|
const identity = `jwt:${auth.userId.slice(0, 8)}...:team=${auth.teamId.slice(0, 8)}...`;
|
|
55272
|
+
if (isJwtExpired(auth.jwt)) {
|
|
55273
|
+
try {
|
|
55274
|
+
const refreshed = await refreshSupabaseJwtRaw(auth.refreshToken);
|
|
55275
|
+
if (refreshed) {
|
|
55276
|
+
auth.jwt = refreshed.jwt;
|
|
55277
|
+
auth.refreshToken = refreshed.refreshToken;
|
|
55278
|
+
const entry = await store.get("access_tokens", auth.mcpToken);
|
|
55279
|
+
if (entry) {
|
|
55280
|
+
const remainingMs = entry.expiresAt - Date.now();
|
|
55281
|
+
if (remainingMs > 0) {
|
|
55282
|
+
await store.set("access_tokens", auth.mcpToken, {
|
|
55283
|
+
...entry,
|
|
55284
|
+
supabaseJwt: refreshed.jwt,
|
|
55285
|
+
supabaseRefreshToken: refreshed.refreshToken
|
|
55286
|
+
}, Math.ceil(remainingMs / 1000));
|
|
55287
|
+
console.error(`[PROACTIVE_REFRESH] OK: JWT refreshed on ${method} (${identity})`);
|
|
55288
|
+
}
|
|
55289
|
+
}
|
|
55290
|
+
}
|
|
55291
|
+
} catch (error48) {
|
|
55292
|
+
if (error48 instanceof SessionExpiredError) {
|
|
55293
|
+
console.error(`[PROACTIVE_REFRESH] Session dead on ${method} (${identity}): ${error48.message}`);
|
|
55294
|
+
await store.del("access_tokens", auth.mcpToken);
|
|
55295
|
+
res.status(401).json({
|
|
55296
|
+
jsonrpc: "2.0",
|
|
55297
|
+
error: { code: -32001, message: error48.message },
|
|
55298
|
+
id: req.body?.id ?? null
|
|
55299
|
+
});
|
|
55300
|
+
return;
|
|
55301
|
+
}
|
|
55302
|
+
console.error(`[PROACTIVE_REFRESH] WARN: refresh failed on ${method}, continuing with stale JWT`, error48);
|
|
55303
|
+
}
|
|
55304
|
+
}
|
|
55216
55305
|
try {
|
|
55217
55306
|
console.error(`[MCP] POST method=${method} auth=${identity}`);
|
|
55218
55307
|
const transport = new StreamableHTTPServerTransport({
|
|
@@ -55290,6 +55379,7 @@ var init_http2 = __esm(async () => {
|
|
|
55290
55379
|
init_bearerAuth();
|
|
55291
55380
|
init_auth2();
|
|
55292
55381
|
init_redis_store();
|
|
55382
|
+
init_client2();
|
|
55293
55383
|
await init_src();
|
|
55294
55384
|
});
|
|
55295
55385
|
|