@kadoa/mcp 0.3.6 → 0.3.7-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -114
- package/dist/index.js +27 -233
- package/package.json +2 -6
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Use [Kadoa](https://kadoa.com) from ChatGPT, Claude.ai, Claude Code, Cursor, and
|
|
|
4
4
|
|
|
5
5
|
## Remote Server (no install needed)
|
|
6
6
|
|
|
7
|
-
A hosted MCP server is available at `https://mcp.kadoa.com/mcp`. Connect from any MCP client — no local install
|
|
7
|
+
A hosted MCP server is available at `https://mcp.kadoa.com/mcp`. Connect from any MCP client — no local install needed. You sign in with your Kadoa account via OAuth.
|
|
8
8
|
|
|
9
9
|
### Claude Code
|
|
10
10
|
|
|
@@ -24,7 +24,7 @@ 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
|
-
### Cursor
|
|
27
|
+
### Cursor
|
|
28
28
|
|
|
29
29
|
Add to `.cursor/mcp.json`:
|
|
30
30
|
|
|
@@ -43,101 +43,6 @@ Add to `.cursor/mcp.json`:
|
|
|
43
43
|
|
|
44
44
|
Point your client to `https://mcp.kadoa.com/mcp` with OAuth authentication.
|
|
45
45
|
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
## Local Setup (stdio)
|
|
49
|
-
|
|
50
|
-
If you prefer to run the MCP server locally (e.g., for development or to use your own API key), install via npx:
|
|
51
|
-
|
|
52
|
-
### Claude Code
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
claude mcp add --transport stdio -e KADOA_API_KEY=tk-your_api_key kadoa -- npx -y @kadoa/mcp
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Add `-s user` to enable for all projects. If you have the [Kadoa CLI](https://www.npmjs.com/package/@kadoa/cli) installed, you can skip the `-e` flag — just run `kadoa login` and the MCP will use your saved key automatically.
|
|
59
|
-
|
|
60
|
-
### Claude Desktop
|
|
61
|
-
|
|
62
|
-
Add to `~/.config/Claude/claude_desktop_config.json`:
|
|
63
|
-
|
|
64
|
-
```json
|
|
65
|
-
{
|
|
66
|
-
"mcpServers": {
|
|
67
|
-
"kadoa": {
|
|
68
|
-
"command": "npx",
|
|
69
|
-
"args": ["-y", "@kadoa/mcp"],
|
|
70
|
-
"env": {
|
|
71
|
-
"KADOA_API_KEY": "tk-your_api_key"
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Restart Claude Desktop.
|
|
79
|
-
|
|
80
|
-
### Cursor
|
|
81
|
-
|
|
82
|
-
Add to `.cursor/mcp.json`:
|
|
83
|
-
|
|
84
|
-
```json
|
|
85
|
-
{
|
|
86
|
-
"mcpServers": {
|
|
87
|
-
"kadoa": {
|
|
88
|
-
"command": "npx",
|
|
89
|
-
"args": ["-y", "@kadoa/mcp"],
|
|
90
|
-
"env": {
|
|
91
|
-
"KADOA_API_KEY": "tk-your_api_key"
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### Codex
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
codex mcp add kadoa -- npx -y @kadoa/mcp
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
Or add to `~/.codex/config.toml`:
|
|
105
|
-
|
|
106
|
-
```toml
|
|
107
|
-
[mcp_servers.kadoa]
|
|
108
|
-
command = "npx"
|
|
109
|
-
args = ["-y", "@kadoa/mcp"]
|
|
110
|
-
|
|
111
|
-
[mcp_servers.kadoa.env]
|
|
112
|
-
KADOA_API_KEY = "tk-your_api_key"
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### Gemini CLI
|
|
116
|
-
|
|
117
|
-
```bash
|
|
118
|
-
gemini mcp add -t stdio kadoa npx -- -y @kadoa/mcp
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Or add to `~/.gemini/settings.json`:
|
|
122
|
-
|
|
123
|
-
```json
|
|
124
|
-
{
|
|
125
|
-
"mcpServers": {
|
|
126
|
-
"kadoa": {
|
|
127
|
-
"command": "npx",
|
|
128
|
-
"args": ["-y", "@kadoa/mcp"],
|
|
129
|
-
"env": {
|
|
130
|
-
"KADOA_API_KEY": "tk-your_api_key"
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
## Get Your API Key
|
|
138
|
-
|
|
139
|
-
Get your API key from [kadoa.com/settings](https://kadoa.com/settings).
|
|
140
|
-
|
|
141
46
|
## Tools
|
|
142
47
|
|
|
143
48
|
| Tool | Description |
|
|
@@ -214,14 +119,10 @@ delete_workflow for each, confirming before proceeding.
|
|
|
214
119
|
|
|
215
120
|
## Troubleshooting
|
|
216
121
|
|
|
217
|
-
**"No API key found"**
|
|
218
|
-
- Run `kadoa login` (requires `npm i -g @kadoa/cli`), or
|
|
219
|
-
- Set `KADOA_API_KEY` in your MCP config or environment
|
|
220
|
-
- API keys start with `tk-`
|
|
221
|
-
|
|
222
122
|
**Claude says "I don't have access to Kadoa"**
|
|
223
123
|
- Verify the MCP server is configured correctly
|
|
224
124
|
- Restart your MCP client
|
|
125
|
+
- Re-authenticate via OAuth if prompted
|
|
225
126
|
|
|
226
127
|
## Deploying the Remote Server
|
|
227
128
|
|
|
@@ -256,24 +157,15 @@ bun run build # Build for distribution
|
|
|
256
157
|
|
|
257
158
|
To develop and test against a local Kadoa backend (instead of the production API), point the MCP at your local `public-api` service using the `KADOA_PUBLIC_API_URI` environment variable.
|
|
258
159
|
|
|
259
|
-
**Prerequisites:** the `public-api` service must be running locally (default port `12380`).
|
|
160
|
+
**Prerequisites:** the `public-api` service must be running locally (default port `12380`).
|
|
260
161
|
|
|
261
162
|
**Run the MCP server locally:**
|
|
262
163
|
|
|
263
164
|
```bash
|
|
264
|
-
KADOA_PUBLIC_API_URI=http://localhost:12380
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
**Add as a local MCP in Claude Code** (alongside the remote one):
|
|
268
|
-
|
|
269
|
-
```bash
|
|
270
|
-
claude mcp add --transport stdio \
|
|
271
|
-
-e KADOA_PUBLIC_API_URI=http://localhost:12380 \
|
|
272
|
-
-e KADOA_API_KEY=tk-your_local_api_key \
|
|
273
|
-
kadoa-local -- bun /path/to/kadoa-mcp/src/index.ts
|
|
165
|
+
KADOA_PUBLIC_API_URI=http://localhost:12380 bun run dev
|
|
274
166
|
```
|
|
275
167
|
|
|
276
|
-
|
|
168
|
+
The server starts in HTTP mode. You authenticate via OAuth the same way as with the remote server.
|
|
277
169
|
|
|
278
170
|
## License
|
|
279
171
|
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -28800,102 +28800,6 @@ var init_mcp = __esm(() => {
|
|
|
28800
28800
|
};
|
|
28801
28801
|
});
|
|
28802
28802
|
|
|
28803
|
-
// node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
|
|
28804
|
-
class ReadBuffer {
|
|
28805
|
-
append(chunk) {
|
|
28806
|
-
this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
|
|
28807
|
-
}
|
|
28808
|
-
readMessage() {
|
|
28809
|
-
if (!this._buffer) {
|
|
28810
|
-
return null;
|
|
28811
|
-
}
|
|
28812
|
-
const index = this._buffer.indexOf(`
|
|
28813
|
-
`);
|
|
28814
|
-
if (index === -1) {
|
|
28815
|
-
return null;
|
|
28816
|
-
}
|
|
28817
|
-
const line = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
|
|
28818
|
-
this._buffer = this._buffer.subarray(index + 1);
|
|
28819
|
-
return deserializeMessage(line);
|
|
28820
|
-
}
|
|
28821
|
-
clear() {
|
|
28822
|
-
this._buffer = undefined;
|
|
28823
|
-
}
|
|
28824
|
-
}
|
|
28825
|
-
function deserializeMessage(line) {
|
|
28826
|
-
return JSONRPCMessageSchema.parse(JSON.parse(line));
|
|
28827
|
-
}
|
|
28828
|
-
function serializeMessage(message) {
|
|
28829
|
-
return JSON.stringify(message) + `
|
|
28830
|
-
`;
|
|
28831
|
-
}
|
|
28832
|
-
var init_stdio = __esm(() => {
|
|
28833
|
-
init_types2();
|
|
28834
|
-
});
|
|
28835
|
-
|
|
28836
|
-
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
28837
|
-
import process3 from "node:process";
|
|
28838
|
-
|
|
28839
|
-
class StdioServerTransport {
|
|
28840
|
-
constructor(_stdin = process3.stdin, _stdout = process3.stdout) {
|
|
28841
|
-
this._stdin = _stdin;
|
|
28842
|
-
this._stdout = _stdout;
|
|
28843
|
-
this._readBuffer = new ReadBuffer;
|
|
28844
|
-
this._started = false;
|
|
28845
|
-
this._ondata = (chunk) => {
|
|
28846
|
-
this._readBuffer.append(chunk);
|
|
28847
|
-
this.processReadBuffer();
|
|
28848
|
-
};
|
|
28849
|
-
this._onerror = (error48) => {
|
|
28850
|
-
this.onerror?.(error48);
|
|
28851
|
-
};
|
|
28852
|
-
}
|
|
28853
|
-
async start() {
|
|
28854
|
-
if (this._started) {
|
|
28855
|
-
throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
|
|
28856
|
-
}
|
|
28857
|
-
this._started = true;
|
|
28858
|
-
this._stdin.on("data", this._ondata);
|
|
28859
|
-
this._stdin.on("error", this._onerror);
|
|
28860
|
-
}
|
|
28861
|
-
processReadBuffer() {
|
|
28862
|
-
while (true) {
|
|
28863
|
-
try {
|
|
28864
|
-
const message = this._readBuffer.readMessage();
|
|
28865
|
-
if (message === null) {
|
|
28866
|
-
break;
|
|
28867
|
-
}
|
|
28868
|
-
this.onmessage?.(message);
|
|
28869
|
-
} catch (error48) {
|
|
28870
|
-
this.onerror?.(error48);
|
|
28871
|
-
}
|
|
28872
|
-
}
|
|
28873
|
-
}
|
|
28874
|
-
async close() {
|
|
28875
|
-
this._stdin.off("data", this._ondata);
|
|
28876
|
-
this._stdin.off("error", this._onerror);
|
|
28877
|
-
const remainingDataListeners = this._stdin.listenerCount("data");
|
|
28878
|
-
if (remainingDataListeners === 0) {
|
|
28879
|
-
this._stdin.pause();
|
|
28880
|
-
}
|
|
28881
|
-
this._readBuffer.clear();
|
|
28882
|
-
this.onclose?.();
|
|
28883
|
-
}
|
|
28884
|
-
send(message) {
|
|
28885
|
-
return new Promise((resolve) => {
|
|
28886
|
-
const json2 = serializeMessage(message);
|
|
28887
|
-
if (this._stdout.write(json2)) {
|
|
28888
|
-
resolve();
|
|
28889
|
-
} else {
|
|
28890
|
-
this._stdout.once("drain", resolve);
|
|
28891
|
-
}
|
|
28892
|
-
});
|
|
28893
|
-
}
|
|
28894
|
-
}
|
|
28895
|
-
var init_stdio2 = __esm(() => {
|
|
28896
|
-
init_stdio();
|
|
28897
|
-
});
|
|
28898
|
-
|
|
28899
28803
|
// node_modules/axios/lib/helpers/bind.js
|
|
28900
28804
|
function bind(fn, thisArg) {
|
|
28901
28805
|
return function wrap() {
|
|
@@ -49043,41 +48947,13 @@ var init_dist2 = __esm(() => {
|
|
|
49043
48947
|
});
|
|
49044
48948
|
|
|
49045
48949
|
// src/client.ts
|
|
49046
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
49047
|
-
import { dirname, join } from "node:path";
|
|
49048
|
-
import { homedir } from "node:os";
|
|
49049
|
-
function getConfigPath() {
|
|
49050
|
-
const home = process.env.HOME || homedir();
|
|
49051
|
-
return join(home, ".kadoa", "config.json");
|
|
49052
|
-
}
|
|
49053
|
-
function loadConfig() {
|
|
49054
|
-
const configFile = getConfigPath();
|
|
49055
|
-
if (!existsSync(configFile))
|
|
49056
|
-
return {};
|
|
49057
|
-
try {
|
|
49058
|
-
return JSON.parse(readFileSync(configFile, "utf-8"));
|
|
49059
|
-
} catch {
|
|
49060
|
-
return {};
|
|
49061
|
-
}
|
|
49062
|
-
}
|
|
49063
|
-
function loadApiKeyFromConfig() {
|
|
49064
|
-
return loadConfig().apiKey;
|
|
49065
|
-
}
|
|
49066
|
-
function resolveApiKey(apiKey) {
|
|
49067
|
-
const key = apiKey || process.env.KADOA_API_KEY || loadApiKeyFromConfig();
|
|
49068
|
-
if (!key) {
|
|
49069
|
-
throw new Error("No API key found. Set KADOA_API_KEY env var or run 'kadoa login' (npm i -g @kadoa/cli).");
|
|
49070
|
-
}
|
|
49071
|
-
return key;
|
|
49072
|
-
}
|
|
49073
48950
|
function createKadoaClient(auth) {
|
|
49074
|
-
|
|
49075
|
-
|
|
49076
|
-
|
|
49077
|
-
|
|
49078
|
-
|
|
49079
|
-
|
|
49080
|
-
return new KadoaClient({ apiKey: resolveApiKey(auth) });
|
|
48951
|
+
const client = new KadoaClient({ bearerToken: auth.jwt });
|
|
48952
|
+
client.axiosInstance.interceptors.request.use((config2) => {
|
|
48953
|
+
config2.headers["x-kadoa-source"] = "mcp";
|
|
48954
|
+
return config2;
|
|
48955
|
+
});
|
|
48956
|
+
return client;
|
|
49081
48957
|
}
|
|
49082
48958
|
var ctxRefreshMutex;
|
|
49083
48959
|
var init_client = __esm(() => {
|
|
@@ -49086,28 +48962,6 @@ var init_client = __esm(() => {
|
|
|
49086
48962
|
});
|
|
49087
48963
|
|
|
49088
48964
|
// src/client.ts
|
|
49089
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
49090
|
-
import { dirname as dirname2, join as join2 } from "node:path";
|
|
49091
|
-
import { homedir as homedir2 } from "node:os";
|
|
49092
|
-
function getConfigPath2() {
|
|
49093
|
-
const home = process.env.HOME || homedir2();
|
|
49094
|
-
return join2(home, ".kadoa", "config.json");
|
|
49095
|
-
}
|
|
49096
|
-
function loadConfig2() {
|
|
49097
|
-
const configFile = getConfigPath2();
|
|
49098
|
-
if (!existsSync2(configFile))
|
|
49099
|
-
return {};
|
|
49100
|
-
try {
|
|
49101
|
-
return JSON.parse(readFileSync2(configFile, "utf-8"));
|
|
49102
|
-
} catch {
|
|
49103
|
-
return {};
|
|
49104
|
-
}
|
|
49105
|
-
}
|
|
49106
|
-
function saveConfig(config2) {
|
|
49107
|
-
const configFile = getConfigPath2();
|
|
49108
|
-
mkdirSync2(dirname2(configFile), { recursive: true });
|
|
49109
|
-
writeFileSync2(configFile, JSON.stringify(config2, null, 2), "utf-8");
|
|
49110
|
-
}
|
|
49111
48965
|
function isJwtExpired(jwt2) {
|
|
49112
48966
|
try {
|
|
49113
48967
|
const payload = JSON.parse(Buffer.from(jwt2.split(".")[1], "base64url").toString());
|
|
@@ -49230,7 +49084,7 @@ function classifyError(error48) {
|
|
|
49230
49084
|
if (httpError.httpStatus === 403) {
|
|
49231
49085
|
return `Access denied${status}. Your current team role may not have permission for this action. Use the whoami tool to check your role, or contact your team admin to request elevated access.`;
|
|
49232
49086
|
}
|
|
49233
|
-
return `Authentication failed${status}.
|
|
49087
|
+
return `Authentication failed${status}. Please re-authenticate via OAuth.`;
|
|
49234
49088
|
case "NOT_FOUND":
|
|
49235
49089
|
return `Not found${status}. The workflow may have been deleted or the ID is incorrect.`;
|
|
49236
49090
|
case "RATE_LIMITED":
|
|
@@ -49248,7 +49102,7 @@ function classifyError(error48) {
|
|
|
49248
49102
|
}
|
|
49249
49103
|
switch (code) {
|
|
49250
49104
|
case "AUTH_ERROR":
|
|
49251
|
-
return "Authentication failed.
|
|
49105
|
+
return "Authentication failed. Please re-authenticate via OAuth.";
|
|
49252
49106
|
case "NOT_FOUND":
|
|
49253
49107
|
return "Not found. The workflow may have been deleted or the ID is incorrect.";
|
|
49254
49108
|
case "RATE_LIMITED":
|
|
@@ -49281,8 +49135,7 @@ function registerTools(server, ctx) {
|
|
|
49281
49135
|
try {
|
|
49282
49136
|
const jwt2 = ctx.supabaseJwt;
|
|
49283
49137
|
const teams = await ctx.client.listTeams(jwt2 ? { bearerToken: jwt2 } : undefined);
|
|
49284
|
-
const
|
|
49285
|
-
const activeTeamId = ctx.teamId ?? config2.teamId ?? teams[0]?.id;
|
|
49138
|
+
const activeTeamId = ctx.teamId ?? teams[0]?.id;
|
|
49286
49139
|
const activeTeam = teams.find((t) => t.id === activeTeamId);
|
|
49287
49140
|
if (activeTeam?.adminEmail) {
|
|
49288
49141
|
message += ` Your team admin is ${activeTeam.adminEmail}.`;
|
|
@@ -49301,13 +49154,11 @@ function registerTools(server, ctx) {
|
|
|
49301
49154
|
}, withErrorHandling("whoami", async () => {
|
|
49302
49155
|
const jwt2 = await getValidJwt(ctx);
|
|
49303
49156
|
const user = await ctx.client.user.getCurrentUser();
|
|
49304
|
-
const authMethod = jwt2 ? "OAuth (JWT)" : "API Key";
|
|
49305
49157
|
const teams = await ctx.client.listTeams(jwt2 ? { bearerToken: jwt2 } : undefined);
|
|
49306
|
-
const
|
|
49307
|
-
const activeTeamId = ctx.teamId ?? config2.teamId ?? teams[0]?.id;
|
|
49158
|
+
const activeTeamId = ctx.teamId ?? teams[0]?.id;
|
|
49308
49159
|
return jsonResult({
|
|
49309
49160
|
email: user.email,
|
|
49310
|
-
authMethod,
|
|
49161
|
+
authMethod: "OAuth",
|
|
49311
49162
|
teams: teams.map((t) => ({
|
|
49312
49163
|
name: t.name,
|
|
49313
49164
|
memberRole: t.memberRole,
|
|
@@ -49951,8 +49802,7 @@ function registerTools(server, ctx) {
|
|
|
49951
49802
|
}, withErrorHandling("team_list", async () => {
|
|
49952
49803
|
const jwt2 = await getValidJwt(ctx);
|
|
49953
49804
|
const teams = await ctx.client.listTeams(jwt2 ? { bearerToken: jwt2 } : undefined);
|
|
49954
|
-
const
|
|
49955
|
-
const activeTeamId = ctx.teamId ?? config2.teamId ?? teams[0]?.id;
|
|
49805
|
+
const activeTeamId = ctx.teamId ?? teams[0]?.id;
|
|
49956
49806
|
return jsonResult({
|
|
49957
49807
|
teams: teams.map((t) => ({
|
|
49958
49808
|
id: t.id,
|
|
@@ -49972,9 +49822,6 @@ function registerTools(server, ctx) {
|
|
|
49972
49822
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true }
|
|
49973
49823
|
}, withErrorHandling("team_switch", async (args) => {
|
|
49974
49824
|
const jwt2 = await getValidJwt(ctx);
|
|
49975
|
-
if (!jwt2) {
|
|
49976
|
-
return errorResult("Team switching requires OAuth authentication (HTTP mode). " + "In stdio mode, re-run with a different KADOA_API_KEY or use 'kadoa login' to authenticate via the CLI.");
|
|
49977
|
-
}
|
|
49978
49825
|
const teams = await ctx.client.listTeams({ bearerToken: jwt2 });
|
|
49979
49826
|
const identifier = args.teamIdentifier;
|
|
49980
49827
|
const match = teams.find((t) => t.id === identifier || t.name.toLowerCase() === identifier.toLowerCase());
|
|
@@ -49987,10 +49834,6 @@ function registerTools(server, ctx) {
|
|
|
49987
49834
|
return errorResult("Failed to refresh session after team switch");
|
|
49988
49835
|
}
|
|
49989
49836
|
ctx.teamId = match.id;
|
|
49990
|
-
const config2 = loadConfig2();
|
|
49991
|
-
config2.teamId = match.id;
|
|
49992
|
-
config2.teamName = match.name;
|
|
49993
|
-
saveConfig(config2);
|
|
49994
49837
|
return jsonResult({
|
|
49995
49838
|
success: true,
|
|
49996
49839
|
teamId: match.id,
|
|
@@ -54383,15 +54226,6 @@ class KadoaOAuthProvider {
|
|
|
54383
54226
|
};
|
|
54384
54227
|
}
|
|
54385
54228
|
async verifyAccessToken(token) {
|
|
54386
|
-
if (token.startsWith("tk-")) {
|
|
54387
|
-
return {
|
|
54388
|
-
token,
|
|
54389
|
-
clientId: "direct-api-key",
|
|
54390
|
-
scopes: [],
|
|
54391
|
-
expiresAt: Math.floor(Date.now() / 1000) + 3600,
|
|
54392
|
-
extra: { apiKey: token }
|
|
54393
|
-
};
|
|
54394
|
-
}
|
|
54395
54229
|
const entry = await this.store.get("access_tokens", token);
|
|
54396
54230
|
if (!entry) {
|
|
54397
54231
|
const sessionCount = await this.store.size("access_tokens");
|
|
@@ -55043,9 +54877,6 @@ function resolveAuth(req) {
|
|
|
55043
54877
|
console.error("[AUTH_RESOLVE] FAIL: req.auth.extra is missing");
|
|
55044
54878
|
return;
|
|
55045
54879
|
}
|
|
55046
|
-
if (typeof extra.apiKey === "string" && extra.apiKey.startsWith("tk-")) {
|
|
55047
|
-
return { kind: "apiKey", apiKey: extra.apiKey };
|
|
55048
|
-
}
|
|
55049
54880
|
if (typeof extra.supabaseJwt === "string") {
|
|
55050
54881
|
const claims = jwtClaims2(extra.supabaseJwt);
|
|
55051
54882
|
const userId = claims.sub;
|
|
@@ -55063,21 +54894,20 @@ function resolveAuth(req) {
|
|
|
55063
54894
|
}
|
|
55064
54895
|
}
|
|
55065
54896
|
return {
|
|
55066
|
-
kind: "jwt",
|
|
55067
54897
|
jwt: extra.supabaseJwt,
|
|
55068
54898
|
refreshToken: extra.supabaseRefreshToken ?? "",
|
|
55069
54899
|
teamId: extra.teamId ?? "",
|
|
55070
54900
|
userId
|
|
55071
54901
|
};
|
|
55072
54902
|
}
|
|
55073
|
-
console.error(`[AUTH_RESOLVE] FAIL: no
|
|
54903
|
+
console.error(`[AUTH_RESOLVE] FAIL: no supabaseJwt in extra (keys: ${Object.keys(extra).join(", ")})`);
|
|
55074
54904
|
return;
|
|
55075
54905
|
}
|
|
55076
|
-
async function startHttpServer() {
|
|
54906
|
+
async function startHttpServer(options) {
|
|
55077
54907
|
const port = parseInt(process.env.PORT || "3000", 10);
|
|
55078
54908
|
const app = createMcpExpressApp({ host: "0.0.0.0" });
|
|
55079
54909
|
app.set("trust proxy", 1);
|
|
55080
|
-
const store = new RedisTokenStore;
|
|
54910
|
+
const store = options?.store ?? new RedisTokenStore;
|
|
55081
54911
|
const provider = new KadoaOAuthProvider(store);
|
|
55082
54912
|
const serverUrl = process.env.MCP_SERVER_URL || `http://localhost:${port}`;
|
|
55083
54913
|
app.use(mcpAuthRouter({
|
|
@@ -55120,17 +54950,17 @@ async function startHttpServer() {
|
|
|
55120
54950
|
});
|
|
55121
54951
|
return;
|
|
55122
54952
|
}
|
|
55123
|
-
const identity =
|
|
54953
|
+
const identity = `jwt:${auth.userId.slice(0, 8)}...:team=${auth.teamId.slice(0, 8)}...`;
|
|
55124
54954
|
try {
|
|
55125
54955
|
console.error(`[MCP] POST method=${method} auth=${identity}`);
|
|
55126
54956
|
const transport = new StreamableHTTPServerTransport({
|
|
55127
54957
|
sessionIdGenerator: undefined
|
|
55128
54958
|
});
|
|
55129
|
-
const server =
|
|
54959
|
+
const server = createServer({
|
|
55130
54960
|
jwt: auth.jwt,
|
|
55131
54961
|
refreshToken: auth.refreshToken,
|
|
55132
54962
|
teamId: auth.teamId
|
|
55133
|
-
})
|
|
54963
|
+
});
|
|
55134
54964
|
await server.connect(transport);
|
|
55135
54965
|
await transport.handleRequest(req, res, req.body);
|
|
55136
54966
|
} catch (error48) {
|
|
@@ -55183,60 +55013,24 @@ var init_http2 = __esm(async () => {
|
|
|
55183
55013
|
|
|
55184
55014
|
// src/index.ts
|
|
55185
55015
|
function createServer(auth) {
|
|
55186
|
-
|
|
55187
|
-
|
|
55188
|
-
|
|
55189
|
-
|
|
55190
|
-
|
|
55191
|
-
|
|
55192
|
-
teamId: auth.teamId
|
|
55193
|
-
};
|
|
55194
|
-
} else if (typeof auth === "object" && auth !== null && "apiKey" in auth) {
|
|
55195
|
-
ctx = {
|
|
55196
|
-
client: createKadoaClient({ apiKey: auth.apiKey })
|
|
55197
|
-
};
|
|
55198
|
-
} else {
|
|
55199
|
-
ctx = {
|
|
55200
|
-
client: createKadoaClient(auth)
|
|
55201
|
-
};
|
|
55202
|
-
}
|
|
55016
|
+
const ctx = {
|
|
55017
|
+
client: createKadoaClient({ jwt: auth.jwt }),
|
|
55018
|
+
supabaseJwt: auth.jwt,
|
|
55019
|
+
supabaseRefreshToken: auth.refreshToken,
|
|
55020
|
+
teamId: auth.teamId
|
|
55021
|
+
};
|
|
55203
55022
|
const server = new McpServer({ name: "kadoa", version: "0.3.2" });
|
|
55204
55023
|
registerTools(server, ctx);
|
|
55205
55024
|
server.server.onerror = (error48) => console.error("[MCP Error]", error48);
|
|
55206
55025
|
return server;
|
|
55207
55026
|
}
|
|
55208
|
-
async function validateApiKey() {
|
|
55209
|
-
const client = createKadoaClient();
|
|
55210
|
-
try {
|
|
55211
|
-
await client.workflow.list({ limit: 1 });
|
|
55212
|
-
} catch (error48) {
|
|
55213
|
-
if (KadoaSdkException.isInstance(error48) && error48.code === "AUTH_ERROR") {
|
|
55214
|
-
console.error("Kadoa MCP: Invalid API key. Check KADOA_API_KEY or run 'kadoa login'.");
|
|
55215
|
-
process.exit(1);
|
|
55216
|
-
}
|
|
55217
|
-
}
|
|
55218
|
-
}
|
|
55219
55027
|
var init_src = __esm(async () => {
|
|
55220
55028
|
init_mcp();
|
|
55221
|
-
init_stdio2();
|
|
55222
55029
|
init_client();
|
|
55223
55030
|
init_tools();
|
|
55224
55031
|
if (!process.env.VITEST && !process.env.BUN_TEST) {
|
|
55225
|
-
const
|
|
55226
|
-
|
|
55227
|
-
const { startHttpServer: startHttpServer2 } = await init_http2().then(() => exports_http);
|
|
55228
|
-
await startHttpServer2();
|
|
55229
|
-
} else {
|
|
55230
|
-
await validateApiKey();
|
|
55231
|
-
const server = createServer();
|
|
55232
|
-
const transport = new StdioServerTransport;
|
|
55233
|
-
await server.connect(transport);
|
|
55234
|
-
console.error("Kadoa MCP Server started");
|
|
55235
|
-
process.on("SIGINT", async () => {
|
|
55236
|
-
await server.close();
|
|
55237
|
-
process.exit(0);
|
|
55238
|
-
});
|
|
55239
|
-
}
|
|
55032
|
+
const { startHttpServer: startHttpServer2 } = await init_http2().then(() => exports_http);
|
|
55033
|
+
await startHttpServer2();
|
|
55240
55034
|
}
|
|
55241
55035
|
});
|
|
55242
55036
|
await init_src();
|
package/package.json
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kadoa/mcp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7-rc.2",
|
|
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",
|
|
7
|
-
"bin": {
|
|
8
|
-
"kadoa-mcp": "dist/index.js"
|
|
9
|
-
},
|
|
10
7
|
"publishConfig": {
|
|
11
8
|
"access": "public"
|
|
12
9
|
},
|
|
@@ -18,8 +15,7 @@
|
|
|
18
15
|
"lint": "bunx biome check",
|
|
19
16
|
"lint:fix": "bunx biome check --write",
|
|
20
17
|
"dev": "bun src/index.ts",
|
|
21
|
-
"
|
|
22
|
-
"build": "bun build src/index.ts --outdir=dist --target=node --external express --external ioredis && node -e \"const f='dist/index.js';require('fs').writeFileSync(f,require('fs').readFileSync(f,'utf8').replace('#!/usr/bin/env bun','#!/usr/bin/env node'))\"",
|
|
18
|
+
"build": "bun build src/index.ts --outdir=dist --target=node --external express --external ioredis",
|
|
23
19
|
"check-types": "tsc --noEmit",
|
|
24
20
|
"test": "BUN_TEST=1 bun test",
|
|
25
21
|
"test:unit": "BUN_TEST=1 bun test tests/unit --timeout=120000",
|