LSP Enforcement Kit: Hooks That Block Claude Code's Grep Habit
Hooks that force Claude Code to use LSP instead of Grep for code navigation. Saves ~80% tokens
At a glance
- What is it?
- A set of Claude Code hooks that intercept Grep, Glob and grep-in-Bash calls on code symbols and return a copy-pasteable LSP command instead. It is a guardrail, not a language server: the LSP MCP server has to be installed separately.
- Who is it for?
- Adopt it if you already run cclsp or Serena with Claude Code and want navigation to go through them by default; the hooks are thin JavaScript files under hooks/ and the installer is idempotent. Do not adopt it if you have no LSP MCP server configured, since the kit blocks calls without providing the replacement, and skip it if your work is mostly prose, config or data files where Grep is the right tool.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 154 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What the LSP Enforcement Kit actually enforces
Claude Code reaches for Grep and Read when it needs to find a symbol. The README walks through a concrete example: finding where handleSubmit is defined costs roughly 9,000 tokens and four tool calls through Grep plus three file reads, against roughly 230 tokens and two calls through find_definition plus one targeted read. The kit's own table puts the per-operation saving between 90% and 98% depending on the task, and its one-week sample across two TypeScript projects claims about 235k tokens saved, or 73%.
Those numbers come from the project's README and are estimates, not measurements anyone can reproduce from the repository alone. The interesting claim is not the percentage. It is the mechanism: a rule in CLAUDE.md asking Claude to prefer LSP works, per the README, about 60% of the time, and hooks make it 100%. That is the whole pitch. If you accept that a prompt-level instruction is advisory and a hook is not, the kit is the difference between asking and preventing.
The audience is narrow and specific: engineers running Claude Code on a codebase where an LSP MCP server is already configured, who are paying per token or watching a context window fill up with grep output. If you are not in that group, the kit has nothing to do.
Six hooks, one tracker, and a provider registry
The architecture is six hooks plus a tracker. Three sit on the PreToolUse side and intercept before the tool runs: lsp-first-guard.js watches Grep calls, lsp-first-glob-guard.js watches Glob, and a third guards grep invoked through Bash. The Glob guard is the more interesting of the three because it has to distinguish a symbol-shaped pattern from a legitimate path pattern. The README states it blocks patterns like *UserService* and **/handleFoo*.ts while allowing *.ts, *subdomain* and src/**. That is a heuristic, not a parser, and the allow-list is short enough that a glob pattern built around a variable name could plausibly be caught.
The PostToolUse side carries the read gate. It is progressive: a Read of a code file is blocked until the session warms up LSP, after which two Reads are allowed for free before LSP navigation is required again. The block message is parametrized by the file Claude tried to open, so the suggested call is mcp__cclsp__get_diagnostics("src/page.tsx") for that exact path rather than a generic instruction. Version 2.3.0 is titled "File-parametrized warmup + actionable block messages", which is where that behaviour lands.
Provider detection is the piece that keeps this from being cclsp-only. hooks/lib/detect-lsp-provider.js reads user-level Claude Code config at ~/.claude.json and ~/.claude/settings.json, matches known server names against a PROVIDERS registry, and the hooks tailor their suggestions accordingly. cclsp gets mcp__cclsp__find_definition and friends; Serena gets mcp__serena__find_symbol, find_referencing_symbols and get_symbols_overview. Both installed means both sets appear. Neither installed means a generic fallback with install hints. Adding a provider is one registry entry rather than edits across six hooks, which is the right shape for this kind of tool.
Installing the kit and getting past the first block
Installation is a clone plus a shell script. The README gives this sequence, and notes that install.sh is idempotent, so re-running it on an upgrade is safe.
git clone https://github.com/nesaminua/claude-code-lsp-enforcement-kit.git
cd claude-code-lsp-enforcement-kit
bash install.sh
# Windows: pwsh ./install.ps1After the installer finishes, restart Claude Code. The hooks are registered through Claude Code's own hook configuration, so a running session will not pick them up.
Verification is a single script, and it is the first thing worth running because it tells you whether provider detection worked before you hit a block message that names the wrong server.
bash scripts/lsp-status.shWhat you should see is the detected LSP MCP provider or providers. If it reports neither, the hooks will still block, but the suggestions will be the generic fallback rather than calls your server actually exposes. Check ~/.claude.json and ~/.claude/settings.json at that point, since that is where detection reads from.
The first real use is not something you invoke. You ask Claude Code a normal navigation question, it tries Grep, and the hook returns a block message shaped like the README's example: the symbol names it found in the pattern, followed by the LSP tool to call for each one. You paste that call back. The second time, the read gate behaves differently because the session has warmed up, and you get two free Reads before it asks for LSP navigation again.
Where the enforcement gets in the way
The kit blocks calls without supplying the replacement. It cannot run find_definition for you; it can only refuse the Grep and print the command. If your LSP MCP server is not installed, misconfigured, or does not index the language you are working in, you have converted a working (if wasteful) navigation path into a dead end. The README's fallback for "neither installed" is install hints, which is honest but does not unblock the session.
The Glob guard's allow-list is the second soft spot. Patterns like *.ts, *subdomain* and src/** are permitted, everything symbol-shaped is not. A repository where filenames legitimately contain service or component names, which is common in TypeScript and Go, will produce false blocks that the reader has to work around manually.
There is also a real cost to the LSP path that the token tables do not show. An LSP server has to be running and the project has to be indexed. On a cold start, or after a large branch switch, the first find_definition can be slower than a grep that returns in milliseconds. The kit's economics assume a warm index. It is the wrong tool for a repository you open once, for a codebase in a language your LSP server does not support, and for work that is mostly Markdown, YAML or SQL, where Grep is genuinely the correct instrument and the hooks are pure friction.
Alternatives: Serena, cclsp, and the CLAUDE.md rule
The kit is not an alternative to Serena or cclsp. It is an alternative to hoping Claude uses them. That distinction matters when you are choosing what to install.
Serena is a symbol-oriented MCP server, MIT licensed, from Oraios AI. It exposes high-level operations like find_symbol, find_referencing_symbols and get_symbols_overview, and the README describes multi-language support across Python, Go, Rust, Java, TypeScript and Vue through its bundled solidlsp wrapper. Installing Serena alone gives Claude the capability; it does not change Claude's default behaviour. cclsp is the narrower option, a standalone MCP server that the README also describes as bundled through the typescript-lsp Claude Code plugin, exposing find_definition, find_references, find_workspace_symbols and get_diagnostics. Same story: capability without enforcement.
The third alternative is the one most people try first, a rule in CLAUDE.md telling Claude to prefer LSP. It costs nothing, requires no hooks, and per the project's own README works around 60% of the time. If that number is accurate for your setup, the kit is buying you the remaining 40%, and you should decide whether the block-and-paste interruption is worth it. The kit's advantage over both is that it is provider-aware: it detects which server you have and prints calls for that server, so it composes with either rather than competing.
Maintenance, licence, and what an upgrade touches
The repository is MIT licensed, which permits commercial use, modification and redistribution with the licence text retained. That is the whole of the licence story here; the kit is a set of hook scripts and a registry, with no runtime service and no data collection described in the README.
The last push was on 2026-04-14, and the most recent release in the same window is v2.3.2, following v2.3.1 and v2.3.0 on the same day. Three releases in under twenty minutes suggests a documentation and packaging pass rather than three separate feature drops; v2.3.1 is titled "README redesign" and v2.3.2 is titled "Hero image". The substantive change in that batch is v2.3.0, "File-parametrized warmup + actionable block messages".
Upgrade cost is low by construction. install.sh is idempotent, so the upgrade path is pulling main and re-running it. The files you would actually modify to extend the kit are hooks/lib/detect-lsp-provider.js for a new provider and the individual hook files for new interception rules. The one thing to watch on upgrade is that hooks are registered in Claude Code's configuration, so an installer change that touches registration needs a session restart before you can tell whether it took effect. The README does not document a rollback procedure, so keep the previous clone until the new one verifies.
Editorial conclusion
Adopt it if you already run cclsp or Serena with Claude Code and want navigation to go through them by default; the hooks are thin JavaScript files under hooks/ and the installer is idempotent. Do not adopt it if you have no LSP MCP server configured, since the kit blocks calls without providing the replacement, and skip it if your work is mostly prose, config or data files where Grep is the right tool. Before rolling it out, run bash scripts/lsp-status.sh to confirm the provider detection found your server, and read hooks/lib/detect-lsp-provider.js to check that your server name is in the PROVIDERS registry.
Frequently asked questions
Does the LSP Enforcement Kit include an LSP server?
No. It detects whether cclsp, Serena or both are installed by reading ~/.claude.json and ~/.claude/settings.json, and tailors its block messages to whatever it finds. If neither is present it falls back to generic install hints, so the kit blocks navigation without providing the replacement.
Why does Claude Code keep getting blocked when I use Glob with the LSP Enforcement Kit?
The Glob guard blocks symbol-shaped patterns such as *UserService* and **/handleFoo*.ts, while allowing path-shaped ones like *.ts, *subdomain* and src/**. If your filenames contain symbol names, the pattern will look symbol-shaped to the hook and be refused.
How do I check that the LSP Enforcement Kit found my LSP provider?
Run bash scripts/lsp-status.sh after installing and restarting Claude Code. If it reports no provider, the hooks will still block calls but will suggest the generic fallback rather than the tool names your server exposes.
Community notes