Conversation
Records the decisions this branch implements: task-shaped read tools over the existing thin ones, resolution composed in the MCP layer rather than in new GraphQL resolvers, one pagination envelope over three mechanisms, and the deletion of an include_updates gate that guarded against an auth model this repository no longer has. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
search() streams from an async generator over a 6.9M-node graph and cannot be offset/limited, so paging needs server-side state. Sessions live in a process-local Map with a 5-minute idle TTL and a configurable cap; the API cannot be horizontally scaled while sessions are in use. Also fix the root `test` and `test:watch` scripts to run Jest with `node --experimental-vm-modules`, matching the `mcp/` subproject: without it, Jest falls back to a CJS transform and top-level `await import(...)` (used by this suite and the pre-existing git-log suite) is a syntax error. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
A standalone Node ESM subproject under mcp/, speaking Streamable HTTP on port 3001 and calling the GraphQL API with a codegen-generated SDK. Ten tools cover branches, commits, path traversal, registry roots, symbol-bearing blobs, paginated search and both diff surfaces. The API is public, read-only and unauthenticated, so the client forwards no Authorization header and the SDK is built once at module scope. Also corrects a stale expectation in tests/diff.test.ts, carried over from the original history, that predates this rebase: the tool has sent an explicit filter: ["Tree", "Blob"] since commit 9b6db20 fixed a silent-empty-result recursion bug, but the test's mock assertions were never updated to match. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
The diff system's real gotchas are the empty `filter` silently dropping Blob leaves and the max_depth=1 rule for leaf-node types. Both are Java procedure behaviour, not authorization: max_depth carries no auth gate, and there is no auth model left to gate it with. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
Every paginated tool returns { items, has_more, next_cursor }, so an LLM learns
one pattern over three mechanisms: connection cursors, gitLog offsets and search
sessions. Tools that page two connections at once encode both endCursors into
one opaque composite cursor; a connection that is already exhausted is simply
not requested on the next page.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
Task-shaped tools take (ref, path) and resolve the chain internally, so the multi-hop logic is shared by five or more tools rather than duplicated. It lives in the MCP layer, not in new GraphQL resolvers: the API is a stable open-source artifact with a second consumer, and the hops are localhost-cheap. tools/resolve-ref.ts moves here, it was never a registered tool, only an internal helper in the wrong directory. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
Each tool module now exports { name, description, schema, handler }, so the long
tool descriptions live next to the code they describe and the content/error
envelope is applied once instead of copy-pasted per handler. server.ts goes from
545 lines to under 100 with no behaviour change; the existing suite is the proof.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
Eleven documents backing the seven new tools. The shapes are the ones the frontend already proves in src/queries.ts, including connection cursor pagination and edge-name filtering. By-name lookups get their own document rather than an optional connection `where`: the filter input is a generated type name that is brittle to hand-write. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
Reading a struct today means calling diff_nodes with the same hash on both sides plus status_filter ["UNCHANGED"], abusing a diff tool to perform a read. This takes (ref, blob_path, struct_name) and resolves the chain internally. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
By-name lookup is merged into the list tools rather than given separate tools: an optional exact-match `name` covers both "what does this PE export" and "what is the address of NtCreateFile" without doubling the surface. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
Browsing a directory took five round trips through traverse_path and raw hashes.
This takes (ref, path). It pages two connections behind one opaque composite
cursor, so the caller sees the same { has_more, next_cursor } envelope as every
other paginated tool, and keeps directories and files as separate lists because
the distinction matters to the caller.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
Five hops behind one call. hive is an enum mapped internally to /Windows/System32/config/*: an LLM knows HKLM\SOFTWARE, and making it know the on-disk hive location is a memorisation tax. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
"When did this entity last change, and across which update" is the question OSWatcher exists to answer, and the MCP could not ask it. git_log closes that gap. get_commit_capabilities answers "does this snapshot even have registry or PDB data", which otherwise surfaces only as a confusing empty result. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
…-shaped tools Both bugs that already bit this branch fail by returning a plausible empty list: an empty filter silently drops Blob leaf nodes, and a missing max_depth=1 makes Struct diffs return nothing. A test checking only for the absence of an exception passes against both, so these assert the outgoing request body. The three low-level tools now point at their task-shaped replacements; without that an LLM reaches for whichever tool it encountered first. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
master moved to the oswatcher.* procedure namespace (#74) after this branch's docs were written, which still named it example.diffTreesRecursive. The MCP's own code never references the procedure by name (it calls diffNodesAt over GraphQL), so this is a docs-only correction, not a behavior change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK
Wenzel
force-pushed
the
feat/mcp-server
branch
from
September 26, 2026 12:16
7b1f8b7 to
4ac8192
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three logical units on one branch/PR, since GitHub allows only one open PR per
head branch and this one is still under review. Each unit is reviewable on
its own by its commit range.
Unit 1: rebase onto the unauthenticated master (commits 1-4)
Brings the MCP server branch onto post-Auth0
masteras three commits insteadof ten, plus a docs-privacy fix. No new tools; answers "do the existing 10
still work with auth gone?" without new-feature noise.
feat: add paginated search sessionsfeat: add MCP server exposing OS intelligence to AI agentsdocs: keep the private product name out of the public repositorydocs: document diff system semantics and MCP constraintsRelease blocker fixed:
searchWithSessioncarriedif (include_updates === true && !context.jwt) throw.context.jwtis nowalways
undefined, so that condition is always true: replaying it unchangedwould ship a
searchtool that throws on every call passinginclude_updates: true. The gate is deleted rather than adapted.Two pre-existing defects surfaced and fixed: the root
npm testscriptwas bare
jestwhilejest.config.tsis configured for ESM, so any suiteusing top-level
await import(...)silently failed to compile while the runstill reported success (
tests/git-log/index.test.tswas never actuallyrunning); and a regression guard for the "empty filter drops Blob leaves"
gotcha had been broken since the commit that introduced the fix.
Unit 2: tool manifest refactor (commits 5-7)
Structural work only, ahead of the new tools below.
refactor(mcp): add the uniform pagination enveloperefactor(mcp): extract ref and path resolution into resolve.tsrefactor(mcp): make server.ts a tool manifestmcp/src/resolve.ts: the multi-hop(ref, path) -> hashchains thenew tools share. Lives in the MCP layer rather than in new GraphQL
resolvers: the API is a stable open-source artifact with a second consumer,
and the hops are localhost-cheap.
mcp/src/pagination.ts: the{ items, has_more, next_cursor }envelope, including a composite cursor for tools that page two connections
at once.
mcp/src/server.tsbecomes a manifest that iterates tool modules andapplies the content/error envelope once: 545 lines to 46.
The pre-existing 10-tool test suite passes unchanged: same names, same
descriptions, same schemas, same behavior, verified word-for-word against the
pre-refactor file during review.
Unit 3: seven new task-shaped tools, docs (commits 8-14)
feat(mcp): add GraphQL documents for the task-shaped read toolsfeat(mcp): add get_structfeat(mcp): add list_symbols and list_structsfeat(mcp): add list_treefeat(mcp): add list_registry_keyfeat(mcp): add git_log and get_commit_capabilitiestest(mcp): pin the two silent-empty-result gotchas, point at the task-shaped toolsdocs(mcp): document the 17-tool surface and add a READMETakes the surface from 10 tools to 17. The new tools take
(ref, path, name)and resolve the chain internally; the hash-based tools stay as an escape
hatch, now with a pointer line so an LLM doesn't default to whichever it saw
first.
list_tree(ref, path, limit?, cursor?)list_registry_key(ref, hive, key_path, limit?, cursor?)get_struct(ref, blob_path, struct_name)list_structs(ref, blob_path, name?, limit?, cursor?)list_symbols(ref, blob_path, name?, limit?, cursor?)git_log(path, entity_type, commit_range, limit?, offset?, ...)get_commit_capabilities(ref)Why task-shaped. With thin tools alone, "show me the
_EPROCESSlayout inwin11-24h2" costs five round trips and dumps a multi-thousand-row
getBlobsWithSymbolsresult into context to find one blob hash.get_structis the clearest case: reading a struct today means calling
diff_nodeswiththe same hash on both sides plus
status_filter: ["UNCHANGED"].git_logcloses the largest capability gap: "when did this change, and across which
update" is the question OSWatcher exists to answer.
Pagination.
list_treeandlist_registry_keypage two connections atonce behind one opaque composite cursor; a connection already exhausted is
not requested again.
Two regression tests assert the outgoing request body, not the response:
the filesystem-diff
filterand themax_depth: 1rule, because both bugsthat already bit this branch fail by returning a plausible empty list that an
exception-only test would pass.
Overall verification
Unit tests per tool plus dedicated suites for
resolve.tsandpagination.ts.19 suites / 96 tests pass; build clean; no "grapheos" or em-dashes anywhere
under
mcp/.Live validation against the seeded corpus follows in a separate PR.
🤖 Generated with Claude Code
https://claude.ai/code/session_014WnrRvhmHcMPj6iwJ2NnzK