@jervaise/jerv-mcp 0.6.0
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/dist/server.d.ts +2 -0
- package/dist/server.js +108 -0
- package/dist/tools.d.ts +21 -0
- package/dist/tools.js +207 -0
- package/package.json +54 -0
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { canonicalToolNames, runJervTool } from "./tools.js";
|
|
4
|
+
function readPackageVersion() {
|
|
5
|
+
try {
|
|
6
|
+
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
7
|
+
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return "0.0.0";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const VERSION = readPackageVersion();
|
|
14
|
+
if (process.argv.includes("--version") || process.argv.includes("-V")) {
|
|
15
|
+
console.log(VERSION);
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
function send(message) {
|
|
19
|
+
const body = JSON.stringify(message);
|
|
20
|
+
process.stdout.write(`Content-Length: ${Buffer.byteLength(body, "utf8")}\r\n\r\n${body}`);
|
|
21
|
+
}
|
|
22
|
+
function toolDescription(name) {
|
|
23
|
+
return `Run canonical Jerv memory tool ${name} through jerv-cli.`;
|
|
24
|
+
}
|
|
25
|
+
function toolsList() {
|
|
26
|
+
return canonicalToolNames.map((name) => ({
|
|
27
|
+
name,
|
|
28
|
+
description: toolDescription(name),
|
|
29
|
+
inputSchema: {
|
|
30
|
+
type: "object",
|
|
31
|
+
additionalProperties: true,
|
|
32
|
+
properties: {},
|
|
33
|
+
},
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
async function handle(request) {
|
|
37
|
+
if (!request.id && request.method?.startsWith("notifications/"))
|
|
38
|
+
return;
|
|
39
|
+
if (request.method === "initialize") {
|
|
40
|
+
send({
|
|
41
|
+
jsonrpc: "2.0",
|
|
42
|
+
id: request.id,
|
|
43
|
+
result: {
|
|
44
|
+
protocolVersion: "2024-11-05",
|
|
45
|
+
capabilities: { tools: {} },
|
|
46
|
+
serverInfo: { name: "jerv-mcp", version: VERSION },
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (request.method === "tools/list") {
|
|
52
|
+
send({ jsonrpc: "2.0", id: request.id, result: { tools: toolsList() } });
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (request.method === "tools/call") {
|
|
56
|
+
const name = String(request.params?.name ?? "");
|
|
57
|
+
if (!canonicalToolNames.includes(name)) {
|
|
58
|
+
send({ jsonrpc: "2.0", id: request.id, error: { code: -32602, message: `unknown tool: ${name}` } });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const result = await runJervTool(name, (request.params?.arguments ?? {}), { cwd: process.cwd() });
|
|
62
|
+
send({
|
|
63
|
+
jsonrpc: "2.0",
|
|
64
|
+
id: request.id,
|
|
65
|
+
result: {
|
|
66
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
send({ jsonrpc: "2.0", id: request.id, error: { code: -32601, message: `unknown method: ${request.method ?? ""}` } });
|
|
72
|
+
}
|
|
73
|
+
function parseFrames(buffer) {
|
|
74
|
+
const requests = [];
|
|
75
|
+
let offset = 0;
|
|
76
|
+
while (offset < buffer.length) {
|
|
77
|
+
const headerEnd = buffer.indexOf("\r\n\r\n", offset, "utf8");
|
|
78
|
+
if (headerEnd < 0)
|
|
79
|
+
break;
|
|
80
|
+
const header = buffer.slice(offset, headerEnd).toString("utf8");
|
|
81
|
+
const match = /content-length:\s*(\d+)/i.exec(header);
|
|
82
|
+
if (!match)
|
|
83
|
+
break;
|
|
84
|
+
const length = Number(match[1]);
|
|
85
|
+
const start = headerEnd + 4;
|
|
86
|
+
const end = start + length;
|
|
87
|
+
if (end > buffer.length)
|
|
88
|
+
break;
|
|
89
|
+
requests.push(JSON.parse(buffer.slice(start, end).toString("utf8")));
|
|
90
|
+
offset = end;
|
|
91
|
+
}
|
|
92
|
+
return { requests, rest: buffer.slice(offset) };
|
|
93
|
+
}
|
|
94
|
+
let frameBuffer = Buffer.alloc(0);
|
|
95
|
+
let queue = Promise.resolve();
|
|
96
|
+
function pump(chunk) {
|
|
97
|
+
frameBuffer = Buffer.concat([frameBuffer, chunk]);
|
|
98
|
+
const parsed = parseFrames(frameBuffer);
|
|
99
|
+
frameBuffer = parsed.rest;
|
|
100
|
+
for (const request of parsed.requests) {
|
|
101
|
+
queue = queue.then(() => handle(request));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
process.stdin.on("data", (chunk) => pump(chunk));
|
|
105
|
+
process.stdin.on("error", (error) => {
|
|
106
|
+
send({ jsonrpc: "2.0", id: null, error: { code: -32000, message: error.message } });
|
|
107
|
+
process.exitCode = 1;
|
|
108
|
+
});
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const canonicalToolNames: readonly ["jerv_resume", "jerv_save", "jerv_block_get", "jerv_block_put", "jerv_northstar_list", "jerv_northstar_show", "jerv_northstar_set", "jerv_recall_list", "jerv_recall_show", "jerv_archival_search", "jerv_archival_insert", "jerv_context_compile", "jerv_event_append", "jerv_governance_action", "jerv_graph_query"];
|
|
2
|
+
export type CanonicalToolName = (typeof canonicalToolNames)[number];
|
|
3
|
+
export interface CliResult {
|
|
4
|
+
stdout: string;
|
|
5
|
+
stderr?: string;
|
|
6
|
+
status?: number | null;
|
|
7
|
+
}
|
|
8
|
+
export interface CliRunOptions {
|
|
9
|
+
cwd?: string;
|
|
10
|
+
input?: string;
|
|
11
|
+
}
|
|
12
|
+
export type CliRunner = (args: string[], options?: CliRunOptions) => Promise<CliResult> | CliResult;
|
|
13
|
+
export interface RunJervToolOptions {
|
|
14
|
+
cwd?: string;
|
|
15
|
+
runCli?: CliRunner;
|
|
16
|
+
}
|
|
17
|
+
type ToolArgs = Record<string, unknown>;
|
|
18
|
+
export declare function redactSecretText(text: string): string;
|
|
19
|
+
export declare function defaultRunCli(args: string[], options?: CliRunOptions): Promise<CliResult>;
|
|
20
|
+
export declare function runJervTool(name: CanonicalToolName, args: ToolArgs, options?: RunJervToolOptions): Promise<unknown>;
|
|
21
|
+
export {};
|
package/dist/tools.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
export const canonicalToolNames = [
|
|
3
|
+
"jerv_resume",
|
|
4
|
+
"jerv_save",
|
|
5
|
+
"jerv_block_get",
|
|
6
|
+
"jerv_block_put",
|
|
7
|
+
"jerv_northstar_list",
|
|
8
|
+
"jerv_northstar_show",
|
|
9
|
+
"jerv_northstar_set",
|
|
10
|
+
"jerv_recall_list",
|
|
11
|
+
"jerv_recall_show",
|
|
12
|
+
"jerv_archival_search",
|
|
13
|
+
"jerv_archival_insert",
|
|
14
|
+
"jerv_context_compile",
|
|
15
|
+
"jerv_event_append",
|
|
16
|
+
"jerv_governance_action",
|
|
17
|
+
"jerv_graph_query",
|
|
18
|
+
];
|
|
19
|
+
const GITHUB_TOKEN_RE = /gh[pousr]_[A-Za-z0-9_]{20,}/g;
|
|
20
|
+
const ASSIGNMENT_SECRET_RE = /\b(?:TOKEN|PASSWORD|PASS|SECRET|API_KEY|PRIVATE_KEY)\s*=\s*[^\s]+/gi;
|
|
21
|
+
export function redactSecretText(text) {
|
|
22
|
+
return text
|
|
23
|
+
.replace(GITHUB_TOKEN_RE, "[REDACTED_SECRET]")
|
|
24
|
+
.replace(ASSIGNMENT_SECRET_RE, (match) => {
|
|
25
|
+
const key = match.split("=")[0]?.trim() || "SECRET";
|
|
26
|
+
return `${key}=[REDACTED_SECRET]`;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function stringArg(args, key) {
|
|
30
|
+
const value = args[key];
|
|
31
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
32
|
+
}
|
|
33
|
+
function booleanArg(args, key) {
|
|
34
|
+
return args[key] === true;
|
|
35
|
+
}
|
|
36
|
+
function arrayArg(args, key) {
|
|
37
|
+
const value = args[key];
|
|
38
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
39
|
+
}
|
|
40
|
+
function requireString(args, key) {
|
|
41
|
+
const value = stringArg(args, key);
|
|
42
|
+
if (!value)
|
|
43
|
+
throw new Error(`${key} is required`);
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
export async function defaultRunCli(args, options = {}) {
|
|
47
|
+
const invocation = process.platform === "win32"
|
|
48
|
+
? { command: "cmd.exe", args: ["/d", "/s", "/c", "jerv-cli", ...args] }
|
|
49
|
+
: { command: "jerv-cli", args };
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const child = spawn(invocation.command, invocation.args, {
|
|
52
|
+
cwd: options.cwd,
|
|
53
|
+
env: { ...process.env, JERV_AGENT_SURFACE: "mcp" },
|
|
54
|
+
windowsHide: true,
|
|
55
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
56
|
+
});
|
|
57
|
+
let stdout = "";
|
|
58
|
+
let stderr = "";
|
|
59
|
+
child.stdout.setEncoding("utf8");
|
|
60
|
+
child.stderr.setEncoding("utf8");
|
|
61
|
+
child.stdout.on("data", (chunk) => { stdout += chunk; });
|
|
62
|
+
child.stderr.on("data", (chunk) => { stderr += chunk; });
|
|
63
|
+
child.on("error", reject);
|
|
64
|
+
child.on("close", (status) => {
|
|
65
|
+
if (status === 0) {
|
|
66
|
+
resolve({ stdout, stderr, status });
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
reject(Object.assign(new Error(`jerv-cli exited with status ${status}`), { stdout, stderr, status }));
|
|
70
|
+
});
|
|
71
|
+
child.stdin.end(options.input);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function toolToCli(name, args) {
|
|
75
|
+
switch (name) {
|
|
76
|
+
case "jerv_resume":
|
|
77
|
+
return { cliArgs: ["resume", "--json"] };
|
|
78
|
+
case "jerv_save": {
|
|
79
|
+
const cliArgs = ["save"];
|
|
80
|
+
const next = stringArg(args, "next");
|
|
81
|
+
const message = stringArg(args, "message");
|
|
82
|
+
const session = stringArg(args, "session");
|
|
83
|
+
if (next)
|
|
84
|
+
cliArgs.push("--next", next);
|
|
85
|
+
if (message)
|
|
86
|
+
cliArgs.push("--message", message);
|
|
87
|
+
for (const lesson of arrayArg(args, "lessons"))
|
|
88
|
+
cliArgs.push("--lesson", lesson);
|
|
89
|
+
if (session)
|
|
90
|
+
cliArgs.push("--session", session);
|
|
91
|
+
return { cliArgs };
|
|
92
|
+
}
|
|
93
|
+
case "jerv_block_get": {
|
|
94
|
+
const cliArgs = ["memory", "block", "get", requireString(args, "block"), "--json"];
|
|
95
|
+
const project = stringArg(args, "project");
|
|
96
|
+
if (project)
|
|
97
|
+
cliArgs.push("--project", project);
|
|
98
|
+
return { cliArgs };
|
|
99
|
+
}
|
|
100
|
+
case "jerv_block_put": {
|
|
101
|
+
const cliArgs = ["memory", "block", "put", requireString(args, "block")];
|
|
102
|
+
const project = stringArg(args, "project");
|
|
103
|
+
if (project)
|
|
104
|
+
cliArgs.push("--project", project);
|
|
105
|
+
return { cliArgs, input: requireString(args, "text") };
|
|
106
|
+
}
|
|
107
|
+
case "jerv_northstar_list": {
|
|
108
|
+
const cliArgs = ["northstar", "list", "--json"];
|
|
109
|
+
if (booleanArg(args, "all"))
|
|
110
|
+
cliArgs.push("--all");
|
|
111
|
+
return { cliArgs };
|
|
112
|
+
}
|
|
113
|
+
case "jerv_northstar_show":
|
|
114
|
+
return { cliArgs: ["northstar", "show", requireString(args, "slug"), "--json"] };
|
|
115
|
+
case "jerv_northstar_set":
|
|
116
|
+
return { cliArgs: ["northstar", "set", requireString(args, "slug")], input: requireString(args, "body") };
|
|
117
|
+
case "jerv_recall_list":
|
|
118
|
+
return { cliArgs: ["recall", "list", "--json"] };
|
|
119
|
+
case "jerv_recall_show":
|
|
120
|
+
return { cliArgs: ["recall", "show", requireString(args, "session")] };
|
|
121
|
+
case "jerv_archival_search": {
|
|
122
|
+
const cliArgs = ["memory", "archival", "search", requireString(args, "query"), "--json"];
|
|
123
|
+
const top = args.top ?? args.topK;
|
|
124
|
+
if (top !== undefined)
|
|
125
|
+
cliArgs.push("--top", String(top));
|
|
126
|
+
return { cliArgs };
|
|
127
|
+
}
|
|
128
|
+
case "jerv_archival_insert": {
|
|
129
|
+
const cliArgs = ["memory", "archival", "insert", requireString(args, "text")];
|
|
130
|
+
const project = stringArg(args, "project");
|
|
131
|
+
if (project)
|
|
132
|
+
cliArgs.push("--project", project);
|
|
133
|
+
for (const tag of arrayArg(args, "tags"))
|
|
134
|
+
cliArgs.push("--tag", tag);
|
|
135
|
+
return { cliArgs };
|
|
136
|
+
}
|
|
137
|
+
case "jerv_context_compile": {
|
|
138
|
+
const cliArgs = ["memory", "context", "compile", "--project", requireString(args, "project"), "--json"];
|
|
139
|
+
const maxItems = args.maxItems;
|
|
140
|
+
if (maxItems !== undefined)
|
|
141
|
+
cliArgs.push("--max-items", String(maxItems));
|
|
142
|
+
return { cliArgs };
|
|
143
|
+
}
|
|
144
|
+
case "jerv_event_append": {
|
|
145
|
+
const cliArgs = ["memory", "event", "append", requireString(args, "type"), "--project", requireString(args, "project"), "--summary", requireString(args, "summary")];
|
|
146
|
+
const session = stringArg(args, "session");
|
|
147
|
+
const raw = stringArg(args, "rawText") ?? stringArg(args, "raw");
|
|
148
|
+
const source = stringArg(args, "source");
|
|
149
|
+
if (session)
|
|
150
|
+
cliArgs.push("--session", session);
|
|
151
|
+
if (raw)
|
|
152
|
+
cliArgs.push("--raw", raw);
|
|
153
|
+
if (source)
|
|
154
|
+
cliArgs.push("--source", source);
|
|
155
|
+
return { cliArgs };
|
|
156
|
+
}
|
|
157
|
+
case "jerv_governance_action":
|
|
158
|
+
{
|
|
159
|
+
const cliArgs = [
|
|
160
|
+
"memory",
|
|
161
|
+
"governance",
|
|
162
|
+
requireString(args, "action"),
|
|
163
|
+
requireString(args, "targetId"),
|
|
164
|
+
"--project",
|
|
165
|
+
requireString(args, "project"),
|
|
166
|
+
"--reason",
|
|
167
|
+
requireString(args, "reason"),
|
|
168
|
+
];
|
|
169
|
+
const supersededBy = stringArg(args, "supersededBy");
|
|
170
|
+
if (supersededBy)
|
|
171
|
+
cliArgs.push("--superseded-by", supersededBy);
|
|
172
|
+
return { cliArgs };
|
|
173
|
+
}
|
|
174
|
+
case "jerv_graph_query":
|
|
175
|
+
return { cliArgs: ["memory", "graph", "query", "--project", requireString(args, "project"), "--json"] };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function parseCliResult(stdout) {
|
|
179
|
+
const trimmed = stdout.trim();
|
|
180
|
+
if (!trimmed)
|
|
181
|
+
return { ok: true };
|
|
182
|
+
try {
|
|
183
|
+
return JSON.parse(trimmed);
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return { ok: true, output: trimmed };
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
export async function runJervTool(name, args, options = {}) {
|
|
190
|
+
try {
|
|
191
|
+
const mapped = toolToCli(name, args);
|
|
192
|
+
const result = await (options.runCli ?? defaultRunCli)(mapped.cliArgs, { cwd: options.cwd, input: mapped.input });
|
|
193
|
+
return parseCliResult(result.stdout);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
const err = error;
|
|
197
|
+
return {
|
|
198
|
+
ok: false,
|
|
199
|
+
error: {
|
|
200
|
+
code: err.code === "ENOENT" ? "JERV_CLI_UNAVAILABLE" : "JERV_CLI_FAILED",
|
|
201
|
+
message: redactSecretText(err.message || "jerv-cli failed"),
|
|
202
|
+
status: err.status ?? null,
|
|
203
|
+
stderr: redactSecretText(err.stderr ?? ""),
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jervaise/jerv-mcp",
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Canonical MCP server for Jerv native memory tools.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Suphi",
|
|
9
|
+
"email": "ssarigollu@gmail.com"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/mutmutco/Jerv-PowerTools#readme",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/mutmutco/Jerv-PowerTools.git",
|
|
15
|
+
"directory": "packages/jerv-mcp"
|
|
16
|
+
},
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/mutmutco/Jerv-PowerTools/issues"
|
|
19
|
+
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"jerv-mcp": "dist/server.js"
|
|
22
|
+
},
|
|
23
|
+
"main": "./dist/server.js",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/tools.d.ts",
|
|
27
|
+
"import": "./dist/tools.js"
|
|
28
|
+
},
|
|
29
|
+
"./server": {
|
|
30
|
+
"types": "./dist/server.d.ts",
|
|
31
|
+
"import": "./dist/server.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist"
|
|
36
|
+
],
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc -p tsconfig.json",
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
43
|
+
"test": "vitest run",
|
|
44
|
+
"check": "npm run typecheck && npm run test && npm run build"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=22"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^22.0.0",
|
|
51
|
+
"typescript": "^5.7.0",
|
|
52
|
+
"vitest": "^4.1.9"
|
|
53
|
+
}
|
|
54
|
+
}
|