MCP server for Claude Code to Claude Code delegation
Project description
Spindle
MCP server for Claude Code to Claude Code delegation. Spawn background agents that run asynchronously, with optional git worktree isolation for safe parallel work.
Features
- Async agent spawning - Fire-and-forget pattern with spool IDs
- Optional blocking with gather/yield - Wait for all results at once, or stream them as agents complete. Alternatively, agent can continue other work, spins are nonblocking by default
- Permission profiles - Control what tools child agents can use (readonly, careful, full)
- Shard isolation - Run agents in sandboxed git worktrees to prevent conflicts
- Model selection - Route tasks to haiku, sonnet, or opus per-agent
- Session continuity - Resume conversations with child agents (auto-recovers expired sessions)
- Rich querying - Search, filter, peek at running output, export results
Requirements
- Python 3.10+
- Claude CLI installed and authenticated
- Git (for shard/worktree functionality)
Install
pip install spindle-mcp
Add to Claude Code's MCP config (~/.claude.json):
{
"mcpServers": {
"spindle": {
"command": "spindle"
}
}
}
Usage
Basic: Spawn and collect
# Spawn an agent
spool_id = spin("Research the Python GIL")
# Do other work...
# Check result
result = unspool(spool_id)
Permission profiles
Control what tools the spawned agent can use:
# Read-only: Can only search and read
spin("Analyze the codebase", permission="readonly")
# Careful (default): Can read/write but limited bash
spin("Fix this bug", permission="careful")
# Full access: No restrictions
spin("Implement the feature", permission="full")
# Shard: Full access + auto-isolated worktree (common for risky work)
spin("Refactor the auth system", permission="shard")
# Careful + shard: Limited tools but isolated
spin("Update configs", permission="careful+shard")
Profiles:
readonly: Read, Grep, Glob, safe bash (ls, cat, git status/log/diff)careful: Read, Write, Edit, Grep, Glob, bash for git/make/pytest/python/npmfull: No restrictionsshard: Full access + auto-creates isolated worktreecareful+shard: Careful permissions + auto-creates isolated worktree
You can also pass explicit allowed_tools to override the profile.
Isolated workspaces with shards
Run agents in isolated git worktrees to prevent conflicts:
# Agent works in its own worktree
spool_id = spin("Refactor auth module", shard=True)
# Check shard status
shard_status(spool_id)
# Merge changes back when done
shard_merge(spool_id)
# Or discard if not needed
shard_abandon(spool_id)
Shards create a git worktree + branch. If SKEIN is available, uses skein shard spawn for richer tracking. Falls back to plain git worktree otherwise.
Wait for completion
# Spawn multiple agents
id1 = spin("Find all TODO comments")
id2 = spin("List unused imports")
id3 = spin("Check for type errors")
# Gather: block until all complete, get all results
results = spin_wait("id1,id2,id3", mode="gather")
# Yield: return as each completes
# Great when results are independent - process each as it lands
result = spin_wait("id1,id2,id3", mode="yield") # Returns first to finish
# With timeout
results = spin_wait("id1,id2", mode="gather", timeout=300)
Yield mode keeps you responsive instead of blocking on the slowest agent.
Model selection and timeouts
# Route quick tasks to haiku (fast, cheap)
spin("Summarize this file", model="haiku")
# Complex work to opus
spin("Design the new architecture", model="opus")
# Auto-kill if it takes too long
spin("Should be quick", timeout=60)
Continue a session
# Get session ID from completed spool
result = unspool(spool_id) # includes session_id
# Continue that conversation
new_id = respin(session_id, "Follow up question")
If the session has expired on Claude's end, respin automatically falls back to transcript injection to recreate context.
Cancel running work
spin_drop(spool_id)
List all spools
spools()
Search and filter
# Search prompts and results
spool_search("authentication")
# Filter by status and time
spool_results(status="error", since="1h")
# Regex search results
spool_grep("error|failed|exception")
# Get statistics
spool_stats()
# Export to file
spool_export("all", format="md")
API
| Tool | Purpose |
|---|---|
spin(prompt, permission?, shard?, system_prompt?, working_dir?, allowed_tools?, tags?) |
Spawn agent, return spool_id |
unspool(spool_id) |
Get result (non-blocking) |
spools() |
List all spools |
spin_wait(spool_ids, mode?, timeout?) |
Block until done |
respin(session_id, prompt) |
Continue session |
spin_drop(spool_id) |
Cancel by killing process |
spool_search(query, field?) |
Search prompts/results |
spool_results(status?, since?, limit?) |
Bulk fetch with filters |
spool_grep(pattern) |
Regex search results |
spool_retry(spool_id) |
Re-run with same params |
spool_peek(spool_id, lines?) |
See partial output while running |
spool_dashboard() |
Overview of running/complete/needs-attention |
spool_stats() |
Get summary statistics |
spool_export(spool_ids, format?, output_path?) |
Export to file |
shard_status(spool_id) |
Check shard worktree status |
shard_merge(spool_id, keep_branch?) |
Merge shard to master |
shard_abandon(spool_id, keep_branch?) |
Discard shard |
Storage
Spools persist to ~/.spindle/spools/{spool_id}.json:
{
"id": "abc12345",
"status": "complete",
"prompt": "...",
"result": "...",
"session_id": "...",
"permission": "careful",
"allowed_tools": "...",
"tags": ["batch-1"],
"shard": {
"worktree_path": "/path/to/worktrees/abc12345-...",
"branch_name": "shard-abc12345-...",
"shard_id": "..."
},
"pid": 12345,
"created_at": "2025-11-26T...",
"completed_at": "2025-11-26T..."
}
CLI Commands
spindle install-service # Install background service (Linux/macOS)
spindle start # Start via systemd (or background if no service)
spindle reload # Restart via systemd to pick up code changes
spindle status # Check if running (hits /health endpoint)
spindle serve --http # Run MCP server directly
Background Service
For persistent background operation:
# Install and enable the service (Linux or macOS)
spindle install-service
# Start it
spindle start
Linux: Writes a systemd user service to ~/.config/systemd/user/spindle.service
macOS: Writes a launchd plist to ~/Library/LaunchAgents/com.spindle.server.plist and loads it immediately
Use --force to overwrite an existing service file. Then spindle reload restarts the service to pick up code changes.
Windows
On Windows, run spindle manually:
spindle serve --http
Or use NSSM to create a Windows service.
WSL
In WSL2 with systemd enabled, spindle install-service works like native Linux. If systemd isn't enabled, you'll get instructions to enable it or run manually.
Hot Reload (MCP tool)
From within Claude Code, call spindle_reload() to restart the server and pick up code changes.
Configuration
Environment variables:
| Variable | Default | Description |
|---|---|---|
SPINDLE_MAX_CONCURRENT |
15 |
Maximum concurrent spools |
Storage location: ~/.spindle/spools/
How It Works
- spin() spawns a detached
claudeCLI process with the given prompt - The process runs in background, writing output to temporary files
- A monitor thread polls for completion
- unspool() returns the result once complete (non-blocking check)
- Spool metadata persists to JSON files, surviving server restarts
For shards:
- A git worktree is created with a new branch
- The agent runs inside that worktree
- After completion, merge back with
shard_merge()or discard withshard_abandon()
Limits
- Max 15 concurrent spools (configurable via
SPINDLE_MAX_CONCURRENT) - 24h auto-cleanup of old spools
- Orphaned spools (dead process) marked as error on restart
Contributing
See CONTRIBUTING.md for development setup and guidelines.
License
MIT - see LICENSE.
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 spindle_mcp-1.0.2.tar.gz.
File metadata
- Download URL: spindle_mcp-1.0.2.tar.gz
- Upload date:
- Size: 31.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c73aadd604841aa6f54d0a3cd2e17aab2ceb769401cd28988d5aed263e1cc568
|
|
| MD5 |
802ad5af56dc979da66402758d62bde7
|
|
| BLAKE2b-256 |
bef4fb91fa2f26504305f461fe664f6efee65cefaac6469cc5b40c39ddb32b80
|
File details
Details for the file spindle_mcp-1.0.2-py3-none-any.whl.
File metadata
- Download URL: spindle_mcp-1.0.2-py3-none-any.whl
- Upload date:
- Size: 26.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
df979c39996a7317f1bc170adbaf0c46362ba4d2b5af9334eb244c48f88f7587
|
|
| MD5 |
b95cf4665e0d18bfe8befabc19bc9500
|
|
| BLAKE2b-256 |
8ed236f243d784e18d50578d92c7417e1ab750eb74740b2b4913f55bcebdfabf
|