@kadoa/mcp 0.3.7 → 0.3.8
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 +23 -239
- 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,50 +48947,12 @@ 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
|
-
let teamId;
|
|
49076
|
-
if (typeof auth === "object" && auth !== null) {
|
|
49077
|
-
if ("jwt" in auth) {
|
|
49078
|
-
client = new KadoaClient({ bearerToken: auth.jwt });
|
|
49079
|
-
teamId = auth.teamId;
|
|
49080
|
-
} else {
|
|
49081
|
-
client = new KadoaClient({ apiKey: auth.apiKey });
|
|
49082
|
-
}
|
|
49083
|
-
} else {
|
|
49084
|
-
client = new KadoaClient({ apiKey: resolveApiKey(auth) });
|
|
49085
|
-
}
|
|
48951
|
+
const client = new KadoaClient({ bearerToken: auth.jwt });
|
|
49086
48952
|
client.axiosInstance.interceptors.request.use((config2) => {
|
|
49087
48953
|
config2.headers["x-kadoa-source"] = "mcp";
|
|
49088
|
-
if (teamId) {
|
|
49089
|
-
config2.headers["x-team-id"] = teamId;
|
|
48954
|
+
if (auth.teamId) {
|
|
48955
|
+
config2.headers["x-team-id"] = auth.teamId;
|
|
49090
48956
|
}
|
|
49091
48957
|
return config2;
|
|
49092
48958
|
});
|
|
@@ -49099,28 +48965,6 @@ var init_client = __esm(() => {
|
|
|
49099
48965
|
});
|
|
49100
48966
|
|
|
49101
48967
|
// src/client.ts
|
|
49102
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
49103
|
-
import { dirname as dirname2, join as join2 } from "node:path";
|
|
49104
|
-
import { homedir as homedir2 } from "node:os";
|
|
49105
|
-
function getConfigPath2() {
|
|
49106
|
-
const home = process.env.HOME || homedir2();
|
|
49107
|
-
return join2(home, ".kadoa", "config.json");
|
|
49108
|
-
}
|
|
49109
|
-
function loadConfig2() {
|
|
49110
|
-
const configFile = getConfigPath2();
|
|
49111
|
-
if (!existsSync2(configFile))
|
|
49112
|
-
return {};
|
|
49113
|
-
try {
|
|
49114
|
-
return JSON.parse(readFileSync2(configFile, "utf-8"));
|
|
49115
|
-
} catch {
|
|
49116
|
-
return {};
|
|
49117
|
-
}
|
|
49118
|
-
}
|
|
49119
|
-
function saveConfig(config2) {
|
|
49120
|
-
const configFile = getConfigPath2();
|
|
49121
|
-
mkdirSync2(dirname2(configFile), { recursive: true });
|
|
49122
|
-
writeFileSync2(configFile, JSON.stringify(config2, null, 2), "utf-8");
|
|
49123
|
-
}
|
|
49124
48968
|
function decodeJwtClaims(jwt2) {
|
|
49125
48969
|
try {
|
|
49126
48970
|
const payload = JSON.parse(Buffer.from(jwt2.split(".")[1], "base64url").toString());
|
|
@@ -49265,7 +49109,7 @@ function classifyError(error48) {
|
|
|
49265
49109
|
if (httpError.httpStatus === 403) {
|
|
49266
49110
|
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.`;
|
|
49267
49111
|
}
|
|
49268
|
-
return `Authentication failed${status}.
|
|
49112
|
+
return `Authentication failed${status}. Please re-authenticate via OAuth.`;
|
|
49269
49113
|
case "NOT_FOUND":
|
|
49270
49114
|
return `Not found${status}. The workflow may have been deleted or the ID is incorrect.`;
|
|
49271
49115
|
case "RATE_LIMITED":
|
|
@@ -49283,7 +49127,7 @@ function classifyError(error48) {
|
|
|
49283
49127
|
}
|
|
49284
49128
|
switch (code) {
|
|
49285
49129
|
case "AUTH_ERROR":
|
|
49286
|
-
return "Authentication failed.
|
|
49130
|
+
return "Authentication failed. Please re-authenticate via OAuth.";
|
|
49287
49131
|
case "NOT_FOUND":
|
|
49288
49132
|
return "Not found. The workflow may have been deleted or the ID is incorrect.";
|
|
49289
49133
|
case "RATE_LIMITED":
|
|
@@ -49329,8 +49173,7 @@ function registerTools(server, ctx) {
|
|
|
49329
49173
|
try {
|
|
49330
49174
|
const jwt2 = ctx.supabaseJwt;
|
|
49331
49175
|
const teams = await ctx.client.listTeams(jwt2 ? { bearerToken: jwt2 } : undefined);
|
|
49332
|
-
const
|
|
49333
|
-
const activeTeamId = ctx.teamId ?? config2.teamId ?? teams[0]?.id;
|
|
49176
|
+
const activeTeamId = ctx.teamId ?? teams[0]?.id;
|
|
49334
49177
|
const activeTeam = teams.find((t) => t.id === activeTeamId);
|
|
49335
49178
|
if (activeTeam?.adminEmail) {
|
|
49336
49179
|
message += ` Your team admin is ${activeTeam.adminEmail}.`;
|
|
@@ -49349,13 +49192,11 @@ function registerTools(server, ctx) {
|
|
|
49349
49192
|
}, withErrorHandling("whoami", async () => {
|
|
49350
49193
|
const jwt2 = await getValidJwt(ctx);
|
|
49351
49194
|
const user = await ctx.client.user.getCurrentUser();
|
|
49352
|
-
const authMethod = jwt2 ? "OAuth (JWT)" : "API Key";
|
|
49353
49195
|
const teams = await ctx.client.listTeams(jwt2 ? { bearerToken: jwt2 } : undefined);
|
|
49354
|
-
const
|
|
49355
|
-
const activeTeamId = ctx.teamId ?? config2.teamId ?? teams[0]?.id;
|
|
49196
|
+
const activeTeamId = ctx.teamId ?? teams[0]?.id;
|
|
49356
49197
|
return jsonResult({
|
|
49357
49198
|
email: user.email,
|
|
49358
|
-
authMethod,
|
|
49199
|
+
authMethod: "OAuth",
|
|
49359
49200
|
teams: teams.map((t) => ({
|
|
49360
49201
|
name: t.name,
|
|
49361
49202
|
memberRole: t.memberRole,
|
|
@@ -49999,8 +49840,7 @@ function registerTools(server, ctx) {
|
|
|
49999
49840
|
}, withErrorHandling("team_list", async () => {
|
|
50000
49841
|
const jwt2 = await getValidJwt(ctx);
|
|
50001
49842
|
const teams = await ctx.client.listTeams(jwt2 ? { bearerToken: jwt2 } : undefined);
|
|
50002
|
-
const
|
|
50003
|
-
const activeTeamId = ctx.teamId ?? config2.teamId ?? teams[0]?.id;
|
|
49843
|
+
const activeTeamId = ctx.teamId ?? teams[0]?.id;
|
|
50004
49844
|
return jsonResult({
|
|
50005
49845
|
teams: teams.map((t) => ({
|
|
50006
49846
|
id: t.id,
|
|
@@ -50020,9 +49860,6 @@ function registerTools(server, ctx) {
|
|
|
50020
49860
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true }
|
|
50021
49861
|
}, withErrorHandling("team_switch", async (args) => {
|
|
50022
49862
|
const jwt2 = await getValidJwt(ctx);
|
|
50023
|
-
if (!jwt2) {
|
|
50024
|
-
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.");
|
|
50025
|
-
}
|
|
50026
49863
|
const teams = await ctx.client.listTeams({ bearerToken: jwt2 });
|
|
50027
49864
|
const identifier = args.teamIdentifier;
|
|
50028
49865
|
const match = teams.find((t) => t.id === identifier || t.name.toLowerCase() === identifier.toLowerCase());
|
|
@@ -50036,10 +49873,6 @@ function registerTools(server, ctx) {
|
|
|
50036
49873
|
} catch (e) {
|
|
50037
49874
|
console.error("[TEAM_SWITCH] WARN: persist failed:", e);
|
|
50038
49875
|
}
|
|
50039
|
-
const config2 = loadConfig2();
|
|
50040
|
-
config2.teamId = match.id;
|
|
50041
|
-
config2.teamName = match.name;
|
|
50042
|
-
saveConfig(config2);
|
|
50043
49876
|
return jsonResult({
|
|
50044
49877
|
success: true,
|
|
50045
49878
|
teamId: match.id,
|
|
@@ -54438,15 +54271,6 @@ class KadoaOAuthProvider {
|
|
|
54438
54271
|
};
|
|
54439
54272
|
}
|
|
54440
54273
|
async verifyAccessToken(token) {
|
|
54441
|
-
if (token.startsWith("tk-")) {
|
|
54442
|
-
return {
|
|
54443
|
-
token,
|
|
54444
|
-
clientId: "direct-api-key",
|
|
54445
|
-
scopes: [],
|
|
54446
|
-
expiresAt: Math.floor(Date.now() / 1000) + 3600,
|
|
54447
|
-
extra: { apiKey: token }
|
|
54448
|
-
};
|
|
54449
|
-
}
|
|
54450
54274
|
const entry = await this.store.get("access_tokens", token);
|
|
54451
54275
|
if (!entry) {
|
|
54452
54276
|
const sessionCount = await this.store.size("access_tokens");
|
|
@@ -55098,9 +54922,6 @@ function resolveAuth(req) {
|
|
|
55098
54922
|
console.error("[AUTH_RESOLVE] FAIL: req.auth.extra is missing");
|
|
55099
54923
|
return;
|
|
55100
54924
|
}
|
|
55101
|
-
if (typeof extra.apiKey === "string" && extra.apiKey.startsWith("tk-")) {
|
|
55102
|
-
return { kind: "apiKey", apiKey: extra.apiKey };
|
|
55103
|
-
}
|
|
55104
54925
|
if (typeof extra.supabaseJwt === "string") {
|
|
55105
54926
|
const claims = jwtClaims2(extra.supabaseJwt);
|
|
55106
54927
|
const userId = claims.sub;
|
|
@@ -55118,7 +54939,6 @@ function resolveAuth(req) {
|
|
|
55118
54939
|
}
|
|
55119
54940
|
}
|
|
55120
54941
|
return {
|
|
55121
|
-
kind: "jwt",
|
|
55122
54942
|
jwt: extra.supabaseJwt,
|
|
55123
54943
|
refreshToken: extra.supabaseRefreshToken ?? "",
|
|
55124
54944
|
teamId: extra.teamId ?? "",
|
|
@@ -55126,7 +54946,7 @@ function resolveAuth(req) {
|
|
|
55126
54946
|
mcpToken: req.auth.token
|
|
55127
54947
|
};
|
|
55128
54948
|
}
|
|
55129
|
-
console.error(`[AUTH_RESOLVE] FAIL: no
|
|
54949
|
+
console.error(`[AUTH_RESOLVE] FAIL: no supabaseJwt in extra (keys: ${Object.keys(extra).join(", ")})`);
|
|
55130
54950
|
return;
|
|
55131
54951
|
}
|
|
55132
54952
|
async function startHttpServer(options) {
|
|
@@ -55176,13 +54996,13 @@ async function startHttpServer(options) {
|
|
|
55176
54996
|
});
|
|
55177
54997
|
return;
|
|
55178
54998
|
}
|
|
55179
|
-
const identity =
|
|
54999
|
+
const identity = `jwt:${auth.userId.slice(0, 8)}...:team=${auth.teamId.slice(0, 8)}...`;
|
|
55180
55000
|
try {
|
|
55181
55001
|
console.error(`[MCP] POST method=${method} auth=${identity}`);
|
|
55182
55002
|
const transport = new StreamableHTTPServerTransport({
|
|
55183
55003
|
sessionIdGenerator: undefined
|
|
55184
55004
|
});
|
|
55185
|
-
const server =
|
|
55005
|
+
const server = createServer({
|
|
55186
55006
|
jwt: auth.jwt,
|
|
55187
55007
|
refreshToken: auth.refreshToken,
|
|
55188
55008
|
teamId: auth.teamId,
|
|
@@ -55206,7 +55026,7 @@ async function startHttpServer(options) {
|
|
|
55206
55026
|
}, ttlSeconds);
|
|
55207
55027
|
console.error(`[PERSIST] OK: updated access token in store (token=${auth.mcpToken.slice(0, 12)}..., team=${state.teamId ?? "unchanged"}, ttl=${ttlSeconds}s)`);
|
|
55208
55028
|
}
|
|
55209
|
-
})
|
|
55029
|
+
});
|
|
55210
55030
|
await server.connect(transport);
|
|
55211
55031
|
await transport.handleRequest(req, res, req.body);
|
|
55212
55032
|
} catch (error48) {
|
|
@@ -55259,61 +55079,25 @@ var init_http2 = __esm(async () => {
|
|
|
55259
55079
|
|
|
55260
55080
|
// src/index.ts
|
|
55261
55081
|
function createServer(auth) {
|
|
55262
|
-
|
|
55263
|
-
|
|
55264
|
-
|
|
55265
|
-
|
|
55266
|
-
|
|
55267
|
-
|
|
55268
|
-
|
|
55269
|
-
persist: auth.persist
|
|
55270
|
-
};
|
|
55271
|
-
} else if (typeof auth === "object" && auth !== null && "apiKey" in auth) {
|
|
55272
|
-
ctx = {
|
|
55273
|
-
client: createKadoaClient({ apiKey: auth.apiKey })
|
|
55274
|
-
};
|
|
55275
|
-
} else {
|
|
55276
|
-
ctx = {
|
|
55277
|
-
client: createKadoaClient(auth)
|
|
55278
|
-
};
|
|
55279
|
-
}
|
|
55082
|
+
const ctx = {
|
|
55083
|
+
client: createKadoaClient({ jwt: auth.jwt, teamId: auth.teamId }),
|
|
55084
|
+
supabaseJwt: auth.jwt,
|
|
55085
|
+
supabaseRefreshToken: auth.refreshToken,
|
|
55086
|
+
teamId: auth.teamId,
|
|
55087
|
+
persist: auth.persist
|
|
55088
|
+
};
|
|
55280
55089
|
const server = new McpServer({ name: "kadoa", version: "0.3.2" });
|
|
55281
55090
|
registerTools(server, ctx);
|
|
55282
55091
|
server.server.onerror = (error48) => console.error("[MCP Error]", error48);
|
|
55283
55092
|
return server;
|
|
55284
55093
|
}
|
|
55285
|
-
async function validateApiKey() {
|
|
55286
|
-
const client = createKadoaClient();
|
|
55287
|
-
try {
|
|
55288
|
-
await client.workflow.list({ limit: 1 });
|
|
55289
|
-
} catch (error48) {
|
|
55290
|
-
if (KadoaSdkException.isInstance(error48) && error48.code === "AUTH_ERROR") {
|
|
55291
|
-
console.error("Kadoa MCP: Invalid API key. Check KADOA_API_KEY or run 'kadoa login'.");
|
|
55292
|
-
process.exit(1);
|
|
55293
|
-
}
|
|
55294
|
-
}
|
|
55295
|
-
}
|
|
55296
55094
|
var init_src = __esm(async () => {
|
|
55297
55095
|
init_mcp();
|
|
55298
|
-
init_stdio2();
|
|
55299
55096
|
init_client();
|
|
55300
55097
|
init_tools();
|
|
55301
55098
|
if (!process.env.VITEST && !process.env.BUN_TEST) {
|
|
55302
|
-
const
|
|
55303
|
-
|
|
55304
|
-
const { startHttpServer: startHttpServer2 } = await init_http2().then(() => exports_http);
|
|
55305
|
-
await startHttpServer2();
|
|
55306
|
-
} else {
|
|
55307
|
-
await validateApiKey();
|
|
55308
|
-
const server = createServer();
|
|
55309
|
-
const transport = new StdioServerTransport;
|
|
55310
|
-
await server.connect(transport);
|
|
55311
|
-
console.error("Kadoa MCP Server started");
|
|
55312
|
-
process.on("SIGINT", async () => {
|
|
55313
|
-
await server.close();
|
|
55314
|
-
process.exit(0);
|
|
55315
|
-
});
|
|
55316
|
-
}
|
|
55099
|
+
const { startHttpServer: startHttpServer2 } = await init_http2().then(() => exports_http);
|
|
55100
|
+
await startHttpServer2();
|
|
55317
55101
|
}
|
|
55318
55102
|
});
|
|
55319
55103
|
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.8",
|
|
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",
|