@kadoa/mcp 0.5.13 → 0.5.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +7 -5
  2. package/dist/index.js +87 -13
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -24,16 +24,17 @@ 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
27
+ ### Claude service connector
28
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.
29
+ A Kadoa workspace admin can generate service connector credentials under **Workspace settings → MCP connector**. The generated OAuth client is fixed to that workspace's service account.
30
30
 
31
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
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**.
33
+ 3. Enter `https://service.mcp.kadoa.com/mcp` and paste the generated OAuth Client ID/Secret under Advanced settings.
34
+ 4. In the connector's tool settings, allow only the tools that organization members should use.
35
+ 5. Organization members find the custom connector under **Settings → Connectors** and select **Connect**.
35
36
 
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
+ The service connector uses a separate hostname from the personal connector at `https://mcp.kadoa.com/mcp`, so Claude users can configure both. 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
 
38
39
  Do not paste a Kadoa workspace API key into Claude.
39
40
 
@@ -65,6 +66,7 @@ Point your client to `https://mcp.kadoa.com/mcp` with OAuth authentication.
65
66
  | `list_workflows` | List all workflows with status |
66
67
  | `get_workflow` | Get detailed workflow information |
67
68
  | `get_workflow_history` | Get the workflow's configuration revision history (audit log) — who changed it, when, from which channel, and a `changedFields` summary per revision |
69
+ | `list_workflow_runs` | List a workflow's execution run history (status, start/finish, record count, errors); filter by outcome and paginate |
68
70
  | `run_workflow` | Execute a workflow |
69
71
  | `fetch_data` | Get extracted data from a workflow |
70
72
  | `delete_workflow` | Delete a workflow |
package/dist/index.js CHANGED
@@ -48467,7 +48467,7 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
48467
48467
  options: localVarRequestOptions
48468
48468
  };
48469
48469
  },
48470
- v4WorkflowsWorkflowIdHistoryGet: async (workflowId, options = {}) => {
48470
+ v4WorkflowsWorkflowIdHistoryGet: async (workflowId, page, limit, status, options = {}) => {
48471
48471
  assertParamExists("v4WorkflowsWorkflowIdHistoryGet", "workflowId", workflowId);
48472
48472
  const localVarPath = `/v4/workflows/{workflowId}/history`.replace(`{${"workflowId"}}`, encodeURIComponent(String(workflowId)));
48473
48473
  const localVarUrlObj = new URL$1(localVarPath, DUMMY_BASE_URL);
@@ -48479,6 +48479,15 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
48479
48479
  const localVarHeaderParameter = {};
48480
48480
  const localVarQueryParameter = {};
48481
48481
  await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration);
48482
+ if (page !== undefined) {
48483
+ localVarQueryParameter["page"] = page;
48484
+ }
48485
+ if (limit !== undefined) {
48486
+ localVarQueryParameter["limit"] = limit;
48487
+ }
48488
+ if (status !== undefined) {
48489
+ localVarQueryParameter["status"] = status;
48490
+ }
48482
48491
  localVarHeaderParameter["Accept"] = "application/json";
48483
48492
  setSearchParams(localVarUrlObj, localVarQueryParameter);
48484
48493
  let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
@@ -48842,8 +48851,8 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
48842
48851
  const localVarOperationServerBasePath = operationServerMap["WorkflowsApi.v4WorkflowsWorkflowIdGet"]?.[localVarOperationServerIndex]?.url;
48843
48852
  return (axios2, basePath) => createRequestFunction(localVarAxiosArgs, axios_default, BASE_PATH, configuration)(axios2, localVarOperationServerBasePath || basePath);
48844
48853
  },
