typegraph-mcp 0.9.27 → 0.9.29

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 CHANGED
@@ -80,6 +80,8 @@ This gives you 14 MCP tools, 5 workflow skills that teach Claude *when* and *how
80
80
 
81
81
  **Other agents** (Cursor, Codex CLI, Gemini CLI, GitHub Copilot) — restart your agent session. The MCP server and skills are already configured.
82
82
 
83
+ For **Codex CLI**, setup now registers the server with `codex mcp add` using absolute paths so the tools work even when Codex launches from outside your project root.
84
+
83
85
  First query takes ~2s (tsserver warmup). Subsequent queries: 1-60ms.
84
86
 
85
87
  ## Requirements
@@ -147,6 +149,26 @@ npx typegraph-mcp check
147
149
 
148
150
  ## Manual MCP configuration
149
151
 
152
+ ### Codex CLI
153
+
154
+ Register the server with absolute paths:
155
+
156
+ ```bash
157
+ codex mcp add typegraph \
158
+ --env TYPEGRAPH_PROJECT_ROOT=/absolute/path/to/your-project \
159
+ --env TYPEGRAPH_TSCONFIG=/absolute/path/to/your-project/tsconfig.json \
160
+ -- npx tsx /absolute/path/to/your-project/plugins/typegraph-mcp/server.ts
161
+ ```
162
+
163
+ Verify with:
164
+
165
+ ```bash
166
+ codex mcp get typegraph
167
+ codex mcp list
168
+ ```
169
+
170
+ ### JSON-based MCP clients
171
+
150
172
  Add to `.claude/mcp.json` (or `~/.claude/mcp.json` for global):
151
173
 
