waycontext
v0.7.0
Published
Engineering intelligence for AI coding agents: indexes a codebase into symbols, a relationship graph and vector embeddings, and serves it over MCP
Maintainers
Readme
WayContext
MCP server that scans and indexes an entire codebase — not just listing symbols, but building a relationship graph (calls, imports, inheritance, WordPress hooks) plus vector embeddings in PostgreSQL/pgvector — so AI agents get comprehensive project context.
What it does
- Hybrid code search. Postgres full-text and pgvector semantic search, fused with Reciprocal Rank Fusion — "purge cache after cron match update" finds the right function whether or not it shares words with your query.
- A real relationship graph.
CALLS,INSTANTIATES,IMPORTS,EXTENDS,IMPLEMENTS, plus WordPressREGISTERS_HOOK/FIRES_HOOK. Ask for a symbol's blast radius before refactoring, not after. - Git history, with no integration to configure. Commits, per-file churn,
.mailmapidentities and issue references, indexed straight from the repo.who_ownsdecays by recency, so it answers who to ask today. - Docs & ADRs alongside code.
search_knowledgeranks prose and code in one list, so an agent can answer why the code is shaped this way, not only where it lives. - Rules an agent can't invent. Normative sentences are extracted from ADRs and fix commits as candidates; nothing reaches an agent until a human confirms it by CLI.
- Engineering memory.
remember/recallso a gotcha debugged once outlives the session. - Architecture as modules. Churn, defect density and a risk score per module, plus co-change coupling and recurring bug clusters.
- One call that fuses all of it.
compose_contextreturns rules, code, docs, memory and past fixes for a task — cited, and packed into a token budget. - Incremental by default. SHA-256 per file,
git diffscoping since the last indexed commit, deleted files pruned. - Languages: JavaScript, TypeScript, JSX/TSX, PHP, Python, Go — plus full symbol extraction for CSS/SCSS, HTML, JSON, and XML.
- Every surface reads one registry. MCP tools, CLI subcommands and HTTP routes are generated from the same operation list, so they cannot drift apart.
What it looks like
Ask in English, with none of the words the code uses:
$ waycontext search_code waycontext "how does it avoid re-indexing files that did not change" 3
[
{ "name": "runIndex", "path": "src/indexer.js", "matched_via": ["vector"] },
{ "name": "indexProject", "path": "src/indexer.js", "matched_via": ["vector"] },
{ "name": "upsertFileRow", "path": "src/indexer.js", "matched_via": ["vector"],
"doc": "Shared by the code and doc branches: `files.hash` is what makes the
incremental skip work…" }
]Not one of those names contains "re-index", "change", or "avoid" — a grep for any of those words finds nothing useful. Then check the blast radius before touching something:
$ waycontext get_callers waycontext embed
[
{ "caller": "embedFixCommits", "path": "src/knowledge/clusters.js", "relation": "CALLS", "line": 209 },
{ "caller": "embedQuery", "path": "src/embeddings.js", "relation": "CALLS", "line": 125 }
]Both are real output from WayContext indexing its own repository. Every one of these is also an MCP tool, which is the point — your agent calls them itself instead of grepping.
Quick start
You need: Node.js ≥ 20, and a PostgreSQL with the pgvector extension. Docker is the easiest way to get the latter. Budget a few hundred MB for the Postgres image and a couple of minutes for a first index; an embedding API key is optional (see the end of this section).
1. Start a database. The pgvector/pgvector:pg16 image already contains the extension, so
nothing is compiled:
DB_PASS=your-password docker compose -f docker/docker-compose.yml up -dAlready have a PostgreSQL with pgvector? Skip this and use its connection string below.
2. Install WayContext and point it at that database.
npm install -g waycontext # or run one-off with: npx waycontext <command>
export DATABASE_URL=postgres://codectx:your-password@localhost:5432/codectxTo avoid setting the variable every time, put it in ~/.config/waycontext/config.json
instead:
{
"DATABASE_URL": "postgres://codectx:your-password@localhost:5432/codectx"
}3. Create the schema, then index something.
waycontext migrate
waycontext index_project myapp /path/to/myapp4. Register the MCP server with your client.
claude mcp add --scope user waycontext -- waycontext-mcpFor any other MCP client, the equivalent is command: "waycontext-mcp" with no arguments —
see Installation.
5. Point the repo's agents at it, by running waycontext init in the repo. It writes a
## WayContext section — the project name plus the workflow to follow — into CLAUDE.md,
AGENTS.md and, where applicable, .github/copilot-instructions.md, and registers the
server in .vscode/mcp.json for Copilot. See
Getting agents to actually use it.
That section is also what the CLI reads to work out which project you mean, so once a
repo has been through init you can drop the <project> argument entirely:
cd /path/to/myapp
waycontext index # same as: waycontext index myapp /path/to/myapp
waycontext search "purge cache after status update"Naming a project explicitly still wins, and if nothing on disk settles it the CLI asks — or, when it isn't attached to a terminal, exits with the list of indexed projects rather than hanging.
Working from a clone instead
./install.sh does all of the above in one step — provisions PostgreSQL + pgvector via
Docker (or apt) if it isn't already reachable, runs npm install, seeds .env with a
generated password, creates the schema, links the CLI and registers the MCP server at user
scope. It's idempotent, so it's also the upgrade path (via ./update.sh).
It also ensures the local WayContext HTTP service is running in the background, so
review URLs are immediately openable without manually running waycontext serve.
git clone https://github.com/lammatrac/waycontext && cd waycontext
./install.shTurning on semantic search
WayContext ships with EMBEDDING_PROVIDER=none, so everything above works with no API key
and no account: the graph tools and the full-text half of search_code are fully functional.
Semantic matching and find_related need vector embeddings, which means an API key —
WayContext doesn't run a local embedding model. Set EMBEDDING_PROVIDER=voyage (recommended,
voyage-code-3 is trained on code) or openai, plus the matching key. It's worth it if you
want natural-language queries: measured recall@10 is 0.66 with embeddings against 0.00
without, on this repo's own commit-replay harness. Indexing this repo costs well under a cent.
See Retrieval quality and
token usage & cost.
Full setup, per-OS notes and configuration: Installation & configuration. Something broken? Troubleshooting is organised by symptom.
Documentation
| Document | What's in it |
|---|---|
| Installation & configuration | Database setup (Docker/apt), CLI, tab completion, MCP registration, embedding providers, token usage & cost |
| Troubleshooting | Install, update, indexing, search and MCP problems, by symptom |
| Architecture & data model | How the pieces fit, every table explained, the four planes, stable symbol identity |
| Algorithms & concepts | AST parsing, SHA-256 hashing, BFS, embeddings & cosine distance, HNSW, tsvector, RRF — and how search_code works |
| Tools & APIs | The MCP tool list, the context API, waycontext serve, the web graph, the VS Code extension |
| Knowledge: rules, memory, docs & history | Rule extraction and the human-confirmation gate, engineering memory, ADR ingestion, git history |
| Retrieval quality | The eval/ harness — replaying real commits as labelled data |
| Changelog | Release history |
| Contributing | Development setup and conventions |
| Security policy | Reporting a vulnerability, and what's deliberately out of scope |
Getting agents to actually use it
Registering an MCP server makes its tools available; it doesn't make an agent prefer them over its own built-in search. WayContext pushes on that in three places, weakest to strongest:
1. The server tells every client, automatically. WayContext sends MCP instructions in
the initialize response — the workflow below, what the tools are not for, and the list of
indexed projects with their root paths. Clients inject this into the agent's system prompt, so
it works in Claude Code, Copilot and Cursor alike with no setup at all. Including the project
list matters more than it looks: every tool takes a required project argument, and a tool
that costs a list_projects round-trip before its first real query loses to a grep that costs
one call.
1. project_overview → orient
2. search_code with the task description → candidate symbols
3. get_graph / get_callers on the target → blast radius
4. get_symbol → read actual source
Re-run index_project after committing changes.2. waycontext init writes it into the repo, as a ## WayContext section stating that
workflow, the project name, and where grep is still the right answer. It writes every
agent-instruction file the repo's clients read, not just CLAUDE.md:
| File | Read by |
|---|---|
| CLAUDE.md | Claude Code |
| AGENTS.md | Copilot, Cursor, Codex, Gemini CLI, Amp |
| .github/copilot-instructions.md | GitHub Copilot (only if the repo already has a .github/; --all forces it) |
| .vscode/mcp.json | VS Code / Copilot — registration, not instruction. install.sh only registers with Claude Code, so without this Copilot never connects to the server in the first place. |
Re-running is idempotent, existing content is preserved, and each file is confirmed
separately. waycontext init <name> --yes is the non-interactive form.
3. An opt-in PreToolUse hook intercepts greps in an indexed project and redirects —
Claude Code only, three escalating modes. See
Installation.
Notes & limits
- Call resolution is name-based (exact +
Class::methodsuffix), not type-inferred — dynamic calls ($fn(),call_user_func) stay as unresolveddst_nameedges, which is still useful signal. - Symbol bodies are truncated at 6 KB for storage/embedding.
- Files > 1 MB skipped (configurable via
MAX_FILE_SIZE). waycontext servehas no authentication and binds to127.0.0.1only. Auth, rate limiting and multi-tenancy are deliberately absent rather than half-present.
Changes
2026-08-08: Added automatic local background service management for
waycontext serveworkflows (service ensure|status|stop, install/update/postinstall auto-ensure, duplicate-start protection, version-aware restart, and worker recovery), integrated reasoning review hosting atGET /reviews/:project/:slug, and returnedreview_urlfrom reasoning graph create/update responses so CLI/MCP/agent flows can open reviews directly.2026-08-08: Hardened reasoning review auto-open on WSL and Linux. The opener now detects WSL, translates Linux paths via
wslpath -w, launches throughcmd.exe /c start "", and falls back to/mnt/c/Windows/System32/cmd.exewhencmd.exeis missing on PATH. Added focused unit coverage intest/reasoning.open.test.jsfor macOS/Windows/ Linux/WSL launch resolution and fallback behavior.2026-08-11:
update_reasoning_graphno longer auto-opens a browser tab on every call (it re-runs repeatedly during a session, unlike the one-timecreate_reasoning_graphinit call, which still auto-opens as before). Updated the tool description and test coverage accordingly.2026-08-11:
create_reasoning_graphalso no longer auto-opens a browser tab, closing the gap left by theupdate_reasoning_graphfix above. Removed the now-deadmaybeAutoOpenReviewhelper, corrected both tools' descriptions and the generated CLAUDE.md wording, and updated test coverage.2026-08-11:
waycontext initnow also asks for the project's root path and writes it into CLAUDE.md/AGENTS.md/.github/copilot-instructions.md next to the project name, so an MCP-calling agent (which has no working directory of its own) knows what to pass asindex_project'spathargument instead of guessing. The stored value is relative to the file's own location (./in the common case) rather than a machine-specific absolute path, since these files are committed and cloned onto other machines; it's resolved back to an absolute path only where one is actually needed (findProjectMarker, against the marker file's known directory). The path prompt also supports shell-style Tab completion of directories. AddedextractExistingPath, updatedfindProjectMarker's root-default preference, and extended test coverage acrossclaudeMdInit.js,initTargets.js, andprojectResolve.js.2026-08-11: Added full symbol extraction for JSON, HTML, CSS, and XML, on par with the existing JS/TS/PHP/Python/Go languages. JSON object keys become
keysymbols (at any nesting depth), CSS rules/at-rules becomerulesymbols, and HTML/XML elements becomeelementsymbols (HTML only for elements carrying anid/class; XML unconditionally). None of the four produce relation-graph edges, since none have a call concept. Addedtree-sitter-json,tree-sitter-html,tree-sitter-css(exact-pinned to avoid a peer-dependency conflict with the pinnedtree-sittercore), and@tree-sitter-grammars/tree-sitter-xml(in place of the spec's originally named[email protected], which fails to build on Node 22).2026-08-11: Added
waycontext_gate.py, a deterministic Claude Code hook enforcing the CLAUDE.md spec→plan→gate→code→review pipeline (blocksEdit/Write/MultiEdit/NotebookEdituntil a gate is approved, blocksGrep/Globuntil WayContext has been queried first), opt-in per directory via awaycontext-tasks/folder or.claude/.gate/enabledmarker. Wired it into setup with a newwaycontext gate install/gate uninstallCLI subcommand (src/gateInit.js) that copies the hook script to~/.claude/hooks/, merges itsenv/hooksinto~/.claude/settings.jsonwithout disturbing unrelated settings or the existing search hook, and merges.gitignore.sampleinto a repo's own.gitignore;install.shnow runs it automatically (best-effort), and the top-levelwaycontext uninstallreverses it.2026-08-11: Renamed the ambiguous "project root" wording
waycontext initwrites into CLAUDE.md/AGENTS.md/copilot-instructions.md to "indexed directory", since the directory WayContext indexes can be a subdirectory of a repo's actual git root.extractExistingPathstill parses the old phrasing for backward compatibility with files an older version wrote.2026-08-08: Upgraded reasoning graphs into a reviewer-first Decision Review UI and made review auto-open default-on.
create_reasoning_graph/update_reasoning_graphnow render an executive-summary layout (decision graph, impact map, risks/conflicts, evidence, approval panel) fromgraph.jsonintowaycontext-review.html(while still mirroringreasoning.htmlfor backward compatibility). Nodes gained explicit review semantics (verified,assumed,inferred,conflict,unknown), confidence scores, and evidence lines, with corresponding patch ops (set_review,set_confidence,set_evidence). Auto-open now defaults to enabled wheneverREASONING_AUTO_OPENis unset, including first-run and MCP-triggered flows; setREASONING_AUTO_OPEN=0to disable.2026-08-08: The CLI now works out which project you mean instead of making you retype it. Every command whose first argument is
<project>can omit it inside a repo that has been throughwaycontext init: the name is read back out of the## WayContextsection inCLAUDE.md(orAGENTS.md, or the Copilot file), searching upward from the working directory and stopping at the git root so the walk can't escape into a parent checkout.waycontext indexneeds no arguments at all —index_projectdeclaresrootDefault: "path"in the registry and the detected repo root fills it. Omitted arguments are filled from the right before the left, which is what makeswaycontext index my-appstill mean the project my-app rather than a path called that. Falling back: sole indexed project, then a prompt identical to init's — with a(y/N)offer to write the answer into the repo's agent files so it's asked once — then, with no TTY, an error naming the indexed projects, because an agent or CI job must fail rather than block on a question nobody can answer. Resolution is skipped entirely when the arguments are already there, reported on stderr so piped JSON stays clean, and left off the MCP surface, where there is no working directory to read.2026-08-08: Made agents actually reach for the tools instead of falling back to their own search. The server now sends MCP
instructionsin theinitializehandshake — the recommended workflow, what the tools are not for, and the indexed projects with their root paths — which every client injects into the agent's system prompt, so it works in Claude Code, Copilot and Cursor with no setup. Including the project list removes alist_projectsround-trip that a requiredprojectargument otherwise forced before the first real query. Tools are annotatedreadOnlyHint(all but the four that write): clients auto-approve read-only tools, so without itsearch_coderaised a permission prompt while the agent's built-inGrepwas pre-approved.waycontext initnow writes a directive section — the 4-step workflow and where grep is still correct, not just the project name — intoAGENTS.mdand.github/copilot-instructions.mdalongsideCLAUDE.md, and registers the server in.vscode/mcp.json, sinceinstall.shonly ever registered it with Claude Code. Two fixes found on the way:upsertSectionappended a newline on every run, so init grew its files forever (invisible with one target, not with four); and where two indexed projects share a root — a stale trial index beside the real one — the instructions name both and say to ask rather than guessing one into the system prompt as fact. The deadbuildGlobalSection/upsertGlobalSectionpair is gone, its content superseded byinstructions;removeGlobalSectionstays so uninstall can still clean up machines older versions wrote to.2026-08-05: Added a new "Support WayContext" section before License, including donation messaging, PayPal support options, and a clarification that Team Edition remains free for individuals/startups while Enterprise Edition provides advanced features and commercial support.
2026-08-05: Added reasoning/decision graphs.
create_reasoning_graphandupdate_reasoning_graphwrite a git-trackablegraph.jsonplus a self-containedwaycontext-review.html(questions, alternatives with pros/cons, a selected answer, risk, affected files) into a target project's owndocs/waycontext/<slug>/, so a feature's requirements and edge-case discovery survive past the chat that produced them.graph.jsonis the source of truth — everyupdate_reasoning_graphcall re-reads it from disk and re-renders the HTML, so a hand-edit between calls is respected rather than overwritten.waycontext init's injectedCLAUDE.mdsection now also tells Claude to render a reasoning graph before presenting a spec or implementation plan for review, instead of asking the developer to read it as markdown.2026-08-05: Fixed two documentation drifts found in a command-list consistency audit:
docs/architecture.mdstill creditedsrc/server.jswith the MCP tool-registration loop that was extracted intosrc/mcpServer.jsback in Phase 5;docs/installation.md's CLI section only showed 6 of the 22 registry operations. Every code-level surface (CLI switch, completion table, MCP registration, HTTP routes, hook script, VS Code extension) was already covered by parity tests and found consistent — only hand-written markdown was out of sync.2026-08-05: Dropped Node 18 support (
engines.nodenow>=20; CI matrix now 20/22). The@hono/node-server2.1.0 bump below turned out to require Node >=20 across its entire 2.x line — there is no version that both patches its CVE and runs on Node 18 — and under real Node 18 its Node→Fetch request bridging (now used by@modelcontextprotocol/sdk's HTTP transport) throwsReferenceError: crypto is not defined, which surfaced as 3 failing MCP-over-HTTP tests on CI's Node 18 leg only. Node 18 reached EOL in April 2025.2026-08-05: Bumped
@modelcontextprotocol/sdkto 1.30.0 and appliednpm audit fix(including a major@hono/node-serverbump to 2.1.0), resolving all 4 known vulnerabilities in transitive dependencies. Full test suite verified green.2026-08-04: Call resolution is now scope-aware. A bare-identifier call/
newmatching one of the enclosing function's own parameters (e.g.function derive(project, log = () => {})callinglog(...)) is no longer treated as a call to a same-named project symbol elsewhere — it previously invented phantom module dependencies inget_module's architecture graph.2026-08-11: Added SCSS to the languages with full symbol extraction, reusing the existing CSS walker as-is (verified
tree-sitter-scss's grammar node types —rule_set,selectors, and every at-rule ending in_statement— are a superset of CSS's, so nested rules and SCSS-only at-rules like@mixin/@includeland asrulesymbols the same way CSS's do). Added thetree-sitter-scssdependency (peer-compatible with the pinnedtree-sittercore, no version substitution needed).
❤️ Support WayContext
WayContext is free for individuals and startups, and we are committed to keeping it that way.
If WayContext has helped you or your team, consider supporting its continued development. Your contribution helps fund new features, bug fixes, documentation, infrastructure, and long-term maintenance.
Donate with PayPal
Every contribution, no matter the size, is greatly appreciated.
WayContext Team Edition will always be free for individuals and startups.
For organizations that require advanced capabilities, dedicated support, and commercial licensing, WayContext Enterprise Edition is available.
License
Apache 2.0 — see LICENSE and NOTICE. The name "WayContext" and the WayContext logo are trademarks and are not covered by that license; see TRADEMARK.md.
