Examples: workspace mode (local + github-clone-tracker)¶
codingest-mcp --workspace <dir> runs KGLite’s graph-serving surface with
codingest’s code-graph builder injected. Two flavours are selected by the
manifest’s workspace.kind field:
workspace.kind: local— a fixed local source directory, watch mode for auto-rebuild on file changes,set_root_dir(path)to swap between sibling subdirectories without restarting. Best for code-review against a checked-out project tree.No
workspace:block (default github-clone-tracker) — the agent callsrepo_management('org/repo')to clone repos into the workspace; the injected codingest builder produces a code graph for each; queries flow against the active one. Best for exploring open-source codebases on demand.
Both share the same source tools (read_source / grep /
list_source), the same read_code_source qualified-name lookup,
and the same trust gates for extensions.
Copy-pasteable starting points ship in the repo’s examples/:
Local code-review →
examples/local_code_review_mcp.yamlGitHub-clone tracker →
examples/open_source_workspace_mcp.yaml
After copying one, run codingest-mcp --selftest --mcp-config <your.yaml>
to confirm it stands up correctly (see the guide’s
Verify your setup).
Variant 1 — workspace.kind: local + watch¶
Bind a local source directory as the active source root for source
tools (read_source / grep / list_source), build a code
graph over it, and auto-rebuild on file changes. The agent can swap
between sibling project directories with set_root_dir(path)
without restarting the server.
Manifest¶
# code_review_mcp.yaml — under /Volumes/EksternalHome/Koding/
name: Code Review
instructions: |
Code review against any project under Koding/. The active root
starts at the directory below; swap with set_root_dir(path).
workspace:
kind: local
root: /Volumes/EksternalHome/Koding # the STARTING root, not a boundary
sandbox_root: /Volumes/EksternalHome/Koding # the boundary — opt in, or swaps are unbounded
watch: true # auto-rebuild on file changes
What gets registered¶
tools/list returns:
- cypher_query
- graph_overview
- read_code_source
- ping
- read_source / grep / list_source
- set_root_dir # local-mode only
- github_issues / github_api # needs builtins.github: true + GITHUB_TOKEN
The flow¶
Boot: the server canonicalises
/Volumes/.../Koding/and binds it as the active root.read_source/grep/list_sourcesandbox to it.The watcher starts on the root with a 500 ms debounce.
First agent action:
cypher_queryagainst the auto-built code graph (modules, functions, calls, imports).Agent narrows interest to a specific file → calls
read_source.Agent decides to switch to a different project under Koding/:
set_root_dir("/Volumes/EksternalHome/Koding/Rust/KGLite"). The active root atomically swaps; source tools rebind; the watch handle moves to the new root.On any file change inside the new root, the watcher fires (after the 500 ms debounce window), the code graph rebuilds on a background thread, and the new graph atomically swaps in. Queries against the previous graph keep working until the swap.
Sandbox boundary¶
Corrected 2026-07-31. This section previously claimed workspace.root was
an “immutable boundary” that set_root_dir validated against, and showed a
refusal — "Error: path '/tmp' escapes the workspace root." — that the server
never produced. No containment existed. The read window was derived from
the active root, so the starts_with checks in the source tools only ever
bounded reads relative to wherever the server already pointed; they never
constrained where it could be pointed. A swap to any readable directory
succeeded.
The boundary is real from kglite 0.15.5 / mcp-methods 0.4.3, and it is
opt-in: set workspace.sandbox_root. Without that key, set_root_dir
remains unbounded — which is the pre-0.4.3 behaviour, preserved deliberately so
the upgrade breaks nobody.
# with workspace.sandbox_root: /Volumes/EksternalHome/Koding
{"name":"set_root_dir","arguments":{"path":"/Volumes/EksternalHome/Koding/Rust/KGLite"}}
→ activated ✓ inside the boundary
{"name":"set_root_dir","arguments":{"path":"/Volumes/EksternalHome/Koding/MCP servers"}}
→ activated ✓ sibling swap, no restart
{"name":"set_root_dir","arguments":{"path":"/tmp"}}
→ refused; the active root does not change ✓ outside the boundary
Set sandbox_root wide enough to cover every directory you intend to swap
between, and no wider. It bounds an operator’s set_root_dir and an adopted
client root alike.
github_issues integration¶
If the project under the active root has a .git/config with a
GitHub remote, github_issues auto-resolves the repo_name
argument from the active root’s git config. Agents can call
{"name": "github_issues", "arguments": {"limit": 5, "state": "all"}}
without supplying repo_name; the active workspace repo is used as
the fallback. Registration still needs both halves: builtins.github: true in the manifest, and the token in env / walked-up .env.
Performance notes¶
Code-tree rebuild is incremental for small files (a single-file edit is fast). Whole-tree rebuilds on large projects (>100k LoC) can take a few seconds — the debounce + background thread keep the agent unblocked during the rebuild.
Atomic swap means agents never see a half-built graph. Queries against the in-flight graph complete; the next query after the swap sees the new graph.
The
.mcp-workspace/directory insideworkspace.rootstores inventory + last-built SHA. Safe to delete — the next boot re-seeds it.
Variant 2 — github-clone-tracker (no workspace: block)¶
Run the server with --workspace <dir> (no workspace.kind: local
in the manifest) and the agent gets repo_management for cloning,
plus the standard source tools against the active clone. A copy-
paste-ready manifest lives at
examples/open_source_workspace_mcp.yaml.
Deployment¶
mkdir /path/to/my-workspace/
cp examples/open_source_workspace_mcp.yaml /path/to/my-workspace/workspace_mcp.yaml
codingest-mcp --workspace /path/to/my-workspace/
The filename inside the workspace dir MUST be workspace_mcp.yaml —
that’s the name the CLI auto-detects when given --workspace <dir>.
What the manifest enables¶
Looking at the example file inline:
name: Open Source Explorer
env_file: ../.env # walks up to find GITHUB_TOKEN for github_*
builtins:
temp_cleanup: on_overview
github: true # opt in to github_issues / github_api
extensions:
csv_http_server: true # FORMAT CSV → localhost URL (CORS-enabled)
instructions: |
Code intelligence for open-source GitHub repositories ...
FIRST STEP: call repo_management('org/repo') to clone + build ...
overview_prefix: |
## Two read paths
... (sticky context shown on bare graph_overview())
tools:
- bundled: repo_management
description: | # per-tool guidance lives with the tool
Manage cloned repositories. FIRST STEP before any other tool.
Common invocations: ... (full text in the file)
- bundled: cypher_query
description: |
Run a Cypher query against the active repo's knowledge graph.
Append `FORMAT CSV` for a CSV-exported result over localhost ...
- bundled: read_source
description: |
Read a file slice from the active repo. Prefer
read_code_source for graph-indexed symbols ...
- bundled: grep
description: |
.gitignore-aware regex search across the active repo's source.
Reach for cypher first for symbol lookups ...
Key choices:
env_file: ../.env— manifest paths are manifest-relative, so this walks up one level from the workspace dir to find the.env. LoadsGITHUB_TOKENforgithub_issues+github_api. Without a token those tools don’t register at boot.builtins.github: true— the opt-in that makes those tools eligible at all. It isfalseby default: since mcp-methods 0.4.5 a reachable token never widens a server’s surface on its own, so a GitHub-focused deployment like this one has to say so explicitly.builtins.temp_cleanup: on_overview— wipes the temp/ dir on every baregraph_overview()call. Withcsv_http_serverenabled, everyFORMAT CSVexport writes a file to temp/; without cleanup they accumulate.extensions.csv_http_server: true— enables the localhost listener soFORMAT CSVexports return URLs the agent can fetch (instead of inlining the CSV body, which blows out for large results). Loopback-only, CORS-enabled.instructions:— server-wide first-message orientation. Kept short: the project pitch + the FIRST STEP framing. The detailed per-tool guidance lives on the tools themselves (seetools[].bundled:overrides below). Splitting reduces the one-time-read context the agent has to retain through a long session.overview_prefix:— sticky context prepended to baregraph_overview()output. Skipped for focused drill-downs (graph_overview(types=...)etc.) so it doesn’t bloat every response. Good place for the “two-read-paths” mental model.tools[].bundled:overrides — replace the agent-facingdescription:for specific bundled tools. The example overrides four:repo_management— its description carries the “FIRST STEP + five common invocations” guidance that used to live in the globalinstructions:blob. Now it ridestools/listnext to the tool’s schema, so the agent sees the guidance every time it considers which tool to call.cypher_query— description teaches theFORMAT CSV→ localhost URL pattern.read_source— disambiguates againstread_code_source(prefer the latter for symbols the graph already indexed) and pinsfile_pathsemantics to the active repo’s clone path.grep— frames the regex-search workhorse against cypher (cypher first for symbols; grep for plain text). Same principle as the other three: per-tool guidance attached to the tool, not buried in a session-wide blob.
Override
descriptionworks on any bundled tool in the catalogue reported by the server. Addinghidden: truedrops a tool fromtools/listand rejects calls — useful for narrowing the agent surface when a bundled tool doesn’t fit your deployment. Unknown tool names fail at boot with the full valid catalogue listed in the error message. The catalogue is the authority; it varies with server mode and optional integrations.
What gets registered¶
For --workspace <dir> mode (no workspace.kind: local):
- cypher_query
- graph_overview
- read_code_source
- ping
- repo_management # github clone tracker
- read_source / grep / list_source # against the active repo
- github_issues / github_api # needs builtins.github: true + GITHUB_TOKEN
Note: set_root_dir is only in the workspace.kind: local
variant above. repo_management is only in github-workspace
mode. The two modes are mutually exclusive.
Lifecycle¶
First call:
repo_management('pydata/xarray')does a shallowgit clone --depth 1, the injected builder produces the code graph, graph is active for subsequent queries.Subsequent calls:
repo_management('pydata/xarray')again short-circuits to “already cloned, activated.”repo_management(update=True)doesgit fetch --depth 1 origin+ reset to remote; graph rebuilds only if the SHA moved (gated bylast_built_sha).Idle sweep: repos untouched for
--stale-after-days(default 7) get tombstoned ininventory.json. The active repo is exempt. Sweep happens on the nextrepo_managementcall.Force rebuild:
repo_management(update=True, force_rebuild=True)bypasses the SHA gate — useful after a kglite upgrade.