152
174
  ```json
package/check.ts CHANGED
@@ -12,7 +12,7 @@
12
12
  import * as fs from "node:fs";
13
13
  import * as path from "node:path";
14
14
  import { createRequire } from "node:module";
15
- import { spawn } from "node:child_process";
15
+ import { spawn, spawnSync } from "node:child_process";
16
16
  import { resolveConfig, type TypegraphConfig } from "./config.js";
17
17
 
18
18
  // ─── Result Type ─────────────────────────────────────────────────────────────
@@ -186,10 +186,17 @@ export async function main(configOverride?: TypegraphConfig): Promise<CheckResul
186
186
  // Check for plugin .mcp.json in the tool directory (embedded plugin install)
187
187
  const pluginMcpPath = path.join(toolDir, ".mcp.json");
188
188
  const hasPluginMcp = fs.existsSync(pluginMcpPath) && fs.existsSync(path.join(toolDir, ".claude-plugin/plugin.json"));
189
+ const codexGet = spawnSync("codex", ["mcp", "get", "typegraph"], {
190
+ stdio: "pipe",
191
+ encoding: "utf-8",
192
+ });
193
+ const hasCodexRegistration = codexGet.status === 0;
189
194
  if (process.env.CLAUDE_PLUGIN_ROOT) {
190
195
  pass("MCP registered via plugin (CLAUDE_PLUGIN_ROOT set)");
191
196
  } else if (hasPluginMcp) {
192
197
  pass("MCP registered via plugin (.mcp.json + .claude-plugin/ present)");
198
+ } else if (hasCodexRegistration) {
199
+ pass("MCP registered in Codex CLI");
193
200
  } else {
194
201
  const mcpJsonPath = path.resolve(projectRoot, ".claude/mcp.json");
195
202
  if (fs.existsSync(mcpJsonPath)) {
@@ -450,4 +457,3 @@ export async function main(configOverride?: TypegraphConfig): Promise<CheckResul
450
457
 
451
458
  return { passed, failed, warned };
452
459
  }
453
-
package/cli.ts CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  import * as fs from "node:fs";
17
17
  import * as path from "node:path";
18
- import { execSync } from "node:child_process";
18
+ import { execSync, spawnSync } from "node:child_process";
19
19
  import * as p from "@clack/prompts";
20
20
  import { resolveConfig } from "./config.js";
21
21
 
@@ -177,6 +177,21 @@ const MCP_SERVER_ENTRY = {
177
177
  },
178
178
  };
179
179
 
180
+ function getAbsoluteMcpServerEntry(projectRoot: string): {
181
+ command: string;
182
+ args: string[];
183
+ env: Record<string, string>;
184
+ } {
185
+ return {
186
+ command: "npx",
187
+ args: ["tsx", path.resolve(projectRoot, PLUGIN_DIR_NAME, "server.ts")],
188
+ env: {
189
+ TYPEGRAPH_PROJECT_ROOT: projectRoot,
190
+ TYPEGRAPH_TSCONFIG: path.resolve(projectRoot, "tsconfig.json"),
191
+ },
192
+ };
193
+ }
194
+
180
195
  /** Register the typegraph MCP server in agent-specific config files */
181
196
  function registerMcpServers(projectRoot: string, selectedAgents: AgentId[]): void {
182
197
  if (selectedAgents.includes("cursor")) {
@@ -259,6 +274,57 @@ function deregisterJsonMcp(projectRoot: string, configPath: string, rootKey: str
259
274
 
260
275
  /** Register MCP server in Codex CLI's TOML config */
261
276
  function registerCodexMcp(projectRoot: string): void {
277
+ const absoluteEntry = getAbsoluteMcpServerEntry(projectRoot);
278
+
279
+ const codexGet = spawnSync("codex", ["mcp", "get", "typegraph"], {
280
+ stdio: "pipe",
281
+ encoding: "utf-8",
282
+ });
283
+
284
+ if (codexGet.status === 0) {
285
+ const output = `${codexGet.stdout ?? ""}${codexGet.stderr ?? ""}`;
286
+ const hasServerPath = output.includes(absoluteEntry.args[1]!);
287
+ const hasProjectRoot = output.includes("TYPEGRAPH_PROJECT_ROOT=*****") || output.includes(projectRoot);
288
+ const hasTsconfig = output.includes("TYPEGRAPH_TSCONFIG=*****") || output.includes(path.resolve(projectRoot, "tsconfig.json"));
289
+ if (hasServerPath && hasProjectRoot && hasTsconfig) {
290
+ p.log.info("Codex CLI: typegraph MCP server already registered");
291
+ return;
292
+ }
293
+ spawnSync("codex", ["mcp", "remove", "typegraph"], {
294
+ stdio: "pipe",
295
+ encoding: "utf-8",
296
+ });
297
+ }
298
+
299
+ const codexAdd = spawnSync(
300
+ "codex",
301
+ [
302
+ "mcp",
303
+ "add",
304
+ "typegraph",
305
+ "--env",
306
+ `TYPEGRAPH_PROJECT_ROOT=${absoluteEntry.env.TYPEGRAPH_PROJECT_ROOT}`,
307
+ "--env",
308
+ `TYPEGRAPH_TSCONFIG=${absoluteEntry.env.TYPEGRAPH_TSCONFIG}`,
309
+ "--",
310
+ absoluteEntry.command,
311
+ ...absoluteEntry.args,
312
+ ],
313
+ {
314
+ stdio: "pipe",
315
+ encoding: "utf-8",
316
+ }
317
+ );
318
+
319
+ if (codexAdd.status === 0) {
320
+ p.log.success("Codex CLI: registered typegraph MCP server");
321
+ return;
322
+ }
323
+
324
+ p.log.warn(
325
+ `Codex CLI registration failed — falling back to ${".codex/config.toml"}`
326
+ );
327
+
262
328
  const configPath = ".codex/config.toml";
263
329
  const fullPath = path.resolve(projectRoot, configPath);
264
330
  let content = "";
@@ -275,9 +341,9 @@ function registerCodexMcp(projectRoot: string): void {
275
341
  const block = [
276
342
  "",
277
343
  "[mcp_servers.typegraph]",
278
- 'command = "npx"',
279
- 'args = ["tsx", "./plugins/typegraph-mcp/server.ts"]',
280
- 'env = { TYPEGRAPH_PROJECT_ROOT = ".", TYPEGRAPH_TSCONFIG = "./tsconfig.json" }',
344
+ `command = "${absoluteEntry.command}"`,
345
+ `args = ["${absoluteEntry.args[0]}", "${absoluteEntry.args[1]}"]`,
346
+ `env = { TYPEGRAPH_PROJECT_ROOT = "${absoluteEntry.env.TYPEGRAPH_PROJECT_ROOT}", TYPEGRAPH_TSCONFIG = "${absoluteEntry.env.TYPEGRAPH_TSCONFIG}" }`,
281
347
  "",
282
348
  ].join("\n");
283
349
 
@@ -292,6 +358,14 @@ function registerCodexMcp(projectRoot: string): void {
292
358
 
293
359
  /** Deregister MCP server from Codex CLI's TOML config */
294
360
  function deregisterCodexMcp(projectRoot: string): void {
361
+ const codexRemove = spawnSync("codex", ["mcp", "remove", "typegraph"], {
362
+ stdio: "pipe",
363
+ encoding: "utf-8",
364
+ });
365
+ if (codexRemove.status === 0) {
366
+ p.log.info("Codex CLI: removed typegraph MCP server");
367
+ }
368
+
295
369
  const configPath = ".codex/config.toml";
296
370
  const fullPath = path.resolve(projectRoot, configPath);
297
371
  if (!fs.existsSync(fullPath)) return;
package/dist/check.js CHANGED
@@ -355,7 +355,7 @@ var init_module_graph = __esm({
355
355
  import * as fs2 from "fs";
356
356
  import * as path3 from "path";
357
357
  import { createRequire } from "module";
358
- import { spawn } from "child_process";
358
+ import { spawn, spawnSync } from "child_process";
359
359
 
360
360
  // config.ts
361
361
  import * as path from "path";
@@ -495,10 +495,17 @@ async function main(configOverride) {
495
495
  }
496
496
  const pluginMcpPath = path3.join(toolDir, ".mcp.json");
497
497
  const hasPluginMcp = fs2.existsSync(pluginMcpPath) && fs2.existsSync(path3.join(toolDir, ".claude-plugin/plugin.json"));
498
+ const codexGet = spawnSync("codex", ["mcp", "get", "typegraph"], {
499
+ stdio: "pipe",
500
+ encoding: "utf-8"
501
+ });
502
+ const hasCodexRegistration = codexGet.status === 0;
498
503
  if (process.env.CLAUDE_PLUGIN_ROOT) {
499
504
  pass("MCP registered via plugin (CLAUDE_PLUGIN_ROOT set)");
500
505
  } else if (hasPluginMcp) {
501
506
  pass("MCP registered via plugin (.mcp.json + .claude-plugin/ present)");
507
+ } else if (hasCodexRegistration) {
508
+ pass("MCP registered in Codex CLI");
502
509
  } else {
503
510
  const mcpJsonPath = path3.resolve(projectRoot, ".claude/mcp.json");
504
511
  if (fs2.existsSync(mcpJsonPath)) {
package/dist/cli.js CHANGED
@@ -375,7 +375,7 @@ __export(check_exports, {
375
375
  import * as fs2 from "fs";
376
376
  import * as path3 from "path";
377
377
  import { createRequire } from "module";
378
- import { spawn } from "child_process";
378
+ import { spawn, spawnSync } from "child_process";
379
379
  function findFirstTsFile(dir) {
380
380
  const skipDirs = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", ".wrangler", "coverage"]);
381
381
  try {
@@ -502,10 +502,17 @@ async function main(configOverride) {
502
502
  }
503
503
  const pluginMcpPath = path3.join(toolDir, ".mcp.json");
504
504
  const hasPluginMcp = fs2.existsSync(pluginMcpPath) && fs2.existsSync(path3.join(toolDir, ".claude-plugin/plugin.json"));
505
+ const codexGet = spawnSync("codex", ["mcp", "get", "typegraph"], {
506
+ stdio: "pipe",
507
+ encoding: "utf-8"
508
+ });
509
+ const hasCodexRegistration = codexGet.status === 0;
505
510
  if (process.env.CLAUDE_PLUGIN_ROOT) {
506
511
  pass("MCP registered via plugin (CLAUDE_PLUGIN_ROOT set)");
507
512
  } else if (hasPluginMcp) {
508
513
  pass("MCP registered via plugin (.mcp.json + .claude-plugin/ present)");
514
+ } else if (hasCodexRegistration) {
515
+ pass("MCP registered in Codex CLI");
509
516
  } else {
510
517
  const mcpJsonPath = path3.resolve(projectRoot3, ".claude/mcp.json");
511
518
  if (fs2.existsSync(mcpJsonPath)) {
@@ -2795,7 +2802,7 @@ var init_server = __esm({
2795
2802
  init_config();
2796
2803
  import * as fs8 from "fs";
2797
2804
  import * as path9 from "path";
2798
- import { execSync as execSync2 } from "child_process";
2805
+ import { execSync as execSync2, spawnSync as spawnSync2 } from "child_process";
2799
2806
  import * as p from "@clack/prompts";
2800
2807
  var AGENT_SNIPPET = `
