Unloop MCP
by synthet1cc
README.md
<p align="center">
<img src="unloop_logo.png" alt="Unloop" width="420">
<br>
<em>Break the Loop. Ship the Code.</em>
</p>
<p align="center">
<strong>MCP server that detects when AI coding assistants get stuck in fix loops and forces them to change approach.</strong>
</p>
<p align="center">
<a href="https://www.npmjs.com/package/unloop-mcp"><img src="https://img.shields.io/npm/v/unloop-mcp?color=E94560&label=npm" alt="npm version"></a>
<a href="https://github.com/protonese3/unloop-mcp/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT License"></a>
<a href="https://www.npmjs.com/package/unloop-mcp"><img src="https://img.shields.io/npm/dm/unloop-mcp?color=green" alt="npm downloads"></a>
<a href="https://github.com/protonese3/unloop-mcp/actions"><img src="https://github.com/protonese3/unloop-mcp/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
</p>
---
## Let your AI set it up for you
Paste this into Claude Code, Cursor, or any MCP-compatible assistant and it will install and configure Unloop for you:
```
Install Unloop MCP to detect fix loops in this project. Here's what to do:
1. Run: claude mcp add unloop -s user -- npx -y unloop-mcp
(Or for Cursor: create .cursor/mcp.json with {"mcpServers":{"unloop":{"command":"npx","args":["-y","unloop-mcp"]}}})
2. Download the rules file for this IDE from https://raw.githubusercontent.com/protonese3/unloop-mcp/master/rules/claude.md
and save it to .claude/rules/unloop.md (or the equivalent for Cursor: .cursor/rules/unloop.mdc)
3. Verify the MCP server is connected by running: claude mcp list
After setup, you must follow the Unloop protocol: call log_fix_attempt after every fix attempt,
obey escalation levels (NUDGE/WARNING/CRITICAL), and call resolve_loop when the error is fixed.
```
---
## What is Unloop?
When you use an AI coding assistant (Claude, Cursor, Copilot, Cline...), it sometimes hits an error and tries to fix it — but keeps attempting the same failing approach 5, 10, 20+ times without realizing it's going in circles.
**Unloop stops this.** It's an [MCP server](https://modelcontextprotocol.io/) that runs alongside your AI assistant, tracking every fix attempt. When it detects repetition, it intervenes with escalating alerts and concrete strategies to redirect the AI toward a different approach.
Without Unloop, a stuck AI burns 30+ minutes and 150k+ tokens on the same error. With Unloop, it course-corrects at attempt 3.
---
## How it Works
<p align="center">
<img src="how-it-works.png" alt="How Unloop Works" width="800">
</p>
### The three-step process
**1. AI Attempts a Fix** — Your AI assistant encounters an error and tries to fix it. Unloop silently tracks every attempt by calling `log_fix_attempt`.
**2. Unloop Detects the Loop** — The engine normalizes each error into a fingerprint (stripping paths, line numbers, timestamps) and compares fix descriptions using [Jaccard similarity](https://en.wikipedia.org/wiki/Jaccard_index). When similarity exceeds 55%, it flags a loop.
**3. AI Gets Redirected** — Escalating alerts force the AI to stop repeating and try a fundamentally different approach. Strategies are targeted by error category (import, type, build, test, runtime).
### Escalation levels
| Level | Trigger | What happens |
|---|---|---|
| **NONE** | 1-2 attempts | Silent tracking. No intervention. |
| **NUDGE** | 3-4 attempts | "You're repeating yourself. Change approach." + strategies |
| **WARNING** | 5-6 attempts | "STOP. Revert your changes. Research first." + strategies |
| **CRITICAL** | 7+ attempts | "STOP. Revert everything. Ask the user for help." |
---
## Quick Start
### 1. Install
```bash
npm install -g unloop-mcp
```
Or use directly without installing:
```bash
npx unloop-mcp
```
### 2. Add the MCP server
**Claude Code** (one command, works in all projects):
```bash
claude mcp add unloop -s user -- npx -y unloop-mcp
```
**Cursor** — add to `.cursor/mcp.json`:
```json
{
"mcpServers": {
"unloop": {
"command": "npx",
"args": ["-y", "unloop-mcp"]
}
}
}
```
**Windsurf** — add to `.windsurf/mcp.json`:
```json
{
"mcpServers": {
"unloop": {
"command": "npx",
"args": ["-y", "unloop-mcp"]
}
}
}
```
### 3. Add the rules file
The rules file tells the AI *when* and *how* to call the Unloop tools. Without it, the AI won't know to use them.
| IDE | Command |
|---|---|
| Claude Code | `cp rules/claude.md your-project/.claude/rules/unloop.md` |
| Cursor | `cp rules/cursor.mdc your-project/.cursor/rules/unloop.mdc` |
| Windsurf | `cp rules/windsurf.md your-project/.windsurfrules` (append) |
| Cline | `cp rules/cline.md your-project/.clinerules` (append) |
### 4. Verify
```bash
claude mcp list
# unloop: ... - ✓ Connected
```
Restart your IDE. The AI now has access to the 4 Unloop tools and the rules instruct it to use them on every fix attempt.
---
## How the detection works
### Error fingerprinting
Each error message is normalized before comparison:
- File paths → stripped (`/Users/alice/src/App.tsx:42` becomes generic)
- Line/column numbers → stripped
- UUIDs, hashes, timestamps, ANSI codes, stack frames → stripped
- Result is SHA-256 hashed into a 16-character fingerprint
This means the same error on different files produces the **same fingerprint**:
```
Cannot find module './Button' in /Users/alice/src/App.tsx:42 → fingerprint a1b2c3...
Cannot find module './Button' in /Users/bob/src/Main.tsx:7 → fingerprint a1b2c3... (same)
```
### Fix similarity
Fix descriptions are tokenized (lowercased, stop-words removed) and compared using Jaccard similarity. If two descriptions share more than 55% of their meaningful words, they're flagged as "the same approach."
This is why the rules file instructs the AI to write **specific** fix descriptions — "Changed import from `./Button` to `@/components/Button` because tsconfig has path aliases" instead of "fixed the import."
### Error categories
Errors are auto-categorized for targeted strategy selection:
| Category | Patterns matched |
|---|---|
| `syntax` | SyntaxError, unexpected token, parsing error |
| `type` | TypeError, type not assignable, TS errors |
| `import` | Cannot find module, module not found |
| `build` | Build failed, compilation error, webpack/vite/tsc |
| `test` | Test failed, assertion errors, jest/vitest/pytest |
| `runtime` | ReferenceError, null pointer, ENOENT, unhandled rejection |
---
## MCP Tools
Unloop exposes 4 tools via the Model Context Protocol:
### `log_fix_attempt`
**Call after every fix attempt.** Records the attempt and returns loop analysis.
```
Parameters:
error_message string The error being fixed
files_involved string[] Files modified in this attempt
fix_description string What was changed and why (be specific)
session_id string? For parallel task isolation (optional)
Returns:
status "ok" | "loop_detected"
loop_level "NONE" | "NUDGE" | "WARNING" | "CRITICAL"
attempt_number number
similar_attempts number How many previous fixes used the same approach
max_similarity number Highest similarity score vs any previous fix (0-1)
diagnosis object? Pattern analysis + what to try next (when loop detected)
strategies array? Escape strategies (when loop detected)
previous_attempts array? History of what was tried
```
When a loop is detected, the `diagnosis` field tells the AI exactly what's happening:
```json
{
"diagnosis": {
"pattern": "You've been changing import/module paths 3 times for an import error. This approach isn't working.",
"suggested_action": "Stop changing paths. Check if the file actually exists at the expected location. Then check the module resolution config.",
"what_was_tried": ["Changed path to ../Button", "Changed path to ../../Button", "Changed to @/Button"],
"what_to_try_next": "Approaches you haven't tried yet: Check the relevant config files. Verify dependencies are installed correctly. Stop changing paths. Check if the file actually exists."
}
}
```
The diagnosis engine classifies each fix attempt into approach categories (path changes, type annotations, null checks, config changes, etc.), detects which approach is being repeated, and suggests a specific pivot based on the error category.
### `check_loop_status`
**Read-only status check.** Returns current state without recording a new attempt. Use before starting complex fixes.
### `get_escape_strategies`
**Get strategies without logging.** Returns category-specific strategies for planning your next move.
### `resolve_loop`
**Call when the error is fixed.** Resets all tracking counters. If you skip this, the next unrelated error inherits stale state.
---
## Strategies
Unloop includes 30+ built-in escape strategies, matched by error category and escalation level. Examples:
**NUDGE (import error):**
- "Verify the package is installed — check package.json, run the install command"
- "Compare with a working import in the same project — copy the pattern that works"
**WARNING (type error):**
- "Check dependency version alignment — @types packages may be out of sync"
- "Simplify the type chain — break complex generics into intermediate variables"
**CRITICAL (any):**
- "Revert ALL changes since the error first appeared"
- "Ask the user — explain what you tried and why it failed"
---
## Testing
```bash
# Unit + integration + E2E tests (75 tests)
npm test
# Quick smoke test — all tools, escalation, isolation (24 checks)
npx tsx smoke-test.ts
# Interactive demo — simulated loop session with colored output
npx tsx demo.ts
```
---
## Project structure
```
src/
├── index.ts # Entry point (stdio transport)
├── server.ts # MCP tool registration and handlers
├── types.ts # Shared TypeScript types
├── detection/
│ ├── fingerprint.ts # Error normalization, hashing, categorization
│ ├── similarity.ts # Jaccard similarity on tokenized descriptions
│ └── escalation.ts # NONE → NUDGE → WARNING → CRITICAL state machine
├── strategies/
│ ├── builtin.ts # 30+ strategies by category and level
│ └── registry.ts # Strategy lookup
└── session/
└── store.ts # In-memory session state with garbage collection
rules/ # IDE-specific instruction files
├── cursor.mdc # Cursor rules
├── claude.md # Claude Code rules
├── windsurf.md # Windsurf rules
└── cline.md # Cline rules
```
---
## Supported IDEs
| IDE | MCP Support | Rules file |
|---|---|---|
| Claude Code | Native (stdio) | `.claude/rules/unloop.md` |
| Cursor | Native (stdio) | `.cursor/rules/unloop.mdc` |
| Windsurf | Native | `.windsurfrules` |
| Cline (VS Code) | Native | `.clinerules` |
---
## Why MCP?
An MCP server works across every IDE that supports the protocol — one codebase, one server, works everywhere. It's designed to be called by AI, not by humans. And it's the emerging standard: Cursor, Claude Code, Windsurf, and Cline all support it natively.
The alternative (a VS Code extension, a custom CLI wrapper, manual prompt engineering) would be single-IDE, harder to maintain, and less reliable.
---
## Contributing
PRs welcome. The codebase is straightforward:
- **Add a strategy** — edit `src/strategies/builtin.ts`, add to the relevant `LEVEL:CATEGORY` key
- **Improve fingerprinting** — edit `src/detection/fingerprint.ts`, add normalization patterns
- **Add an error category** — edit the `CATEGORY_PATTERNS` array in `fingerprint.ts`
- **Add IDE support** — create a new rules file in `rules/`, add to the CLI init command
Run `npm test` before submitting. All 75 tests must pass.
---
## License
MIT
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessUnresponsive