understudy
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., "@understudy Add an is_palindrome(text) function to textutil.py with a test and run pytest -q."
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.
understudy
Let Claude Code hand routine coding tasks to a local model, with your tests as the gate and Claude as the reviewer.
understudy is an MCP server. It gives Claude Code (or any MCP client) a
delegate tool: Claude describes a small, well-defined change, a local model running on your machine does the
work in an isolated git worktree, and your project's tests are run after every attempt. When the tests pass,
Claude gets back the diff to review, and nothing reaches your code until Claude (and you) accept it.
The point is to spend your Claude usage on the hard parts (planning, tricky code, review) and let a free local model do the typing for the routine ones.
flowchart LR
C[Claude Code] -- "delegate(task, test_command)" --> U[understudy]
U --> W[isolated git worktree]
W --> M[local model<br/>via OpenCode + Ollama]
M --> T{tests pass?}
T -- "no: errors fed back<br/>(up to N rounds)" --> M
T -- yes --> R[diff + test output<br/>+ warnings]
R --> C
C -- "accept(id)" --> G[one commit on your branch]
C -- "revise(id, feedback)" --> M
C -- "discard(id)" --> X[thrown away]Why
Save quota. The local model writes the code. Claude only reads a diff, instead of reading files, writing code and re-running tests itself.
Safe by construction. All work happens in a separate git worktree on its own branch. Your working tree is never touched until
accept, and a merge conflict is rolled back automatically.Tests are a hard gate. Failing test output is fed straight back to the local model, for free, until the tests pass or the round limit is reached.
Claude stays the reviewer. The Claude you're already talking to reviews the result, so no second Claude session is needed. understudy also flags the classic small-model mistakes: when existing tests or functions were deleted, it says so, because "tests pass" can just mean "the failing test was removed".
A failed revision never loses a passing version. If
revisebreaks the tests, the change is rolled back to the last version that passed.
Related MCP server: cc-delegate
Requirements
Python 3.10+ and git
Ollama with a coding model pulled
OpenCode, configured with your Ollama models (understudy drives the model through OpenCode's file-editing tools). Any other command-line coding agent can be used instead; see Using another builder.
Setup
1. Give your model enough context
Ollama runs models with a 4,096-token context by default, which is too small for a coding agent: the model loses its instructions and stops without making changes. Create a variant with a larger context window (this doesn't download anything and leaves the original model untouched):
curl http://localhost:11434/api/create -d '{"model": "devstral-small-2:24b-16k", "from": "devstral-small-2:24b", "parameters": {"num_ctx": 16384}}'Alternatively, set OLLAMA_CONTEXT_LENGTH=16384 in the environment of the Ollama server.
2. Add the model to OpenCode
In ~/.config/opencode/opencode.json, list the model under your Ollama provider:
{
"provider": {
"ollama": {
"npm": "@ai-sdk/openai-compatible",
"options": { "baseURL": "http://localhost:11434/v1" },
"models": {
"devstral-small-2:24b-16k": { "name": "Devstral Small 2 (16k)" }
}
}
}
}3. Install understudy and add it to Claude Code
pipx install git+https://github.com/Barry-Lenhart/understudy.git
claude mcp add understudy --scope user \
-e UNDERSTUDY_MODEL=ollama/devstral-small-2:24b-16k \
-- understudy-mcpA delegation can take several minutes on local hardware. Claude Code's default MCP tool timeout is generous, but
if you have set MCP_TOOL_TIMEOUT yourself, make sure it allows for that (e.g. 1800000, 30 minutes in ms).
Then, in any git project, ask Claude something like:
Use understudy to add an
is_palindrome(text)function totextutil.pywith a test. Run the tests withpytest -q.
Tools
Tool | What it does |
| Creates a worktree, has the local model do the task, and runs |
| Sends review feedback; the model tries again and the tests re-run. If every attempt breaks the tests, the previous passing version is kept. |
| Squash-merges the change into your current branch as one commit and cleans up. Refuses if you have uncommitted changes. |
| Deletes the worktree and branch. |
| Lists open delegations. |
Result statuses
Status | Meaning |
| The change passes the tests. Review the diff and warnings. |
| Still failing after the maximum number of rounds. |
| No |
| The model didn't change anything. |
| The model/tool errored before making changes (see |
| A |
| A |
Test commands
test_command is a shell command run inside the worktree. Worktrees don't contain your virtualenv, so use
{repo} to point at the main checkout, e.g. {repo}/.venv/bin/python -m pytest -q. The path is also available
as $UNDERSTUDY_REPO.
Configuration
All settings are environment variables (set them with -e in claude mcp add).
Variable | Default | |
|
| Model passed to OpenCode ( |
|
| Build → test attempts per call. |
|
| Seconds per model run. |
|
| Seconds per test run. |
|
| Where worktrees are created. Must be outside |
|
|
|
| Command for the | |
|
| Longer diffs are truncated in results. |
Using another builder
Set UNDERSTUDY_BUILDER=command and UNDERSTUDY_BUILDER_COMMAND to any command that edits files in the current
directory. {prompt_file} is replaced with a file containing the instructions and {workdir} with the
worktree path. For example, with aider:
UNDERSTUDY_BUILDER_COMMAND='aider --model ollama_chat/devstral-small-2:24b-16k --yes --no-auto-commits --message-file {prompt_file}'Choosing a model
From testing on a consumer GPU:
Model (16k context) | Typical small task | Notes |
| ~100 s | Clean, targeted edits; follows review feedback. Recommended. |
| ~20–30 s | Much faster, but edits are sloppier and it often ignores feedback. |
Local models are only as good as they are: understudy's job is to make them safe to use (isolation, test gate, warnings, rollback) and to keep Claude in the reviewer's seat. Give them small, specific tasks.
How it works
Each delegation gets a branch
understudy/<id>and a worktree under~/.cache/understudy/worktrees/. Bookkeeping lives in.git/understudy/, so it never shows up ingit status.The model's changes are committed in the worktree before tests run, and anything the test run leaves behind is cleaned up, so build artifacts never end up in the diff.
acceptsquash-merges the branch as a single commit authored by you.
Development
git clone https://github.com/Barry-Lenhart/understudy.git && cd understudy
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/pytest
.venv/bin/ruff check . && .venv/bin/ruff format --check .The tests use a fake builder, so they need neither Ollama nor OpenCode.
License
Available Tools
5 toolsacceptA
Merge a delegation into the current branch as a single commit (the task
text is used as the message unless commit_message is given), then clean
up its worktree. Refuses if the working tree has uncommitted changes.
Inputs: id (required), repo_path, commit_message.
delegateA
Hand a coding task to the local model.
The model edits an isolated git worktree; test_command (a shell command,
e.g. "pytest -q") is run after each attempt and failures are fed back to the
model, up to UNDERSTUDY_MAX_ROUNDS times. "{repo}" in the test command is
replaced with the repository path, e.g. "{repo}/.venv/bin/pytest -q".
Returns the diff and test output for you to review, plus an id for
accept/revise/discard. repo_path defaults to the current directory.
Inputs: task (required), repo_path, test_command.
discardADestructive
Throw away a delegation's worktree and branch without merging.
Inputs: id (required), repo_path.
reviseA
Send review feedback on a delegation; the local model tries again and the tests are re-run. Returns the updated diff and test output.
Inputs: id (required), feedback (required), repo_path.
statusARead-only
List delegations in this repository that have not been accepted or discarded.
Inputs: repo_path. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 5 tools
Each tool maps to a distinct phase of the delegation lifecycle: delegate starts, revise updates, accept finalizes, discard cancels, and status inspects. There is no overlap or ambiguity between responsibilities.
All tool names are single-word imperative verbs (delegate, revise, accept, discard, status), forming a predictable pattern. No mixed conventions or stylistic deviations.
Five tools is the ideal scope for a delegation workflow: create, iterate, merge, cancel, and list. Each tool earns its place without redundancy or bloat.
The lifecycle is fully covered: delegate creates a task, revise handles iterative feedback, accept and discard resolve it, and status provides visibility. No essential operation is missing.
Related MCP Connectors
AI-native git hosting — repos, PRs, issues, CI gates, and AI code review over MCP (60 tools).
Team memory for AI coding agents and code reviews, reviewed by humans and served over MCP.
GitHub Copilot MCP: code, repo, PR, workflow tools for AI assistants.
Build, test, publish, and run Kovca workflows from AI agents over remote MCP.
Related MCP Servers
- AlicenseAqualityDmaintenanceDelegate coding tasks to external AI coding agents in isolated git worktrees with independent verification, enabling any MCP client to orchestrate multi-agent workflows.5MIT
- AlicenseAqualityCmaintenanceDelegates heavy development tasks from a supervisor to an autonomous worker on a cheaper model via MCP, with isolated git worktrees and provider-agnostic support.31MIT
- AlicenseAqualityBmaintenanceEnables delegating complete coding tasks to a local OpenAI-compatible model through MCP, using bounded Git worktrees and compact receipts so conversation history is not carried.4MIT
- -licenseNot gradedqualityNot gradedmaintenanceEnables MCP clients to securely execute bounded coding tasks through registered backends, with idempotent job submission, status polling, and artifact retrieval. It isolates each job in Git worktrees and supports optional branch publishing and pull request creation under strict policy constraints.-