2801
2808
  ## TypeScript Navigation (typegraph-mcp)
@@ -2915,6 +2922,16 @@ var MCP_SERVER_ENTRY = {
2915
2922
  TYPEGRAPH_TSCONFIG: "./tsconfig.json"
2916
2923
  }
2917
2924
  };
2925
+ function getAbsoluteMcpServerEntry(projectRoot3) {
2926
+ return {
2927
+ command: "npx",
2928
+ args: ["tsx", path9.resolve(projectRoot3, PLUGIN_DIR_NAME, "server.ts")],
2929
+ env: {
2930
+ TYPEGRAPH_PROJECT_ROOT: projectRoot3,
2931
+ TYPEGRAPH_TSCONFIG: path9.resolve(projectRoot3, "tsconfig.json")
2932
+ }
2933
+ };
2934
+ }
2918
2935
  function registerMcpServers(projectRoot3, selectedAgents) {
2919
2936
  if (selectedAgents.includes("cursor")) {
2920
2937
  registerJsonMcp(projectRoot3, ".cursor/mcp.json", "mcpServers");
@@ -2977,6 +2994,51 @@ function deregisterJsonMcp(projectRoot3, configPath, rootKey) {
2977
2994
  }
2978
2995
  }
2979
2996
  function registerCodexMcp(projectRoot3) {
2997
+ const absoluteEntry = getAbsoluteMcpServerEntry(projectRoot3);
2998
+ const codexGet = spawnSync2("codex", ["mcp", "get", "typegraph"], {
2999
+ stdio: "pipe",
3000
+ encoding: "utf-8"
3001
+ });
3002
+ if (codexGet.status === 0) {
3003
+ const output = `${codexGet.stdout ?? ""}${codexGet.stderr ?? ""}`;
3004
+ const hasServerPath = output.includes(absoluteEntry.args[1]);
3005
+ const hasProjectRoot = output.includes("TYPEGRAPH_PROJECT_ROOT=*****") || output.includes(projectRoot3);
3006
+ const hasTsconfig = output.includes("TYPEGRAPH_TSCONFIG=*****") || output.includes(path9.resolve(projectRoot3, "tsconfig.json"));
3007
+ if (hasServerPath && hasProjectRoot && hasTsconfig) {
3008
+ p.log.info("Codex CLI: typegraph MCP server already registered");
3009
+ return;
3010
+ }
3011
+ spawnSync2("codex", ["mcp", "remove", "typegraph"], {
3012
+ stdio: "pipe",
3013
+ encoding: "utf-8"
3014
+ });
3015
+ }
3016
+ const codexAdd = spawnSync2(
3017
+ "codex",
3018
+ [
3019
+ "mcp",
3020
+ "add",
3021
+ "typegraph",
3022
+ "--env",
3023
+ `TYPEGRAPH_PROJECT_ROOT=${absoluteEntry.env.TYPEGRAPH_PROJECT_ROOT}`,
3024
+ "--env",
3025
+ `TYPEGRAPH_TSCONFIG=${absoluteEntry.env.TYPEGRAPH_TSCONFIG}`,
3026
+ "--",
3027
+ absoluteEntry.command,
3028
+ ...absoluteEntry.args
3029
+ ],
3030
+ {
3031
+ stdio: "pipe",
3032
+ encoding: "utf-8"
3033
+ }
3034
+ );
3035
+ if (codexAdd.status === 0) {
3036
+ p.log.success("Codex CLI: registered typegraph MCP server");
3037
+ return;
3038
+ }
3039
+ p.log.warn(
3040
+ `Codex CLI registration failed \u2014 falling back to ${".codex/config.toml"}`
3041
+ );
2980
3042
  const configPath = ".codex/config.toml";
2981
3043
  const fullPath = path9.resolve(projectRoot3, configPath);
2982
3044
  let content = "";
@@ -2990,9 +3052,9 @@ function registerCodexMcp(projectRoot3) {
2990
3052
  const block = [
2991
3053
  "",
2992
3054
  "[mcp_servers.typegraph]",
2993
- 'command = "npx"',
2994
- 'args = ["tsx", "./plugins/typegraph-mcp/server.ts"]',
2995
- 'env = { TYPEGRAPH_PROJECT_ROOT = ".", TYPEGRAPH_TSCONFIG = "./tsconfig.json" }',
3055
+ `command = "${absoluteEntry.command}"`,
3056
+ `args = ["${absoluteEntry.args[0]}", "${absoluteEntry.args[1]}"]`,
3057
+ `env = { TYPEGRAPH_PROJECT_ROOT = "${absoluteEntry.env.TYPEGRAPH_PROJECT_ROOT}", TYPEGRAPH_TSCONFIG = "${absoluteEntry.env.TYPEGRAPH_TSCONFIG}" }`,
2996
3058
  ""
2997
3059
  ].join("\n");
2998
3060
  const dir = path9.dirname(fullPath);
@@ -3004,6 +3066,13 @@ function registerCodexMcp(projectRoot3) {
3004
3066
  p.log.success(`${configPath}: registered typegraph MCP server`);
3005
3067
  }
3006
3068
  function deregisterCodexMcp(projectRoot3) {
3069
+ const codexRemove = spawnSync2("codex", ["mcp", "remove", "typegraph"], {
3070
+ stdio: "pipe",
3071
+ encoding: "utf-8"
3072
+ });
3073
+ if (codexRemove.status === 0) {
3074
+ p.log.info("Codex CLI: removed typegraph MCP server");
3075
+ }
3007
3076
  const configPath = ".codex/config.toml";
3008
3077
  const fullPath = path9.resolve(projectRoot3, configPath);
3009
3078
  if (!fs8.existsSync(fullPath)) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typegraph-mcp",
3
- "version": "0.9.27",
3
+ "version": "0.9.29",
4
4
  "description": "Type-aware codebase navigation for AI coding agents — 14 MCP tools powered by tsserver + oxc",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -43,6 +43,7 @@
43
43
  "devDependencies": {
44
44
  "@types/node": "^25.3.0",
45
45
  "tsup": "^8.5.0",
46
+ "tsx": "^4.21.0",
46
47
  "typescript": "^5.8.0"
47
48
  }
48
49
  }