Building a Clojure Agent Environment

Table of Contents

1. Executive recommendation

Build the environment in two layers and do not confuse them.

The lower layer is deterministic and has nothing to do with language models: a parser that can repair delimiters, a formatter, a linter, and a running nREPL. It is cheap, it is boring, and it is the layer that actually prevents the failure this whole subject is about. The upper layer is agent-facing context — CLAUDE.md, skills, hooks, an MCP server — and it is worth nothing on top of a missing lower layer.

The evidence for putting it that way is in §3: across 93 Clojure projects on this machine, clj-kondo configuration appears in 20 and delimiter repair in 2. The upper layer is better provisioned than the lower one, which is exactly backwards.

Minimum viable stack, in the order it should be adopted:

  1. A hook that repairs delimiters on every write. One line of configuration.
  2. clj-kondo and a formatter, which most projects here already have.
  3. One long-lived nREPL the agent can evaluate against.
  4. CLAUDE.md naming the above, so an agent knows the REPL exists.
  5. Only then: an MCP server, if the extra tools earn their tokens.

2. How the pieces fit together

Distinguishing official capability from community tooling matters here, because the community tools are configured through the official mechanisms and inherit their constraints.

2.1. Supplied by the agent (official)

mechanism what it is lifetime
CLAUDE.md project instructions, loaded into context every session always resident
skills a directory with SKILL.md, loaded on demand by description match loaded when relevant
slash commands user-invoked entry points on invocation
hooks shell commands the harness runs at lifecycle points every matching event
MCP servers external processes exposing tools over a protocol while connected
subagents separate context windows with their own tool sets per delegation

The distinction that governs design: CLAUDE.md costs context on every turn, skills cost context only when their description matches, and hooks cost no context at all. A hook is the only one of these that is deterministic — it runs whether or not the model decided to invoke it.

That asymmetry is the whole argument for putting delimiter repair in a hook rather than in a skill. A skill that says "always balance your parentheses" is a request. A PreToolUse hook is a guarantee.

2.2. Supplied by the community

Four integration models, and they are not interchangeable:

  • Hook-shaped. A binary the harness invokes on write. Deterministic, no context cost, no model involvement. clj-paren-repair-claude-hook, brepl.
  • CLI-shaped. A command the model calls through bash. Costs a tool call, works with any agent that has a shell. clj-nrepl-eval, clj-paren-repair.
  • MCP-shaped. A server exposing typed tools. Richest, most expensive in tokens and setup, and overlaps whatever the harness already does natively. clojure-mcp, nrepl-mcp-server.
  • Prompt-shaped. Text, nothing more. clojure-system-prompt.

3. What is actually deployed

Surveys of this subject list what exists. The more useful question is what a working Clojure shop has actually adopted, so the audit below walked every project under two GitHub orgs carrying a deps.edn, project.clj, shadow-cljs.edn or bb.edn.

layer check present
agent context CLAUDE.md 20 / 93
agent context .claude/skills/ 10 / 93
deterministic clj-kondo configuration 20 / 93
deterministic clojure-lsp configuration 1 / 93
deterministic formatter configuration 14 / 93
runtime nREPL or CIDER in deps 22 / 93
deterministic delimiter-repair hook 2 / 93
tools MCP server configured 0 / 93

Three readings.

The cheapest control is the least adopted. Delimiter repair is one line of configuration and it is the direct answer to the failure mode every survey names. Two projects have it.

Linting is not the same as indexing. clj-kondo at 20 and clojure-lsp at 1 means the corpus checks its code but gives an agent no symbol index to navigate with. An agent without clojure-lsp does what a reader without an index does: greps.

Twelve projects have a REPL an agent could use and no CLAUDE.md to mention it. That is the widest gap between capability and reachability in the corpus, and it costs nothing to close. It is also a clean instance of Norman's gulf of evaluation: the capability is present and documented, and the presentation makes it unreachable.

3.1. A note on how this table was produced

