فهرست منبع

fix(cli): stop `serve --mcp` from confusing humans — hide it + explain on a TTY (#867)

`codegraph serve --mcp` is the stdio MCP server an AI agent launches for itself
(the installer wires it into every agent's MCP config), not a command a human
runs. Run by hand in a terminal it just hung waiting for JSON-RPC, looking
broken.

- Hide `serve` from `--help` (commander `{ hidden: true }`); it stays fully
  invocable, so agents are unaffected.
- When stdin is an interactive TTY (a person — never the agent's pipe or the
  detached daemon), print what it is and point to `codegraph status` /
  `codegraph daemon`, then exit instead of hanging.
- README: drop `serve --mcp` from the CLI Reference and stop the troubleshooting
  section from telling users to run it; keep the accurate "your agent launches
  it" note.

Verified: agent path intact (22 MCP handshake/daemon tests pass), `serve` absent
from --help, and the TTY path prints the message and exits cleanly.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Colby Mchenry 1 هفته پیش
والد
کامیت
64ff7597d0
4فایلهای تغییر یافته به همراه31 افزوده شده و 3 حذف شده
  1. 1 0
      CHANGELOG.md
  2. 1 2
      README.md
  3. 7 0
      __tests__/cli-version.test.ts
  4. 22 1
      src/bin/codegraph.ts

+ 1 - 0
CHANGELOG.md

@@ -17,6 +17,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ### Fixes
 
+- Running `codegraph serve --mcp` by hand no longer just hangs in silence. That command is the MCP server your AI agent starts for itself — not a step you run directly — and in a terminal it used to sit there waiting for input that never comes, looking broken. It now recognizes when a person runs it and explains what to do instead (`codegraph status`, `codegraph daemon`), and it's been dropped from the command listing so it stops looking like something you need to launch.
 - Cross-file static method calls like `ClassName.staticMethod()` now resolve correctly. CodeGraph was linking the call to the *class* instead of the method (and recording it as a construction), so `callers` and `impact` for a static method came back empty — a real blind spot in TypeScript and JavaScript codebases that lean on static utility classes (Python and other languages with the same call shape benefit too). The call now links to the method itself. Thanks @contextFlow-lab. (#825)
 - `codegraph affected` now accepts `./`-prefixed and absolute file paths, not just bare project-relative ones. Passing `./src/x.ts` or an absolute path — common when the file list comes from another tool — used to silently match nothing and report no affected tests. Thanks @contextFlow-lab. (#825)
 - The CodeGraph MCP server no longer risks getting stuck at 100% CPU after an unexpected internal error. Previously such an error was logged but the process was left running in a broken state, where it could spin a CPU core indefinitely and had to be killed by hand. The server now logs the error and exits cleanly, so a fresh one starts on the next request. Thanks @songhlc. (#850)

+ 1 - 2
README.md

@@ -485,7 +485,6 @@ codegraph callers <symbol>        # Find what calls a function/method (--limit,
 codegraph callees <symbol>        # Find what a function/method calls (--limit, --json)
 codegraph impact <symbol>         # Analyze what code is affected by changing a symbol (--depth, --json)
 codegraph affected [files...]     # Find test files affected by changes (see below)
-codegraph serve --mcp             # Start MCP server
 codegraph daemon                  # Manage background daemons — pick one to stop (alias: daemons)
 codegraph telemetry [on|off]      # Show or change anonymous usage telemetry
 codegraph upgrade [version]       # Update to the latest release (--check, --force)
@@ -723,7 +722,7 @@ Framework routing is validated the same way, on a canonical app per framework: E
 - **You're on an old (pre-0.9) install.** Reinstall to get the bundled runtime — `curl -fsSL https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh | sh` (macOS/Linux), `irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex` (Windows), or `npm i -g @colbymchenry/codegraph@latest`.
 - **`codegraph status` shows `Journal:` other than `wal`** — WAL couldn't be enabled on this filesystem (common on network shares and WSL2 `/mnt`), so reads can block on writes. Move the project (with its `.codegraph/` folder) onto a local disk.
 
-**MCP server not connecting** — Ensure the project is initialized/indexed, verify the path in your MCP config, and check that `codegraph serve --mcp` works from the command line.
+**MCP server not connecting** — Your agent starts the server itself, so you don't launch it by hand. Make sure the project is initialized and indexed (`codegraph status`) and that the path in your MCP config is correct. If it still won't connect, re-run `codegraph install` to rewrite the config.
 
 **Missing symbols** — The MCP server auto-syncs on save (wait a couple seconds). Run `codegraph sync` manually if needed. Check that the file's language is supported and isn't inside a `.gitignore`d or default-excluded directory (e.g. `node_modules`, `dist`).
 

+ 7 - 0
__tests__/cli-version.test.ts

@@ -48,6 +48,13 @@ describe('codegraph version affordances', () => {
     expect(out).toContain('Commands:');
   });
 
+  it('hides the internal `serve` command from --help', () => {
+    // `serve --mcp` is the stdio entry point an AI agent launches for itself,
+    // not a human command — it must not appear in the listing. (It stays fully
+    // invocable; the mcp-initialize suite covers that the agent path works.)
+    expect(run(['--help'])).not.toMatch(/^\s+serve\b/m);
+  });
+
   it('a trailing `-v` is still the subcommand\'s --verbose, not the version intercept', () => {
     // A fresh temp dir outside any indexed project: `index -v` parses `-v` as
     // the index command's --verbose, then short-circuits at "not initialized"

+ 22 - 1
src/bin/codegraph.ts

@@ -1349,7 +1349,12 @@ program
  * codegraph serve
  */
 program
-  .command('serve')
+  // Hidden from `--help`: this is the stdio entry point an AI agent launches
+  // for itself (the installer wires `args: ['serve','--mcp']` into every
+  // agent's MCP config), not a command a human runs. It still works when
+  // invoked — hiding only removes it from the listing. See the interactive-TTY
+  // guard below, which explains this to anyone who runs it by hand.
+  .command('serve', { hidden: true })
   .description('Start CodeGraph as an MCP server for AI assistants')
   .option('-p, --path <path>', 'Project path (optional for MCP mode, uses rootUri from client)')
   .option('--mcp', 'Run as MCP server (stdio transport)')
@@ -1365,6 +1370,22 @@ program
 
     try {
       if (options.mcp) {
+        // `serve --mcp` is the stdio MCP server an AI agent launches for itself,
+        // not a command to run by hand. A human in a terminal would otherwise
+        // see it hang waiting for JSON-RPC on stdin, which reads as broken. If
+        // stdin is an interactive TTY, explain instead of hanging. The agent's
+        // pipe and the detached daemon both have a non-TTY stdin, so this only
+        // ever fires for a person who typed it.
+        if (process.stdin.isTTY && !process.env.CODEGRAPH_DAEMON_INTERNAL) {
+          console.error(chalk.bold('\nCodeGraph MCP server\n'));
+          console.error("This is the MCP server your AI agent (Claude Code, Cursor, Codex, opencode, …)");
+          console.error("starts automatically — you don't run it yourself.");
+          console.error(`\nIt's already wired up by ${chalk.cyan('codegraph install')}. To check on things:`);
+          console.error(`  ${chalk.cyan('codegraph status')}   ${chalk.dim('— is this project indexed and healthy?')}`);
+          console.error(`  ${chalk.cyan('codegraph daemon')}   ${chalk.dim('— list or stop background MCP servers')}`);
+          console.error(chalk.dim('\n(Running it directly only does something when an MCP client drives it over stdin.)'));
+          return;
+        }
         // Start MCP server - it handles initialization lazily based on rootUri from client
         const { MCPServer } = await import('../mcp/index');
         const server = new MCPServer(projectPath);