@kadoa/mcp 0.5.10 → 0.5.12
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/README.md +2 -2
- package/dist/index.js +313 -102
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,8 +72,8 @@ Once the MCP server is configured, you can manage the full workflow lifecycle th
|
|
|
72
72
|
> You: Fetch https://example.com as markdown.
|
|
73
73
|
|
|
74
74
|
Claude calls scrape with smart routing and returns compact markdown.
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
Routing is fully automatic — it escalates through the stealth tiers on
|
|
76
|
+
its own when a site blocks bots. Ask for HTML when you need the raw source.
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
The `scrape` tool is available only to workspaces enabled for the Scrape API. Use a workflow instead for structured extraction, recurring runs, monitoring, or multi-page navigation.
|
package/dist/index.js
CHANGED
|
@@ -51861,13 +51861,23 @@ This is the main page content.`
|
|
|
51861
51861
|
});
|
|
51862
51862
|
|
|
51863
51863
|
// src/client.ts
|
|
51864
|
+
function resolveServiceAccountApiKey(credentialRef) {
|
|
51865
|
+
const configuredRef = process.env.MCP_SERVICE_ACCOUNT_CREDENTIAL_REF?.trim();
|
|
51866
|
+
if (!configuredRef || credentialRef !== configuredRef) {
|
|
51867
|
+
throw new Error("Unknown service-account credential reference");
|
|
51868
|
+
}
|
|
51869
|
+
const apiKey = process.env.MCP_SERVICE_ACCOUNT_KADOA_API_KEY?.trim();
|
|
51870
|
+
if (!apiKey) {
|
|
51871
|
+
throw new Error("Service-account API key is not configured");
|
|
51872
|
+
}
|
|
51873
|
+
return apiKey;
|
|
51874
|
+
}
|
|
51864
51875
|
function createKadoaClient(auth) {
|
|
51865
|
-
const
|
|
51876
|
+
const { principal } = auth;
|
|
51877
|
+
const client = principal.type === "user" ? new KadoaClient({ bearerToken: principal.supabaseJwt }) : new KadoaClient({ apiKey: auth.apiKey ?? resolveServiceAccountApiKey(principal.credentialRef) });
|
|
51866
51878
|
client.axiosInstance.interceptors.request.use((config2) => {
|
|
51867
51879
|
config2.headers["x-kadoa-source"] = "mcp";
|
|
51868
|
-
|
|
51869
|
-
config2.headers["x-team-id"] = auth.teamId;
|
|
51870
|
-
}
|
|
51880
|
+
config2.headers["x-team-id"] = principal.teamId;
|
|
51871
51881
|
return config2;
|
|
51872
51882
|
});
|
|
51873
51883
|
return client;
|
|
@@ -51882,6 +51892,7 @@ var init_client = __esm(() => {
|
|
|
51882
51892
|
// src/client.ts
|
|
51883
51893
|
var exports_client = {};
|
|
51884
51894
|
__export(exports_client, {
|
|
51895
|
+
resolveServiceAccountApiKey: () => resolveServiceAccountApiKey2,
|
|
51885
51896
|
refreshSupabaseJwtRaw: () => refreshSupabaseJwtRaw,
|
|
51886
51897
|
refreshSupabaseJwt: () => refreshSupabaseJwt,
|
|
51887
51898
|
isJwtExpired: () => isJwtExpired,
|
|
@@ -51892,13 +51903,23 @@ __export(exports_client, {
|
|
|
51892
51903
|
KadoaSdkException: () => KadoaSdkException,
|
|
51893
51904
|
KadoaClient: () => KadoaClient
|
|
51894
51905
|
});
|
|
51906
|
+
function resolveServiceAccountApiKey2(credentialRef) {
|
|
51907
|
+
const configuredRef = process.env.MCP_SERVICE_ACCOUNT_CREDENTIAL_REF?.trim();
|
|
51908
|
+
if (!configuredRef || credentialRef !== configuredRef) {
|
|
51909
|
+
throw new Error("Unknown service-account credential reference");
|
|
51910
|
+
}
|
|
51911
|
+
const apiKey = process.env.MCP_SERVICE_ACCOUNT_KADOA_API_KEY?.trim();
|
|
51912
|
+
if (!apiKey) {
|
|
51913
|
+
throw new Error("Service-account API key is not configured");
|
|
51914
|
+
}
|
|
51915
|
+
return apiKey;
|
|
51916
|
+
}
|
|
51895
51917
|
function createKadoaClient2(auth) {
|
|
51896
|
-
const
|
|
51918
|
+
const { principal } = auth;
|
|
51919
|
+
const client = principal.type === "user" ? new KadoaClient({ bearerToken: principal.supabaseJwt }) : new KadoaClient({ apiKey: auth.apiKey ?? resolveServiceAccountApiKey2(principal.credentialRef) });
|
|
51897
51920
|
client.axiosInstance.interceptors.request.use((config2) => {
|
|
51898
51921
|
config2.headers["x-kadoa-source"] = "mcp";
|
|
51899
|
-
|
|
51900
|
-
config2.headers["x-team-id"] = auth.teamId;
|
|
51901
|
-
}
|
|
51922
|
+
config2.headers["x-team-id"] = principal.teamId;
|
|
51902
51923
|
return config2;
|
|
51903
51924
|
});
|
|
51904
51925
|
return client;
|
|
@@ -51955,7 +51976,7 @@ async function _doRefreshRaw(supabaseRefreshToken) {
|
|
|
51955
51976
|
return { jwt: data.access_token, refreshToken: data.refresh_token };
|
|
51956
51977
|
}
|
|
51957
51978
|
const body = await res.text().catch(() => "");
|
|
51958
|
-
console.error(`[JWT_REFRESH] FAIL: Supabase returned ${res.status}
|
|
51979
|
+
console.error(`[JWT_REFRESH] FAIL: Supabase returned ${res.status}: ${body}`);
|
|
51959
51980
|
if (body.includes("session_expired") || body.includes("refresh_token_not_found") || body.includes("refresh_token_already_used")) {
|
|
51960
51981
|
throw new SessionExpiredError("Your Kadoa session has expired due to inactivity. Please reconnect to re-authenticate.");
|
|
51961
51982
|
}
|
|
@@ -51968,7 +51989,7 @@ async function refreshSupabaseJwt(ctx) {
|
|
|
51968
51989
|
}
|
|
51969
51990
|
try {
|
|
51970
51991
|
const refreshToken = ctx.supabaseRefreshToken;
|
|
51971
|
-
console.error(`[JWT_REFRESH] Refreshing Supabase JWT (
|
|
51992
|
+
console.error(`[JWT_REFRESH] Refreshing Supabase JWT (team=${ctx.teamId ?? "unknown"})`);
|
|
51972
51993
|
const result = await refreshSupabaseJwtRaw(refreshToken);
|
|
51973
51994
|
if (!result)
|
|
51974
51995
|
return;
|
|
@@ -51984,7 +52005,7 @@ async function refreshSupabaseJwt(ctx) {
|
|
|
51984
52005
|
} catch (e) {
|
|
51985
52006
|
console.error("[JWT_REFRESH] WARN: persist failed, tokens updated in-memory only:", e);
|
|
51986
52007
|
}
|
|
51987
|
-
console.error(`[JWT_REFRESH] OK: token refreshed (team=${ctx.teamId ?? "unknown"}
|
|
52008
|
+
console.error(`[JWT_REFRESH] OK: token refreshed (team=${ctx.teamId ?? "unknown"})`);
|
|
51988
52009
|
return result.jwt;
|
|
51989
52010
|
} catch (error48) {
|
|
51990
52011
|
if (error48 instanceof SessionExpiredError)
|
|
@@ -52276,15 +52297,13 @@ function registerTools(server, ctx, capabilities) {
|
|
|
52276
52297
|
}
|
|
52277
52298
|
if (capabilities.scrape) {
|
|
52278
52299
|
server.registerTool("scrape", {
|
|
52279
|
-
description: "Immediately fetch one URL as LLM-friendly markdown or raw HTML without creating a workflow. " + "Markdown is the default.
|
|
52300
|
+
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.",
|
|
52280
52301
|
inputSchema: strictSchema({
|
|
52281
52302
|
url: exports_external.url().refine((value) => {
|
|
52282
52303
|
const protocol = new URL(value).protocol;
|
|
52283
52304
|
return protocol === "http:" || protocol === "https:";
|
|
52284
52305
|
}, "URL must use HTTP or HTTPS").describe("HTTP or HTTPS URL to fetch"),
|
|
52285
|
-
format: exports_external.enum(["html", "markdown"]).optional().describe("Output format. Defaults to markdown for compact LLM consumption; use html for raw source.")
|
|
52286
|
-
fetchMode: exports_external.enum(["auto", "http", "browser"]).optional().describe("Fetch mode. Omit or use auto for smart routing; browser renders JavaScript."),
|
|
52287
|
-
proxy: exports_external.enum(["auto", "dc", "isp", "residential", "stealth"]).optional().describe("Proxy tier. Use stealth only when normal routing is bot-blocked.")
|
|
52306
|
+
format: exports_external.enum(["html", "markdown"]).optional().describe("Output format. Defaults to markdown for compact LLM consumption; use html for raw source.")
|
|
52288
52307
|
}),
|
|
52289
52308
|
annotations: {
|
|
52290
52309
|
readOnlyHint: true,
|
|
@@ -52295,9 +52314,7 @@ function registerTools(server, ctx, capabilities) {
|
|
|
52295
52314
|
}, withErrorHandling("scrape", async (args) => {
|
|
52296
52315
|
const response = await ctx.client.scrape.fetch({
|
|
52297
52316
|
url: args.url,
|
|
52298
|
-
format: args.format ?? "markdown"
|
|
52299
|
-
...args.fetchMode != null && { fetchMode: args.fetchMode },
|
|
52300
|
-
...args.proxy != null && { proxy: args.proxy }
|
|
52317
|
+
format: args.format ?? "markdown"
|
|
52301
52318
|
});
|
|
52302
52319
|
return jsonResult(response);
|
|
52303
52320
|
}));
|
|
@@ -52307,6 +52324,12 @@ function registerTools(server, ctx, capabilities) {
|
|
|
52307
52324
|
inputSchema: {},
|
|
52308
52325
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true }
|
|
52309
52326
|
}, withErrorHandling("whoami", async () => {
|
|
52327
|
+
if (ctx.principal.type === "serviceAccount") {
|
|
52328
|
+
return jsonResult({
|
|
52329
|
+
authMethod: "Shared organization service account",
|
|
52330
|
+
teamId: ctx.principal.teamId
|
|
52331
|
+
});
|
|
52332
|
+
}
|
|
52310
52333
|
const jwt2 = await getValidJwt(ctx);
|
|
52311
52334
|
const user = await ctx.client.user.getCurrentUser();
|
|
52312
52335
|
const teams = await ctx.client.listTeams(jwt2 ? { bearerToken: jwt2 } : undefined);
|
|
@@ -53181,6 +53204,12 @@ function registerTools(server, ctx, capabilities) {
|
|
|
53181
53204
|
inputSchema: {},
|
|
53182
53205
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true }
|
|
53183
53206
|
}, withErrorHandling("team_list", async () => {
|
|
53207
|
+
if (ctx.principal.type === "serviceAccount") {
|
|
53208
|
+
return jsonResult({
|
|
53209
|
+
teams: [{ id: ctx.principal.teamId, active: true, sharedServiceAccount: true }],
|
|
53210
|
+
activeTeamId: ctx.principal.teamId
|
|
53211
|
+
});
|
|
53212
|
+
}
|
|
53184
53213
|
const jwt2 = await getValidJwt(ctx);
|
|
53185
53214
|
const teams = await ctx.client.listTeams(jwt2 ? { bearerToken: jwt2 } : undefined);
|
|
53186
53215
|
const activeTeamId = ctx.teamId ?? teams[0]?.id;
|
|
@@ -53202,6 +53231,9 @@ function registerTools(server, ctx, capabilities) {
|
|
|
53202
53231
|
},
|
|
53203
53232
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true }
|
|
53204
53233
|
}, withErrorHandling("team_switch", async (args) => {
|
|
53234
|
+
if (ctx.principal.type === "serviceAccount") {
|
|
53235
|
+
return errorResult("This shared organization connector is fixed to one team and cannot switch teams.");
|
|
53236
|
+
}
|
|
53205
53237
|
const jwt2 = await getValidJwt(ctx);
|
|
53206
53238
|
const teams = await ctx.client.listTeams({ bearerToken: jwt2 });
|
|
53207
53239
|
const identifier = args.teamIdentifier;
|
|
@@ -53589,8 +53621,8 @@ function registerTools(server, ctx, capabilities) {
|
|
|
53589
53621
|
}
|
|
53590
53622
|
var SchemaFieldShape, DASHBOARD_BASE_URL = "https://www.kadoa.com", WORKFLOW_AUDIT_WATCHED_KEYS;
|
|
53591
53623
|
var init_tools = __esm(() => {
|
|
53592
|
-
init_zod();
|
|
53593
53624
|
init_dist2();
|
|
53625
|
+
init_zod();
|
|
53594
53626
|
init_client2();
|
|
53595
53627
|
SchemaFieldShape = {
|
|
53596
53628
|
name: exports_external.string().describe("Field name"),
|
|
@@ -53619,7 +53651,7 @@ var package_default;
|
|
|
53619
53651
|
var init_package = __esm(() => {
|
|
53620
53652
|
package_default = {
|
|
53621
53653
|
name: "@kadoa/mcp",
|
|
53622
|
-
version: "0.5.
|
|
53654
|
+
version: "0.5.12",
|
|
53623
53655
|
description: "Kadoa MCP Server — manage workflows from Claude Desktop, Cursor, and other MCP clients",
|
|
53624
53656
|
type: "module",
|
|
53625
53657
|
main: "dist/index.js",
|
|
@@ -57651,11 +57683,87 @@ var init_bearerAuth = __esm(() => {
|
|
|
57651
57683
|
init_errors4();
|
|
57652
57684
|
});
|
|
57653
57685
|
|
|
57686
|
+
// src/service-account-config.ts
|
|
57687
|
+
function getServiceAccountOAuthConfig() {
|
|
57688
|
+
const values = Object.fromEntries(Object.entries(ENV_KEYS).map(([field, envKey]) => [field, process.env[envKey]?.trim()]));
|
|
57689
|
+
if (Object.values(values).every((value) => !value))
|
|
57690
|
+
return;
|
|
57691
|
+
const missing = Object.entries(values).filter(([, value]) => !value).map(([field]) => ENV_KEYS[field]);
|
|
57692
|
+
if (missing.length > 0) {
|
|
57693
|
+
throw new Error(`Incomplete service-account OAuth configuration; missing ${missing.join(", ")}`);
|
|
57694
|
+
}
|
|
57695
|
+
const redirectUri = new URL(values.redirectUri);
|
|
57696
|
+
if (redirectUri.protocol !== "https:" && redirectUri.hostname !== "localhost") {
|
|
57697
|
+
throw new Error("MCP_SERVICE_ACCOUNT_OAUTH_REDIRECT_URI must use HTTPS");
|
|
57698
|
+
}
|
|
57699
|
+
return {
|
|
57700
|
+
clientId: values.clientId,
|
|
57701
|
+
clientSecret: values.clientSecret,
|
|
57702
|
+
redirectUri: redirectUri.toString(),
|
|
57703
|
+
credentialRef: values.credentialRef,
|
|
57704
|
+
teamId: values.teamId,
|
|
57705
|
+
label: values.label
|
|
57706
|
+
};
|
|
57707
|
+
}
|
|
57708
|
+
function getConfiguredServiceAccountClient(clientId) {
|
|
57709
|
+
const configuredClientId = process.env.MCP_SERVICE_ACCOUNT_OAUTH_CLIENT_ID?.trim();
|
|
57710
|
+
if (!configuredClientId || configuredClientId !== clientId)
|
|
57711
|
+
return;
|
|
57712
|
+
const config2 = getServiceAccountOAuthConfig();
|
|
57713
|
+
if (!config2)
|
|
57714
|
+
return;
|
|
57715
|
+
return {
|
|
57716
|
+
client_id: config2.clientId,
|
|
57717
|
+
client_secret: config2.clientSecret,
|
|
57718
|
+
client_id_issued_at: 0,
|
|
57719
|
+
client_name: config2.label,
|
|
57720
|
+
redirect_uris: [config2.redirectUri],
|
|
57721
|
+
token_endpoint_auth_method: "client_secret_post",
|
|
57722
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
57723
|
+
response_types: ["code"]
|
|
57724
|
+
};
|
|
57725
|
+
}
|
|
57726
|
+
var ENV_KEYS;
|
|
57727
|
+
var init_service_account_config = __esm(() => {
|
|
57728
|
+
ENV_KEYS = {
|
|
57729
|
+
clientId: "MCP_SERVICE_ACCOUNT_OAUTH_CLIENT_ID",
|
|
57730
|
+
clientSecret: "MCP_SERVICE_ACCOUNT_OAUTH_CLIENT_SECRET",
|
|
57731
|
+
redirectUri: "MCP_SERVICE_ACCOUNT_OAUTH_REDIRECT_URI",
|
|
57732
|
+
credentialRef: "MCP_SERVICE_ACCOUNT_CREDENTIAL_REF",
|
|
57733
|
+
teamId: "MCP_SERVICE_ACCOUNT_TEAM_ID",
|
|
57734
|
+
label: "MCP_SERVICE_ACCOUNT_LABEL"
|
|
57735
|
+
};
|
|
57736
|
+
});
|
|
57737
|
+
|
|
57654
57738
|
// src/auth.ts
|
|
57655
57739
|
import { randomBytes, createHash as createHash2 } from "node:crypto";
|
|
57656
57740
|
function randomToken(bytes = 32) {
|
|
57657
57741
|
return randomBytes(bytes).toString("hex");
|
|
57658
57742
|
}
|
|
57743
|
+
function readPrincipal(entry) {
|
|
57744
|
+
if (entry.principal)
|
|
57745
|
+
return entry.principal;
|
|
57746
|
+
if (entry.supabaseJwt && entry.supabaseRefreshToken) {
|
|
57747
|
+
return {
|
|
57748
|
+
type: "user",
|
|
57749
|
+
supabaseJwt: entry.supabaseJwt,
|
|
57750
|
+
supabaseRefreshToken: entry.supabaseRefreshToken,
|
|
57751
|
+
teamId: entry.teamId
|
|
57752
|
+
};
|
|
57753
|
+
}
|
|
57754
|
+
throw new InvalidTokenError("Stored token has no valid principal");
|
|
57755
|
+
}
|
|
57756
|
+
function persistPrincipal(principal) {
|
|
57757
|
+
if (principal.type === "serviceAccount") {
|
|
57758
|
+
return { principal, teamId: principal.teamId };
|
|
57759
|
+
}
|
|
57760
|
+
return {
|
|
57761
|
+
principal,
|
|
57762
|
+
supabaseJwt: principal.supabaseJwt,
|
|
57763
|
+
supabaseRefreshToken: principal.supabaseRefreshToken,
|
|
57764
|
+
teamId: principal.teamId
|
|
57765
|
+
};
|
|
57766
|
+
}
|
|
57659
57767
|
function generatePKCE() {
|
|
57660
57768
|
const verifier = randomBytes(32).toString("base64url").replace(/[^a-zA-Z0-9\-._~]/g, "").slice(0, 128);
|
|
57661
57769
|
const challenge = createHash2("sha256").update(verifier).digest("base64url");
|
|
@@ -57753,7 +57861,7 @@ class KadoaOAuthProvider {
|
|
|
57753
57861
|
const store = this.store;
|
|
57754
57862
|
return {
|
|
57755
57863
|
async getClient(clientId) {
|
|
57756
|
-
return store.get("clients", clientId);
|
|
57864
|
+
return getConfiguredServiceAccountClient(clientId) ?? store.get("clients", clientId);
|
|
57757
57865
|
},
|
|
57758
57866
|
async registerClient(client) {
|
|
57759
57867
|
const clientId = randomToken(16);
|
|
@@ -57768,10 +57876,9 @@ class KadoaOAuthProvider {
|
|
|
57768
57876
|
};
|
|
57769
57877
|
}
|
|
57770
57878
|
async authorize(client, params, res) {
|
|
57771
|
-
const supabaseUrl = process.env.SUPABASE_URL;
|
|
57772
57879
|
const serverUrl = process.env.MCP_SERVER_URL;
|
|
57773
|
-
if (!
|
|
57774
|
-
throw new Error("
|
|
57880
|
+
if (!serverUrl) {
|
|
57881
|
+
throw new Error("MCP_SERVER_URL must be configured");
|
|
57775
57882
|
}
|
|
57776
57883
|
const state = randomToken();
|
|
57777
57884
|
const { verifier, challenge } = generatePKCE();
|
|
@@ -57780,6 +57887,14 @@ class KadoaOAuthProvider {
|
|
|
57780
57887
|
params,
|
|
57781
57888
|
supabaseCodeVerifier: verifier
|
|
57782
57889
|
}, 600);
|
|
57890
|
+
const configuredServiceClientId = process.env.MCP_SERVICE_ACCOUNT_OAUTH_CLIENT_ID?.trim();
|
|
57891
|
+
if (configuredServiceClientId === client.client_id) {
|
|
57892
|
+
const serviceConfig = getServiceAccountOAuthConfig();
|
|
57893
|
+
if (!serviceConfig)
|
|
57894
|
+
throw new Error("Service-account OAuth configuration is unavailable");
|
|
57895
|
+
res.type("html").send(renderServiceAccountConsentPage(state, serviceConfig.label));
|
|
57896
|
+
return;
|
|
57897
|
+
}
|
|
57783
57898
|
res.type("html").send(renderLoginPage(state));
|
|
57784
57899
|
}
|
|
57785
57900
|
async handleGoogleLogin(req, res) {
|
|
@@ -57897,8 +58012,9 @@ class KadoaOAuthProvider {
|
|
|
57897
58012
|
if (teams.length === 1) {
|
|
57898
58013
|
const refreshed = await setActiveTeamAndRefresh(supabaseJwt, supabaseRefreshToken, teams[0].id);
|
|
57899
58014
|
await this.completeAuthFlow(pending, res, {
|
|
57900
|
-
|
|
57901
|
-
|
|
58015
|
+
type: "user",
|
|
58016
|
+
supabaseJwt: refreshed.jwt,
|
|
58017
|
+
supabaseRefreshToken: refreshed.refreshToken,
|
|
57902
58018
|
teamId: teams[0].id
|
|
57903
58019
|
});
|
|
57904
58020
|
return;
|
|
@@ -57927,29 +58043,28 @@ class KadoaOAuthProvider {
|
|
|
57927
58043
|
await this.store.del("auth_codes", authorizationCode);
|
|
57928
58044
|
throw new Error("Authorization code expired");
|
|
57929
58045
|
}
|
|
58046
|
+
if (entry.clientId !== _client.client_id) {
|
|
58047
|
+
throw new Error("OAuth client mismatch for authorization code");
|
|
58048
|
+
}
|
|
57930
58049
|
if (redirectUri && redirectUri !== entry.redirectUri) {
|
|
57931
58050
|
throw new Error("redirect_uri mismatch");
|
|
57932
58051
|
}
|
|
57933
58052
|
const accessToken = randomToken();
|
|
57934
58053
|
const refreshToken = randomToken();
|
|
57935
58054
|
const expiresAt = Date.now() + ACCESS_TOKEN_TTL * 1000;
|
|
58055
|
+
const principal = readPrincipal(entry);
|
|
57936
58056
|
await this.store.set("access_tokens", accessToken, {
|
|
57937
|
-
|
|
57938
|
-
supabaseRefreshToken: entry.supabaseRefreshToken,
|
|
57939
|
-
teamId: entry.teamId,
|
|
58057
|
+
...persistPrincipal(principal),
|
|
57940
58058
|
clientId: entry.clientId,
|
|
57941
58059
|
expiresAt
|
|
57942
58060
|
}, ACCESS_TOKEN_TTL);
|
|
57943
58061
|
await this.store.set("refresh_tokens", refreshToken, {
|
|
57944
|
-
|
|
57945
|
-
supabaseRefreshToken: entry.supabaseRefreshToken,
|
|
57946
|
-
teamId: entry.teamId,
|
|
58062
|
+
...persistPrincipal(principal),
|
|
57947
58063
|
clientId: entry.clientId
|
|
57948
58064
|
}, 2592000);
|
|
57949
58065
|
await this.store.del("auth_codes", authorizationCode);
|
|
57950
|
-
const claims = jwtClaims(entry.supabaseJwt);
|
|
57951
58066
|
const sessionCount = await this.store.size("access_tokens");
|
|
57952
|
-
console.log(`[AUTH] LOGIN: tokens issued (
|
|
58067
|
+
console.log(`[AUTH] LOGIN: tokens issued (mode=${principal.type}, team=${principal.teamId}, ttl=${ACCESS_TOKEN_TTL}s, active_sessions=${sessionCount})`);
|
|
57953
58068
|
return {
|
|
57954
58069
|
access_token: accessToken,
|
|
57955
58070
|
token_type: "bearer",
|
|
@@ -57961,12 +58076,36 @@ class KadoaOAuthProvider {
|
|
|
57961
58076
|
const entry = await this.store.get("refresh_tokens", refreshToken);
|
|
57962
58077
|
if (!entry) {
|
|
57963
58078
|
const sessionCount = await this.store.size("refresh_tokens");
|
|
57964
|
-
console.error(`[AUTH] REFRESH_FAIL: unknown refresh token (
|
|
58079
|
+
console.error(`[AUTH] REFRESH_FAIL: unknown refresh token (active_sessions=${sessionCount})`);
|
|
57965
58080
|
throw new InvalidTokenError("Unknown or expired refresh token");
|
|
57966
58081
|
}
|
|
58082
|
+
if (entry.clientId !== _client.client_id) {
|
|
58083
|
+
throw new Error("OAuth client mismatch for refresh token");
|
|
58084
|
+
}
|
|
57967
58085
|
await this.store.del("refresh_tokens", refreshToken);
|
|
57968
|
-
|
|
57969
|
-
|
|
58086
|
+
const storedPrincipal = readPrincipal(entry);
|
|
58087
|
+
if (storedPrincipal.type === "serviceAccount") {
|
|
58088
|
+
const newAccessToken2 = randomToken();
|
|
58089
|
+
const newRefreshToken2 = randomToken();
|
|
58090
|
+
const expiresAt2 = Date.now() + ACCESS_TOKEN_TTL * 1000;
|
|
58091
|
+
await this.store.set("access_tokens", newAccessToken2, {
|
|
58092
|
+
...persistPrincipal(storedPrincipal),
|
|
58093
|
+
clientId: entry.clientId,
|
|
58094
|
+
expiresAt: expiresAt2
|
|
58095
|
+
}, ACCESS_TOKEN_TTL);
|
|
58096
|
+
await this.store.set("refresh_tokens", newRefreshToken2, {
|
|
58097
|
+
...persistPrincipal(storedPrincipal),
|
|
58098
|
+
clientId: entry.clientId
|
|
58099
|
+
}, 2592000);
|
|
58100
|
+
return {
|
|
58101
|
+
access_token: newAccessToken2,
|
|
58102
|
+
token_type: "bearer",
|
|
58103
|
+
expires_in: ACCESS_TOKEN_TTL,
|
|
58104
|
+
refresh_token: newRefreshToken2
|
|
58105
|
+
};
|
|
58106
|
+
}
|
|
58107
|
+
let { supabaseJwt, supabaseRefreshToken } = storedPrincipal;
|
|
58108
|
+
const claims = jwtClaims(storedPrincipal.supabaseJwt);
|
|
57970
58109
|
const context = `email=${claims.email}, team=${entry.teamId}`;
|
|
57971
58110
|
try {
|
|
57972
58111
|
const { refreshSupabaseJwtRaw: refreshSupabaseJwtRaw2, SessionExpiredError: SessionExpiredError2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
|
|
@@ -57990,17 +58129,19 @@ class KadoaOAuthProvider {
|
|
|
57990
58129
|
const newAccessToken = randomToken();
|
|
57991
58130
|
const newRefreshToken = randomToken();
|
|
57992
58131
|
const expiresAt = Date.now() + ACCESS_TOKEN_TTL * 1000;
|
|
57993
|
-
|
|
58132
|
+
const refreshedPrincipal = {
|
|
58133
|
+
type: "user",
|
|
57994
58134
|
supabaseJwt,
|
|
57995
58135
|
supabaseRefreshToken,
|
|
57996
|
-
teamId
|
|
58136
|
+
teamId
|
|
58137
|
+
};
|
|
58138
|
+
await this.store.set("access_tokens", newAccessToken, {
|
|
58139
|
+
...persistPrincipal(refreshedPrincipal),
|
|
57997
58140
|
clientId: entry.clientId,
|
|
57998
58141
|
expiresAt
|
|
57999
58142
|
}, ACCESS_TOKEN_TTL);
|
|
58000
58143
|
await this.store.set("refresh_tokens", newRefreshToken, {
|
|
58001
|
-
|
|
58002
|
-
supabaseRefreshToken,
|
|
58003
|
-
teamId,
|
|
58144
|
+
...persistPrincipal(refreshedPrincipal),
|
|
58004
58145
|
clientId: entry.clientId
|
|
58005
58146
|
}, 2592000);
|
|
58006
58147
|
return {
|
|
@@ -58014,25 +58155,28 @@ class KadoaOAuthProvider {
|
|
|
58014
58155
|
const entry = await this.store.get("access_tokens", token);
|
|
58015
58156
|
if (!entry) {
|
|
58016
58157
|
const sessionCount = await this.store.size("access_tokens");
|
|
58017
|
-
console.error(`[AUTH] VERIFY_FAIL: unknown token (
|
|
58158
|
+
console.error(`[AUTH] VERIFY_FAIL: unknown token (active_sessions=${sessionCount})`);
|
|
58018
58159
|
throw new InvalidTokenError("Unknown or expired access token");
|
|
58019
58160
|
}
|
|
58020
58161
|
if (entry.expiresAt < Date.now()) {
|
|
58021
58162
|
const expiredAgo = Math.round((Date.now() - entry.expiresAt) / 1000);
|
|
58022
|
-
|
|
58023
|
-
console.error(`[AUTH] VERIFY_FAIL: token expired ${expiredAgo}s ago (email=${claims.email}, team=${entry.teamId}, token=${token.slice(0, 12)}...)`);
|
|
58163
|
+
console.error(`[AUTH] VERIFY_FAIL: token expired ${expiredAgo}s ago (team=${entry.teamId})`);
|
|
58024
58164
|
await this.store.del("access_tokens", token);
|
|
58025
58165
|
throw new InvalidTokenError("Access token expired");
|
|
58026
58166
|
}
|
|
58167
|
+
const principal = readPrincipal(entry);
|
|
58027
58168
|
return {
|
|
58028
58169
|
token,
|
|
58029
58170
|
clientId: entry.clientId,
|
|
58030
58171
|
scopes: [],
|
|
58031
58172
|
expiresAt: Math.floor(entry.expiresAt / 1000),
|
|
58032
58173
|
extra: {
|
|
58033
|
-
|
|
58034
|
-
|
|
58035
|
-
|
|
58174
|
+
principal,
|
|
58175
|
+
...principal.type === "user" ? {
|
|
58176
|
+
supabaseJwt: principal.supabaseJwt,
|
|
58177
|
+
supabaseRefreshToken: principal.supabaseRefreshToken
|
|
58178
|
+
} : {},
|
|
58179
|
+
teamId: principal.teamId
|
|
58036
58180
|
}
|
|
58037
58181
|
};
|
|
58038
58182
|
}
|
|
@@ -58062,6 +58206,29 @@ class KadoaOAuthProvider {
|
|
|
58062
58206
|
res.redirect(redirectUrl.toString());
|
|
58063
58207
|
}
|
|
58064
58208
|
}
|
|
58209
|
+
async handleServiceAccountConsent(req, res) {
|
|
58210
|
+
const { state } = req.body;
|
|
58211
|
+
if (!state) {
|
|
58212
|
+
res.status(400).send("Missing state parameter");
|
|
58213
|
+
return;
|
|
58214
|
+
}
|
|
58215
|
+
const pending = await this.store.get("pending_auths", state);
|
|
58216
|
+
if (!pending) {
|
|
58217
|
+
res.status(400).send("Unknown or expired state parameter");
|
|
58218
|
+
return;
|
|
58219
|
+
}
|
|
58220
|
+
const config2 = getServiceAccountOAuthConfig();
|
|
58221
|
+
if (!config2 || pending.client.client_id !== config2.clientId) {
|
|
58222
|
+
res.status(403).send("Service-account authorization is not available for this client");
|
|
58223
|
+
return;
|
|
58224
|
+
}
|
|
58225
|
+
await this.store.del("pending_auths", state);
|
|
58226
|
+
await this.completeAuthFlow(pending, res, {
|
|
58227
|
+
type: "serviceAccount",
|
|
58228
|
+
credentialRef: config2.credentialRef,
|
|
58229
|
+
teamId: config2.teamId
|
|
58230
|
+
});
|
|
58231
|
+
}
|
|
58065
58232
|
async handleTeamSelection(req, res) {
|
|
58066
58233
|
const { token, teamId } = req.body;
|
|
58067
58234
|
if (!token || !teamId) {
|
|
@@ -58086,8 +58253,9 @@ class KadoaOAuthProvider {
|
|
|
58086
58253
|
try {
|
|
58087
58254
|
const refreshed = await setActiveTeamAndRefresh(entry.supabaseJwt, entry.supabaseRefreshToken, teamId);
|
|
58088
58255
|
await this.completeAuthFlow(entry.pending, res, {
|
|
58089
|
-
|
|
58090
|
-
|
|
58256
|
+
type: "user",
|
|
58257
|
+
supabaseJwt: refreshed.jwt,
|
|
58258
|
+
supabaseRefreshToken: refreshed.refreshToken,
|
|
58091
58259
|
teamId
|
|
58092
58260
|
});
|
|
58093
58261
|
} catch (error48) {
|
|
@@ -58101,12 +58269,10 @@ class KadoaOAuthProvider {
|
|
|
58101
58269
|
res.redirect(redirectUrl.toString());
|
|
58102
58270
|
}
|
|
58103
58271
|
}
|
|
58104
|
-
async completeAuthFlow(pending, res,
|
|
58272
|
+
async completeAuthFlow(pending, res, principal) {
|
|
58105
58273
|
const mcpCode = randomToken();
|
|
58106
58274
|
await this.store.set("auth_codes", mcpCode, {
|
|
58107
|
-
|
|
58108
|
-
supabaseRefreshToken: credentials.refreshToken,
|
|
58109
|
-
teamId: credentials.teamId,
|
|
58275
|
+
...persistPrincipal(principal),
|
|
58110
58276
|
codeChallenge: pending.params.codeChallenge,
|
|
58111
58277
|
clientId: pending.client.client_id,
|
|
58112
58278
|
redirectUri: pending.params.redirectUri,
|
|
@@ -58120,6 +58286,33 @@ class KadoaOAuthProvider {
|
|
|
58120
58286
|
res.redirect(redirectUrl.toString());
|
|
58121
58287
|
}
|
|
58122
58288
|
}
|
|
58289
|
+
function renderServiceAccountConsentPage(state, label) {
|
|
58290
|
+
return `<!DOCTYPE html>
|
|
58291
|
+
<html lang="en">
|
|
58292
|
+
<head>
|
|
58293
|
+
<meta charset="utf-8" />
|
|
58294
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
58295
|
+
<title>Connect shared Kadoa workspace</title>
|
|
58296
|
+
<style>
|
|
58297
|
+
body { font-family: ui-sans-serif, system-ui, sans-serif; background: #fafafa; color: #18181b; min-height: 100vh; display: grid; place-items: center; margin: 0; }
|
|
58298
|
+
main { width: min(420px, calc(100% - 2rem)); background: white; border: 1px solid #e4e4e7; border-radius: 8px; padding: 2rem; box-sizing: border-box; }
|
|
58299
|
+
h1 { font-size: 1.25rem; margin: 0 0 0.75rem; }
|
|
58300
|
+
p { color: #52525b; line-height: 1.5; }
|
|
58301
|
+
button { width: 100%; border: 1px solid #9a3412; border-radius: 4px; padding: 0.7rem 1rem; color: white; background: #c2410c; font: inherit; font-weight: 600; cursor: pointer; }
|
|
58302
|
+
</style>
|
|
58303
|
+
</head>
|
|
58304
|
+
<body>
|
|
58305
|
+
<main>
|
|
58306
|
+
<h1>Connect ${escapeHtml(label)}</h1>
|
|
58307
|
+
<p>Claude will use the shared Kadoa service account for this organization. Activity will not be attributed to your individual Kadoa identity.</p>
|
|
58308
|
+
<form method="POST" action="/auth/service-account">
|
|
58309
|
+
<input type="hidden" name="state" value="${escapeHtml(state)}" />
|
|
58310
|
+
<button type="submit">Continue with shared workspace</button>
|
|
58311
|
+
</form>
|
|
58312
|
+
</main>
|
|
58313
|
+
</body>
|
|
58314
|
+
</html>`;
|
|
58315
|
+
}
|
|
58123
58316
|
function renderTeamSelectionPage(teams, selectionToken) {
|
|
58124
58317
|
const teamButtons = teams.map((t) => `
|
|
58125
58318
|
<button type="submit" name="teamId" value="${t.id}" class="team-btn">
|
|
@@ -58542,6 +58735,7 @@ function escapeHtml(str) {
|
|
|
58542
58735
|
var TEAM_SELECTION_TTL, ACCESS_TOKEN_TTL;
|
|
58543
58736
|
var init_auth2 = __esm(() => {
|
|
58544
58737
|
init_errors4();
|
|
58738
|
+
init_service_account_config();
|
|
58545
58739
|
TEAM_SELECTION_TTL = 10 * 60 * 1000;
|
|
58546
58740
|
ACCESS_TOKEN_TTL = 7 * 24 * 3600;
|
|
58547
58741
|
});
|
|
@@ -58657,14 +58851,21 @@ function jwtClaims2(jwt2) {
|
|
|
58657
58851
|
}
|
|
58658
58852
|
function resolveAuth(req) {
|
|
58659
58853
|
const extra = req.auth?.extra;
|
|
58660
|
-
if (!extra) {
|
|
58661
|
-
console.error("[AUTH_RESOLVE] FAIL: req.auth.extra is missing");
|
|
58854
|
+
if (!extra || !extra.principal || typeof extra.principal !== "object") {
|
|
58855
|
+
console.error("[AUTH_RESOLVE] FAIL: req.auth.extra.principal is missing");
|
|
58662
58856
|
return;
|
|
58663
58857
|
}
|
|
58664
|
-
|
|
58665
|
-
|
|
58666
|
-
|
|
58667
|
-
|
|
58858
|
+
const principal = extra.principal;
|
|
58859
|
+
if (principal.type === "serviceAccount") {
|
|
58860
|
+
if (!principal.credentialRef || !principal.teamId) {
|
|
58861
|
+
console.error("[AUTH_RESOLVE] FAIL: service-account principal is incomplete");
|
|
58862
|
+
return;
|
|
58863
|
+
}
|
|
58864
|
+
return { principal, mcpToken: req.auth.token };
|
|
58865
|
+
}
|
|
58866
|
+
if (principal.type === "user") {
|
|
58867
|
+
const claims = jwtClaims2(principal.supabaseJwt);
|
|
58868
|
+
if (!claims.sub) {
|
|
58668
58869
|
console.error(`[AUTH_RESOLVE] FAIL: JWT missing sub claim (email=${claims.email ?? "unknown"})`);
|
|
58669
58870
|
return;
|
|
58670
58871
|
}
|
|
@@ -58672,20 +58873,14 @@ function resolveAuth(req) {
|
|
|
58672
58873
|
if (exp) {
|
|
58673
58874
|
const remainingSec = exp - Math.floor(Date.now() / 1000);
|
|
58674
58875
|
if (remainingSec <= 0) {
|
|
58675
|
-
console.error(`[AUTH_RESOLVE] WARN: JWT already expired ${-remainingSec}s ago (
|
|
58876
|
+
console.error(`[AUTH_RESOLVE] WARN: JWT already expired ${-remainingSec}s ago (team=${principal.teamId})`);
|
|
58676
58877
|
} else if (remainingSec < 300) {
|
|
58677
|
-
console.error(`[AUTH_RESOLVE] WARN: JWT expires in ${remainingSec}s (
|
|
58878
|
+
console.error(`[AUTH_RESOLVE] WARN: JWT expires in ${remainingSec}s (team=${principal.teamId})`);
|
|
58678
58879
|
}
|
|
58679
58880
|
}
|
|
58680
|
-
return {
|
|
58681
|
-
jwt: extra.supabaseJwt,
|
|
58682
|
-
refreshToken: extra.supabaseRefreshToken ?? "",
|
|
58683
|
-
teamId: extra.teamId ?? "",
|
|
58684
|
-
userId,
|
|
58685
|
-
mcpToken: req.auth.token
|
|
58686
|
-
};
|
|
58881
|
+
return { principal, mcpToken: req.auth.token };
|
|
58687
58882
|
}
|
|
58688
|
-
console.error(
|
|
58883
|
+
console.error("[AUTH_RESOLVE] FAIL: unknown principal type");
|
|
58689
58884
|
return;
|
|
58690
58885
|
}
|
|
58691
58886
|
async function startHttpServer(options) {
|
|
@@ -58710,6 +58905,9 @@ async function startHttpServer(options) {
|
|
|
58710
58905
|
app.post("/auth/login", express8.urlencoded({ extended: false }), (req, res) => {
|
|
58711
58906
|
provider.handleEmailPasswordLogin(req, res);
|
|
58712
58907
|
});
|
|
58908
|
+
app.post("/auth/service-account", express8.urlencoded({ extended: false }), (req, res) => {
|
|
58909
|
+
provider.handleServiceAccountConsent(req, res);
|
|
58910
|
+
});
|
|
58713
58911
|
app.post("/auth/sso", express8.urlencoded({ extended: false }), (req, res) => {
|
|
58714
58912
|
provider.handleSSOLogin(req, res);
|
|
58715
58913
|
});
|
|
@@ -58735,19 +58933,24 @@ async function startHttpServer(options) {
|
|
|
58735
58933
|
});
|
|
58736
58934
|
return;
|
|
58737
58935
|
}
|
|
58738
|
-
|
|
58739
|
-
|
|
58936
|
+
let principal = auth.principal;
|
|
58937
|
+
const identity2 = `mode=${principal.type}:team=${principal.teamId.slice(0, 8)}...`;
|
|
58938
|
+
if (principal.type === "user" && isJwtExpired(principal.supabaseJwt)) {
|
|
58740
58939
|
try {
|
|
58741
|
-
const refreshed = await refreshSupabaseJwtRaw(
|
|
58940
|
+
const refreshed = await refreshSupabaseJwtRaw(principal.supabaseRefreshToken);
|
|
58742
58941
|
if (refreshed) {
|
|
58743
|
-
|
|
58744
|
-
|
|
58942
|
+
principal = {
|
|
58943
|
+
...principal,
|
|
58944
|
+
supabaseJwt: refreshed.jwt,
|
|
58945
|
+
supabaseRefreshToken: refreshed.refreshToken
|
|
58946
|
+
};
|
|
58745
58947
|
const entry = await store.get("access_tokens", auth.mcpToken);
|
|
58746
58948
|
if (entry) {
|
|
58747
58949
|
const remainingMs = entry.expiresAt - Date.now();
|
|
58748
58950
|
if (remainingMs > 0) {
|
|
58749
58951
|
await store.set("access_tokens", auth.mcpToken, {
|
|
58750
58952
|
...entry,
|
|
58953
|
+
principal,
|
|
58751
58954
|
supabaseJwt: refreshed.jwt,
|
|
58752
58955
|
supabaseRefreshToken: refreshed.refreshToken
|
|
58753
58956
|
}, Math.ceil(remainingMs / 1000));
|
|
@@ -58774,31 +58977,32 @@ async function startHttpServer(options) {
|
|
|
58774
58977
|
const transport = new StreamableHTTPServerTransport({
|
|
58775
58978
|
sessionIdGenerator: undefined
|
|
58776
58979
|
});
|
|
58777
|
-
const
|
|
58778
|
-
|
|
58779
|
-
|
|
58780
|
-
|
|
58781
|
-
|
|
58782
|
-
const entry = await store.get("access_tokens", auth.mcpToken);
|
|
58783
|
-
if (!entry) {
|
|
58784
|
-
console.error(`[PERSIST] WARN: access token gone from store (token=${auth.mcpToken.slice(0, 12)}...)`);
|
|
58785
|
-
return;
|
|
58786
|
-
}
|
|
58787
|
-
const remainingMs = entry.expiresAt - Date.now();
|
|
58788
|
-
if (remainingMs <= 0) {
|
|
58789
|
-
console.error(`[PERSIST] WARN: access token already expired, skipping persist`);
|
|
58790
|
-
return;
|
|
58791
|
-
}
|
|
58792
|
-
const ttlSeconds = Math.ceil(remainingMs / 1000);
|
|
58793
|
-
await store.set("access_tokens", auth.mcpToken, {
|
|
58794
|
-
...entry,
|
|
58795
|
-
supabaseJwt: state.supabaseJwt ?? entry.supabaseJwt,
|
|
58796
|
-
supabaseRefreshToken: state.supabaseRefreshToken ?? entry.supabaseRefreshToken,
|
|
58797
|
-
teamId: state.teamId ?? entry.teamId
|
|
58798
|
-
}, ttlSeconds);
|
|
58799
|
-
console.error(`[PERSIST] OK: updated access token in store (token=${auth.mcpToken.slice(0, 12)}..., team=${state.teamId ?? "unchanged"}, ttl=${ttlSeconds}s)`);
|
|
58980
|
+
const persist = principal.type === "user" ? async (state) => {
|
|
58981
|
+
const entry = await store.get("access_tokens", auth.mcpToken);
|
|
58982
|
+
if (!entry) {
|
|
58983
|
+
console.error("[PERSIST] WARN: access token gone from store");
|
|
58984
|
+
return;
|
|
58800
58985
|
}
|
|
58801
|
-
|
|
58986
|
+
const remainingMs = entry.expiresAt - Date.now();
|
|
58987
|
+
if (remainingMs <= 0) {
|
|
58988
|
+
console.error("[PERSIST] WARN: access token already expired, skipping persist");
|
|
58989
|
+
return;
|
|
58990
|
+
}
|
|
58991
|
+
const updatedPrincipal = {
|
|
58992
|
+
type: "user",
|
|
58993
|
+
supabaseJwt: state.supabaseJwt ?? principal.supabaseJwt,
|
|
58994
|
+
supabaseRefreshToken: state.supabaseRefreshToken ?? principal.supabaseRefreshToken,
|
|
58995
|
+
teamId: state.teamId ?? principal.teamId
|
|
58996
|
+
};
|
|
58997
|
+
await store.set("access_tokens", auth.mcpToken, {
|
|
58998
|
+
...entry,
|
|
58999
|
+
principal: updatedPrincipal,
|
|
59000
|
+
supabaseJwt: updatedPrincipal.supabaseJwt,
|
|
59001
|
+
supabaseRefreshToken: updatedPrincipal.supabaseRefreshToken,
|
|
59002
|
+
teamId: updatedPrincipal.teamId
|
|
59003
|
+
}, Math.ceil(remainingMs / 1000));
|
|
59004
|
+
} : undefined;
|
|
59005
|
+
const server = await createServer({ principal, persist });
|
|
58802
59006
|
await server.connect(transport);
|
|
58803
59007
|
await transport.handleRequest(req, res, req.body);
|
|
58804
59008
|
} catch (error48) {
|
|
@@ -58852,11 +59056,18 @@ var init_http2 = __esm(async () => {
|
|
|
58852
59056
|
|
|
58853
59057
|
// src/index.ts
|
|
58854
59058
|
async function createServer(auth, options) {
|
|
58855
|
-
const
|
|
58856
|
-
|
|
59059
|
+
const principal = "principal" in auth ? auth.principal : {
|
|
59060
|
+
type: "user",
|
|
58857
59061
|
supabaseJwt: auth.jwt,
|
|
58858
59062
|
supabaseRefreshToken: auth.refreshToken,
|
|
58859
|
-
teamId: auth.teamId
|
|
59063
|
+
teamId: auth.teamId
|
|
59064
|
+
};
|
|
59065
|
+
const ctx = {
|
|
59066
|
+
client: createKadoaClient({ principal }),
|
|
59067
|
+
principal,
|
|
59068
|
+
supabaseJwt: principal.type === "user" ? principal.supabaseJwt : undefined,
|
|
59069
|
+
supabaseRefreshToken: principal.type === "user" ? principal.supabaseRefreshToken : undefined,
|
|
59070
|
+
teamId: principal.teamId,
|
|
58860
59071
|
persist: auth.persist
|
|
58861
59072
|
};
|
|
58862
59073
|
const server = new McpServer({ name: "kadoa", version: package_default.version }, {
|