The MCP layer: the compiler as a server for agents
More and more code is written with an AI assistant in the loop, and that assistant has the same problem a new team member has: it does not know the codebase, and it has no reliable memory of the language. The Model Context Protocol (MCP) is a small, open standard for letting such an assistant call tools: the assistant's client starts a server process, asks it which tools it offers, and calls them with JSON arguments. kola mcp is such a server, built into the kola binary. It lets an assistant ask the compiler questions ("what does this function touch, who calls it, which tests cover it") instead of reading and guessing, and it lets the assistant edit code by symbol, with every edit checked before it reaches the disk.
Nothing needs installing beyond kola itself. The language card, the standard library and the onboarding guide are embedded in the binary, so a copied binary can serve a project with no configuration.
Quick start
An MCP client starts kola mcp [DIR] and talks to it over standard input and output. With no directory, the server serves its current working directory.
# one session, over stdio
kola mcp ~/dev/myproject
# several sessions sharing one index: start a background server once
kola mcp up ~/dev/myproject # listens on .kola/mcp.sock inside the project
kola mcp status ~/dev/myproject
kola mcp down ~/dev/myprojectWhen several assistant sessions work on one project, kola mcp up starts a long-running background process (a daemon) that owns the project index and listens on a Unix socket, a local file-like endpoint at .kola/mcp.sock. Each session's kola mcp then becomes a thin relay into that socket, starting the daemon if it is not running, and all sessions share one index. --no-daemon keeps a session on plain stdio.
A client configuration usually looks like this:
{ "mcpServers": { "kola": { "command": "kola", "args": ["mcp", "--std", "std"] } } }The --std std matters if you are working on the standard library itself. The standard library is embedded in the binary, so without the flag the server answers about the std compiled into kola while you edit the copy on disk. A relative path works because the client starts the server in the project root. A client loads the tool list when it starts, so restart it after changing this file.
The index is refreshed on each request, keyed by a hash of each file's bytes, so a lookup reparses only the files that changed. There is no file watcher that could miss an event and serve a stale answer.
What the server offers
kola mcp publishes 87 tools. They fall into a few groups.
Group | Tools | What you get |
|---|---|---|
Compiler facts |
| The checker's own answers as JSON: signatures, effect rows, contracts, the call graph, the capabilities a program reaches. |
Navigation |
| The file tree, references, what a change affects, and cross-file views of packages and module edges. |
Toolchain |
| Runs the real compiler. Diagnostics cite the card rule and propose a repair, the same feedback a person gets on the command line. |
Tests |
| Which tests a change needs, what no test reaches, and the test debt described below. |
Editing |
| Edits by definition rather than by line, each checked before it is written. |
Packages |
| Manifest edits, checked like code edits. |
Docs |
| Ranked text search over the project's Markdown documents. |
Process |
| The project's working records (see below). |
Reference designs |
| A built-in catalog of reference architectures, each with its safety contracts and measured limits. Guidance, not a verdict on your code. |
Dev sessions |
| Runs one program under the interpreter with live reload or restart, owned by the server. |
The server also offers resources, documents a client can read: kola://card (the language card, the only source of truth for syntax and semantics), kola://std-card (the standard library), kola://guide (onboarding for an assistant writing kola), kola://skill (the same guide packaged as a skill file), kola://plan, the latest handoff, every decision record, and the reference architectures with their example sources.
Editing by symbol
Text edits go wrong in predictable ways: a search-and-replace meant for one function also hits a similarly named one, or an inserted item is missing a separator and the file stops parsing. The edit tools work on whole definitions instead. kola_edit_symbol {file, name, body} replaces one definition. The body must parse to exactly one item of the same kind and name (a different name is refused, because a rename strands callers and is a different tool). kola_replace_in_symbol makes an anchored change inside one definition, so a long body is not retyped; its expect_matches count is a contract, and a wrong count is refused.
Every edit is checked in memory first: the edited file, then every file whose imports reach it. Only when that check is green is the file written, atomically. The answer reports what was replaced, what was checked, and which tests the edit owes. Every write tool accepts dry_run: true to return the diff without writing anything. kola_batch runs several edits as one transaction, and kola_undo takes back the last one (the server keeps twenty).
The server also refuses arguments a tool does not declare, naming each unknown key. A tool that silently ignored a misspelled field would report success about work it did not do.
Test debt
Every edit is booked in a ledger at .kola/ledger.json. For each changed definition the ledger records which test files reach it and whether they have since passed on both executors, the interpreter and the native build. The interpreter leg runs by default with each edit; kola_settle (or kola mcp settle) runs the native leg, and kola_waive records a deliberate exception. Edits made outside the server, in an editor or by a script, are noticed by hash on the next request and booked the same way.
kola mcp owed prints the outstanding debt and exits with status 2 while anything is pending. That makes it easy to enforce outside the assistant, where enforcement belongs: a git pre-push hook or the assistant harness's end-of-session hook runs it, and nothing ships with tests owed.
What the server walks
The served project is the package graph, not just one directory. Every directory a kola.work entry or a sibling package resolves to is indexed, inside the root or beside it. A file outside the root is written @<pack>/<path>, and that spelling is both what answers print and what calls accept. Published packages in the local package cache can be read but are not indexed, and writes into them are refused. --only DIR restricts the walk to some subdirectories; --except DIR keeps one out, such as a vendored reference tree. --docs DIR and --handoff PATH point the process tools at a different documents directory or handoff file.
Working records
The process tools turn a project's working habits into tools that refuse incomplete input. All their records are plain Markdown files in the repository; the server reads and writes them but never owns them.
handoff_readandhandoff_writecarry a session's state to the next one. A handoff without what was done, what remains and which checks were touched is refused.adr_record,adr_listandadr_getkeep decision records, short numbered files with a status, the context, the decision and its consequences.inbox_put,inbox_listandinbox_takefile escalations. An entry without a reproduction is refused: an issue must contain everything needed to fix it.gap_record,gap_checkandgap_closetrack known deficiencies, each with a runnable reproduction.gap_checkreruns them all; a reproduction that now succeeds means the gap closed and its workarounds should be deleted.
The server's instructions suggest a rhythm. Start with handoff_read and gap_check. Before designing anything, docs_search the project's documents, since the question may already have been decided. Record decisions with adr_record rather than leaving them in a chat. End with handoff_write.
How answers describe themselves
Each answer says what kind of answer it is, so an assistant cannot mistake a guess for a fact.
Test selection over-approximates.
kola_affected_testsreports how many test files it selected out of how many and on what basis, selects more on any doubt (a name collision adds tests, an unparseable file is always included), and points at the full suite. It speeds up the inner loop; it does not replace the suite.Search returns candidates. A
docs_searchhit proves nothing until it is read, and no hits does not prove absence. Compiler facts and search results are kept visibly apart so the facts stay authoritative.Cross-file symbol answers are matched by name and can over-report when two modules use the same name. Each carries its module path, and
kola_symbolgives the exact answer.Every answer states the scope it was served under (
--only,--except), and the server's startup reply names its commit, the card version and a hash of its standard library.
Issues from outside: the public inbox
A published package can advertise a public MCP endpoint through the package registry, with its guide, its query tools and an issue inbox. Anything arriving there comes from strangers, so it is treated as hostile data. The two sides share a written schema and no code. The public side accepts an issue only against a published package version and requires a title, the package, a reproduction, the expected and the actual behaviour. A person reviews pending issues, and only an approved one is passed to kola mcp intake, which enforces the schema a second time and writes the issue into the project's inbox as a plain file.
# on the registry side
issues list # 1 shamir crash on open
issues approve 1 | kola mcp intake ~/dev/my-pack-repo
# -> filed docs/inbox/001-crash-on-open.md (public issue, schema v1)
# reproduce in a sandbox before acting on itPackages, manifests and capability budgets have their own chapter, Packages (kola.pack). For what the compiler does underneath these tools, see Under the hood: how kola works. The full tool list is in kola help mcp.