@gamaze/hicortex 0.20.2 → 0.20.4
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 +16 -0
- package/assets/dashboard.html +318 -8
- package/assets/identity.html +349 -8
- package/assets/viz.html +352 -8
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +22 -0
- package/dist/dashboard.d.ts +17 -0
- package/dist/dashboard.js +32 -0
- package/dist/mcp-server.js +10 -0
- package/dist/mcp-stdio.d.ts +138 -0
- package/dist/mcp-stdio.js +308 -0
- package/package.json +4 -2
- package/server.json +44 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Hicortex stdio MCP bridge (#375) — the `hicortex mcp` subcommand.
|
|
4
|
+
*
|
|
5
|
+
* Registry/stdio MCP clients (Claude Desktop, Cursor, the MCP Registry's own
|
|
6
|
+
* install flow) launch a stdio command and speak MCP over stdin/stdout. The
|
|
7
|
+
* daemon's MCP surface is HTTP/SSE on :8787, so this module bridges the two:
|
|
8
|
+
* a low-level SDK `Server` over `StdioServerTransport` downstream (the client
|
|
9
|
+
* side) and an SDK `Client` over `SSEClientTransport` upstream (the daemon
|
|
10
|
+
* side), forwarding tools/list + tools/call. The daemon's MCP surface is
|
|
11
|
+
* tools-only (no resources/prompts registered in mcp-server.ts) and ping is
|
|
12
|
+
* auto-answered by the SDK Protocol base, so tools-forwarding loses nothing.
|
|
13
|
+
*
|
|
14
|
+
* WHY proxy instead of in-process stdio with direct DB access (design note,
|
|
15
|
+
* issue #375):
|
|
16
|
+
* 1. db.ts enables WAL but sets no busy_timeout — a second writer process
|
|
17
|
+
* (the common case: the daemon already running on server-mode installs)
|
|
18
|
+
* takes immediate SQLITE_BUSY during nightly consolidation's long
|
|
19
|
+
* transactions → tool-call failures.
|
|
20
|
+
* 2. createMcpServer()'s nine tool handlers close over ~10 module-level
|
|
21
|
+
* vars initialized by the ~250-line boot inside startServer() —
|
|
22
|
+
* in-process would mean refactoring the production boot path or
|
|
23
|
+
* duplicating it (drift).
|
|
24
|
+
* 3. warmEmbedder loads a 150-300 MB ONNX model per process — one per MCP
|
|
25
|
+
* client (Claude Desktop + CC + Cursor = 3x), vs zero for the bridge.
|
|
26
|
+
* 4. mcp-server.ts's own header states the model: "One process, one DB
|
|
27
|
+
* connection, one embedder".
|
|
28
|
+
*
|
|
29
|
+
* Target resolution (precedence): HICORTEX_SERVER_URL env → remote bridge,
|
|
30
|
+
* NEVER spawns anything; else config via the SAME semantics as
|
|
31
|
+
* learnings-identity.resolveConfig() (client-mode serverUrl → remote;
|
|
32
|
+
* server-mode → http://127.0.0.1:<port ?? 8787>); no usable config →
|
|
33
|
+
* http://127.0.0.1:8787. Token: HICORTEX_AUTH_TOKEN env → config.authToken.
|
|
34
|
+
* The token rides SSEClientTransport's requestInit headers (verified in the
|
|
35
|
+
* installed SDK 1.28: merged into BOTH the GET /sse and POST /messages).
|
|
36
|
+
*
|
|
37
|
+
* Local autostart: when the target is loopback and /health is
|
|
38
|
+
* connection-refused, spawn a DETACHED `cli.js server --port <n>` (unref,
|
|
39
|
+
* stdio ignored) and poll /health (~250 ms interval, 30 s cap). Concurrent
|
|
40
|
+
* bridges racing EADDRINUSE self-heal — the loser's child dies, the winner's
|
|
41
|
+
* daemon answers the poll, so the loop keeps polling regardless of child
|
|
42
|
+
* state. /health answering but not ok = a foreign or broken service on the
|
|
43
|
+
* port: explicit error, never a spawn. A remote target that is down is
|
|
44
|
+
* likewise an explicit error — we never spawn for remote URLs.
|
|
45
|
+
*
|
|
46
|
+
* STDIO DISCIPLINE: stdout carries ONLY the MCP protocol. Every diagnostic
|
|
47
|
+
* goes to stderr; fatal errors are a one-liner on stderr + non-zero exit
|
|
48
|
+
* (thrown to cli.ts's catch). Cancellation downstream→upstream rides the
|
|
49
|
+
* SDK-native path: the Protocol base aborts the handler's extra.signal on
|
|
50
|
+
* notifications/cancelled, and passing that signal into client.callTool makes
|
|
51
|
+
* the upstream Client emit its own notifications/cancelled with the CORRECT
|
|
52
|
+
* upstream request id (a verbatim forward would carry the downstream id,
|
|
53
|
+
* which means nothing to the daemon) — and reject the in-flight bridge call.
|
|
54
|
+
*/
|
|
55
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
|
+
exports.isLoopbackHost = isLoopbackHost;
|
|
57
|
+
exports.resolveBridgeTarget = resolveBridgeTarget;
|
|
58
|
+
exports.resolveBridgeToken = resolveBridgeToken;
|
|
59
|
+
exports.probeHealthOnce = probeHealthOnce;
|
|
60
|
+
exports.decideAutostart = decideAutostart;
|
|
61
|
+
exports.ensureDaemonReady = ensureDaemonReady;
|
|
62
|
+
exports.runMcpStdio = runMcpStdio;
|
|
63
|
+
const node_child_process_1 = require("node:child_process");
|
|
64
|
+
const node_fs_1 = require("node:fs");
|
|
65
|
+
const node_path_1 = require("node:path");
|
|
66
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
67
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
68
|
+
const index_js_2 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
69
|
+
const sse_js_1 = require("@modelcontextprotocol/sdk/client/sse.js");
|
|
70
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
71
|
+
const learnings_identity_js_1 = require("./learnings-identity.js");
|
|
72
|
+
// Version for the downstream Server declaration — read from package.json
|
|
73
|
+
// relative to __dirname exactly like mcp-server.ts does (both compile into
|
|
74
|
+
// dist/, so ".." lands on the package root). The bridge reports the SAME
|
|
75
|
+
// identity as the daemon's own McpServer so every surface agrees.
|
|
76
|
+
let VERSION = "0.0.0";
|
|
77
|
+
try {
|
|
78
|
+
const pkg = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, "..", "package.json"), "utf-8"));
|
|
79
|
+
VERSION = pkg.version;
|
|
80
|
+
}
|
|
81
|
+
catch { /* fallback — matches mcp-server.ts */ }
|
|
82
|
+
const DEFAULT_BRIDGE_PORT = 8787;
|
|
83
|
+
const HEALTH_PROBE_TIMEOUT_MS = 2000;
|
|
84
|
+
const AUTOSTART_POLL_INTERVAL_MS = 250;
|
|
85
|
+
const AUTOSTART_POLL_TOTAL_MS = 30_000;
|
|
86
|
+
/** Loopback check for URL hostnames (Node's URL keeps the brackets on [::1]). */
|
|
87
|
+
function isLoopbackHost(hostname) {
|
|
88
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
89
|
+
}
|
|
90
|
+
function parseTargetUrl(raw, source) {
|
|
91
|
+
const url = raw.trim().replace(/\/+$/, "");
|
|
92
|
+
let parsed;
|
|
93
|
+
try {
|
|
94
|
+
parsed = new URL(url);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// A user-editable env value / config value that is not a URL must fail
|
|
98
|
+
// with a message naming the input — not a bare "Invalid URL".
|
|
99
|
+
const origin = source === "option" ? "the given server URL"
|
|
100
|
+
: source === "env" ? "HICORTEX_SERVER_URL"
|
|
101
|
+
: source === "config" ? "the config serverUrl"
|
|
102
|
+
: "the default server URL";
|
|
103
|
+
throw new Error(`Cannot bridge to ${origin}: "${url}" is not a valid URL.`);
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
url,
|
|
107
|
+
local: isLoopbackHost(parsed.hostname),
|
|
108
|
+
port: parseInt(parsed.port, 10) || DEFAULT_BRIDGE_PORT,
|
|
109
|
+
source,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Resolve the daemon/server the bridge should talk to. Precedence: explicit
|
|
114
|
+
* option (the runMcpStdio test seam) → HICORTEX_SERVER_URL env → config file
|
|
115
|
+
* (client-mode serverUrl = remote; server-mode = localhost, the
|
|
116
|
+
* resolveConfig() semantics already shared by both CC hooks) → default local
|
|
117
|
+
* 8787. Blank/whitespace env values are ignored, not mistaken for targets.
|
|
118
|
+
*/
|
|
119
|
+
function resolveBridgeTarget(explicitUrl) {
|
|
120
|
+
if (typeof explicitUrl === "string" && explicitUrl.trim() !== "") {
|
|
121
|
+
return parseTargetUrl(explicitUrl, "option");
|
|
122
|
+
}
|
|
123
|
+
const envUrl = process.env.HICORTEX_SERVER_URL;
|
|
124
|
+
if (typeof envUrl === "string" && envUrl.trim() !== "") {
|
|
125
|
+
return parseTargetUrl(envUrl, "env");
|
|
126
|
+
}
|
|
127
|
+
// resolveConfig() already encodes both config shapes (client → remote
|
|
128
|
+
// serverUrl; server → http://127.0.0.1:<port ?? 8787>) and returns null
|
|
129
|
+
// when there is no usable config.
|
|
130
|
+
const config = (0, learnings_identity_js_1.resolveConfig)();
|
|
131
|
+
if (config)
|
|
132
|
+
return parseTargetUrl(config.serverUrl, "config");
|
|
133
|
+
return parseTargetUrl(`http://127.0.0.1:${DEFAULT_BRIDGE_PORT}`, "default");
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Resolve the bearer token for the upstream connection: explicit option →
|
|
137
|
+
* HICORTEX_AUTH_TOKEN env → config.authToken. Undefined = no token (a local
|
|
138
|
+
* daemon needs none — loopback bypasses auth).
|
|
139
|
+
*/
|
|
140
|
+
function resolveBridgeToken(explicitToken) {
|
|
141
|
+
if (typeof explicitToken === "string" && explicitToken.trim() !== "")
|
|
142
|
+
return explicitToken.trim();
|
|
143
|
+
const envToken = process.env.HICORTEX_AUTH_TOKEN;
|
|
144
|
+
if (typeof envToken === "string" && envToken.trim() !== "")
|
|
145
|
+
return envToken.trim();
|
|
146
|
+
return (0, learnings_identity_js_1.resolveConfig)()?.authToken;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* One GET /health probe. Connection refused / timeout / DNS failure →
|
|
150
|
+
* { reachable: false }; a response that is not ok → { reachable: true, ok:
|
|
151
|
+
* false } — the two carry different autostart decisions, so a boolean alone
|
|
152
|
+
* cannot express them.
|
|
153
|
+
*/
|
|
154
|
+
async function probeHealthOnce(url, timeoutMs = HEALTH_PROBE_TIMEOUT_MS) {
|
|
155
|
+
try {
|
|
156
|
+
const resp = await fetch(`${url}/health`, { signal: AbortSignal.timeout(timeoutMs) });
|
|
157
|
+
return { reachable: true, ok: resp.ok };
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return { reachable: false, ok: false };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Pure decision from one health probe: healthy → bridge; refused + loopback
|
|
165
|
+
* → spawn a local daemon; refused + remote → fail with an actionable message
|
|
166
|
+
* (never spawn for remote URLs); answering-but-not-ok → fail explicitly (a
|
|
167
|
+
* foreign or broken service owns the port — spawning next to it cannot help).
|
|
168
|
+
*/
|
|
169
|
+
function decideAutostart(probe, target) {
|
|
170
|
+
if (probe.ok)
|
|
171
|
+
return { action: "bridge" };
|
|
172
|
+
if (probe.reachable) {
|
|
173
|
+
return {
|
|
174
|
+
action: "fail",
|
|
175
|
+
reason: `Something is answering at ${target.url}/health but it is not a healthy Hicortex server. ` +
|
|
176
|
+
`A foreign or broken service owns that port — inspect it (e.g. lsof -i :${target.port}), ` +
|
|
177
|
+
`then either free the port or point HICORTEX_SERVER_URL at the real Hicortex server.`,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if (!target.local) {
|
|
181
|
+
return {
|
|
182
|
+
action: "fail",
|
|
183
|
+
reason: `Cannot reach the Hicortex server at ${target.url}. Start it on the server machine ` +
|
|
184
|
+
`(check with \`hicortex status\`, start with \`npx @gamaze/hicortex server\`) or fix HICORTEX_SERVER_URL. ` +
|
|
185
|
+
`If it answers 401 once up, set HICORTEX_AUTH_TOKEN to the server's auth token.`,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
return { action: "spawn" };
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Spawn a detached daemon: `node cli.js server --port <n>`. Detached + unref
|
|
192
|
+
* + ignored stdio — the daemon OUTLIVES this bridge (the product model; init
|
|
193
|
+
* installs it as a persistent daemon for exactly this) and never touches the
|
|
194
|
+
* bridge's stdio MCP wire. The spawned child's exit is not monitored on
|
|
195
|
+
* purpose: in the EADDRINUSE race (two bridges started the same missing
|
|
196
|
+
* daemon), the loser's child dies and the winner's daemon answers the
|
|
197
|
+
* /health poll — monitoring would teach us nothing actionable.
|
|
198
|
+
*/
|
|
199
|
+
function defaultSpawnDaemon(port) {
|
|
200
|
+
const child = (0, node_child_process_1.spawn)(process.execPath, [(0, node_path_1.join)(__dirname, "cli.js"), "server", "--port", String(port)], { detached: true, stdio: "ignore" });
|
|
201
|
+
child.unref();
|
|
202
|
+
}
|
|
203
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
204
|
+
/**
|
|
205
|
+
* Ensure something healthy answers at the target before bridging: probe once,
|
|
206
|
+
* decide, and if spawning — poll until healthy (or the deadline). Throws on
|
|
207
|
+
* every fail-path (explicit error, never silent degradation).
|
|
208
|
+
*/
|
|
209
|
+
async function ensureDaemonReady(target, options = {}) {
|
|
210
|
+
const probe = options.probeHealth ?? ((url) => probeHealthOnce(url));
|
|
211
|
+
const autostart = options.autostart ?? true;
|
|
212
|
+
const intervalMs = options.pollIntervalMs ?? AUTOSTART_POLL_INTERVAL_MS;
|
|
213
|
+
const totalMs = options.pollTotalMs ?? AUTOSTART_POLL_TOTAL_MS;
|
|
214
|
+
const initial = await probe(target.url);
|
|
215
|
+
const decision = decideAutostart(initial, target);
|
|
216
|
+
if (decision.action === "bridge")
|
|
217
|
+
return;
|
|
218
|
+
if (decision.action === "fail")
|
|
219
|
+
throw new Error(decision.reason);
|
|
220
|
+
if (!autostart) {
|
|
221
|
+
throw new Error(`No Hicortex server is running at ${target.url} (autostart disabled).`);
|
|
222
|
+
}
|
|
223
|
+
await (options.spawnDaemon ?? defaultSpawnDaemon)(target.port);
|
|
224
|
+
const deadline = Date.now() + totalMs;
|
|
225
|
+
while (Date.now() < deadline) {
|
|
226
|
+
await sleep(intervalMs);
|
|
227
|
+
// A mid-boot non-ok answer (daemon warming up) is NOT a foreign service —
|
|
228
|
+
// only the INITIAL probe's reachable-non-ok fails. Keep polling.
|
|
229
|
+
const current = await probe(target.url);
|
|
230
|
+
if (current.ok)
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
throw new Error(`The Hicortex server did not become healthy at ${target.url}/health within ` +
|
|
234
|
+
`${Math.round(totalMs / 1000)}s of autostart. Try \`npx @gamaze/hicortex server\` in a terminal ` +
|
|
235
|
+
`to see the daemon's startup error, then re-run this command.`);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Run the stdio MCP bridge. Resolves only after the downstream transport
|
|
239
|
+
* closes (the lifecycle handlers then exit the process); every setup failure
|
|
240
|
+
* throws for cli.ts to report on stderr and exit 1.
|
|
241
|
+
*/
|
|
242
|
+
async function runMcpStdio(options = {}) {
|
|
243
|
+
const target = resolveBridgeTarget(options.serverUrl);
|
|
244
|
+
const token = resolveBridgeToken(options.authToken);
|
|
245
|
+
await ensureDaemonReady(target, options);
|
|
246
|
+
// Upstream: the daemon's SSE MCP endpoint. requestInit headers ride BOTH
|
|
247
|
+
// the GET /sse and the POST /messages (SDK 1.28 _commonHeaders/send).
|
|
248
|
+
const upstream = new sse_js_1.SSEClientTransport(new URL(`${target.url}/sse`), token !== undefined ? { requestInit: { headers: { Authorization: `Bearer ${token}` } } } : {});
|
|
249
|
+
const client = new index_js_2.Client({ name: "hicortex-mcp-bridge", version: VERSION });
|
|
250
|
+
try {
|
|
251
|
+
await client.connect(upstream);
|
|
252
|
+
}
|
|
253
|
+
catch (err) {
|
|
254
|
+
// 401 from the daemon's auth middleware (remote connections; loopback is
|
|
255
|
+
// exempt). SseError carries the HTTP status as .code.
|
|
256
|
+
if (err.code === 401) {
|
|
257
|
+
throw new Error(`The Hicortex server at ${target.url} rejected the connection (401). ` +
|
|
258
|
+
`Set HICORTEX_AUTH_TOKEN to the server's auth token — it is printed by \`hicortex status\` on the server box.`);
|
|
259
|
+
}
|
|
260
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
261
|
+
}
|
|
262
|
+
// Downstream: a low-level Server over stdio advertising exactly what the
|
|
263
|
+
// daemon offers (tools). Ping is auto-answered by the Protocol base.
|
|
264
|
+
const server = new index_js_1.Server({ name: "hicortex", version: VERSION }, { capabilities: { tools: {} } });
|
|
265
|
+
// The proxy core — the SDK's documented proxy pattern. Forward the two
|
|
266
|
+
// tools requests and pass extra.signal through so a downstream
|
|
267
|
+
// notifications/cancelled aborts the upstream call (which emits the
|
|
268
|
+
// correctly-id'd cancellation to the daemon). Nothing else is forwarded
|
|
269
|
+
// request-wise: the daemon is tools-only and the base class answers ping.
|
|
270
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async (_request, extra) => (await client.listTools(undefined, { signal: extra.signal })));
|
|
271
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request, extra) => (await client.callTool(request.params, undefined, { signal: extra.signal })));
|
|
272
|
+
// Upstream → downstream notifications, best-effort: the daemon's tool-list
|
|
273
|
+
// changes or log messages reach the client; a closed far end must not kill
|
|
274
|
+
// the bridge from inside a notification handler.
|
|
275
|
+
client.fallbackNotificationHandler = async (notification) => {
|
|
276
|
+
try {
|
|
277
|
+
await server.notification(notification);
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
// Best-effort by design.
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
// Lifecycle: whichever side ends first tears down the other. The `exiting`
|
|
284
|
+
// guard keeps our OWN client.close() (graceful path) from being read as an
|
|
285
|
+
// upstream loss.
|
|
286
|
+
let exiting = false;
|
|
287
|
+
const shutdown = (code) => {
|
|
288
|
+
if (exiting)
|
|
289
|
+
return;
|
|
290
|
+
exiting = true;
|
|
291
|
+
void Promise.allSettled([server.close(), client.close()]).then(() => process.exit(code));
|
|
292
|
+
};
|
|
293
|
+
// Downstream closed (the MCP client went away) → close upstream → exit 0.
|
|
294
|
+
server.onclose = () => shutdown(0);
|
|
295
|
+
// Upstream transport died → the bridge cannot serve anything → exit 1.
|
|
296
|
+
client.onclose = () => {
|
|
297
|
+
if (exiting)
|
|
298
|
+
return;
|
|
299
|
+
console.error("[hicortex] mcp: lost the connection to the Hicortex server");
|
|
300
|
+
shutdown(1);
|
|
301
|
+
};
|
|
302
|
+
process.once("SIGINT", () => shutdown(0));
|
|
303
|
+
process.once("SIGTERM", () => shutdown(0));
|
|
304
|
+
const downstream = options.downstream ?? new stdio_js_1.StdioServerTransport();
|
|
305
|
+
await server.connect(downstream);
|
|
306
|
+
// Diagnostics NEVER touch stdout (the MCP wire) — stderr only.
|
|
307
|
+
console.error(`[hicortex] mcp: bridging stdio <-> ${target.url}/sse (target: ${target.source})`);
|
|
308
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gamaze/hicortex",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.4",
|
|
4
4
|
"description": "Persistent agent identity for AI agents \u2014 a hand-edited identity layer, nightly-distilled experience, and lessons injected every session, shared across your whole fleet. Works with Hermes, OpenClaw, Claude Code, Pi, and opencode.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"hicortex": "dist/cli.js"
|
|
8
8
|
},
|
|
9
|
+
"mcpName": "io.github.gamaze-labs/hicortex",
|
|
9
10
|
"openclaw": {
|
|
10
11
|
"extensions": [
|
|
11
12
|
"./dist/index.js"
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
"opencode-plugin/",
|
|
33
34
|
"openclaw.plugin.json",
|
|
34
35
|
"domains.example.json",
|
|
36
|
+
"server.json",
|
|
35
37
|
"README.md",
|
|
36
38
|
"THIRD_PARTY_NOTICES.md"
|
|
37
39
|
],
|
|
@@ -76,4 +78,4 @@
|
|
|
76
78
|
"tar-stream": "^2.2.0",
|
|
77
79
|
"undici": "^8.10.0"
|
|
78
80
|
}
|
|
79
|
-
}
|
|
81
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.gamaze-labs/hicortex",
|
|
4
|
+
"title": "Hicortex",
|
|
5
|
+
"description": "Shared fleet memory for AI agents \u2014 what one agent learns, the whole fleet knows.",
|
|
6
|
+
"version": "0.20.4",
|
|
7
|
+
"packages": [
|
|
8
|
+
{
|
|
9
|
+
"registryType": "npm",
|
|
10
|
+
"identifier": "@gamaze/hicortex",
|
|
11
|
+
"version": "0.20.4",
|
|
12
|
+
"transport": {
|
|
13
|
+
"type": "stdio",
|
|
14
|
+
"command": "npx",
|
|
15
|
+
"args": [
|
|
16
|
+
"-y",
|
|
17
|
+
"@gamaze/hicortex",
|
|
18
|
+
"mcp"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"environmentVariables": [
|
|
22
|
+
{
|
|
23
|
+
"name": "HICORTEX_AUTH_TOKEN",
|
|
24
|
+
"description": "Bearer token for a remote Hicortex server (server prints it via `hicortex status`). Optional \u2014 a local server needs no token.",
|
|
25
|
+
"isRequired": false,
|
|
26
|
+
"format": "string",
|
|
27
|
+
"isSecret": true
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "HICORTEX_SERVER_URL",
|
|
31
|
+
"description": "URL of a remote Hicortex server (e.g. https://your-server:8787). Optional \u2014 omit for a local server on this machine.",
|
|
32
|
+
"isRequired": false,
|
|
33
|
+
"format": "string",
|
|
34
|
+
"isSecret": false
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"repository": {
|
|
40
|
+
"url": "https://github.com/gamaze-labs/hicortex",
|
|
41
|
+
"source": "github"
|
|
42
|
+
},
|
|
43
|
+
"websiteUrl": "https://hicortex.gamaze.com"
|
|
44
|
+
}
|