Commit Graph
6 Commits
Author SHA1 Message Date
Colby MchenryandClaude Opus 5.5 f4c4dae91c feat(mcp): the server gives hosts its title and icon (#3095)
`initialize` now answers with serverInfo.title "CodeGraph" and one
embedded SVG in serverInfo.icons, on the direct path and from the
daemon proxy, which share SERVER_INFO. A host that shows them can list
CodeGraph by name and logo instead of a plain `codegraph`.

The icon is the site's favicon (site/public/favicon.svg) on a light
tile. The bare mark is dark ink on nothing, and on a dark background
only its two white dots show (checked in a browser at 16 to 64 px). It
is spelled out in the source instead of as a base64 blob, so a reader
can see it is three lines and three circles.

`sizes` is left off the icon on purpose. It's optional and says nothing
about an SVG, and @modelcontextprotocol/sdk 1.18.0 typed it as a string
where the spec settled on an array, so a host on that SDK would fail the
whole handshake on the array form.

Left out from PR #879, on purpose: `_meta.icons` and icons on every
tool. Those add about 1 KB per tool to each tools/list for no host we
could find that needs them; Codex reads serverInfo.icons.

What each host shows, read from its source or binary on 2026-10-30:
- Codex: keeps serverInfo.title and .icons and sends them to its app
  (McpServerStatus.serverInfo); the terminal UI's /mcp doesn't read them.
- opencode: reads serverInfo.title into the server list; its dialogs
  still print the config name, and it doesn't read icons.
- Claude Code 2.1.296: connects fine with these fields (checked with
  `claude mcp list` in a sandbox home), and no read of title or icons
  turns up in its binary, so it keeps showing the config name.
- Cursor: closed source and not on this machine. Its docs mention no
  server title or icon, so this is unverified.

Validation: 3 tests in mcp-initialize fail on main's session and pass
here; mcp-daemon now pins the proxy's serverInfo to the same object.

Reimplements PR #879 by @fedosov.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 18:38:45 +00:00
Colby MchenryandClaude Opus 5.5 a25706950f test: wait for what holds an MCP test's dir before removing it (#2624)
Two MCP test files sometimes failed under full-suite load on Windows with
EBUSY in their afterEach, and then left their temp dir behind:

- mcp-status-freshness: every case leaves an index-freshness measurement
  worker still ending, and the deadline case's worker is terminated in the
  middle of its measurement. Until it exits, the worker can hold the index
  or the project dir, and the removal raced that exit (EBUSY on
  codegraph.db or on the dir, ENOTEMPTY on .codegraph). The afterEach now
  awaits endFreshnessMeasurements(), the server's own shutdown wait, before
  it closes the index and removes the dir.
- mcp-initialize: after the killed server's 'close', the dir itself can
  stay busy a moment longer (EBUSY rmdir). It freed within about 80 ms,
  with no process left under the server. A bounded `maxRetries` doesn't
  help: on Node 22, fs.rmSync never retries EBUSY on a directory and throws
  at once. So removeWhenReleased() retries the removal itself, for up to
  5 s, and a Windows-only test holds a dir as a child's cwd to prove the
  helper waits.

Under CPU load (14 of 16 cores busy, two runs at once), the status
teardown failed 20 times in 240 without the wait and 0 times in 240 with
it. Alternating real runs of both files failed in 5 of 18 on main and 0 of
10 with the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 18:09:04 +00:00
Colby Mchenry 0a9f3de72c fix: make the full test suite pass on Windows (#2053)
* test: close resolution graphs before removing Windows fixtures

Close ArkTS and JVM graph connections in afterEach, including failed assertions. Verified both files on Windows 11 and macOS (32 tests).

* test: compare explore source lines across LF and CRLF checkouts

Normalize only line endings when matching emitted source against checked-out fixtures. Preserve the closure coverage and whole-line assertions. Both files pass on Windows 11 and macOS (15 tests).

* test: exercise old Git fallback without a POSIX shell shim

Inject only the unsupported ls-files invocation and run every other Git command against real repositories. Assert that each rejected call retries, use portable graph paths, and clean up fixtures. Passes on Windows 11 and macOS.

* test: normalize Python fixture before generating CRLF cases

The tests added in #2004 produced CRCRLF from a Windows checkout. Normalize the input to LF before constructing either variant. All 28 native and WASM cases pass on Windows 11 and macOS.

* test: allow time for eviction fixture indexes on Windows

The #2036 eviction test builds and reconciles nine real indexes and consistently exceeds its default five-second timeout on the VM. Allow 15 seconds for this test alone, preserving every assertion. The full file passes on Windows 11 and macOS (21 tests).

* test: finish MCP processes before resetting Windows fixtures

Launch actual servers with their runtime flags and await termination before directory removal. Use asynchronous removal retries for daemon fixtures. Establish a ready daemon and stop its first proxy before simulating PID reuse, then make source stale without opening a competing SQLite writer. Preserve lock and database byte-equality assertions. All touched files validated on Windows 11 and macOS.

* test: allow Windows named-pipe fallback polling to finish

The VM takes up to 25.7 seconds to complete the nominal six-second retry loop. Allow 30 seconds for the three fallback waits and 40 seconds for the two shorter enclosing tests, retaining their lock-preservation and read-only assertions. The full daemon file is validated on Windows 11 and macOS.

* fix(daemon): retry transient Windows PID-file sharing violations

A transient EPERM replacing daemon.pid aborted startup, observed in the quiet-client test and reproduced with a real locked handle at ba3c21e5. Retry only Windows sharing errors with five bounded delays, rechecking ownership each time and cleaning temporary files. Keep publication synchronous before client processing. Cover transient/permanent failures, changed ownership, non-retryable errors, and a real Windows handle; require a confirmed daemon round-trip before measuring a quiet session. Validated on Windows 11 and macOS.
2026-09-27 16:26:10 +00:00
Colby MchenryandClaude Opus 4.8 ddb1a8f72d fix: issue-triage quick wins (extraction, MCP probes, gitignore, CJK, impact) (#654)
Batch of small, localized fixes from an open-issue triage:

- .codegraph/.gitignore now ignores everything but itself, so the database,
  daemon.pid, sockets, and logs stop showing up in git status (#492, #484)
- MCP server answers resources/list and prompts/list with empty lists instead
  of -32601, clearing scary log lines in opencode/Codex (#621)
- index SAP HANA .xsjs/.xsjslib as JavaScript (#556) and TS .mts/.cts (#366)
- visit anonymous AMD/CommonJS/IIFE wrapper bodies so their inner functions and
  calls are indexed instead of coming up empty (#528)
- batch the changed-file lookup so a huge first sync no longer hits
  "too many SQL variables" (#540)
- list files with `git ls-files -z` so non-ASCII/CJK paths survive
  core.quotepath and are no longer silently skipped (#541)
- attach Go methods on generic receivers (*T[P]) to their type (#583, RC1)
- impact no longer climbs the structural `contains` edge, so a leaf symbol
  stops dragging in its sibling methods (#536)
- README: explicit `codegraph install` step, run in a new shell (#631)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 17:49:15 -05:00
995da54430 feat(mcp): share one serve --mcp per project across MCP clients (#411)
One shared, detached daemon per project root: every `codegraph serve --mcp` is a thin stdio<->socket proxy (Unix socket / Windows named pipe) to it, so N agents in one repo share a single file watcher, SQLite connection, and tree-sitter warm-up instead of N copies. The daemon outlives any single session and reaps via client-refcount + idle timeout; `CODEGRAPH_NO_DAEMON=1` opts out.

Hardened during review: detached-process lifecycle (preserves the #277 watchdog via the proxy; the daemon no longer orphans on host SIGKILL), atomic lockfile + pid-verified stale-clear (no double-daemon on concurrent startup), realpath root canonicalization. Validated on macOS, Linux (Docker - 3x fewer inotify watches for 3 agents), and Windows (named pipes); A/B confirms byte-identical tool output vs direct mode. Closes #411.

Co-Authored-By: Colby McHenry <me@colbymchenry.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 19:54:56 -05:00
Colby MchenryandClaude Opus 4.7 36c8dbc404 fix(mcp): don't block initialize handshake on heavy init (#172) (#177)
The MCP `initialize` handler was awaiting `tryInitializeDefault` —
which opens the SQLite DB and runs `await initGrammars()` (tree-sitter
WASM bootstrap) — before sending the JSON-RPC response. On slow
filesystems (Docker Desktop VirtioFS on macOS, WSL2) this could exceed
Claude Code's ~30s handshake timeout, leaving the codegraph child
process alive and unresponsive with no tools visible in the client.

Send the response first; defer the open to a tracked background
promise. The lazy retry path used by `tools/list` and `tools/call`
now awaits that promise instead of racing it with `openSync`, so we
never double-open the SQLite file.

Adds a subprocess-based regression test that asserts the JSON-RPC
response arrives on stdout before `startWatching()` logs to stderr.
This ordering check catches the regression on any filesystem, not
just slow ones where the timing matters in practice.

Reported by @sashanclrp; isolated by @sgrimm's wire capture.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 10:20:02 -05:00