48845
- async v4WorkflowsWorkflowIdHistoryGet(workflowId, options) {
48846
- const localVarAxiosArgs = await localVarAxiosParamCreator.v4WorkflowsWorkflowIdHistoryGet(workflowId, options);
48854
+ async v4WorkflowsWorkflowIdHistoryGet(workflowId, page, limit, status, options) {
48855
+ const localVarAxiosArgs = await localVarAxiosParamCreator.v4WorkflowsWorkflowIdHistoryGet(workflowId, page, limit, status, options);
48847
48856
  const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
48848
48857
  const localVarOperationServerBasePath = operationServerMap["WorkflowsApi.v4WorkflowsWorkflowIdHistoryGet"]?.[localVarOperationServerIndex]?.url;
48849
48858
  return (axios2, basePath) => createRequestFunction(localVarAxiosArgs, axios_default, BASE_PATH, configuration)(axios2, localVarOperationServerBasePath || basePath);
@@ -49308,7 +49317,7 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
49308
49317
  async executeExtraction(options) {
49309
49318
  this.validateOptions(options);
49310
49319
  const config2 = {
49311
- ...merge3(DEFAULT_OPTIONS, options)
49320
+ ...merge3(merge3({}, DEFAULT_OPTIONS), options)
49312
49321
  };
49313
49322
  const isRealTime = config2.interval === "REAL_TIME";
49314
49323
  if (isRealTime) {
@@ -49591,7 +49600,7 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
49591
49600
  assert2(startedJob.jobId, "Job ID is not set");
49592
49601
  debug4("Job started: %O", startedJob);
49593
49602
  this._jobId = startedJob.jobId;
49594
- const finishedJob = await this.workflowsCoreService.waitForJobCompletion(this._workflowId, startedJob.jobId);
49603
+ const finishedJob = await this.workflowsCoreService.waitForJobCompletion(this._workflowId, startedJob.jobId, { timeoutMs: 30 * 60 * 1000 });
49595
49604
  debug4("Job finished: %O", finishedJob);
49596
49605
  return this;
49597
49606
  }
@@ -50019,7 +50028,7 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
50019
50028
  }));
50020
50029
  return channels;
50021
50030
  }
50022
- }, PUBLIC_API_URI, WSS_API_URI, REALTIME_API_URI, SDK_VERSION = "0.35.0", SDK_NAME = "kadoa-node-sdk", SDK_LANGUAGE = "node", debug6, isDrainControlMessage = (message) => message.type === "control.draining", isRealtimeEvent = (message) => message.type !== "heartbeat" && message.type !== "control.draining", _Realtime = class _Realtime2 {
50031
+ }, PUBLIC_API_URI, WSS_API_URI, REALTIME_API_URI, SDK_VERSION = "0.36.0", SDK_NAME = "kadoa-node-sdk", SDK_LANGUAGE = "node", debug6, isDrainControlMessage = (message) => message.type === "control.draining", isRealtimeEvent = (message) => message.type !== "heartbeat" && message.type !== "control.draining", _Realtime = class _Realtime2 {
50023
50032
  constructor(config2) {
50024
50033
  this.drainingSockets = /* @__PURE__ */ new Set;
50025
50034
  this.lastHeartbeat = Date.now();
@@ -50941,6 +50950,15 @@ var import_debug, __require2, ChangeDifferenceType, KadoaErrorCode, _KadoaSdkExc
50941
50950
  });
50942
50951
  return response.data;
50943
50952
  }
