Token-efficient MCP server: fused search + batched fuzzy edit, structured test/git/lint/todos/commit tools.
Project description
BeagleLathe
Token-efficient MCP server for Claude Code. Eleven tools that collapse the sequences Claude Code runs most often — search, read, edit, test runs, git inspection, lint/typecheck, TODO scans, and commit-message drafts — from 3–5 round-trips down to 1.
Free tier: 200 tool calls/month. Pro: unlimited.
What it does
| Tool | Replaces | Savings |
|---|---|---|
search |
glob → grep → read-to-confirm | ~3 calls → 1 |
edit |
read → edit → verify-read | ~3 calls → 1 |
read |
cat + head/tail on large files | ~2 calls → 1 |
sh |
Bash for read-only inspection | structured output, fewer re-reads |
run_tests |
Bash(pytest) → grep → read → grep → read | ~5 calls → 1 |
git_status |
git status + manual parsing | structured branch/staged/untracked |
git_diff |
git diff + hunk parsing | structured hunks per file |
changed_files |
git diff --numstat + status parsing | one call vs branch |
lint_and_typecheck |
run each linter via Bash, grep output | one call across languages |
extract_todos |
grep + per-line git blame |
one call, sorted by age |
commit_message |
hand-write conventional commit | heuristic draft from staged diff |
Every session, BeagleLathe counts what it saved. Run /lathe-savings to see the numbers.
Install
Prerequisites: Python 3.10+ and the Claude Code CLI.
One-liner (recommended)
curl -LsSf https://beaglelathe.dev/install.sh | sh
Picks the best installer available (uv → pipx → pip), installs beaglelathe, registers the plugin with Claude Code, and walks you through magic-link sign-in. Pass --no-login to skip the sign-in step: curl -LsSf https://beaglelathe.dev/install.sh | sh -s -- --no-login.
Manual install
Pick one of these — each is one shell line + beaglelathe login:
# With uv (https://docs.astral.sh/uv) — fastest, isolated venv
uv tool install beaglelathe && beaglelathe login
# With pipx (https://pipx.pypa.io) — isolated venv
pipx install beaglelathe && beaglelathe login
# With pip directly (Python 3.10+) — fine on macOS; on Linux see note below
pip install beaglelathe && beaglelathe login
beaglelathe login registers the plugin with Claude Code (MCP server, slash commands, agents, hooks) and then walks you through magic-link sign-in. The install step is idempotent — re-running is a no-op once the plugin is registered. If you'd rather install without signing in, use beaglelathe install instead.
Linux + PEP 668 (Debian/Ubuntu): modern distros block pip install system-wide. Use uv tool install or pipx install instead — the one-liner above picks one automatically.
Alternative: install from the GitHub marketplace
For power users, the plugin is also published to the BeagleLathe Claude Code marketplace. You still need the Python package installed (the MCP server runs the beaglelathe console script):
uv tool install beaglelathe # or: pipx install / pip install
claude plugin marketplace add BeagleLathe/lathe
claude plugin install lathe@beaglelathe
Or inside a Claude Code session:
/plugin marketplace add BeagleLathe/lathe
/plugin install lathe@beaglelathe
Per-project (no global install)
To wire BeagleLathe into a single project's .mcp.json instead of installing the plugin globally:
uv tool install beaglelathe # or: pipx install / pip install
beaglelathe install --mcp-json
Verify
Open Claude Code in your project and run:
/mcp
You should see beaglelathe listed with eleven tools: search, edit, read, sh, run_tests, git_status, git_diff, changed_files, lint_and_typecheck, extract_todos, commit_message. If you have the plugin installed, a lathe:code badge appears in the sidebar.
Sign in (optional, free)
BeagleLathe tools work without an account. Sign in to track usage against your free tier:
beaglelathe login
This opens a magic-link URL in your browser. Click it, confirm, and credentials are saved to ~/.beaglelathe/credentials.json.
After signing in:
beaglelathe status
plan: Free tier
calls used: 0 this month
remaining: 200 calls
resets: 2026-06-01T00:00:00+00:00
account: you@example.com
CLI reference
beaglelathe install Register the plugin with Claude Code (idempotent)
beaglelathe login Auto-install if needed, then sign in via magic link
beaglelathe logout Clear stored credentials
beaglelathe whoami Print signed-in user
beaglelathe status Show plan and remaining calls
beaglelathe savings Show local savings summary
beaglelathe upgrade Open Stripe checkout (Free → Pro)
beaglelathe serve Start the MCP server (what Claude Code runs automatically)
All commands accept --help.
Slash commands (inside Claude Code)
/lathe List all commands
/lathe-login Run the login flow
/lathe-logout Clear credentials
/lathe-status View plan and calls remaining
/lathe-savings View local savings counter
/lathe-upgrade Upgrade to Pro
/lathe-update Update to latest version
/lathe-help Troubleshooting and quick reference
Statusline integration
BeagleLathe ships a Claude Code statusLine component that shows your current plan, calls remaining, and locally-tracked savings count on every prompt. To enable, add this to ~/.claude/settings.json:
"statusLine": {
"type": "command",
"command": "$CLAUDE_PLUGIN_ROOT/scripts/statusline.sh"
}
Or call the CLI directly:
"statusLine": {
"type": "command",
"command": "beaglelathe statusline"
}
Output looks like Lathe · Free · 158 left · saved 42. Reads only from ~/.beaglelathe/credentials.json and ~/.beaglelathe/state.db; never makes a network call.
Agents
Three agent definitions ship in agents/:
- lathe-code (default, Sonnet) — full tool access, instructs the model to use BeagleLathe tools first.
- lathe-explore (Haiku) — read-only search and inspection, returns compressed summaries.
- lathe-plan (Haiku) — reads the codebase, returns a concrete step-by-step implementation plan.
Tool reference
search
pattern Regex (ripgrep syntax). Required.
path_glob Glob to limit scope. Default: **/*
max_results Cap on total matches. Default: 50
context_lines Lines before/after each match. Default: 2
case_sensitive Default: false
Returns matches grouped by file with surrounding context. Always prefer this over separate file-discovery + content searches.
edit
edits Array of {path, old_string, new_string, replace_all?}. Required.
validate Run syntax validation after writing. Default: true
Matching cascade: exact → normalized (whitespace/smart-quote fold) → fuzzy (rapidfuzz ≥ 90). All-or-nothing: if any edit fails the dry run, nothing is written.
read
path File path. Required.
mode "full" | "truncated" | "skeleton". Default: "truncated"
symbol With mode=truncated, keep this symbol's body in full. Optional.
Use "ClassName.method" for nested symbols.
AST-aware truncation keeps top-level signatures, types, imports, and class skeletons, and stubs function bodies. mode=full returns the file verbatim; mode=skeleton returns just declaration lines (cheapest fallback for unsupported languages or huge files). Pass symbol with mode=truncated to keep one symbol's body in full while stubbing the rest. Hard cap: 200,000 chars.
sh
command A single read-only shell command.
Allowed commands: ls, cat, head, tail, wc, pwd, which, file, stat, du, df, find, tree, echo, git. For git: only log, status, diff, show, blame, branch, remote, config, rev-parse, ls-files, describe. No pipes, redirects, or subshells.
run_tests
command Override auto-detected test command. Optional.
path Restrict tests to this path. Optional.
pattern Test name filter (-k for pytest). Optional.
max_failures Cap on failure entries returned. Default: 20.
Auto-detects pytest / jest / vitest / cargo test / go test from files in cwd. Output: framework, command, duration_ms, summary{total, passed, failed, skipped, errors}, failures[{name, file, line, message, source_excerpt, traceback_tail}], truncated_failures. Collection / import errors land in error_output.
git_status
include_untracked Default: true.
Returns {branch, upstream, ahead, behind, staged, unstaged, untracked, renamed, conflicts}. Detached HEAD adds {detached: true, head_sha}. Returns {error: "not a git repository"} outside a repo.
git_diff
path Limit diff to this path. Optional.
from_ref, to_ref Compare specific refs. Optional.
staged Diff staged changes only. Default: false.
max_files Default: 50.
max_hunks_per_file Default: 20.
Returns {files: [{path, status, additions, deletions, hunks: [{old_start, old_lines, new_start, new_lines, header, content}]}], truncated_files, truncated_hunks}. Statuses: added | modified | deleted | renamed | copied | type_changed. Binary files get binary: true with no hunks. Unknown refs return {error: "unknown ref: ..."}.
changed_files
branch Compare against this branch. Default: auto-detected main/master/develop.
include_uncommitted Fold in working-tree changes. Default: true.
Returns {branch_compared, files: [{path, additions, deletions, status}], totals{files, additions, deletions}}. Untracked files appear with status: "added".
lint_and_typecheck
paths Files or directories to check. Default: ["."].
level "syntax" or "full". Default: "full".
max_issues Cap on issues returned. Default: 100.
Dispatches by extension: .py → ruff + mypy (or py_compile in syntax mode); .ts/.tsx → tsc + eslint; .js/.jsx → eslint; .go → go vet + golangci-lint; .rs → cargo check + clippy. Runners that aren't installed are listed in skipped_runners. Output: {level, summary{errors, warnings, by_file}, issues[{file, line, column, severity, code, source, message}], ran_runners, skipped_runners, truncated}.
extract_todos
path_glob Glob to limit scope. Default: **/*
markers List of markers. Default: ["TODO", "FIXME", "XXX", "HACK"]
include_blame Attach git blame author + age in days. Default: true
context_lines Lines around each match. Default: 1
sort_by "age" or "file". Default: age (oldest first)
max_results Default: 100
Returns {todos: [{marker, file, line, text, context, author?, commit?, age_days?}], total, by_marker, truncated}. Without git (or for untracked files), blame fields are omitted.
commit_message
source "staged" | "working_tree" | "last_commit". Default: staged
type_override Force a conventional-commit type. Optional.
scope_override Force a scope. Optional.
max_subject_length Cap on subject line. Default: 60.
Returns {type, scope, subject, message, source, files}. Heuristics: test paths → test, docs/*.md → docs, .github/ → ci, pyproject.toml/package.json/etc. → build, all-additions in source → feat, all-deletions → refactor, otherwise fix. Scope is the longest common directory after stripping src//lib//app//etc. Intended as a starting draft the agent can polish.
Troubleshooting
ripgrep not found
BeagleLathe ships vendored ripgrep binaries for all supported platforms. If you hit this on an unusual platform, install ripgrep manually (brew install ripgrep on macOS, apt install ripgrep on Linux) and restart Claude Code.
Tools not showing in /mcp
Check that beaglelathe is on PATH (which beaglelathe) and the plugin is registered (claude plugin list). If either is missing, re-run beaglelathe install (or the install one-liner from beaglelathe.dev) and restart Claude Code.
Auth failures / "session expired"
Run beaglelathe login to get a fresh token. Tokens have a 24-hour TTL — re-run login whenever the CLI reports "session expired."
Quota exceeded
Run /lathe-upgrade or beaglelathe upgrade to open Stripe checkout. Pro is unlimited.
edit reports "old_string not found"
The string you provided doesn't match what's in the file. Read the current file content first with read, then retry with the exact text.
edit reports "old_string not unique"
Add more surrounding lines to make the match unique, or set replace_all: true if you want all occurrences replaced.
First tool call is slow The MCP server starts on first use. ~3 seconds is normal. Subsequent calls in the same session are fast.
FAQ
Do you store my code?
No. search and edit run entirely on your machine. The auth backend only receives a call count (an integer) on each sync — never file contents, never diffs, never code.
Does it work offline? Yes. All four tools work without a network connection. The usage sync is best-effort; if the backend is unreachable the tool still succeeds.
What's the free tier exactly? 200 tool calls per month, per account. A "call" is one invocation of any BeagleLathe tool (search, edit, read, or sh). Resets on the 1st of each month.
Can I use it without signing in? Yes. The tools work anonymously. Without an account, savings are tracked locally but not synced, and there is no usage quota enforced.
Python version? 3.10 or higher.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file beaglelathe-0.2.5.tar.gz.
File metadata
- Download URL: beaglelathe-0.2.5.tar.gz
- Upload date:
- Size: 9.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6d06b293bad79c4d856b9ef30635152243d4e10fab517b2afaef99c8461da1d5
|
|
| MD5 |
dd5a42383188bfda0f246c0c01635874
|
|
| BLAKE2b-256 |
9c41013ca8ac18300cd2144d4b77fc8cdac265723fa54be762fbd34994bc4b0f
|
File details
Details for the file beaglelathe-0.2.5-py3-none-any.whl.
File metadata
- Download URL: beaglelathe-0.2.5-py3-none-any.whl
- Upload date:
- Size: 9.2 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dd0edd73e19fe50bedf1ac22cf4167ba43bef4555ffdeeb593ea9eaec6b3c3f6
|
|
| MD5 |
51e0499b9332dcd1e617d7a20f34f4a1
|
|
| BLAKE2b-256 |
9c4385e7af84089fe1d25fa7cc6eff099ab7510a169ace38754aca730c7e1d00
|