@kadoa/mcp 0.3.7-rc.1 → 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 +22 -236
- 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,44 +48947,8 @@ 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
|
-
if (typeof auth === "object" && auth !== null) {
|
|
49076
|
-
if ("jwt" in auth) {
|
|
49077
|
-
client = new KadoaClient({ bearerToken: auth.jwt });
|
|
49078
|
-
} else {
|
|
49079
|
-
client = new KadoaClient({ apiKey: auth.apiKey });
|
|
49080
|
-
}
|
|
49081
|
-
} else {
|
|
49082
|
-
client = new KadoaClient({ apiKey: resolveApiKey(auth) });
|
|
49083
|
-
}
|
|
48951
|
+
const client = new KadoaClient({ bearerToken: auth.jwt });
|
|
49084
48952
|
client.axiosInstance.interceptors.request.use((config2) => {
|
|
49085
48953
|
config2.headers["x-kadoa-source"] = "mcp";
|
|
49086
48954
|
return config2;
|
|
@@ -49094,28 +48962,6 @@ var init_client = __esm(() => {
|
|
|
49094
48962
|
});
|
|
49095
48963
|
|
|
49096
48964
|
// src/client.ts
|
|
49097
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
49098
|
-
import { dirname as dirname2, join as join2 } from "node:path";
|
|
49099
|
-
import { homedir as homedir2 } from "node:os";
|
|
49100
|
-
function getConfigPath2() {
|
|
49101
|
-
const home = process.env.HOME || homedir2();
|
|
49102
|
-
return join2(home, ".kadoa", "config.json");
|
|
49103
|
-
}
|
|
49104
|
-
function loadConfig2() {
|
|
49105
|
-
const configFile = getConfigPath2();
|
|
49106
|
-
if (!existsSync2(configFile))
|
|
49107
|
-
return {};
|
|
49108
|
-
try {
|
|
49109
|
-
return JSON.parse(readFileSync2(configFile, "utf-8"));
|
|
49110
|
-
} catch {
|
|
49111
|
-
return {};
|
|
49112
|
-
}
|
|
49113
|
-
}
|
|
49114
|
-
function saveConfig(config2) {
|
|
49115
|
-
const configFile = getConfigPath2();
|
|
49116
|
-
mkdirSync2(dirname2(configFile), { recursive: true });
|
|
49117
|
-
writeFileSync2(configFile, JSON.stringify(config2, null, 2), "utf-8");
|
|
49118
|
-
}
|
|
49119
48965
|
function isJwtExpired(jwt2) {
|
|
49120
48966
|
try {
|
|
49121
48967
|
const payload = JSON.parse(Buffer.from(jwt2.split(".")[1], "base64url").toString());
|
|
@@ -49238,7 +49084,7 @@ function classifyError(error48) {
|
|
|
49238
49084
|
if (httpError.httpStatus === 403) {
|
|
49239
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.`;
|
|
49240
49086
|
}
|
|
49241
|
-
return `Authentication failed${status}.
|
|
49087
|
+
return `Authentication failed${status}. Please re-authenticate via OAuth.`;
|
|
49242
49088
|
case "NOT_FOUND":
|
|
49243
49089
|
return `Not found${status}. The workflow may have been deleted or the ID is incorrect.`;
|
|
49244
49090
|
case "RATE_LIMITED":
|
|
@@ -49256,7 +49102,7 @@ function classifyError(error48) {
|
|
|
49256
49102
|
}
|
|
49257
49103
|
switch (code) {
|
|
49258
49104
|
case "AUTH_ERROR":
|
|
49259
|
-
return "Authentication failed.
|
|
49105
|
+
return "Authentication failed. Please re-authenticate via OAuth.";
|
|
49260
49106
|
case "NOT_FOUND":
|
|
49261
49107
|
return "Not found. The workflow may have been deleted or the ID is incorrect.";
|
|
49262
49108
|
case "RATE_LIMITED":
|
|
@@ -49289,8 +49135,7 @@ function registerTools(server, ctx) {
|
|
|
49289
49135
|
try {
|
|
49290
49136
|
const jwt2 = ctx.supabaseJwt;
|
|
49291
49137
|
const teams = await ctx.client.listTeams(jwt2 ? { bearerToken: jwt2 } : undefined);
|
|
49292
|
-
const
|
|
49293
|
-
const activeTeamId = ctx.teamId ?? config2.teamId ?? teams[0]?.id;
|
|
49138
|
+
const activeTeamId = ctx.teamId ?? teams[0]?.id;
|
|
49294
49139
|
const activeTeam = teams.find((t) => t.id === activeTeamId);
|
|
49295
49140
|
if (activeTeam?.adminEmail) {
|
|
49296
49141
|
message += ` Your team admin is ${activeTeam.adminEmail}.`;
|
|
@@ -49309,13 +49154,11 @@ function registerTools(server, ctx) {
|
|
|
49309
49154
|
}, withErrorHandling("whoami", async () => {
|
|
49310
49155
|
const jwt2 = await getValidJwt(ctx);
|
|
49311
49156
|
const user = await ctx.client.user.getCurrentUser();
|
|
49312
|
-
const authMethod = jwt2 ? "OAuth (JWT)" : "API Key";
|
|
49313
49157
|
const teams = await ctx.client.listTeams(jwt2 ? { bearerToken: jwt2 } : undefined);
|
|
49314
|
-
const
|
|
49315
|
-
const activeTeamId = ctx.teamId ?? config2.teamId ?? teams[0]?.id;
|
|
49158
|
+
const activeTeamId = ctx.teamId ?? teams[0]?.id;
|
|
49316
49159
|
return jsonResult({
|
|
49317
49160
|
email: user.email,
|
|
49318
|
-
authMethod,
|
|
49161
|
+
authMethod: "OAuth",
|
|
49319
49162
|
teams: teams.map((t) => ({
|
|
49320
49163
|
name: t.name,
|
|
49321
49164
|
memberRole: t.memberRole,
|
|
@@ -49959,8 +49802,7 @@ function registerTools(server, ctx) {
|
|
|
49959
49802
|
}, withErrorHandling("team_list", async () => {
|
|
49960
49803
|
const jwt2 = await getValidJwt(ctx);
|
|
49961
49804
|
const teams = await ctx.client.listTeams(jwt2 ? { bearerToken: jwt2 } : undefined);
|
|
49962
|
-
const
|
|
49963
|
-
const activeTeamId = ctx.teamId ?? config2.teamId ?? teams[0]?.id;
|
|
49805
|
+
const activeTeamId = ctx.teamId ?? teams[0]?.id;
|
|
49964
49806
|
return jsonResult({
|
|
49965
49807
|
teams: teams.map((t) => ({
|
|
49966
49808
|
id: t.id,
|
|
@@ -49980,9 +49822,6 @@ function registerTools(server, ctx) {
|
|
|
49980
49822
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true }
|
|
49981
49823
|
}, withErrorHandling("team_switch", async (args) => {
|
|
49982
49824
|
const jwt2 = await getValidJwt(ctx);
|
|
49983
|
-
if (!jwt2) {
|
|
49984
|
-
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.");
|
|
49985
|
-
}
|
|
49986
49825
|
const teams = await ctx.client.listTeams({ bearerToken: jwt2 });
|
|
49987
49826
|
const identifier = args.teamIdentifier;
|
|
49988
49827
|
const match = teams.find((t) => t.id === identifier || t.name.toLowerCase() === identifier.toLowerCase());
|
|
@@ -49995,10 +49834,6 @@ function registerTools(server, ctx) {
|
|
|
49995
49834
|
return errorResult("Failed to refresh session after team switch");
|
|
49996
49835
|
}
|
|
49997
49836
|
ctx.teamId = match.id;
|
|
49998
|
-
const config2 = loadConfig2();
|
|
49999
|
-
config2.teamId = match.id;
|
|
50000
|
-
config2.teamName = match.name;
|
|
50001
|
-
saveConfig(config2);
|
|
50002
49837
|
return jsonResult({
|
|
50003
49838
|
success: true,
|
|
50004
49839
|
teamId: match.id,
|
|
@@ -54391,15 +54226,6 @@ class KadoaOAuthProvider {
|
|
|
54391
54226
|
};
|
|
54392
54227
|
}
|
|
54393
54228
|
async verifyAccessToken(token) {
|
|
54394
|
-
if (token.startsWith("tk-")) {
|
|
54395
|
-
return {
|
|
54396
|
-
token,
|
|
54397
|
-
clientId: "direct-api-key",
|
|
54398
|
-
scopes: [],
|
|
54399
|
-
expiresAt: Math.floor(Date.now() / 1000) + 3600,
|
|
54400
|
-
extra: { apiKey: token }
|
|
54401
|
-
};
|
|
54402
|
-
}
|
|
54403
54229
|
const entry = await this.store.get("access_tokens", token);
|
|
54404
54230
|
if (!entry) {
|
|
54405
54231
|
const sessionCount = await this.store.size("access_tokens");
|
|
@@ -55051,9 +54877,6 @@ function resolveAuth(req) {
|
|
|
55051
54877
|
console.error("[AUTH_RESOLVE] FAIL: req.auth.extra is missing");
|
|
55052
54878
|
return;
|
|
55053
54879
|
}
|
|
55054
|
-
if (typeof extra.apiKey === "string" && extra.apiKey.startsWith("tk-")) {
|
|
55055
|
-
return { kind: "apiKey", apiKey: extra.apiKey };
|
|
55056
|
-
}
|
|
55057
54880
|
if (typeof extra.supabaseJwt === "string") {
|
|
55058
54881
|
const claims = jwtClaims2(extra.supabaseJwt);
|
|
55059
54882
|
const userId = claims.sub;
|
|
@@ -55071,21 +54894,20 @@ function resolveAuth(req) {
|
|
|
55071
54894
|
}
|
|
55072
54895
|
}
|
|
55073
54896
|
return {
|
|
55074
|
-
kind: "jwt",
|
|
55075
54897
|
jwt: extra.supabaseJwt,
|
|
55076
54898
|
refreshToken: extra.supabaseRefreshToken ?? "",
|
|
55077
54899
|
teamId: extra.teamId ?? "",
|
|
55078
54900
|
userId
|
|
55079
54901
|
};
|
|
55080
54902
|
}
|
|
55081
|
-
console.error(`[AUTH_RESOLVE] FAIL: no
|
|
54903
|
+
console.error(`[AUTH_RESOLVE] FAIL: no supabaseJwt in extra (keys: ${Object.keys(extra).join(", ")})`);
|
|
55082
54904
|
return;
|
|
55083
54905
|
}
|
|
55084
|
-
async function startHttpServer() {
|
|
54906
|
+
async function startHttpServer(options) {
|
|
55085
54907
|
const port = parseInt(process.env.PORT || "3000", 10);
|
|
55086
54908
|
const app = createMcpExpressApp({ host: "0.0.0.0" });
|
|
55087
54909
|
app.set("trust proxy", 1);
|
|
55088
|
-
const store = new RedisTokenStore;
|
|
54910
|
+
const store = options?.store ?? new RedisTokenStore;
|
|
55089
54911
|
const provider = new KadoaOAuthProvider(store);
|
|
55090
54912
|
const serverUrl = process.env.MCP_SERVER_URL || `http://localhost:${port}`;
|
|
55091
54913
|
app.use(mcpAuthRouter({
|
|
@@ -55128,17 +54950,17 @@ async function startHttpServer() {
|
|
|
55128
54950
|
});
|
|
55129
54951
|
return;
|
|
55130
54952
|
}
|
|
55131
|
-
const identity =
|
|
54953
|
+
const identity = `jwt:${auth.userId.slice(0, 8)}...:team=${auth.teamId.slice(0, 8)}...`;
|
|
55132
54954
|
try {
|
|
55133
54955
|
console.error(`[MCP] POST method=${method} auth=${identity}`);
|
|
55134
54956
|
const transport = new StreamableHTTPServerTransport({
|
|
55135
54957
|
sessionIdGenerator: undefined
|
|
55136
54958
|
});
|
|
55137
|
-
const server =
|
|
54959
|
+
const server = createServer({
|
|
55138
54960
|
jwt: auth.jwt,
|
|
55139
54961
|
refreshToken: auth.refreshToken,
|
|
55140
54962
|
teamId: auth.teamId
|
|
55141
|
-
})
|
|
54963
|
+
});
|
|
55142
54964
|
await server.connect(transport);
|
|
55143
54965
|
await transport.handleRequest(req, res, req.body);
|
|
55144
54966
|
} catch (error48) {
|
|
@@ -55191,60 +55013,24 @@ var init_http2 = __esm(async () => {
|
|
|
55191
55013
|
|
|
55192
55014
|
// src/index.ts
|
|
55193
55015
|
function createServer(auth) {
|
|
55194
|
-
|
|
55195
|
-
|
|
55196
|
-
|
|
55197
|
-
|
|
55198
|
-
|
|
55199
|
-
|
|
55200
|
-
teamId: auth.teamId
|
|
55201
|
-
};
|
|
55202
|
-
} else if (typeof auth === "object" && auth !== null && "apiKey" in auth) {
|
|
55203
|
-
ctx = {
|
|
55204
|
-
client: createKadoaClient({ apiKey: auth.apiKey })
|
|
55205
|
-
};
|
|
55206
|
-
} else {
|
|
55207
|
-
ctx = {
|
|
55208
|
-
client: createKadoaClient(auth)
|
|
55209
|
-
};
|
|
55210
|
-
}
|
|
55016
|
+
const ctx = {
|
|
55017
|
+
client: createKadoaClient({ jwt: auth.jwt }),
|
|
55018
|
+
supabaseJwt: auth.jwt,
|
|
55019
|
+
supabaseRefreshToken: auth.refreshToken,
|
|
55020
|
+
teamId: auth.teamId
|
|
55021
|
+
};
|
|
55211
55022
|
const server = new McpServer({ name: "kadoa", version: "0.3.2" });
|
|
55212
55023
|
registerTools(server, ctx);
|
|
55213
55024
|
server.server.onerror = (error48) => console.error("[MCP Error]", error48);
|
|
55214
55025
|
return server;
|
|
55215
55026
|
}
|
|
55216
|
-
async function validateApiKey() {
|
|
55217
|
-
const client = createKadoaClient();
|
|
55218
|
-
try {
|
|
55219
|
-
await client.workflow.list({ limit: 1 });
|
|
55220
|
-
} catch (error48) {
|
|
55221
|
-
if (KadoaSdkException.isInstance(error48) && error48.code === "AUTH_ERROR") {
|
|
55222
|
-
console.error("Kadoa MCP: Invalid API key. Check KADOA_API_KEY or run 'kadoa login'.");
|
|
55223
|
-
process.exit(1);
|
|
55224
|
-
}
|
|
55225
|
-
}
|
|
55226
|
-
}
|
|
55227
55027
|
var init_src = __esm(async () => {
|
|
55228
55028
|
init_mcp();
|
|
55229
|
-
init_stdio2();
|
|
55230
55029
|
init_client();
|
|
55231
55030
|
init_tools();
|
|
55232
55031
|
if (!process.env.VITEST && !process.env.BUN_TEST) {
|
|
55233
|
-
const
|
|
55234
|
-
|
|
55235
|
-
const { startHttpServer: startHttpServer2 } = await init_http2().then(() => exports_http);
|
|
55236
|
-
await startHttpServer2();
|
|
55237
|
-
} else {
|
|
55238
|
-
await validateApiKey();
|
|
55239
|
-
const server = createServer();
|
|
55240
|
-
const transport = new StdioServerTransport;
|
|
55241
|
-
await server.connect(transport);
|
|
55242
|
-
console.error("Kadoa MCP Server started");
|
|
55243
|
-
process.on("SIGINT", async () => {
|
|
55244
|
-
await server.close();
|
|
55245
|
-
process.exit(0);
|
|
55246
|
-
});
|
|
55247
|
-
}
|
|
55032
|
+
const { startHttpServer: startHttpServer2 } = await init_http2().then(() => exports_http);
|
|
55033
|
+
await startHttpServer2();
|
|
55248
55034
|
}
|
|
55249
55035
|
});
|
|
55250
55036
|
await init_src();
|
package/package.json
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kadoa/mcp",
|
|
3
|
-
"version": "0.3.7-rc.
|
|
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",
|