50953
+ async listWorkflowRuns(id, options) {
50954
+ const response = await this.workflowsApi.v4WorkflowsWorkflowIdHistoryGet({
50955
+ workflowId: id,
50956
+ page: options?.page,
50957
+ limit: options?.limit,
50958
+ status: options?.status
50959
+ });
50960
+ return response.data;
50961
+ }
50944
50962
  async delete(id) {
50945
50963
  await this.workflowsApi.v4WorkflowsWorkflowIdDelete({
50946
50964
  workflowId: id
@@ -51703,7 +51721,7 @@ var init_dist2 = __esm(() => {
51703
51721
  return WorkflowsApiFp(this.configuration).v4WorkflowsWorkflowIdGet(requestParameters.workflowId, options).then((request) => request(this.axios, this.basePath));
51704
51722
  }
51705
51723
  v4WorkflowsWorkflowIdHistoryGet(requestParameters, options) {
51706
- return WorkflowsApiFp(this.configuration).v4WorkflowsWorkflowIdHistoryGet(requestParameters.workflowId, options).then((request) => request(this.axios, this.basePath));
51724
+ return WorkflowsApiFp(this.configuration).v4WorkflowsWorkflowIdHistoryGet(requestParameters.workflowId, requestParameters.page, requestParameters.limit, requestParameters.status, options).then((request) => request(this.axios, this.basePath));
51707
51725
  }
51708
51726
  v4WorkflowsWorkflowIdJobsJobIdGet(requestParameters, options) {
51709
51727
  return WorkflowsApiFp(this.configuration).v4WorkflowsWorkflowIdJobsJobIdGet(requestParameters.workflowId, requestParameters.jobId, options).then((request) => request(this.axios, this.basePath));
@@ -51799,7 +51817,7 @@ This is the main page content.`
51799
51817
  DEFAULT_OPTIONS = {
51800
51818
  mode: "run",
51801
51819
  pollingInterval: 5000,
51802
- maxWaitTime: 300000,
51820
+ maxWaitTime: 30 * 60 * 1000,
51803
51821
  location: { type: "auto" },
51804
51822
  bypassPreview: true,
51805
51823
  autoStart: true
@@ -52141,6 +52159,21 @@ function deriveStatusLabel(workflow) {
52141
52159
  return ds ?? "Unknown";
52142
52160
  }
52143
52161
  }
52162
+ function mapRunStatus(state) {
52163
+ switch (state) {
52164
+ case "FINISHED":
52165
+ return "success";
52166
+ case "IN_PROGRESS":
52167
+ case "VALIDATING":
52168
+ return "in_progress";
52169
+ case "FAILED":
52170
+ case "FAILED_INSUFFICIENT_FUNDS":
52171
+ case "NOT_SUPPORTED":
52172
+ return "failed";
52173
+ default:
52174
+ return "failed";
52175
+ }
52176
+ }
52144
52177
  function workflowDashboardUrl(workflowId) {
52145
52178
  return `${DASHBOARD_BASE_URL}/workflow/${workflowId}`;
52146
52179
  }
@@ -52735,6 +52768,35 @@ function registerTools(server, ctx, capabilities) {
52735
52768
  entries
52736
52769
  });
52737
52770
  }));
52771
+ server.registerTool("list_workflow_runs", {
52772
+ description: "List a workflow's execution run history — each run's status, start/finish time, " + "record count, and errors. Use to answer 'when did this last succeed?' " + "(status='success', limit=1) or 'recent success/failure pattern?'. Distinct from " + "get_workflow_history, which is the config audit log.",
52773
+ inputSchema: {
52774
+ workflowId: exports_external.string().describe("The workflow ID"),
52775
+ status: exports_external.enum(["success", "failed", "in_progress"]).optional().describe("Filter runs by outcome"),
52776
+ page: exports_external.preprocess(coerceNumber(), exports_external.number()).optional().describe("Page number, 1-based (default: 1)"),
52777
+ limit: exports_external.preprocess(coerceNumber(), exports_external.number()).optional().describe("Runs per page (default: 25)")
52778
+ },
52779
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true }
52780
+ }, withErrorHandling("list_workflow_runs", async (args) => {
52781
+ const response = await ctx.client.workflow.listWorkflowRuns(args.workflowId, {
52782
+ page: args.page,
52783
+ limit: args.limit,
52784
+ status: args.status
52785
+ });
52786
+ return jsonResult({
52787
+ workflowId: response.workflowId,
52788
+ runs: (response.workflowRuns ?? []).map((r) => ({
52789
+ id: r.id,
52790
+ status: mapRunStatus(r.state),
52791
+ startedAt: r.startedAt,
52792
+ finishedAt: r.finishedAt,
52793
+ records: r.records,
52794
+ errors: r.errors
52795
+ })),
52796
+ totalCount: response.totalCount,
52797
+ pagination: response.pagination
52798
+ });
52799
+ }));
52738
52800
  server.registerTool("run_workflow", {
52739
52801
  description: "Run a workflow to extract fresh data. The run is asynchronous and may take several minutes. Do NOT poll or sleep-wait for completion. Return the workflow ID to the user and let them check status with get_workflow or fetch results later with fetch_data.",
52740
52802
  inputSchema: {
@@ -53662,7 +53724,7 @@ var package_default;
53662
53724
  var init_package = __esm(() => {
53663
53725
  package_default = {
53664
53726
  name: "@kadoa/mcp",
53665
- version: "0.5.13",
53727
+ version: "0.5.15",
53666
53728
  description: "Kadoa MCP Server — manage workflows from Claude Desktop, Cursor, and other MCP clients",
53667
53729
  type: "module",
53668
53730
  main: "dist/index.js",
@@ -53686,7 +53748,7 @@ var init_package = __esm(() => {
53686
53748
  prepublishOnly: "bun run check-types && bun run test:unit && bun run build"
53687
53749
  },
53688
53750
  dependencies: {
53689
- "@kadoa/node-sdk": "^0.35.0",
53751
+ "@kadoa/node-sdk": "^0.36.0",
53690
53752
  "@modelcontextprotocol/sdk": "^1.26.0",
53691
53753
  express: "^5.2.1",
53692
53754
  "express-rate-limit": "^8.2.1",
@@ -57695,6 +57757,15 @@ var init_bearerAuth = __esm(() => {
57695
57757
  init_errors4();
57696
57758
  });
57697
57759
 
57760
+ // src/managed-connector-client.ts
57761
+ function isManagedConnectorClientId(clientId) {
57762
+ return MANAGED_CONNECTOR_CLIENT_ID_PREFIXES.some((prefix) => clientId.startsWith(prefix));
57763
+ }
57764
+ var MANAGED_CONNECTOR_CLIENT_ID_PREFIXES;
57765
+ var init_managed_connector_client = __esm(() => {
57766
+ MANAGED_CONNECTOR_CLIENT_ID_PREFIXES = ["kadoa_mcp_", "kadoa_claude_"];
57767
+ });
57768
+
57698
57769
  // src/managed-connector-registry.ts
57699
57770
  class ManagedConnectorRegistry {
57700
57771
  baseUrl;
@@ -57894,7 +57965,7 @@ class KadoaOAuthProvider {
57894
57965
  const thisProvider = this;
57895
57966
  return {
57896
57967
  async getClient(clientId) {
57897
- if (clientId.startsWith("kadoa_claude_")) {
57968
+ if (isManagedConnectorClientId(clientId)) {
57898
57969
  const connector = await thisProvider.getManagedRegistry().get(clientId);
57899
57970
  if (!connector)
57900
57971
  return;
@@ -58834,6 +58905,7 @@ function escapeHtml(str) {
58834
58905
  var TEAM_SELECTION_TTL, ACCESS_TOKEN_TTL;
58835
58906
  var init_auth2 = __esm(() => {
58836
58907
  init_errors4();
58908
+ init_managed_connector_client();
58837
58909
  init_managed_connector_registry();
58838
58910
  TEAM_SELECTION_TTL = 10 * 60 * 1000;
58839
58911
  ACCESS_TOKEN_TTL = 7 * 24 * 3600;
@@ -58954,7 +59026,7 @@ function managedTokenHandler(options) {
58954
59026
  return async (req, res, next) => {
58955
59027
  const body = req.body ?? {};
58956
59028
  const clientId = typeof body.client_id === "string" ? body.client_id : "";
58957
- if (!clientId.startsWith("kadoa_claude_")) {
59029
+ if (!isManagedConnectorClientId(clientId)) {
58958
59030
  next();
58959
59031
  return;
58960
59032
  }
@@ -59001,6 +59073,7 @@ function managedTokenHandler(options) {
59001
59073
  }
59002
59074
  var init_managed_token_handler = __esm(() => {
59003
59075
  init_errors4();
59076
+ init_managed_connector_client();
59004
59077
  init_managed_connector_registry();
59005
59078
  });
59006
59079
 
@@ -59064,7 +59137,7 @@ async function startHttpServer(options) {
59064
59137
  limit: 50,
59065
59138
  standardHeaders: true,
59066
59139
  legacyHeaders: false,
59067
- skip: (req) => !String(req.body?.client_id ?? "").startsWith("kadoa_claude_"),
59140
+ skip: (req) => !isManagedConnectorClientId(String(req.body?.client_id ?? "")),
59068
59141
  message: {
59069
59142
  error: "too_many_requests",
59070
59143
  error_description: "Too many managed connector token requests"
@@ -59229,6 +59302,7 @@ var init_http2 = __esm(async () => {
59229
59302
  init_auth2();
59230
59303
  init_redis_store();
59231
59304
  init_client2();
59305
+ init_managed_connector_client();
59232
59306
  init_managed_connector_registry();
59233
59307
  init_managed_token_handler();
59234
59308
  await init_src();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kadoa/mcp",
3
- "version": "0.5.13",
3
+ "version": "0.5.15",
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",
@@ -24,7 +24,7 @@
24
24
  "prepublishOnly": "bun run check-types && bun run test:unit && bun run build"
25
25
  },
26
26
  "dependencies": {
27
- "@kadoa/node-sdk": "^0.35.0",
27
+ "@kadoa/node-sdk": "^0.36.0",
28
28
  "@modelcontextprotocol/sdk": "^1.26.0",
29
29
  "express": "^5.2.1",
30
30
  "express-rate-limit": "^8.2.1",