gaslamp 1.0.0 → 1.0.1
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/bin/gaslamp.mjs +2 -2
- package/package.json +2 -2
- package/src/setup.mjs +42 -0
package/bin/gaslamp.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// gaslamp
|
|
2
|
+
// gaslamp: Minimal MCP server that lets Claude and Codex talk to each other.
|
|
3
3
|
//
|
|
4
4
|
// gaslamp run the MCP server on stdio (what Codex spawns)
|
|
5
5
|
// gaslamp serve same as above, explicit
|
|
@@ -18,7 +18,7 @@ import { fileURLToPath } from "node:url";
|
|
|
18
18
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
19
19
|
const pkg = JSON.parse(readFileSync(join(HERE, "..", "package.json"), "utf8"));
|
|
20
20
|
|
|
21
|
-
const HELP = `gaslamp ${pkg.version}
|
|
21
|
+
const HELP = `gaslamp ${pkg.version}: Minimal MCP server that lets Claude and Codex talk to each other.
|
|
22
22
|
|
|
23
23
|
Usage:
|
|
24
24
|
gaslamp [serve] Run the MCP server on stdio (what Codex spawns).
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gaslamp",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Minimal MCP server that lets Claude and Codex talk to each other.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"gaslamp": "bin/gaslamp.mjs"
|
package/src/setup.mjs
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
// the package is published, or for from-source dev.
|
|
17
17
|
|
|
18
18
|
import { spawnSync } from "node:child_process";
|
|
19
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { homedir } from "node:os";
|
|
19
21
|
import { dirname, join, resolve } from "node:path";
|
|
20
22
|
import { fileURLToPath } from "node:url";
|
|
21
23
|
import { which } from "./which.mjs";
|
|
@@ -26,6 +28,45 @@ function run(label, cmd, args) {
|
|
|
26
28
|
return r.status === 0;
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
// `codex mcp add` cannot persist per-server timeouts (its `-c` flag is a runtime
|
|
32
|
+
// override that never lands in the block), so we patch ~/.codex/config.toml
|
|
33
|
+
// directly. Without this, Codex's MCP client kills any tools/call that runs past
|
|
34
|
+
// its default tool_timeout_sec — a substantial consult (30-45 min) dies with
|
|
35
|
+
// `timed out awaiting tools/call after 120s` while the gaslamp child keeps
|
|
36
|
+
// working, orphaned. The server itself has no timeout; this is the client side.
|
|
37
|
+
// Idempotent: sets the keys in-place if present, inserts them if not.
|
|
38
|
+
function patchCodexTimeouts() {
|
|
39
|
+
const tool = process.env.GASLAMP_TOOL_TIMEOUT_SEC || "100000"; // ~27.8h — matches Claude Code's own default MCP tool timeout (1e8 ms), so both directions are symmetric
|
|
40
|
+
const startup = process.env.GASLAMP_STARTUP_TIMEOUT_SEC || "30"; // headroom for npx cold-start
|
|
41
|
+
const cfgPath = join(process.env.CODEX_HOME || join(homedir(), ".codex"), "config.toml");
|
|
42
|
+
|
|
43
|
+
let text;
|
|
44
|
+
try { text = readFileSync(cfgPath, "utf8"); }
|
|
45
|
+
catch { console.warn(` (could not read ${cfgPath}; skipped timeout patch)`); return; }
|
|
46
|
+
|
|
47
|
+
const lines = text.split("\n");
|
|
48
|
+
const header = lines.findIndex((l) => l.trim() === "[mcp_servers.gaslamp]");
|
|
49
|
+
if (header < 0) { console.warn(" (no [mcp_servers.gaslamp] block found; skipped timeout patch)"); return; }
|
|
50
|
+
|
|
51
|
+
// Block runs from the header to the next table header (line starting with `[`).
|
|
52
|
+
let end = lines.length;
|
|
53
|
+
for (let i = header + 1; i < lines.length; i++) { if (/^\s*\[/.test(lines[i])) { end = i; break; } }
|
|
54
|
+
const block = lines.slice(header, end);
|
|
55
|
+
|
|
56
|
+
const setKey = (key, val) => {
|
|
57
|
+
const i = block.findIndex((l) => new RegExp(`^\\s*${key}\\s*=`).test(l));
|
|
58
|
+
if (i >= 0) { block[i] = `${key} = ${val}`; return; }
|
|
59
|
+
let at = block.length; // insert before trailing blank lines
|
|
60
|
+
while (at > 1 && block[at - 1].trim() === "") at--;
|
|
61
|
+
block.splice(at, 0, `${key} = ${val}`);
|
|
62
|
+
};
|
|
63
|
+
setKey("tool_timeout_sec", tool);
|
|
64
|
+
setKey("startup_timeout_sec", startup);
|
|
65
|
+
|
|
66
|
+
writeFileSync(cfgPath, [...lines.slice(0, header), ...block, ...lines.slice(end)].join("\n"));
|
|
67
|
+
process.stdout.write(` timeouts: tool_timeout_sec=${tool}s startup_timeout_sec=${startup}s in ${cfgPath}\n`);
|
|
68
|
+
}
|
|
69
|
+
|
|
29
70
|
// Quietly remove an existing registration (ignore "not found" noise).
|
|
30
71
|
function remove(cli, name, scopeArgs = []) {
|
|
31
72
|
spawnSync(cli, ["mcp", "remove", name, ...scopeArgs], { stdio: "ignore" });
|
|
@@ -62,6 +103,7 @@ export function runSetup(argv = []) {
|
|
|
62
103
|
serveCmd = ["mcp", "add", "gaslamp", "--", "npx", "-y", "gaslamp", "serve"];
|
|
63
104
|
}
|
|
64
105
|
const okCodex = run("codex -> claude", "codex", serveCmd);
|
|
106
|
+
if (okCodex) patchCodexTimeouts(); // codex mcp add can't persist these; do it ourselves
|
|
65
107
|
|
|
66
108
|
// --- Claude -> Codex: claude gets gaslamp's codex tools (user scope) --------
|
|
67
109
|
remove("claude", "gaslamp", ["-s", "user"]);
|