The first two runs of the audit were wrong, and both errors were caught by one assertion — this repository must report an nREPL, because it ran cider-nrepl twenty minutes ago.

  • BSD grep -qs pat f1 f2 f3 exits non-zero when any named file is missing, even when another matched. The first run reported delimiter repair and nREPL presence at zero across the whole corpus.
  • Passing a flag where a pattern was expected left grep with a pattern and no file, so it read the enclosing loop's standard input and silently truncated the run at 32 of 93 rows. The totals looked plausible.

A count is not evidence until something in the corpus is known in advance and the count agrees with it.

4. Comparison of existing tools

project model maturity licence capability limitation
clojure-mcp TESTED MCP 778★, v0.5.1 EPL-2.0 eval, form-aware edit, paren repair, deps inspection self-described alpha; overlaps native edit tools
clojure-mcp-light TESTED hook + CLI 189★, v0.2.2 EPL-2.0 delimiter repair on write; CLI nREPL eval no structural editing
brepl hook + CLI 70★, v2.7.1 see repo bracket fix via parmezan, eval, live file sync overlaps the above; pick one
clojure-system-prompt prompt 44★ MIT REPL-first conventions as text no enforcement
nrepl-mcp-server MCP 37★ none stated eval, namespace inspection last pushed 2025-06; no licence
awesome-clojure-llm index 24★ none stated curated list a list, not a tool
clojure-skills skills 12★ see repo paren repair, eval, LSP integration as skills skills are advisory, not enforced
lisplens CLI + MCP 3★ MPL-2.0 token-efficient polyglot Lisp editing small, young
forj MCP + hooks 2★ none stated agent teams, checkpoints, gates no licence; very young

The no licence column matters more than the star count. Three of these projects ship without one, which means no grant of rights — fine for reading, a problem for anything a business depends on.

The star counts above are the weak signal, and a better one exists. The sibling survey github-skills-search crawled the Clojure AI ecosystem on the same day this note was written: two hops from four seed repositories to 743 users and 180,255 star edges, yielding 118 candidates with a structured card each. Its ranking uses independent co-stars — cohort users who starred a repository, excluding those recruited through an engagement with that same repository. Without the correction a seed's own contributors inflate its count by construction; one project in that survey drops from 62 raw co-stars to 3 independent ones.

That survey also had to solve a problem this note did not notice: since 2026-06-30 GitHub restricts stargazer and watcher lists to repository admins, so a cohort cannot be built from who starred a thing. It is built from public engagement instead — contributors, fork owners, issue and pull-request authors.

Two things it found that the table above misses entirely.

ECA (editor-code-assistant/eca, 1,011 stars) is the flagship product written in Clojure in this space — an editor-agnostic coding assistant, second by independent co-stars across the whole cohort. It is a peer of the harness, not a plugin for one, which is why a survey organised around "what can I add to Claude Code" does not surface it.

There are eight or more Clojure MCP SDKs, a category this note omits by framing MCP as something to consume rather than something to build. Co-stars favour the early entrants — modex, mcp-clojure-sdk — while the newer plumcp (client and server, Clojure and ClojureScript, OAuth 2.1, all transports, used by ECA and Nubank) and mcp-server (RokLenarcic) are the more complete. Worth knowing before writing an MCP server by hand.

4.1. Where this note disagrees with its sources

Two LLM-generated surveys of this question were available. Both are useful and both contain errors that would have cost time.

The hook schema is wrong in one of them. It gives "matchers": [{"tool": "Edit|Write"}]. Claude Code takes a singular matcher string. Copying the survey produces a hook that sits in settings.json looking configured and never fires — the worst failure mode available, since it is invisible. The tool's own README has the correct shape.

The alpha claim checks out. The same survey flags clojure-mcp as alpha; its README says so directly at line 475. Recorded because a claim that survives checking deserves the same note as one that does not.

5. Ranked stack

5.1. Tier 0 — before anything else

CLAUDE.md naming how to start the REPL, and a running nREPL. Without these the remainder is decoration. Twelve projects in the audit have the second and not the first.

