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., "@understudyAdd 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.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| repo_path | No | ||
| commit_message | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the sparse annotations (destructiveHint=false), the description discloses several meaningful behaviors: the merge is squashed to a single commit, the task text is the default message, the delegation worktree is cleaned up afterward, and the call refuses under dirty-tree conditions. These add real operational context the annotations do not provide. The word 'clean up' sits near the line of contradicting destructiveHint=false, but since content is preserved via the merge, it is not a hard contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two compact sentences carry the core action first, the commit-message nuance second, and the critical refusal guard last. Every clause earns its place with no filler or restatement of the tool's name.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation-style operation with no output schema, the description covers the core behavior, a parameter default, a side effect (worktree cleanup), and a precondition guard. The main gaps are the semantics of id and repo_path, and what 'refuses' looks like in practice, but an agent can reasonably invoke the tool knowing it must supply a delegation id.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate. It does explain commit_message's role (overrides task text as the message) and implies it is optional. But the required id parameter is never described as the delegation identifier, and repo_path's purpose is left entirely unexplained. Partial compensation only.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb ('Merge'), a specific resource ('a delegation'), and a precise outcome: a single commit into the current branch plus worktree cleanup. This clearly distinguishes it from sibling tools like delegate, revise, discard, and status, since only accept performs the merge-and-cleanup operation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The workflow context (delegation lifecycle) makes the intended use implied, and the refusal condition ('Refuses if the working tree has uncommitted changes') gives a concrete when-not-to-call signal. However, it never names alternatives or explains when to prefer discard or revise over accept, leaving sibling differentiation to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| task | Yes | ||
| repo_path | No | ||
| test_command | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes beyond the annotations by explaining the iterative test-feedback loop, the replacement of '{repo}' in the test command, and the fact that it returns a diff and test output. It adds value about the execution model that the annotations don't convey. It doesn't contradict the annotations (destructiveHint=false), which is fine. However, it doesn't mention potential side effects or cleanup of the worktree, but the isolated worktree implies safety.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and efficient: a one-sentence purpose, a clear explanation of the workflow, specific details on the test_command semantics, and a concise summary of the return value. Every sentence adds necessary information without fluff. The most important details (the loop, the replacement) are front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 3 parameters (one required), no output schema, and no sibling descriptions, the description is quite complete. It explains the main behaviors and parameters. It falls short of a 5 because it doesn't mention the maximum number of rounds explicitly (only the variable name), or whether the repository is cleaned up after. But these are minor given the complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must explain parameters. It explains test_command in detail (shell command, placeholder replacement, example usage) and mentions repo_path defaults to current directory. The task parameter is obvious from the purpose. This compensates well for the lack of schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: to hand a coding task to a local model for execution in an isolated git worktree. It specifies the verb 'hand' and the resource 'coding task', and differentiates itself from siblings by mentioning the workflow (editing, testing, feedback loop) that leads to review actions. However, it doesn't explicitly name sibling tools like 'revise' or 'accept', so differentiation is implicit rather than direct.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains the workflow: the tool runs a test command after each attempt, feeds failures back to the model, and up to a certain number of rounds. It mentions the returned id for accept/revise/discard, which implies when to use this tool (to delegate a task) versus siblings (to act on the result). It doesn't explicitly say when NOT to use it, but the context is clear enough for an agent to decide.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
discardADestructive
Throw away a delegation's worktree and branch without merging.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| repo_path | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare destructiveHint=true, and the description adds specificity about what gets destroyed: the delegation's worktree and branch. This is useful context beyond the annotation, though it does not mention irreversibility or potential side effects in detail.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, tightly worded sentence with no filler. It front-loads the action and immediately communicates the key distinction from merging, making it easy to parse.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
While the tool is simple and the destructive annotation covers the main risk, the description leaves parameter semantics undocumented and provides no guidance on how to supply 'id' or 'repo_path'. For a destructive operation, more context about required inputs and effects would be expected.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema description coverage, the description carries the full burden for parameter meaning, but it only indirectly suggests that 'id' identifies a delegation. It does not clarify 'repo_path' or confirm how the parameters are used, leaving the agent to guess about the second parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action ('Throw away'), a specific resource ('a delegation's worktree and branch'), and a key constraint ('without merging'). This clearly distinguishes it from the sibling tool 'accept', which would merge the delegation's changes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the tool is used when you want to discard a delegation's work rather than merge it, but it does not explicitly state when to use this tool versus alternatives like 'revise' or 'accept'. There is no direct guidance on when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| feedback | Yes | ||
| repo_path | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (destructiveHint=false), the description discloses meaningful behavior: the model retries, tests are re-run, and the tool returns the updated diff and test output. This adds useful context about what happens when the tool is invoked, though it does not mention any side effects on existing delegation state.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no fluff. It front-loads the action and effect, then states the return value, making it quick for an agent to parse and act on.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the main workflow and return value, but leaves repo_path undefined and does not explain when to use this tool versus accept/discard. For a 3-parameter tool with no output schema and minimal annotations, these are meaningful gaps that could hinder correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, but it only loosely implies that 'id' refers to a delegation and 'feedback' is the review feedback. The 'repo_path' parameter is entirely unexplained, leaving an agent without enough information to populate all parameters correctly.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action ('Send review feedback on a delegation'), identifies the resource (delegation), and explains the effect (the local model tries again and tests are re-run). This clearly distinguishes it from sibling tools like accept, discard, and status, which handle different outcomes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context for when to use the tool: when review feedback should be sent to trigger another attempt. It does not explicitly name alternatives or exclusions, but the behavior is distinct enough from accept/discard/status that an agent can infer the appropriate use case.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
statusARead-only
List delegations in this repository that have not been accepted or discarded.
| Name | Required | Description | Default |
|---|---|---|---|
| repo_path | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the safe read-only nature is covered. The description adds the useful scoping constraint of pending delegations but does not detail return format or pagination; for a simple read-only listing this is acceptable.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler. The verb, resource, and filter are immediately clear, and every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter, read-only listing tool with a defined output schema and a clear description of what is returned, no additional invocation-critical information is missing. The tool is simple enough that this description is self-contained.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, and the description does not explain repo_path, its expected format, or why it is optional. The parameter name and default provide some clue, but the description does not fully compensate for the missing schema detail.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States the specific verb 'List', the resource 'delegations', and the scope 'in this repository' with a clear status filter: 'not been accepted or discarded.' This distinguishes it from sibling tools like accept and discard.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly implies this tool is for viewing pending delegations before accepting or discarding them. It does not explicitly name alternatives or exclusions, but the status filter gives sufficient usage context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.1.0- First observed
accept - First observed
delegate - First observed
discard - First observed
revise - First observed
status
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.
Maintenance
Related MCP Connectors
AI-native git hosting — repos, PRs, issues, CI gates, and AI code review over MCP (60 tools).
A MCP server built for developers enabling Git based project management with project and personal…
Persistent memory and cross-session learning for AI coding assistants (hosted remote MCP).
Roadmap, tasks, releases and user feedback your coding agent reads and writes over MCP.
Related MCP Servers
- AlicenseAqualityBmaintenanceDelegate coding tasks to external AI coding agents in isolated git worktrees with independent verification, enabling any MCP client to orchestrate multi-agent workflows.5MIT
- AlicenseAqualityAmaintenanceDelegates 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
- AlicenseNot gradedqualityCmaintenanceAn MCP server that lets ChatGPT or any MCP client securely delegate coding tasks to a local Claude Code instance, with git checkpointing, approval gates, and structured results. Supports code review, test running, and rollback via simple tool calls.8 npmMIT
- AlicenseAqualityAmaintenanceEnables 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