taru-mcp 0.1.8 → 0.1.9
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 +4 -4
- package/bin/taru-mcp.mjs +104 -41
- package/package.json +1 -1
- package/setup.sh +3 -3
package/README.md
CHANGED
|
@@ -53,10 +53,10 @@ cp node_modules/taru-mcp/samples/AGENTS.md ./AGENTS.md
|
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
55
|
# Claude Code
|
|
56
|
-
claude mcp add taru --
|
|
56
|
+
claude mcp add taru -- node node_modules/taru-mcp/bin/taru-mcp.mjs --token xxv_your_token
|
|
57
57
|
|
|
58
58
|
# Codex
|
|
59
|
-
codex mcp add taru --
|
|
59
|
+
codex mcp add taru -- node node_modules/taru-mcp/bin/taru-mcp.mjs --token xxv_your_token
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
### 4. Or configure MCP manually
|
|
@@ -67,7 +67,7 @@ If `claude mcp add` doesn't work, create `.mcp.json` in your project root:
|
|
|
67
67
|
{
|
|
68
68
|
"mcpServers": {
|
|
69
69
|
"taru": {
|
|
70
|
-
"command": "
|
|
70
|
+
"command": "node node_modules/taru-mcp/bin/taru-mcp.mjs",
|
|
71
71
|
"args": ["--token", "xxv_your_token"]
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -80,7 +80,7 @@ For Claude Code global config (`~/.claude.json`):
|
|
|
80
80
|
{
|
|
81
81
|
"mcpServers": {
|
|
82
82
|
"taru": {
|
|
83
|
-
"command": "
|
|
83
|
+
"command": "node node_modules/taru-mcp/bin/taru-mcp.mjs",
|
|
84
84
|
"args": ["--token", "xxv_your_token"]
|
|
85
85
|
}
|
|
86
86
|
}
|
package/bin/taru-mcp.mjs
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// Suppress all stderr output to prevent Codex MCP transport from closing.
|
|
4
|
+
// Codex monitors stderr and kills the transport if anything is written to it.
|
|
5
|
+
if (!process.env.TARU_DEBUG) {
|
|
6
|
+
process.stderr.write = () => true;
|
|
7
|
+
}
|
|
8
|
+
process.on('warning', () => {});
|
|
9
|
+
process.on('uncaughtException', () => {});
|
|
10
|
+
process.on('unhandledRejection', () => {});
|
|
11
|
+
|
|
3
12
|
import { createInterface } from "node:readline";
|
|
4
|
-
import { argv, env,
|
|
13
|
+
import { argv, env, stdout } from "node:process";
|
|
5
14
|
import { request as httpsRequest } from "node:https";
|
|
6
15
|
import { request as httpRequest } from "node:http";
|
|
16
|
+
import { appendFileSync } from "node:fs";
|
|
17
|
+
|
|
18
|
+
const LOG_FILE = process.env.TARU_LOG || "";
|
|
19
|
+
function log(msg) {
|
|
20
|
+
if (LOG_FILE) {
|
|
21
|
+
try { appendFileSync(LOG_FILE, `[${new Date().toISOString()}] ${msg}\n`); } catch {}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
7
24
|
|
|
8
25
|
// --- Parse args ---
|
|
9
26
|
|
|
@@ -20,10 +37,10 @@ for (let i = 0; i < args.length; i++) {
|
|
|
20
37
|
} else if ((args[i] === "--token" || args[i] === "-t") && args[i + 1]) {
|
|
21
38
|
token = args[++i];
|
|
22
39
|
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
23
|
-
|
|
40
|
+
console.error(`taru-mcp — MCP proxy for taru knowledge graph
|
|
24
41
|
|
|
25
42
|
Usage:
|
|
26
|
-
|
|
43
|
+
node node_modules/taru-mcp/bin/taru-mcp.mjs [options]
|
|
27
44
|
|
|
28
45
|
Options:
|
|
29
46
|
--workspace-id, -w Workspace UUID (env: TARU_WORKSPACE_ID)
|
|
@@ -31,39 +48,72 @@ Options:
|
|
|
31
48
|
--help, -h Show this help
|
|
32
49
|
|
|
33
50
|
Examples:
|
|
34
|
-
claude mcp add taru --
|
|
35
|
-
codex mcp add taru --
|
|
51
|
+
claude mcp add taru -- node node_modules/taru-mcp/bin/taru-mcp.mjs --token tru_...
|
|
52
|
+
codex mcp add taru -- node node_modules/taru-mcp/bin/taru-mcp.mjs --token tru_...
|
|
36
53
|
`);
|
|
37
54
|
process.exit(0);
|
|
38
55
|
}
|
|
39
56
|
}
|
|
40
57
|
|
|
41
58
|
url = url.replace(/\/+$/, "");
|
|
42
|
-
// If workspace ID was explicitly provided, use the scoped endpoint; otherwise use token-based auto endpoint
|
|
43
59
|
const hasExplicitWorkspace = args.some(a => a === "--workspace-id" || a === "-w") || env.TARU_WORKSPACE_ID;
|
|
44
60
|
const endpoint = hasExplicitWorkspace ? `${url}/mcp/${workspaceId}` : `${url}/mcp`;
|
|
45
61
|
const isHttps = endpoint.startsWith("https://");
|
|
46
62
|
|
|
47
|
-
// Only log to stderr when debug is enabled (Codex kills transport on stderr output)
|
|
48
|
-
if (env.TARU_DEBUG) stderr.write(`[taru-mcp] endpoint: ${endpoint}\n`);
|
|
49
|
-
|
|
50
63
|
// --- JSON-RPC proxy: stdin → HTTP POST → stdout ---
|
|
51
64
|
|
|
65
|
+
let pendingRequests = 0;
|
|
66
|
+
let stdinClosed = false;
|
|
67
|
+
|
|
52
68
|
const rl = createInterface({ input: process.stdin });
|
|
53
69
|
|
|
54
70
|
rl.on("line", async (line) => {
|
|
55
71
|
if (!line.trim()) return;
|
|
56
72
|
|
|
73
|
+
pendingRequests++;
|
|
74
|
+
const parsed = safeParse(line);
|
|
75
|
+
const id = parsed?.id ?? null;
|
|
76
|
+
const method = parsed?.method || parsed?.params?.name || "unknown";
|
|
77
|
+
log(`REQ id=${id} method=${method}`);
|
|
78
|
+
|
|
57
79
|
try {
|
|
58
|
-
const body = await post(endpoint, line);
|
|
80
|
+
const { status, body } = await post(endpoint, line);
|
|
81
|
+
log(`RES id=${id} status=${status} len=${body?.length || 0}`);
|
|
59
82
|
|
|
60
83
|
// 202 Accepted = notification, no response needed
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
|
|
84
|
+
if (status === 202) {
|
|
85
|
+
pendingRequests--;
|
|
86
|
+
maybeExit();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Server returned non-200: wrap in JSON-RPC error
|
|
91
|
+
if (status < 200 || status >= 300) {
|
|
92
|
+
const errResp = JSON.stringify({
|
|
93
|
+
jsonrpc: "2.0",
|
|
94
|
+
id,
|
|
95
|
+
error: { code: -32603, message: `server error (${status}): ${body}` },
|
|
96
|
+
});
|
|
97
|
+
stdout.write(errResp + "\n");
|
|
98
|
+
pendingRequests--;
|
|
99
|
+
maybeExit();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Verify response is valid JSON before forwarding
|
|
104
|
+
const responseJson = safeParse(body);
|
|
105
|
+
if (responseJson) {
|
|
106
|
+
stdout.write(body + "\n");
|
|
107
|
+
} else {
|
|
108
|
+
const errResp = JSON.stringify({
|
|
109
|
+
jsonrpc: "2.0",
|
|
110
|
+
id,
|
|
111
|
+
error: { code: -32603, message: `invalid server response` },
|
|
112
|
+
});
|
|
113
|
+
stdout.write(errResp + "\n");
|
|
114
|
+
}
|
|
64
115
|
} catch (err) {
|
|
65
|
-
|
|
66
|
-
const id = parsed?.id ?? null;
|
|
116
|
+
log(`ERR id=${id} ${err.message}`);
|
|
67
117
|
const errResp = JSON.stringify({
|
|
68
118
|
jsonrpc: "2.0",
|
|
69
119
|
id,
|
|
@@ -71,39 +121,52 @@ rl.on("line", async (line) => {
|
|
|
71
121
|
});
|
|
72
122
|
stdout.write(errResp + "\n");
|
|
73
123
|
}
|
|
124
|
+
|
|
125
|
+
pendingRequests--;
|
|
126
|
+
maybeExit();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
rl.on("close", () => {
|
|
130
|
+
stdinClosed = true;
|
|
131
|
+
maybeExit();
|
|
74
132
|
});
|
|
75
133
|
|
|
76
|
-
|
|
134
|
+
// Only exit after stdin closes AND all pending HTTP requests are done
|
|
135
|
+
function maybeExit() {
|
|
136
|
+
if (stdinClosed && pendingRequests === 0) {
|
|
137
|
+
process.exit(0);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
77
140
|
|
|
78
141
|
// --- HTTP POST helper ---
|
|
79
142
|
|
|
80
143
|
function post(targetUrl, body) {
|
|
81
144
|
return new Promise((resolve, reject) => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
resolve(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
145
|
+
try {
|
|
146
|
+
const parsed = new URL(targetUrl);
|
|
147
|
+
const requester = isHttps ? httpsRequest : httpRequest;
|
|
148
|
+
|
|
149
|
+
const headers = { "Content-Type": "application/json" };
|
|
150
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
151
|
+
|
|
152
|
+
const req = requester(
|
|
153
|
+
parsed,
|
|
154
|
+
{ method: "POST", headers },
|
|
155
|
+
(res) => {
|
|
156
|
+
const chunks = [];
|
|
157
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
158
|
+
res.on("end", () => {
|
|
159
|
+
resolve({ status: res.statusCode, body: Buffer.concat(chunks).toString() });
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
req.on("error", (err) => reject(err));
|
|
165
|
+
req.write(body);
|
|
166
|
+
req.end();
|
|
167
|
+
} catch (err) {
|
|
168
|
+
reject(err);
|
|
169
|
+
}
|
|
107
170
|
});
|
|
108
171
|
}
|
|
109
172
|
|
package/package.json
CHANGED
package/setup.sh
CHANGED
|
@@ -90,14 +90,14 @@ register_mcp() {
|
|
|
90
90
|
token_args=" --token $TOKEN"
|
|
91
91
|
fi
|
|
92
92
|
|
|
93
|
-
local mcp_bin="$(pwd)/node_modules
|
|
93
|
+
local mcp_bin="$(pwd)/node_modules/taru-mcp/bin/taru-mcp.mjs"
|
|
94
94
|
|
|
95
95
|
if command -v "$cli" &>/dev/null; then
|
|
96
96
|
echo "==> Registering MCP server with $cli..."
|
|
97
|
-
$cli mcp add taru -- "$mcp_bin" --url "$URL"$token_args
|
|
97
|
+
$cli mcp add taru -- node "$mcp_bin" --url "$URL"$token_args
|
|
98
98
|
else
|
|
99
99
|
echo "==> '$cli' not found, skipping MCP registration."
|
|
100
|
-
echo " Run this later: $cli mcp add taru -- $mcp_bin --url $URL$token_args"
|
|
100
|
+
echo " Run this later: $cli mcp add taru -- node $mcp_bin --url $URL$token_args"
|
|
101
101
|
fi
|
|
102
102
|
}
|
|
103
103
|
|