5.2. Tier 1 — deterministic, cheap, adopt immediately

A delimiter-repair hook on PreToolUse and PostToolUse for Write|Edit; clj-kondo; a formatter. This tier costs no context and no tokens, and it is where the actual guarantee lives.

5.3. Tier 2 — runtime feedback

A CLI nREPL client the model can call through bash. This is the cheapest way to make claims executable, and it works under any agent with a shell rather than only under one harness.

5.4. Tier 3 — MCP, on the author's own terms

The most useful guidance here is upstream's, and it is more specific than either survey that recommended the tool. clojure-mcp's README says to start with clojure-mcp-light, because a CLI assistant already has good inline-diff editing and shell tools, and that this "works well for most Clojure development". It then gives two reasons to add the full server on :cli-assist:

  • Structural editing as a fallback. The light sibling repairs delimiters after an edit succeeds, but cannot help when a find-and-replace match string does not match the code — quantified upstream at under 5% of edits. Structural editing targets a form by type and name, so the problem does not arise.
  • A first-class REPL tool. Models "tend to use MCP tools more readily than CLI commands", which may raise how often the REPL is consulted at all.

That second reason is the interesting one, because it is a claim about model behaviour rather than capability. clj-nrepl-eval and clojure_eval do the same thing; the argument is that one gets reached for and the other does not. It is exactly the sort of claim the ecosystem has no way to check — see the evaluation gap below.

So the two tools are complementary rather than alternatives, which settles the disagreement between the two surveys: the one recommending the light sibling first was reading upstream correctly, and the one calling the full server the foundation was not wrong to want it, only wrong about the order.

5.5. Tier 4 — multi-agent orchestration

Defer. The projects here are young and unlicensed.

6. The failure modes, and what actually addresses each

failure why it happens what fixes it kind
unbalanced delimiters the model edits text, not forms repair hook on write deterministic
edit lands in the wrong form line-based patching has no notion of a form structural edit tool tool-assisted
invented API training data, not this codebase evaluate it in the REPL runtime
stale assumption about state the file is not the running system evaluate it in the REPL runtime
silent breakage of a caller no symbol index clojure-lsp deterministic
green gate, broken behaviour the gate was never run against a known-bad input calibrate the gate discipline

The last row is the one no tool supplies. See §8.

7. A .claude/ layout

.claude/
  settings.json          hooks: paren repair on Write|Edit, both Pre and Post
  skills/
    clojure-repl/        how to reach the REPL here, and what to evaluate first
    clojure-test/        run the focused test, then the namespace, then the suite
    clojure-review/      what to check that a linter cannot
CLAUDE.md                the REPL address, the aliases, the tier boundaries
.clj-kondo/config.edn
.lsp/config.edn

The split: CLAUDE.md carries what is true every turn and nothing more; skills carry procedures with a trigger; settings.json carries what must happen whether or not the model agrees.

8. A verification ladder that has been calibrated

