@kadoa/mcp 0.5.11 → 0.5.13
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 +15 -2
- package/dist/index.js +330 -154
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -24,6 +24,19 @@ claude mcp add kadoa --transport http https://mcp.kadoa.com/mcp
|
|
|
24
24
|
2. Enter the URL: `https://mcp.kadoa.com/mcp`
|
|
25
25
|
3. Sign in with your Kadoa account via OAuth
|
|
26
26
|
|
|
27
|
+
### Claude organization shared workspace
|
|
28
|
+
|
|
29
|
+
A Kadoa workspace admin can generate organization connector credentials under **Workspace settings → Claude connector**. The generated OAuth client is fixed to that workspace's service account.
|
|
30
|
+
|
|
31
|
+
1. In Kadoa, select **Generate credentials** and copy the MCP URL, OAuth Client ID, and OAuth Client Secret. The secret is shown once.
|
|
32
|
+
2. In Claude organization **Admin settings → Connectors**, select **Add custom connector**.
|
|
33
|
+
3. Enter `https://mcp.kadoa.com/mcp` and paste the generated OAuth Client ID/Secret under Advanced settings.
|
|
34
|
+
4. Organization members find the custom connector under **Settings → Connectors** and select **Connect**.
|
|
35
|
+
|
|
36
|
+
Members do not need Kadoa accounts. All actions are attributed to the shared Kadoa service account, and team switching is unavailable. To rotate access, rotate the secret in Kadoa, replace it in Claude, and reconnect. Revoking the connector stops access immediately.
|
|
37
|
+
|
|
38
|
+
Do not paste a Kadoa workspace API key into Claude.
|
|
39
|
+
|
|
27
40
|
### Cursor
|
|
28
41
|
|
|
29
42
|
Add to `.cursor/mcp.json`:
|
|
@@ -72,8 +85,8 @@ Once the MCP server is configured, you can manage the full workflow lifecycle th
|
|
|
72
85
|
> You: Fetch https://example.com as markdown.
|
|
73
86
|
|
|
74
87
|
Claude calls scrape with smart routing and returns compact markdown.
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
Routing is fully automatic — it escalates through the stealth tiers on
|
|
89
|
+
its own when a site blocks bots. Ask for HTML when you need the raw source.
|
|
77
90
|
```
|
|
78
91
|
|
|
79
92
|
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,20 +51861,11 @@ 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
|
-
}
|
|
51875
51864
|
function createKadoaClient(auth) {
|
|
51876
51865
|
const { principal } = auth;
|
|
51877
|
-
const client =
|
|
51866
|
+
const client = new KadoaClient({
|
|
51867
|
+
bearerToken: principal.type === "user" ? principal.supabaseJwt : principal.kadoaAccessToken
|
|
51868
|
+
});
|
|
51878
51869
|
client.axiosInstance.interceptors.request.use((config2) => {
|
|
51879
51870
|
config2.headers["x-kadoa-source"] = "mcp";
|
|
51880
51871
|
config2.headers["x-team-id"] = principal.teamId;
|
|
@@ -51889,10 +51880,24 @@ var init_client = __esm(() => {
|
|
|
51889
51880
|
ctxRefreshMutex = new WeakMap;
|
|
51890
51881
|
});
|
|
51891
51882
|
|
|
51883
|
+
// src/error-summary.ts
|
|
51884
|
+
function redactCredentialText(value) {
|
|
51885
|
+
return value.replace(/Bearer\s+[^\s,;]+/gi, "Bearer [redacted]").replace(/\b(?:kcs_|tk-)[A-Za-z0-9._~-]+\b/g, "[redacted]").replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, "[redacted]");
|
|
51886
|
+
}
|
|
51887
|
+
function safeErrorSummary(error48) {
|
|
51888
|
+
const record3 = error48 && typeof error48 === "object" ? error48 : {};
|
|
51889
|
+
const response = record3.response && typeof record3.response === "object" ? record3.response : {};
|
|
51890
|
+
return {
|
|
51891
|
+
name: error48 instanceof Error ? error48.name : "Error",
|
|
51892
|
+
message: redactCredentialText(error48 instanceof Error ? error48.message : String(error48)),
|
|
51893
|
+
...typeof record3.code === "string" ? { code: record3.code } : {},
|
|
51894
|
+
...typeof response.status === "number" ? { status: response.status } : {}
|
|
51895
|
+
};
|
|
51896
|
+
}
|
|
51897
|
+
|
|
51892
51898
|
// src/client.ts
|
|
51893
51899
|
var exports_client = {};
|
|
51894
51900
|
__export(exports_client, {
|
|
51895
|
-
resolveServiceAccountApiKey: () => resolveServiceAccountApiKey2,
|
|
51896
51901
|
refreshSupabaseJwtRaw: () => refreshSupabaseJwtRaw,
|
|
51897
51902
|
refreshSupabaseJwt: () => refreshSupabaseJwt,
|
|
51898
51903
|
isJwtExpired: () => isJwtExpired,
|
|
@@ -51903,20 +51908,11 @@ __export(exports_client, {
|
|
|
51903
51908
|
KadoaSdkException: () => KadoaSdkException,
|
|
51904
51909
|
KadoaClient: () => KadoaClient
|
|
51905
51910
|
});
|
|
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
|
-
}
|
|
51917
51911
|
function createKadoaClient2(auth) {
|
|
51918
51912
|
const { principal } = auth;
|
|
51919
|
-
const client =
|
|
51913
|
+
const client = new KadoaClient({
|
|
51914
|
+
bearerToken: principal.type === "user" ? principal.supabaseJwt : principal.kadoaAccessToken
|
|
51915
|
+
});
|
|
51920
51916
|
client.axiosInstance.interceptors.request.use((config2) => {
|
|
51921
51917
|
config2.headers["x-kadoa-source"] = "mcp";
|
|
51922
51918
|
config2.headers["x-team-id"] = principal.teamId;
|
|
@@ -52043,6 +52039,21 @@ var init_client2 = __esm(() => {
|
|
|
52043
52039
|
ctxRefreshMutex2 = new WeakMap;
|
|
52044
52040
|
});
|
|
52045
52041
|
|
|
52042
|
+
// src/error-summary.ts
|
|
52043
|
+
function redactCredentialText2(value) {
|
|
52044
|
+
return value.replace(/Bearer\s+[^\s,;]+/gi, "Bearer [redacted]").replace(/\b(?:kcs_|tk-)[A-Za-z0-9._~-]+\b/g, "[redacted]").replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, "[redacted]");
|
|
52045
|
+
}
|
|
52046
|
+
function safeErrorSummary2(error48) {
|
|
52047
|
+
const record3 = error48 && typeof error48 === "object" ? error48 : {};
|
|
52048
|
+
const response = record3.response && typeof record3.response === "object" ? record3.response : {};
|
|
52049
|
+
return {
|
|
52050
|
+
name: error48 instanceof Error ? error48.name : "Error",
|
|
52051
|
+
message: redactCredentialText2(error48 instanceof Error ? error48.message : String(error48)),
|
|
52052
|
+
...typeof record3.code === "string" ? { code: record3.code } : {},
|
|
52053
|
+
...typeof response.status === "number" ? { status: response.status } : {}
|
|
52054
|
+
};
|
|
52055
|
+
}
|
|
52056
|
+
|
|
52046
52057
|
// src/tools.ts
|
|
52047
52058
|
function strictSchema(shape) {
|
|
52048
52059
|
return exports_external.object(shape).strict();
|
|
@@ -52290,22 +52301,20 @@ function registerTools(server, ctx, capabilities) {
|
|
|
52290
52301
|
}
|
|
52291
52302
|
} catch {}
|
|
52292
52303
|
}
|
|
52293
|
-
console.error(`[Tool Error] ${name}:`, error48);
|
|
52304
|
+
console.error(`[Tool Error] ${name}:`, safeErrorSummary2(error48));
|
|
52294
52305
|
return errorResult(message);
|
|
52295
52306
|
}
|
|
52296
52307
|
};
|
|
52297
52308
|
}
|
|
52298
52309
|
if (capabilities.scrape) {
|
|
52299
52310
|
server.registerTool("scrape", {
|
|
52300
|
-
description: "Immediately fetch one URL as LLM-friendly markdown or raw HTML without creating a workflow. " + "Markdown is the default.
|
|
52311
|
+
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.",
|
|
52301
52312
|
inputSchema: strictSchema({
|
|
52302
52313
|
url: exports_external.url().refine((value) => {
|
|
52303
52314
|
const protocol = new URL(value).protocol;
|
|
52304
52315
|
return protocol === "http:" || protocol === "https:";
|
|
52305
52316
|
}, "URL must use HTTP or HTTPS").describe("HTTP or HTTPS URL to fetch"),
|
|
52306
|
-
format: exports_external.enum(["html", "markdown"]).optional().describe("Output format. Defaults to markdown for compact LLM consumption; use html for raw source.")
|
|
52307
|
-
fetchMode: exports_external.enum(["auto", "http", "browser"]).optional().describe("Fetch mode. Omit or use auto for smart routing; browser renders JavaScript."),
|
|
52308
|
-
proxy: exports_external.enum(["auto", "dc", "isp", "residential", "stealth"]).optional().describe("Proxy tier. Use stealth only when normal routing is bot-blocked.")
|
|
52317
|
+
format: exports_external.enum(["html", "markdown"]).optional().describe("Output format. Defaults to markdown for compact LLM consumption; use html for raw source.")
|
|
52309
52318
|
}),
|
|
52310
52319
|
annotations: {
|
|
52311
52320
|
readOnlyHint: true,
|
|
@@ -52316,9 +52325,7 @@ function registerTools(server, ctx, capabilities) {
|
|
|
52316
52325
|
}, withErrorHandling("scrape", async (args) => {
|
|
52317
52326
|
const response = await ctx.client.scrape.fetch({
|
|
52318
52327
|
url: args.url,
|
|
52319
|
-
format: args.format ?? "markdown"
|
|
52320
|
-
...args.fetchMode != null && { fetchMode: args.fetchMode },
|
|
52321
|
-
...args.proxy != null && { proxy: args.proxy }
|
|
52328
|
+
format: args.format ?? "markdown"
|
|
52322
52329
|
});
|
|
52323
52330
|
return jsonResult(response);
|
|
52324
52331
|
}));
|
|
@@ -53625,8 +53632,8 @@ function registerTools(server, ctx, capabilities) {
|
|
|
53625
53632
|
}
|
|
53626
53633
|
var SchemaFieldShape, DASHBOARD_BASE_URL = "https://www.kadoa.com", WORKFLOW_AUDIT_WATCHED_KEYS;
|
|
53627
53634
|
var init_tools = __esm(() => {
|
|
53628
|
-
init_zod();
|
|
53629
53635
|
init_dist2();
|
|
53636
|
+
init_zod();
|
|
53630
53637
|
init_client2();
|
|
53631
53638
|
SchemaFieldShape = {
|
|
53632
53639
|
name: exports_external.string().describe("Field name"),
|
|
@@ -53655,7 +53662,7 @@ var package_default;
|
|
|
53655
53662
|
var init_package = __esm(() => {
|
|
53656
53663
|
package_default = {
|
|
53657
53664
|
name: "@kadoa/mcp",
|
|
53658
|
-
version: "0.5.
|
|
53665
|
+
version: "0.5.13",
|
|
53659
53666
|
description: "Kadoa MCP Server — manage workflows from Claude Desktop, Cursor, and other MCP clients",
|
|
53660
53667
|
type: "module",
|
|
53661
53668
|
main: "dist/index.js",
|
|
@@ -53682,6 +53689,7 @@ var init_package = __esm(() => {
|
|
|
53682
53689
|
"@kadoa/node-sdk": "^0.35.0",
|
|
53683
53690
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
53684
53691
|
express: "^5.2.1",
|
|
53692
|
+
"express-rate-limit": "^8.2.1",
|
|
53685
53693
|
ioredis: "^5.6.1",
|
|
53686
53694
|
zod: "^4.3.6"
|
|
53687
53695
|
},
|
|
@@ -57687,55 +57695,69 @@ var init_bearerAuth = __esm(() => {
|
|
|
57687
57695
|
init_errors4();
|
|
57688
57696
|
});
|
|
57689
57697
|
|
|
57690
|
-
// src/
|
|
57691
|
-
|
|
57692
|
-
|
|
57693
|
-
|
|
57694
|
-
|
|
57695
|
-
|
|
57696
|
-
|
|
57697
|
-
|
|
57698
|
+
// src/managed-connector-registry.ts
|
|
57699
|
+
class ManagedConnectorRegistry {
|
|
57700
|
+
baseUrl;
|
|
57701
|
+
serviceToken;
|
|
57702
|
+
timeoutMs;
|
|
57703
|
+
constructor(options) {
|
|
57704
|
+
this.baseUrl = (options?.baseUrl ?? process.env.KADOA_PUBLIC_API_URI ?? "https://api.kadoa.com").replace(/\/$/, "");
|
|
57705
|
+
this.serviceToken = options?.serviceToken ?? process.env.MCP_REGISTRY_SERVICE_TOKEN ?? "";
|
|
57706
|
+
this.timeoutMs = options?.timeoutMs ?? Number.parseInt(process.env.MCP_REGISTRY_TIMEOUT_MS ?? "5000", 10);
|
|
57707
|
+
if (this.serviceToken.length < 32)
|
|
57708
|
+
throw new Error("MCP_REGISTRY_SERVICE_TOKEN must be at least 32 characters");
|
|
57698
57709
|
}
|
|
57699
|
-
|
|
57700
|
-
|
|
57701
|
-
|
|
57710
|
+
async get(clientId) {
|
|
57711
|
+
let response;
|
|
57712
|
+
try {
|
|
57713
|
+
response = await fetch(`${this.baseUrl}/v4/internal/mcp/connectors/${encodeURIComponent(clientId)}`, {
|
|
57714
|
+
headers: { "x-mcp-registry-token": this.serviceToken },
|
|
57715
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
57716
|
+
});
|
|
57717
|
+
} catch {
|
|
57718
|
+
throw new ManagedConnectorRegistryUnavailableError;
|
|
57719
|
+
}
|
|
57720
|
+
if (response.status === 404)
|
|
57721
|
+
return null;
|
|
57722
|
+
if (!response.ok)
|
|
57723
|
+
throw new ManagedConnectorRegistryUnavailableError;
|
|
57724
|
+
return response.json();
|
|
57725
|
+
}
|
|
57726
|
+
async authenticate(clientId, clientSecret) {
|
|
57727
|
+
let response;
|
|
57728
|
+
try {
|
|
57729
|
+
response = await fetch(`${this.baseUrl}/v4/internal/mcp/connectors/${encodeURIComponent(clientId)}/authenticate`, {
|
|
57730
|
+
method: "POST",
|
|
57731
|
+
headers: {
|
|
57732
|
+
"content-type": "application/json",
|
|
57733
|
+
"x-mcp-registry-token": this.serviceToken
|
|
57734
|
+
},
|
|
57735
|
+
body: JSON.stringify({ clientSecret }),
|
|
57736
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
57737
|
+
});
|
|
57738
|
+
} catch {
|
|
57739
|
+
throw new ManagedConnectorRegistryUnavailableError;
|
|
57740
|
+
}
|
|
57741
|
+
if (response.status === 401 || response.status === 404)
|
|
57742
|
+
throw new InvalidManagedConnectorClientError;
|
|
57743
|
+
if (!response.ok)
|
|
57744
|
+
throw new ManagedConnectorRegistryUnavailableError;
|
|
57745
|
+
return response.json();
|
|
57702
57746
|
}
|
|
57703
|
-
return {
|
|
57704
|
-
clientId: values.clientId,
|
|
57705
|
-
clientSecret: values.clientSecret,
|
|
57706
|
-
redirectUri: redirectUri.toString(),
|
|
57707
|
-
credentialRef: values.credentialRef,
|
|
57708
|
-
teamId: values.teamId,
|
|
57709
|
-
label: values.label
|
|
57710
|
-
};
|
|
57711
57747
|
}
|
|
57712
|
-
|
|
57713
|
-
|
|
57714
|
-
|
|
57715
|
-
|
|
57716
|
-
|
|
57717
|
-
|
|
57718
|
-
|
|
57719
|
-
return {
|
|
57720
|
-
client_id: config2.clientId,
|
|
57721
|
-
client_secret: config2.clientSecret,
|
|
57722
|
-
client_id_issued_at: 0,
|
|
57723
|
-
client_name: config2.label,
|
|
57724
|
-
redirect_uris: [config2.redirectUri],
|
|
57725
|
-
token_endpoint_auth_method: "client_secret_post",
|
|
57726
|
-
grant_types: ["authorization_code", "refresh_token"],
|
|
57727
|
-
response_types: ["code"]
|
|
57748
|
+
var ManagedConnectorRegistryUnavailableError, InvalidManagedConnectorClientError;
|
|
57749
|
+
var init_managed_connector_registry = __esm(() => {
|
|
57750
|
+
ManagedConnectorRegistryUnavailableError = class ManagedConnectorRegistryUnavailableError extends Error {
|
|
57751
|
+
constructor() {
|
|
57752
|
+
super("Managed connector registry is unavailable");
|
|
57753
|
+
this.name = "ManagedConnectorRegistryUnavailableError";
|
|
57754
|
+
}
|
|
57728
57755
|
};
|
|
57729
|
-
|
|
57730
|
-
|
|
57731
|
-
|
|
57732
|
-
|
|
57733
|
-
|
|
57734
|
-
clientSecret: "MCP_SERVICE_ACCOUNT_OAUTH_CLIENT_SECRET",
|
|
57735
|
-
redirectUri: "MCP_SERVICE_ACCOUNT_OAUTH_REDIRECT_URI",
|
|
57736
|
-
credentialRef: "MCP_SERVICE_ACCOUNT_CREDENTIAL_REF",
|
|
57737
|
-
teamId: "MCP_SERVICE_ACCOUNT_TEAM_ID",
|
|
57738
|
-
label: "MCP_SERVICE_ACCOUNT_LABEL"
|
|
57756
|
+
InvalidManagedConnectorClientError = class InvalidManagedConnectorClientError extends Error {
|
|
57757
|
+
constructor() {
|
|
57758
|
+
super("Managed connector client authentication failed");
|
|
57759
|
+
this.name = "InvalidManagedConnectorClientError";
|
|
57760
|
+
}
|
|
57739
57761
|
};
|
|
57740
57762
|
});
|
|
57741
57763
|
|
|
@@ -57858,14 +57880,36 @@ async function setActiveTeamAndRefresh(jwt2, refreshToken, teamId) {
|
|
|
57858
57880
|
|
|
57859
57881
|
class KadoaOAuthProvider {
|
|
57860
57882
|
store;
|
|
57861
|
-
|
|
57883
|
+
managedRegistry;
|
|
57884
|
+
constructor(store, managedRegistry) {
|
|
57862
57885
|
this.store = store;
|
|
57886
|
+
this.managedRegistry = managedRegistry;
|
|
57887
|
+
}
|
|
57888
|
+
getManagedRegistry() {
|
|
57889
|
+
this.managedRegistry ??= new ManagedConnectorRegistry;
|
|
57890
|
+
return this.managedRegistry;
|
|
57863
57891
|
}
|
|
57864
57892
|
get clientsStore() {
|
|
57865
57893
|
const store = this.store;
|
|
57894
|
+
const thisProvider = this;
|
|
57866
57895
|
return {
|
|
57867
57896
|
async getClient(clientId) {
|
|
57868
|
-
|
|
57897
|
+
if (clientId.startsWith("kadoa_claude_")) {
|
|
57898
|
+
const connector = await thisProvider.getManagedRegistry().get(clientId);
|
|
57899
|
+
if (!connector)
|
|
57900
|
+
return;
|
|
57901
|
+
return {
|
|
57902
|
+
client_id: connector.clientId,
|
|
57903
|
+
client_id_issued_at: 0,
|
|
57904
|
+
client_name: "Kadoa shared workspace",
|
|
57905
|
+
redirect_uris: connector.redirectUris,
|
|
57906
|
+
token_endpoint_auth_method: connector.tokenEndpointAuthMethod,
|
|
57907
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
57908
|
+
response_types: ["code"],
|
|
57909
|
+
managedConnector: connector
|
|
57910
|
+
};
|
|
57911
|
+
}
|
|
57912
|
+
return store.get("clients", clientId);
|
|
57869
57913
|
},
|
|
57870
57914
|
async registerClient(client) {
|
|
57871
57915
|
const clientId = randomToken(16);
|
|
@@ -57884,21 +57928,18 @@ class KadoaOAuthProvider {
|
|
|
57884
57928
|
if (!serverUrl) {
|
|
57885
57929
|
throw new Error("MCP_SERVER_URL must be configured");
|
|
57886
57930
|
}
|
|
57931
|
+
const managedConnector = client.managedConnector;
|
|
57932
|
+
if (managedConnector) {
|
|
57933
|
+
await this.completeManagedAuthFlow({ clientId: client.client_id, params, supabaseCodeVerifier: "" }, res, managedConnector);
|
|
57934
|
+
return;
|
|
57935
|
+
}
|
|
57887
57936
|
const state = randomToken();
|
|
57888
|
-
const { verifier
|
|
57937
|
+
const { verifier } = generatePKCE();
|
|
57889
57938
|
await this.store.set("pending_auths", state, {
|
|
57890
|
-
client,
|
|
57939
|
+
clientId: client.client_id,
|
|
57891
57940
|
params,
|
|
57892
57941
|
supabaseCodeVerifier: verifier
|
|
57893
57942
|
}, 600);
|
|
57894
|
-
const configuredServiceClientId = process.env.MCP_SERVICE_ACCOUNT_OAUTH_CLIENT_ID?.trim();
|
|
57895
|
-
if (configuredServiceClientId === client.client_id) {
|
|
57896
|
-
const serviceConfig = getServiceAccountOAuthConfig();
|
|
57897
|
-
if (!serviceConfig)
|
|
57898
|
-
throw new Error("Service-account OAuth configuration is unavailable");
|
|
57899
|
-
res.type("html").send(renderServiceAccountConsentPage(state, serviceConfig.label));
|
|
57900
|
-
return;
|
|
57901
|
-
}
|
|
57902
57943
|
res.type("html").send(renderLoginPage(state));
|
|
57903
57944
|
}
|
|
57904
57945
|
async handleGoogleLogin(req, res) {
|
|
@@ -58053,6 +58094,9 @@ class KadoaOAuthProvider {
|
|
|
58053
58094
|
if (redirectUri && redirectUri !== entry.redirectUri) {
|
|
58054
58095
|
throw new Error("redirect_uri mismatch");
|
|
58055
58096
|
}
|
|
58097
|
+
if (entry.managedConnector) {
|
|
58098
|
+
throw new InvalidGrantError("Managed authorization code requires confidential-client exchange");
|
|
58099
|
+
}
|
|
58056
58100
|
const accessToken = randomToken();
|
|
58057
58101
|
const refreshToken = randomToken();
|
|
58058
58102
|
const expiresAt = Date.now() + ACCESS_TOKEN_TTL * 1000;
|
|
@@ -58076,6 +58120,75 @@ class KadoaOAuthProvider {
|
|
|
58076
58120
|
refresh_token: refreshToken
|
|
58077
58121
|
};
|
|
58078
58122
|
}
|
|
58123
|
+
async exchangeManagedAuthorizationCode(connector, authorizationCode, redirectUri) {
|
|
58124
|
+
const entry = await this.store.get("auth_codes", authorizationCode);
|
|
58125
|
+
if (!entry || !entry.managedConnector)
|
|
58126
|
+
throw new InvalidGrantError("Unknown authorization code");
|
|
58127
|
+
if (entry.expiresAt < Date.now()) {
|
|
58128
|
+
await this.store.del("auth_codes", authorizationCode);
|
|
58129
|
+
throw new InvalidGrantError("Authorization code expired");
|
|
58130
|
+
}
|
|
58131
|
+
if (entry.clientId !== connector.clientId)
|
|
58132
|
+
throw new InvalidGrantError("OAuth client mismatch");
|
|
58133
|
+
if (redirectUri && redirectUri !== entry.redirectUri)
|
|
58134
|
+
throw new InvalidGrantError("redirect_uri mismatch");
|
|
58135
|
+
const binding = entry.managedConnector;
|
|
58136
|
+
if (binding.connectorId !== connector.connectorId || binding.teamId !== connector.teamId || binding.serviceUserId !== connector.serviceUserId || binding.secretVersion !== connector.secretVersion) {
|
|
58137
|
+
throw new InvalidGrantError("Managed connector binding changed");
|
|
58138
|
+
}
|
|
58139
|
+
const tokens = await this.issueManagedTokens(connector);
|
|
58140
|
+
await this.store.del("auth_codes", authorizationCode);
|
|
58141
|
+
return tokens;
|
|
58142
|
+
}
|
|
58143
|
+
async exchangeManagedRefreshToken(connector, refreshToken) {
|
|
58144
|
+
const entry = await this.store.get("refresh_tokens", refreshToken);
|
|
58145
|
+
if (!entry || !entry.managedConnector)
|
|
58146
|
+
throw new InvalidGrantError("Unknown or expired refresh token");
|
|
58147
|
+
if (entry.clientId !== connector.clientId)
|
|
58148
|
+
throw new InvalidGrantError("OAuth client mismatch");
|
|
58149
|
+
const binding = entry.managedConnector;
|
|
58150
|
+
if (binding.connectorId !== connector.connectorId || binding.teamId !== connector.teamId || binding.serviceUserId !== connector.serviceUserId) {
|
|
58151
|
+
throw new InvalidGrantError("Managed connector binding changed");
|
|
58152
|
+
}
|
|
58153
|
+
await this.store.del("refresh_tokens", refreshToken);
|
|
58154
|
+
return this.issueManagedTokens(connector);
|
|
58155
|
+
}
|
|
58156
|
+
async issueManagedTokens(connector) {
|
|
58157
|
+
const accessToken = randomToken();
|
|
58158
|
+
const refreshToken = randomToken();
|
|
58159
|
+
const accessTokenTtl = Math.min(ACCESS_TOKEN_TTL, connector.expiresIn);
|
|
58160
|
+
const expiresAt = Date.now() + accessTokenTtl * 1000;
|
|
58161
|
+
const principal = {
|
|
58162
|
+
type: "serviceAccount",
|
|
58163
|
+
connectorId: connector.connectorId,
|
|
58164
|
+
teamId: connector.teamId,
|
|
58165
|
+
serviceUserId: connector.serviceUserId,
|
|
58166
|
+
secretVersion: connector.secretVersion,
|
|
58167
|
+
kadoaAccessToken: connector.kadoaAccessToken,
|
|
58168
|
+
kadoaAccessTokenExpiresAt: Date.now() + connector.expiresIn * 1000
|
|
58169
|
+
};
|
|
58170
|
+
await this.store.set("access_tokens", accessToken, {
|
|
58171
|
+
...persistPrincipal(principal),
|
|
58172
|
+
clientId: connector.clientId,
|
|
58173
|
+
expiresAt
|
|
58174
|
+
}, accessTokenTtl);
|
|
58175
|
+
await this.store.set("refresh_tokens", refreshToken, {
|
|
58176
|
+
clientId: connector.clientId,
|
|
58177
|
+
managedConnector: {
|
|
58178
|
+
connectorId: connector.connectorId,
|
|
58179
|
+
clientId: connector.clientId,
|
|
58180
|
+
teamId: connector.teamId,
|
|
58181
|
+
serviceUserId: connector.serviceUserId,
|
|
58182
|
+
secretVersion: connector.secretVersion
|
|
58183
|
+
}
|
|
58184
|
+
}, 2592000);
|
|
58185
|
+
return {
|
|
58186
|
+
access_token: accessToken,
|
|
58187
|
+
token_type: "bearer",
|
|
58188
|
+
expires_in: accessTokenTtl,
|
|
58189
|
+
refresh_token: refreshToken
|
|
58190
|
+
};
|
|
58191
|
+
}
|
|
58079
58192
|
async exchangeRefreshToken(_client, refreshToken) {
|
|
58080
58193
|
const entry = await this.store.get("refresh_tokens", refreshToken);
|
|
58081
58194
|
if (!entry) {
|
|
@@ -58086,8 +58199,12 @@ class KadoaOAuthProvider {
|
|
|
58086
58199
|
if (entry.clientId !== _client.client_id) {
|
|
58087
58200
|
throw new Error("OAuth client mismatch for refresh token");
|
|
58088
58201
|
}
|
|
58202
|
+
if (entry.managedConnector) {
|
|
58203
|
+
throw new InvalidGrantError("Managed refresh token requires confidential-client exchange");
|
|
58204
|
+
}
|
|
58205
|
+
const persistedEntry = entry;
|
|
58089
58206
|
await this.store.del("refresh_tokens", refreshToken);
|
|
58090
|
-
const storedPrincipal = readPrincipal(
|
|
58207
|
+
const storedPrincipal = readPrincipal(persistedEntry);
|
|
58091
58208
|
if (storedPrincipal.type === "serviceAccount") {
|
|
58092
58209
|
const newAccessToken2 = randomToken();
|
|
58093
58210
|
const newRefreshToken2 = randomToken();
|
|
@@ -58110,7 +58227,7 @@ class KadoaOAuthProvider {
|
|
|
58110
58227
|
}
|
|
58111
58228
|
let { supabaseJwt, supabaseRefreshToken } = storedPrincipal;
|
|
58112
58229
|
const claims = jwtClaims(storedPrincipal.supabaseJwt);
|
|
58113
|
-
const context = `email=${claims.email}, team=${
|
|
58230
|
+
const context = `email=${claims.email}, team=${persistedEntry.teamId}`;
|
|
58114
58231
|
try {
|
|
58115
58232
|
const { refreshSupabaseJwtRaw: refreshSupabaseJwtRaw2, SessionExpiredError: SessionExpiredError2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
|
|
58116
58233
|
const refreshed = await refreshSupabaseJwtRaw2(supabaseRefreshToken);
|
|
@@ -58129,7 +58246,7 @@ class KadoaOAuthProvider {
|
|
|
58129
58246
|
console.error(`[AUTH] REFRESH_WARN: unexpected error, using stale JWT (${context}):`, error48);
|
|
58130
58247
|
}
|
|
58131
58248
|
const freshClaims = jwtClaims(supabaseJwt);
|
|
58132
|
-
const teamId = freshClaims.activeTeamId ??
|
|
58249
|
+
const teamId = freshClaims.activeTeamId ?? persistedEntry.teamId;
|
|
58133
58250
|
const newAccessToken = randomToken();
|
|
58134
58251
|
const newRefreshToken = randomToken();
|
|
58135
58252
|
const expiresAt = Date.now() + ACCESS_TOKEN_TTL * 1000;
|
|
@@ -58169,6 +58286,13 @@ class KadoaOAuthProvider {
|
|
|
58169
58286
|
throw new InvalidTokenError("Access token expired");
|
|
58170
58287
|
}
|
|
58171
58288
|
const principal = readPrincipal(entry);
|
|
58289
|
+
if (principal.type === "serviceAccount") {
|
|
58290
|
+
const connector = await this.getManagedRegistry().get(entry.clientId);
|
|
58291
|
+
if (!connector || connector.connectorId !== principal.connectorId || connector.teamId !== principal.teamId || connector.serviceUserId !== principal.serviceUserId || connector.secretVersion !== principal.secretVersion) {
|
|
58292
|
+
await this.store.del("access_tokens", token);
|
|
58293
|
+
throw new InvalidTokenError("Managed connector is inactive or changed");
|
|
58294
|
+
}
|
|
58295
|
+
}
|
|
58172
58296
|
return {
|
|
58173
58297
|
token,
|
|
58174
58298
|
clientId: entry.clientId,
|
|
@@ -58210,29 +58334,6 @@ class KadoaOAuthProvider {
|
|
|
58210
58334
|
res.redirect(redirectUrl.toString());
|
|
58211
58335
|
}
|
|
58212
58336
|
}
|
|
58213
|
-
async handleServiceAccountConsent(req, res) {
|
|
58214
|
-
const { state } = req.body;
|
|
58215
|
-
if (!state) {
|
|
58216
|
-
res.status(400).send("Missing state parameter");
|
|
58217
|
-
return;
|
|
58218
|
-
}
|
|
58219
|
-
const pending = await this.store.get("pending_auths", state);
|
|
58220
|
-
if (!pending) {
|
|
58221
|
-
res.status(400).send("Unknown or expired state parameter");
|
|
58222
|
-
return;
|
|
58223
|
-
}
|
|
58224
|
-
const config2 = getServiceAccountOAuthConfig();
|
|
58225
|
-
if (!config2 || pending.client.client_id !== config2.clientId) {
|
|
58226
|
-
res.status(403).send("Service-account authorization is not available for this client");
|
|
58227
|
-
return;
|
|
58228
|
-
}
|
|
58229
|
-
await this.store.del("pending_auths", state);
|
|
58230
|
-
await this.completeAuthFlow(pending, res, {
|
|
58231
|
-
type: "serviceAccount",
|
|
58232
|
-
credentialRef: config2.credentialRef,
|
|
58233
|
-
teamId: config2.teamId
|
|
58234
|
-
});
|
|
58235
|
-
}
|
|
58236
58337
|
async handleTeamSelection(req, res) {
|
|
58237
58338
|
const { token, teamId } = req.body;
|
|
58238
58339
|
if (!token || !teamId) {
|
|
@@ -58273,12 +58374,33 @@ class KadoaOAuthProvider {
|
|
|
58273
58374
|
res.redirect(redirectUrl.toString());
|
|
58274
58375
|
}
|
|
58275
58376
|
}
|
|
58377
|
+
async completeManagedAuthFlow(pending, res, connector) {
|
|
58378
|
+
const mcpCode = randomToken();
|
|
58379
|
+
await this.store.set("auth_codes", mcpCode, {
|
|
58380
|
+
managedConnector: {
|
|
58381
|
+
connectorId: connector.connectorId,
|
|
58382
|
+
clientId: connector.clientId,
|
|
58383
|
+
teamId: connector.teamId,
|
|
58384
|
+
serviceUserId: connector.serviceUserId,
|
|
58385
|
+
secretVersion: connector.secretVersion
|
|
58386
|
+
},
|
|
58387
|
+
codeChallenge: pending.params.codeChallenge,
|
|
58388
|
+
clientId: pending.clientId,
|
|
58389
|
+
redirectUri: pending.params.redirectUri,
|
|
58390
|
+
expiresAt: Date.now() + 600000
|
|
58391
|
+
}, 600);
|
|
58392
|
+
const redirectUrl = new URL(pending.params.redirectUri);
|
|
58393
|
+
redirectUrl.searchParams.set("code", mcpCode);
|
|
58394
|
+
if (pending.params.state)
|
|
58395
|
+
redirectUrl.searchParams.set("state", pending.params.state);
|
|
58396
|
+
res.redirect(redirectUrl.toString());
|
|
58397
|
+
}
|
|
58276
58398
|
async completeAuthFlow(pending, res, principal) {
|
|
58277
58399
|
const mcpCode = randomToken();
|
|
58278
58400
|
await this.store.set("auth_codes", mcpCode, {
|
|
58279
58401
|
...persistPrincipal(principal),
|
|
58280
58402
|
codeChallenge: pending.params.codeChallenge,
|
|
58281
|
-
clientId: pending.
|
|
58403
|
+
clientId: pending.clientId,
|
|
58282
58404
|
redirectUri: pending.params.redirectUri,
|
|
58283
58405
|
expiresAt: Date.now() + 600000
|
|
58284
58406
|
}, 600);
|
|
@@ -58290,33 +58412,6 @@ class KadoaOAuthProvider {
|
|
|
58290
58412
|
res.redirect(redirectUrl.toString());
|
|
58291
58413
|
}
|
|
58292
58414
|
}
|
|
58293
|
-
function renderServiceAccountConsentPage(state, label) {
|
|
58294
|
-
return `<!DOCTYPE html>
|
|
58295
|
-
<html lang="en">
|
|
58296
|
-
<head>
|
|
58297
|
-
<meta charset="utf-8" />
|
|
58298
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
58299
|
-
<title>Connect shared Kadoa workspace</title>
|
|
58300
|
-
<style>
|
|
58301
|
-
body { font-family: ui-sans-serif, system-ui, sans-serif; background: #fafafa; color: #18181b; min-height: 100vh; display: grid; place-items: center; margin: 0; }
|
|
58302
|
-
main { width: min(420px, calc(100% - 2rem)); background: white; border: 1px solid #e4e4e7; border-radius: 8px; padding: 2rem; box-sizing: border-box; }
|
|
58303
|
-
h1 { font-size: 1.25rem; margin: 0 0 0.75rem; }
|
|
58304
|
-
p { color: #52525b; line-height: 1.5; }
|
|
58305
|
-
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; }
|
|
58306
|
-
</style>
|
|
58307
|
-
</head>
|
|
58308
|
-
<body>
|
|
58309
|
-
<main>
|
|
58310
|
-
<h1>Connect ${escapeHtml(label)}</h1>
|
|
58311
|
-
<p>Claude will use the shared Kadoa service account for this organization. Activity will not be attributed to your individual Kadoa identity.</p>
|
|
58312
|
-
<form method="POST" action="/auth/service-account">
|
|
58313
|
-
<input type="hidden" name="state" value="${escapeHtml(state)}" />
|
|
58314
|
-
<button type="submit">Continue with shared workspace</button>
|
|
58315
|
-
</form>
|
|
58316
|
-
</main>
|
|
58317
|
-
</body>
|
|
58318
|
-
</html>`;
|
|
58319
|
-
}
|
|
58320
58415
|
function renderTeamSelectionPage(teams, selectionToken) {
|
|
58321
58416
|
const teamButtons = teams.map((t) => `
|
|
58322
58417
|
<button type="submit" name="teamId" value="${t.id}" class="team-btn">
|
|
@@ -58739,7 +58834,7 @@ function escapeHtml(str) {
|
|
|
58739
58834
|
var TEAM_SELECTION_TTL, ACCESS_TOKEN_TTL;
|
|
58740
58835
|
var init_auth2 = __esm(() => {
|
|
58741
58836
|
init_errors4();
|
|
58742
|
-
|
|
58837
|
+
init_managed_connector_registry();
|
|
58743
58838
|
TEAM_SELECTION_TTL = 10 * 60 * 1000;
|
|
58744
58839
|
ACCESS_TOKEN_TTL = 7 * 24 * 3600;
|
|
58745
58840
|
});
|
|
@@ -58840,6 +58935,75 @@ class RedisTokenStore {
|
|
|
58840
58935
|
var KEY_PREFIX = "kadoa-mcp";
|
|
58841
58936
|
var init_redis_store = () => {};
|
|
58842
58937
|
|
|
58938
|
+
// src/managed-token-handler.ts
|
|
58939
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
58940
|
+
function requireString(body, key, maxLength) {
|
|
58941
|
+
const value = body[key];
|
|
58942
|
+
if (typeof value !== "string" || !value)
|
|
58943
|
+
throw new InvalidRequestError(`${key} is required`);
|
|
58944
|
+
if (value.length > maxLength)
|
|
58945
|
+
throw new InvalidRequestError(`${key} is invalid`);
|
|
58946
|
+
return value;
|
|
58947
|
+
}
|
|
58948
|
+
function verifyPkce(verifier, challenge) {
|
|
58949
|
+
return createHash3("sha256").update(verifier).digest("base64url") === challenge;
|
|
58950
|
+
}
|
|
58951
|
+
function managedTokenHandler(options) {
|
|
58952
|
+
const { provider } = options;
|
|
58953
|
+
let registry2 = options.registry;
|
|
58954
|
+
return async (req, res, next) => {
|
|
58955
|
+
const body = req.body ?? {};
|
|
58956
|
+
const clientId = typeof body.client_id === "string" ? body.client_id : "";
|
|
58957
|
+
if (!clientId.startsWith("kadoa_claude_")) {
|
|
58958
|
+
next();
|
|
58959
|
+
return;
|
|
58960
|
+
}
|
|
58961
|
+
res.setHeader("Cache-Control", "no-store");
|
|
58962
|
+
try {
|
|
58963
|
+
if (clientId.length > 128)
|
|
58964
|
+
throw new InvalidClientError("Invalid client credentials");
|
|
58965
|
+
const clientSecret = requireString(body, "client_secret", 256);
|
|
58966
|
+
registry2 ??= new ManagedConnectorRegistry;
|
|
58967
|
+
const connector = await registry2.authenticate(clientId, clientSecret);
|
|
58968
|
+
const grantType = requireString(body, "grant_type", 64);
|
|
58969
|
+
let tokens;
|
|
58970
|
+
if (grantType === "authorization_code") {
|
|
58971
|
+
const code = requireString(body, "code", 512);
|
|
58972
|
+
const verifier = requireString(body, "code_verifier", 128);
|
|
58973
|
+
const redirectUri = typeof body.redirect_uri === "string" ? body.redirect_uri : undefined;
|
|
58974
|
+
const challenge = await provider.challengeForAuthorizationCode({ client_id: clientId }, code);
|
|
58975
|
+
if (!verifyPkce(verifier, challenge))
|
|
58976
|
+
throw new InvalidGrantError("code_verifier does not match the challenge");
|
|
58977
|
+
tokens = await provider.exchangeManagedAuthorizationCode(connector, code, redirectUri);
|
|
58978
|
+
} else if (grantType === "refresh_token") {
|
|
58979
|
+
const refreshToken = requireString(body, "refresh_token", 512);
|
|
58980
|
+
tokens = await provider.exchangeManagedRefreshToken(connector, refreshToken);
|
|
58981
|
+
} else {
|
|
58982
|
+
throw new UnsupportedGrantTypeError("The grant type is not supported by this authorization server.");
|
|
58983
|
+
}
|
|
58984
|
+
res.status(200).json(tokens);
|
|
58985
|
+
} catch (error48) {
|
|
58986
|
+
let oauthError;
|
|
58987
|
+
let status = 400;
|
|
58988
|
+
if (error48 instanceof InvalidManagedConnectorClientError) {
|
|
58989
|
+
oauthError = new InvalidClientError("Invalid client credentials");
|
|
58990
|
+
} else if (error48 instanceof ManagedConnectorRegistryUnavailableError) {
|
|
58991
|
+
oauthError = new TemporarilyUnavailableError("Managed connector authentication is temporarily unavailable");
|
|
58992
|
+
status = 503;
|
|
58993
|
+
} else if (error48 instanceof OAuthError) {
|
|
58994
|
+
oauthError = error48;
|
|
58995
|
+
} else {
|
|
58996
|
+
oauthError = new InvalidClientError("Invalid client credentials");
|
|
58997
|
+
}
|
|
58998
|
+
res.status(status).json(oauthError.toResponseObject());
|
|
58999
|
+
}
|
|
59000
|
+
};
|
|
59001
|
+
}
|
|
59002
|
+
var init_managed_token_handler = __esm(() => {
|
|
59003
|
+
init_errors4();
|
|
59004
|
+
init_managed_connector_registry();
|
|
59005
|
+
});
|
|
59006
|
+
|
|
58843
59007
|
// src/http.ts
|
|
58844
59008
|
var exports_http = {};
|
|
58845
59009
|
__export(exports_http, {
|
|
@@ -58861,7 +59025,7 @@ function resolveAuth(req) {
|
|
|
58861
59025
|
}
|
|
58862
59026
|
const principal = extra.principal;
|
|
58863
59027
|
if (principal.type === "serviceAccount") {
|
|
58864
|
-
if (!principal.
|
|
59028
|
+
if (!principal.connectorId || !principal.teamId || !principal.serviceUserId || !principal.kadoaAccessToken) {
|
|
58865
59029
|
console.error("[AUTH_RESOLVE] FAIL: service-account principal is incomplete");
|
|
58866
59030
|
return;
|
|
58867
59031
|
}
|
|
@@ -58892,8 +59056,20 @@ async function startHttpServer(options) {
|
|
|
58892
59056
|
const app = createMcpExpressApp({ host: "0.0.0.0" });
|
|
58893
59057
|
app.set("trust proxy", 1);
|
|
58894
59058
|
const store = options?.store ?? new RedisTokenStore;
|
|
58895
|
-
const
|
|
59059
|
+
const managedRegistry = options?.managedRegistry ?? (process.env.MCP_REGISTRY_SERVICE_TOKEN ? new ManagedConnectorRegistry : undefined);
|
|
59060
|
+
const provider = new KadoaOAuthProvider(store, managedRegistry);
|
|
58896
59061
|
const serverUrl = process.env.MCP_SERVER_URL || `http://localhost:${port}`;
|
|
59062
|
+
app.post("/token", express8.urlencoded({ extended: false }), rate_limit_default({
|
|
59063
|
+
windowMs: 15 * 60 * 1000,
|
|
59064
|
+
limit: 50,
|
|
59065
|
+
standardHeaders: true,
|
|
59066
|
+
legacyHeaders: false,
|
|
59067
|
+
skip: (req) => !String(req.body?.client_id ?? "").startsWith("kadoa_claude_"),
|
|
59068
|
+
message: {
|
|
59069
|
+
error: "too_many_requests",
|
|
59070
|
+
error_description: "Too many managed connector token requests"
|
|
59071
|
+
}
|
|
59072
|
+
}), managedTokenHandler({ provider, registry: managedRegistry }));
|
|
58897
59073
|
app.use(mcpAuthRouter({
|
|
58898
59074
|
provider,
|
|
58899
59075
|
issuerUrl: new URL(serverUrl),
|
|
@@ -58909,9 +59085,6 @@ async function startHttpServer(options) {
|
|
|
58909
59085
|
app.post("/auth/login", express8.urlencoded({ extended: false }), (req, res) => {
|
|
58910
59086
|
provider.handleEmailPasswordLogin(req, res);
|
|
58911
59087
|
});
|
|
58912
|
-
app.post("/auth/service-account", express8.urlencoded({ extended: false }), (req, res) => {
|
|
58913
|
-
provider.handleServiceAccountConsent(req, res);
|
|
58914
|
-
});
|
|
58915
59088
|
app.post("/auth/sso", express8.urlencoded({ extended: false }), (req, res) => {
|
|
58916
59089
|
provider.handleSSOLogin(req, res);
|
|
58917
59090
|
});
|
|
@@ -59010,7 +59183,7 @@ async function startHttpServer(options) {
|
|
|
59010
59183
|
await server.connect(transport);
|
|
59011
59184
|
await transport.handleRequest(req, res, req.body);
|
|
59012
59185
|
} catch (error48) {
|
|
59013
|
-
console.error(`[MCP] 500 method=${method} auth=${identity2} error=`, error48);
|
|
59186
|
+
console.error(`[MCP] 500 method=${method} auth=${identity2} error=`, safeErrorSummary2(error48));
|
|
59014
59187
|
if (!res.headersSent) {
|
|
59015
59188
|
res.status(500).json({
|
|
59016
59189
|
jsonrpc: "2.0",
|
|
@@ -59052,9 +59225,12 @@ var init_http2 = __esm(async () => {
|
|
|
59052
59225
|
init_streamableHttp();
|
|
59053
59226
|
init_router();
|
|
59054
59227
|
init_bearerAuth();
|
|
59228
|
+
init_dist4();
|
|
59055
59229
|
init_auth2();
|
|
59056
59230
|
init_redis_store();
|
|
59057
59231
|
init_client2();
|
|
59232
|
+
init_managed_connector_registry();
|
|
59233
|
+
init_managed_token_handler();
|
|
59058
59234
|
await init_src();
|
|
59059
59235
|
});
|
|
59060
59236
|
|
|
@@ -59107,7 +59283,7 @@ async function createServer(auth, options) {
|
|
|
59107
59283
|
}
|
|
59108
59284
|
}
|
|
59109
59285
|
registerTools(server, ctx, capabilities);
|
|
59110
|
-
server.server.onerror = (error48) => console.error("[MCP Error]", error48);
|
|
59286
|
+
server.server.onerror = (error48) => console.error("[MCP Error]", safeErrorSummary(error48));
|
|
59111
59287
|
return server;
|
|
59112
59288
|
}
|
|
59113
59289
|
var init_src = __esm(async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kadoa/mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.13",
|
|
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",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"@kadoa/node-sdk": "^0.35.0",
|
|
28
28
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
29
29
|
"express": "^5.2.1",
|
|
30
|
+
"express-rate-limit": "^8.2.1",
|
|
30
31
|
"ioredis": "^5.6.1",
|
|
31
32
|
"zod": "^4.3.6"
|
|
32
33
|
},
|