Grove
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Grove scan the tiny project and show me any gaps between docs and tests"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Grove
Grove is Software Forest's open-source workshop for coding agents. It runs on your own machine and does four things:
Maps a codebase. A read-only scan walks a project directory and records what it finds.
Keeps a three-layer blueprint of that project:
declared— what the docs say (headings and sentences from README, CHANGELOG anddocs/),actual— what the code appears to contain (modules and symbol names),tests— what the tests appear to prove (test files and test names).
Where the layers disagree, Grove records a finding, for example "the README says it greets a user by name, and no test mentions greeting".
Stores lasting knowledge. Notes and ingested architecture maps are kept with full revision history and are full-text searchable.
Tracks local work items from
readythroughin_progressandreviewtodone.
Agents such as Grok Bot and Claude Code do the work. Grove is the workshop they use: a place to look things up, see what is unproven, and keep track.
What Grove is not
Not an autonomous coder. It never edits your code, and it does not implement changes.
It does not open pull requests. It does not talk to GitHub or to any other network service. An issue URL on a work item is stored as text and never fetched.
Not an AI. v0 calls no model and no embeddings API. Everything is deterministic: regular expressions, token overlap and SQLite.
Not a judge. Every finding is labelled
heuristic. Treat findings as places to look, then read the code.
Related MCP server: crux
Install
uv syncThis creates .venv/ with the grove command at .venv/bin/grove. Requires
Python 3.13 and uv. Dependencies are pinned in
pyproject.toml and uv.lock.
Run the tests with uv run pytest -q.
Data directory and GROVE_HOME
Grove keeps everything in one SQLite file, grove.db, inside its data
directory:
--home DIRon the command line, if given;otherwise the
GROVE_HOMEenvironment variable, if set;otherwise
~/.grove.
The directory is created on first use. The usual choice is the default,
~/.grove. You can point GROVE_HOME at another directory if you prefer.
Grove writes only inside its data directory. Keep the data directory outside the projects you scan.
A first walk
The repository ships a tiny pretend project in fixtures/tiny. Its README
claims two behaviours (adding two integers, greeting a user by name); both are
implemented, and only the first has a test.
alias grove=.venv/bin/grove # or: uv run grove
grove project create tiny --root fixtures/tiny
grove scan tiny
grove gaps tiny --text
grove recommend tiny --textgrove gaps tiny --text prints:
items:
- 1 declared_untested No test name overlaps this declared statement: "Greets a user by name." (README.md:6)
- 2 actual_undocumented Module src/tiny/calc.py is not mentioned in the declared layer
total: 2and grove recommend tiny points at finding 1: the greeting behaviour exists
in the code and nothing tests it.
CLI
Every command prints one JSON envelope on stdout and exits 0 on success or
1 on error. Add --text for a plain-text rendering.
grove --home DIR status
grove --home DIR project create NAME --root PATH
grove --home DIR project list
grove --home DIR project get NAME
grove --home DIR scan NAME [--max-files N] [--max-seconds S]
grove --home DIR blueprint NAME [--layer declared|actual|tests] [--query TEXT]
grove --home DIR gaps NAME [--kind KIND]
grove --home DIR ingest-map NAME PATH
grove --home DIR knowledge write NAME --key KEY --title TITLE --file PATH
grove --home DIR knowledge get KEY [--project NAME] [--revision N]
grove --home DIR search QUERY [--project NAME]
grove --home DIR work create NAME --title TITLE [--issue URL] [--plan TEXT]
grove --home DIR work list NAME [--status STATUS]
grove --home DIR work get ID
grove --home DIR work update ID [--status STATUS] [--plan TEXT] [--notes TEXT] [--title TEXT]
grove --home DIR recommend NAME
grove mcp--home DIR is optional and wins over GROVE_HOME. List commands accept
--limit (default 50, maximum 200) and --offset. For knowledge write, use
- as NAME to write a workspace-level note that belongs to no project.
More examples:
grove blueprint tiny --layer actual --text
grove ingest-map tiny fixtures/map_sample.md
grove gaps tiny --kind map_untested
grove knowledge write tiny --key greet-plan --title "Greet test plan" --file notes.md
grove search "greet plan"
grove work create tiny --title "Add a test for greet"
grove work update 1 --status in_progressThe JSON envelope
{"ok": true, "data": {"...": "..."}}
{"ok": false, "error": {"code": "not_found", "message": "..."}}
{"ok": true, "data": {"items": [], "total": 0, "offset": 0, "limit": 50}}Error codes are not_found, invalid, io and internal. Usage mistakes
(an unknown command, a missing argument) are reported as invalid too.
MCP server
grove mcp serves the same operations to an agent over MCP stdio. stdout
carries MCP JSON-RPC only; logs go to stderr. It offers exactly sixteen tools:
grove_status, grove_project_create, grove_project_list,
grove_project_get, grove_scan, grove_blueprint_get, grove_gaps,
grove_ingest_map, grove_knowledge_write, grove_knowledge_get,
grove_knowledge_search, grove_work_create, grove_work_list,
grove_work_get, grove_work_update, grove_recommend_next.
Each tool result is the JSON envelope above, as one text block; isError is
set when ok is false.
Grok Bot
Add Grove as a local (stdio) server with AddMcpServer. Replace
/path/to/grove with the directory where you cloned this repository.
env is optional; without it Grove uses ~/.grove.
{
"name": "grove",
"command": "uv",
"args": ["run", "--quiet", "--project", "/path/to/grove", "grove", "mcp"]
}If you would rather point at the virtualenv directly, set command to
/path/to/grove/.venv/bin/grove and args to ["mcp"].
A remote URL will not work for this server. Grove speaks MCP over stdio only: the agent must start the process itself, on the machine that holds the code and the data directory. There is no HTTP endpoint to connect to.
Claude Code and other MCP clients
Any client that launches stdio servers can use the same command, args and
env. See CLAUDE.md for how Claude Code is expected to use Grove.
Honest limits
The scanner is not a parser. It reads one line at a time with language-specific regular expressions.
Languages: Python, Rust, TypeScript/JavaScript and Go. Other languages are counted as files and otherwise ignored.
Text inside a string or block comment that looks like a definition is recorded as one. Definitions split across lines can be missed. Class methods in TypeScript/JavaScript and types inside a Go
type ( ... )block are not recorded.Test names come from pytest-style functions and classes, Rust
#[test]attributes andfn test_*, JavaScripttest(/it(calls with a literal name, and Gofunc Test*. Test bodies are not read: Grove knows a test's name, not what it asserts.Docs are read as Markdown: headings and sentences, with code fences, badges and licence text skipped. At most 400 items are kept per document.
The scan honours
.gitignorefiles (a deeper file cannot re-include what a shallower one ignored), always skips.git,node_modules,target,venv,.venv,dist,build,__pycache__,.mypy_cacheand.pytest_cache, does not follow symlinks, and skips files over 512 KB.Caps: 4000 files and 8 seconds by default. When a cap is hit the scan stops, keeps what it has, and says so in the result and on the project.
The gap heuristic is token overlap, nothing more. Words are lower-cased, split on punctuation and camelCase, stripped of a few English suffixes and compared as sets.
Before comparing, each declared sentence gets a class by fixed rules, first
match wins (see src/sf_grove/claims.py):
prose: a lead-in ending with ":", or a fragment of six words or fewer with no verb-like word ("Not a judge.").limit: under a heading about limits, caveats or known issues, or phrased as "can be missed" / "not supported".behaviour: promise words such as never, always, must, only, does not, will not, cannot, "no" plus a noun, refuses or rejects.prose: positioning in the form "Name is a/the/someone's ...".setup: under an Install, Setup, Requirements, Development, Testing or Examples heading; starts with an instruction verb ("Run", "Install", "See"); or names a path, file name, version number or dependency.behaviour: everything else.
setup and prose sentences produce no finding. They stay in the declared
blueprint with extra.class and extra.class_reason, and the scan's
gap_notes.declared_skipped counts them. Every declared finding carries
evidence.class and evidence.class_reason. limit findings are listed
after behaviour findings and their summary starts with [limit].
declared_untested: abehaviourorlimitsentence shares no word with any test name or test file name. One shared word is enough to stay silent, so on a large project with many tests this finds little.declared_unseen: the same comparison against module paths and symbol names.actual_undocumented: a module whose file name is not mentioned in the docs; at most 25 per scan, shallowest paths first.map_untested: a block of an ingested map that carries the labeluntested. This is the map author's statement, copied; Grove did not check it.
A finding is a reason to go and read, not a result. grove recommend ranks by
fixed rules, not by understanding: map_untested, then untested behaviour
seen in code, then other untested behaviour, then the oldest ready work
item, then an untested limit, then actual_undocumented. The classes are
word rules, not grammar: a real promise with no promise word can land in
setup because it names a path, and an install note that uses "never" stays
behaviour.
Map ingest is simple. It recognises the labels documented,
observed-in-code and untested in paragraphs, list items and table rows.
See BUILD_NOTES.md for what it misses.
Licence
MIT; see LICENSE. Grove is a Software Forest project. It is not affiliated
with, endorsed by, or derived from any other product or company.
Available Tools
16 toolsgrove_blueprint_getARead-only
Read a project's blueprint items, paginated: what docs declare (declared), what the code appears to contain (actual: modules and symbol names) and what tests appear to prove (tests: test names). Optionally filter by layer and by a case-insensitive substring of title, body or path. Reads Grove's stored result of the last scan; it does not rescan. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: project (required), layer, limit, query, +1 more.
grove_gapsARead-only
List a project's gap findings, paginated, with counts per kind. Kinds: declared_untested (docs state it, no test name overlaps), declared_unseen (docs state it, no code name overlaps), actual_undocumented (module never mentioned in docs), map_untested (an ingested map marks it untested). Every finding is a token-overlap heuristic to be checked by reading the code, not a verdict. Use after grove_scan or grove_ingest_map. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: project (required), kind, limit, offset.
grove_ingest_mapA
Ingest a markdown architecture map (for example a MAP.md) into a project. The file is only read, never modified. Its full text is stored as a knowledge document (key from the file name; a new revision each time), and blocks carrying the evidence labels documented / observed-in-code / untested seed declared items, actual items and map_untested findings. Use when a human or agent has written a map of the codebase. Re-ingesting replaces what the same map seeded before. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: path (required), project (required).
grove_knowledge_getARead-only
Fetch a knowledge document by key: the latest revision, or a given revision number. Keys are unique per project (and at workspace level); pass project when the same key exists in more than one project. Without project, a workspace-level document wins. Use after grove_knowledge_search to read a full document. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: key (required), project, revision.
grove_knowledge_searchARead-only
Full-text search (SQLite FTS5) over the titles and bodies of the latest revision of every knowledge document, including ingested maps. All words must match; end a word with * for a prefix match. Returns ranked hits with a snippet, paginated; fetch a full document with grove_knowledge_get. Use before starting work, to recall what is already known. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: query (required), limit, offset, project.
grove_knowledge_writeA
Save a lasting note (a decision, a convention, a lesson) under a stable slug key. Writing an existing key adds a new revision; earlier revisions are kept and never overwritten. Give a project to scope the note to it, or omit project for a workspace-level note. Use for knowledge worth finding again in later sessions, not for scratch notes. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: key (required), body (required), title (required), project.
grove_project_createA
Register a codebase as a Grove project: a unique name plus the root directory to map. Use once per codebase, before grove_scan. Only a row in Grove's own database is written; the directory itself is not touched. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: name (required), root_path (required).
grove_project_getARead-only
Get one project by name or id, with last_scanned_at, the last scan's cap warnings, and item, finding and work-item counts. Use to check whether a scan is recent or was cut short by a cap. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: project (required).
grove_project_listARead-only
List registered projects, paginated. Use to find a project name or id. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: limit, offset.
grove_recommend_nextARead-only
Suggest one next step for a project by fixed priority, with a short rationale and the finding or work item ids behind it: map_untested findings first, then declared_untested findings whose words also appear in the code, then other declared_untested findings, then the oldest ready work item, then actual_undocumented findings. A deterministic heuristic; no model is called. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: project (required).
grove_scanA
Scan a project's root directory and rebuild its blueprint (declared / actual / tests layers), then recompute gap findings. Use after creating a project and whenever the code or docs have changed. The scan is deterministic and read-only: it never writes inside the project, honours .gitignore, and stops at the file and time caps (reported in the result). Returns counts per layer and per finding kind plus a small sample; page the rest with grove_gaps and grove_blueprint_get. It replaces earlier scan-derived items and findings but keeps knowledge, work items and map-ingested items. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: project (required), max_files, max_seconds.
grove_statusARead-only
Report Grove's version, data directory, database path and project count. Use it first to confirm which Grove home this session is using. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: no named parameters.
grove_work_createA
Create a local work item for a project, starting in status ready. issue_url is stored as plain text for reference and is never fetched or opened. Use to record a unit of work that should end in a reviewed change; Grove tracks it but does not implement it or open a pull request. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: title (required), project (required), plan, issue_url.
grove_work_getARead-only
Get one work item by id, with its plan and notes. Use before continuing a piece of work. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: id (required).
grove_work_listARead-only
List a project's work items, paginated, optionally only those in one status (ready, in_progress, review, done, blocked). Use to see what is open. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: project (required), limit, offset, status.
grove_work_updateA
Update a work item: any of status, plan, notes, title (at least one). Each given field replaces the stored value. Use to move work through ready -> in_progress -> review -> done (or blocked) and to keep the plan and notes current. This only updates Grove's local record. Grove is local only: scans are read-only and nothing is sent to GitHub or any other service.
Inputs: id (required), plan, notes, title, +1 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 16 tools
Each tool targets a distinct resource+action: project CRUD, scan, blueprint read, gap listing, map ingest, knowledge, work items, and recommendation are all clearly separated, and descriptions explain when to use which (e.g. scan vs blueprint_get vs gaps). The only mild overlap is grove_blueprint_get / grove_gaps / grove_recommend_next, since all three surface scan-derived findings, but their scopes and outputs differ enough to choose confidently.
Most names follow a predictable grove_<resource>_<verb> pattern (project_create, project_list, project_get, knowledge_write/get/search, work_create/list/get/update), all snake_case with a uniform prefix. A handful deviate in ordering or shape (grove_scan, grove_status, grove_gaps, grove_ingest_map, grove_recommend_next), which makes the set slightly less mechanical but still fully readable.
Sixteen tools sit at the upper edge of the comfortable range but each one maps to a real operation across four sub-areas (projects/scanning, knowledge, work items, recommendation), so nothing feels padded. It is slightly heavy for a local analysis tool, but no tool is obviously redundant.
The lifecycle is well covered: project register/list/get, scan and re-scan, blueprint and gap retrieval, knowledge write/get/search with revisions, and full work-item create/list/get/update plus a recommendation entry point. The notable gaps are the absence of project update/delete (no way to remove or rename a registered project) and no deletion/cleanup for knowledge or work items.
Maintenance
Related MCP Connectors
Agent-native notes, tasks, dev-docs, vaults, sync & handoffs. MCP + OpenAPI dual surface.
Team memory for AI coding agents and code reviews, reviewed by humans and served over MCP.
A shared team wiki your coding agents read and write — across every repo and every MCP client.
Hosted MCP server and Agent Skills for specs, engineering threads, and project memory.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceProvides a stdio MCP bridge for coding agents to query and record engineering knowledge locally, preserving debugging history, failed attempts, and verified solutions.3 npmMIT
- FlicenseNot gradedqualityCmaintenanceProvides a local-first code indexing and search engine for coding agents via MCP, enabling precise codebase queries, symbol lookup, and freshness-aware retrieval.-
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to locally search, query, and understand codebases with token-efficient context, dependency graphs, history, architecture diagrams, and metrics through MCP.15 npm3MIT
- AlicenseNot gradedqualityBmaintenanceEnables coding agents to inspect files, search text, compare directories, read symbols, and apply structured edits against large codebases through bounded, local-first MCP tools.4Apache 2.0