step gate on failure
1 parses hook repairs the write before it lands
2 formatted formatter rewrites
3 lints clj-kondo reports
4 loads (require 'ns :reload) in the REPL
5 tests run the focused test first

Step 1, run rather than asserted:

known-bad   (defn f [x] (inc x)    ->  updatedInput: (defn f [x] (inc x))
known-good  (defn f [x] (inc x))   ->  no output at all
tool absent from PATH              ->  exit 1, naming the install step

The middle line is what makes the first line mean something. If balanced input also produced output, a check that greps for the repair would pass against a hook that does nothing.

The runtime half, against a live nREPL:

$ clj-nrepl-eval -p 42527 "(+ 1 2 3"
=> 6

The input is missing a delimiter. The client repaired it and evaluated it. That single line exercises both halves of the lower layer at once.

9. Skills are not scarce — they are invisible to the way I looked

The audit in §3 counted .claude/skills/ directories in two GitHub orgs and found 10 of 93. A skill-specific crawl of GitHub, run the same day, found 1,251 repositories holding 4,414 skill directories. Both numbers are correct. They measure different things, and the gap between them is the finding.

Dedicated skill repositories barely exist — topic:claude-skills clojure returns 1, topic:agent-skills clojure returns 3. Skills live inside other repositories, where a repository-level search cannot see them, and where my directory-existence check only sees them if I happen to be standing in the right tree.

Three consequences.

Large Clojure products already ship skills. Penpot (60k stars) carries nrepl-eval. Metabase (49k) carries clojure-eval, clojure-review, clojure-write and add-malli-schemas. Defold ships clojure-code-style for its editor; XTDB ships xtdb-testing and xtdb-object-boundaries. Shipping a skill is now part of how a large Clojure codebase onboards an agent, and the practice is further along than a survey of one person's repositories suggests.

Skills propagate, and nobody is tracking provenance. Metabase's three skills appear in 19 to 31 other repositories — forks, dotfiles, and marketplaces including aiskillstore and awesome-omni-skill. Nearly half of all matched skill directories, 47.8%, sit in eight or more registry repositories that mirror them in bulk. A skill is a Markdown file that tells an agent what to do, it copies as easily as a dotfile, and one of the places Metabase's skills turn up is a malicious-skill benchmark. That is a supply chain with no provenance layer, and this note has nothing to offer about it.

The center of gravity is narrower than the volume suggests. Among classified skill names, REPL and evaluation is the largest single group at 55 of 768. Everything else is specific — a library, a frontend framework, a house style. The generic "Clojure skill" is rarer than the count implies.

9.1. Why this correction is worth keeping

The crawl committed six predictions before running and falsified two of its own, including the one about how many repositories it would find. A survey that can be wrong about its own subject in a way it records is worth more than one that cannot, and it is the practice this note's verification ladder is trying to import.

10. The gap nobody is filling

The landscape survey's strongest negative finding: evaluation is the largest gap in this ecosystem. There is no benchmark for Clojure code generation, the one effort at a fine-tuning dataset was abandoned in 2023, and apart from one project requiring a Rama cluster there is no maintained evaluation tooling.

That matters for everything above. Every recommendation in every survey of this subject — including this note — rests on plausibility and on small local checks. Nobody can currently answer whether adding an MCP server changes outcomes, whether a delimiter hook reduces failed edits by a measurable amount, or whether a skill fires when it should. The tools are good and the evidence that they help is anecdote.

There are early signs of a measurement culture — skill A/B tests, benchmark receipts, an encrypted challenge suite — but they are per-project, not shared. Until that changes, the honest framing of a recommended stack is this is what the people building the tools believe, not this is what works.

11. Across the Lisp family

dialect delimiter repair live runtime index what differs
Clojure mature nREPL clojure-lsp best provisioned by a wide margin
ClojureScript shared shadow-cljs / piggieback shared the REPL is the hard part, not the editing
Common Lisp ad-hoc SLIME/SWANK none agent-facing the runtime is excellent and unreachable
Scheme, Racket ad-hoc varies Racket has one fragmented across implementations
Emacs Lisp native to the editor the editor is the runtime none the agent is outside the thing that has the REPL
Fennel, Janet none found none found none the tooling does not exist yet

The generalisable part: delimiter repair is a parsing problem and ports across the family almost unchanged; live runtime access is a protocol problem and does not port at all. Clojure's advantage is nREPL, not its parentheses.

12. Adoption plan

  1. Today. Add the repair hook. One line, no context cost.
  2. This week. Calibrate it — feed it an unbalanced form and watch it fire. An uncalibrated hook is indistinguishable from no hook.
  3. This week. Add CLAUDE.md to the twelve projects that have a REPL and no note saying so.
  4. Next. clj-kondo where missing; clojure-lsp anywhere an agent navigates rather than reads.
  5. Then. A CLI nREPL client, so claims become executable under any harness.
  6. Only then. MCP, on a minimal profile, and measure whether the extra tools change outcomes.
  7. Not yet. Multi-agent orchestration.

13. Related