Skip to main content
Glama
README.md
<p align="center">
  <img src="assets/logo.png" alt="Refract" width="360">
</p>

# Refract

[![CI](https://github.com/LoudiliMed/Refract/actions/workflows/ci.yml/badge.svg)](https://github.com/LoudiliMed/Refract/actions/workflows/ci.yml)

[![Refract MCP server](https://glama.ai/mcp/servers/LoudiliMed/Refract/badges/card.svg)](https://glama.ai/mcp/servers/LoudiliMed/Refract)

> Cuts up to 98% of the tokens your AI agents spend using MCP tools — without losing anything.

---

## What it actually changes

| Server | Tools | Before | After | Reduction |
|---|---|---|---|---|
| filesystem (Anthropic) | 14 | 1,892 tok | 236 tok | **−88%** |
| sequential-thinking | 1 | 926 tok | 20 tok | **−98%** |
| Google Calendar | 5 | 5,010 tok | 660 tok | **−87%** |
| Enterprise (Cal + Gmail + Drive) | 12 | 8,649 tok | 882 tok | **−90%** |
| sample_app.js (JavaScript) | — | 799 tok | 284 tok | **−64.5%** |
| sample_app.ts (TypeScript) | — | 378 tok | 266 tok | **−29.6%** |
| ast_extractor.py (Python) | — | 3,633 tok | 890 tok | **−75.5%** |

Fewer tokens sent = lower API bills, faster responses. And nothing is lost. Every check confirmed tools stay 100% usable after compression.

Reproduce these numbers yourself — every input is a static fixture in the repo, tokens counted with tiktoken cl100k_base:

```bash
python benchmarks/run_benchmark.py            # the table above
python benchmarks/run_benchmark.py --json     # machine-readable
python benchmarks/run_benchmark.py --fixture path/to/your_schemas.json
```

---

## Install

One-liner (macOS / Linux) — installs the package and configures Claude Desktop:

```bash
curl -sSL https://raw.githubusercontent.com/LoudiliMed/Refract/main/install-refract.sh | sh
```

> Piping a script from the internet into your shell deserves a quick look first: [inspect install-refract.sh on GitHub](https://github.com/LoudiliMed/Refract/blob/main/install-refract.sh). It never uses sudo.

Or with pip directly:

```bash
pip install refract-mcp
```

fastembed and tree-sitter are installed by default.

---

## Two modes

### Mode 1 — MCP Proxy

Sits between your agent and any MCP server. Compresses tool schemas on the fly so your agent does not load the full catalogue on every request.

**Local subprocess (stdio):**

```bash
refract-proxy --target "npx @modelcontextprotocol/server-filesystem /tmp" --verbose
```

**Remote HTTP/SSE server:**

```bash
# --url implies SSE transport (explicit, recommended for remote endpoints)
refract-proxy --url https://my-mcp-server.com/sse

# or with --transport flag (auto-detection can be overridden)
refract-proxy --target https://my-mcp-server.com/sse --transport sse
```

**Proxy flags:**

| Flag | Default | Description |
|---|---|---|
| `--target URL` | required | MCP target: stdio command, HTTP URL, or JSON file |
| `--stdio-cmd CMD` | — | Alias for `--target` for stdio commands |
| `--url URL` | — | Remote SSE/HTTP endpoint — implies `--transport sse` |
| `--transport {stdio,sse,http}` | auto | Force transport to the target: `stdio`, `sse` (legacy), or `http` (Streamable HTTP) |
| `--sse-timeout SECONDS` | 30 | Connection timeout for SSE targets (retries 3×) |
| `--mode {stdio,http}` | stdio | How the proxy serves your agent |
| `--port PORT` | 8080 | Proxy listen port in `--mode http` |
| `--verbose` | off | Print token counts per request |
| `--log-level` | WARNING | DEBUG / INFO / WARNING / ERROR |

Add it to Claude Desktop:

```json
{
  "mcpServers": {
    "my-server-via-refract": {
      "command": "/path/to/refract-proxy",
      "args": [
        "--target",
        "npx @modelcontextprotocol/server-filesystem /path/to/folder",
        "--verbose"
      ]
    }
  }
}
```

For a remote MCP server (SSE):

```json
{
  "mcpServers": {
    "remote-via-refract": {
      "command": "/path/to/refract-proxy",
      "args": ["--url", "https://my-mcp-server.com/sse"]
    }
  }
}
```

#### refract-wrap-all — wrap every server at once

Instead of editing entries one by one (or running `refract-install` per server), `refract-wrap-all` rewrites **all** stdio servers in `claude_desktop_config.json` to go through `refract-proxy` in a single command:

```bash
# Preview what would change — writes nothing
refract-wrap-all --dry-run

# Wrap every stdio server not already going through refract
refract-wrap-all

# Restore the original commands
refract-wrap-all --unwrap
```

Example: this entry

```json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["@modelcontextprotocol/server-filesystem", "/tmp"],
      "env": {"MY_VAR": "1"}
    }
  }
}
```

becomes

```json
{
  "mcpServers": {
    "filesystem": {
      "command": "/path/to/refract-proxy",
      "args": ["--stdio-cmd", "npx @modelcontextprotocol/server-filesystem /tmp"],
      "env": {"MY_VAR": "1"}
    }
  }
}
```

Guarantees:

- A backup of the config is taken **before every write** (`claude_desktop_config.json.bak`, then `.bak2`, `.bak3`… — an existing backup is never overwritten).
- `env`, `cwd` and any other server fields are preserved.
- Servers already going through `refract-proxy` or `refract-server` are skipped (`already wrapped`).
- Remote SSE/HTTP servers (`url` entries) are skipped — only stdio servers are wrapped.
- `--unwrap` is an exact round-trip: wrap then unwrap restores every original command, args and fields.

#### Transports supported by refract-proxy

| Flag | Value | Description |
|---|---|---|
| `--transport http` | Streamable HTTP | **Current standard** (MCP spec 2025-03-26). Use with remote MCP servers. |
| `--transport sse` | SSE | Legacy transport, kept for compatibility. Use if the server does not support Streamable HTTP. |
| `--transport stdio` | stdio subprocess | Local command (default when `--target` is a command). |
| *(omit)* | auto-detect | Inferred from `--target`: HTTP URL → SSE, command → stdio. |

Both `sse` and `http` require an HTTP(S) URL in `--target`.

```bash
# Connect to a remote MCP server via Streamable HTTP (recommended)
refract-proxy --target "https://my-mcp-server.com/mcp" --transport http

# Connect via SSE (legacy)
refract-proxy --target "https://my-mcp-server.com/sse" --transport sse

# Local subprocess (auto-detected, --transport stdio optional)
refract-proxy --target "npx @modelcontextprotocol/server-filesystem /tmp"
```

### Mode 2 — MCP Server

Exposes your codebase as an MCP server. Your agent can index a repo, get compressed file context, expand specific functions, analyze impact, detect breaking changes, and map security risks.

```bash
refract-server --root /path/to/your/repo
```

Add it to Claude Desktop:

```json
{
  "mcpServers": {
    "refract-code": {
      "command": "/path/to/refract-server",
      "args": ["--root", "/path/to/your/repo"]
    }
  }
}
```

---

## How it works, no jargon

Imagine a library with 50 books.

Without Refract: your agent gets a detailed summary of all 50 books on every question, even if the answer only needs one of them.

With Refract: your agent first gets a list of titles (the index). Once it knows which book it needs, it only receives that book's content.

Technically:

The index (always sent): just tool names and a short description of each.

The detail (sent only when needed): the full description of the tool actually used, everything required to use it correctly, nothing more.

The verification: after every compression, Refract automatically checks that nothing important was removed. If there is any doubt, it sends the full version instead of taking a risk.

No AI model is involved in this process. It is fully automatic, fast, and deterministic.

---

## MCP Proxy tools

| Tool | What it does |
|---|---|
| Compression | Compresses tool schemas on the fly, up to 98% reduction |
| Signal check | Verifies callable contract after every compression |
| Semantic routing | Identifies the right tool using embeddings (opt-in) |
| Prompt caching | Injects Anthropic cache_control for repeated requests |

## MCP Server tools

| Tool | Input | Output |
|---|---|---|
| index_repo | repo path | aggregated index of all Python, JS, TS files |
| get_compressed | file path | compressed structure + token stats |
| expand | file path + function names | verbatim source + dependency context |
| blast_radius | file path + function name | all functions that break if target changes |
| semantic_diff | file path + old source + new source | breaking changes vs body-only changes |
| semantic_diff_branches | repo path + file + function + base/head git refs | semantic_diff of one function between two branches/commits |
| security_surface | repo path | map of dangerous calls (subprocess, eval, pickle, requests) |

---

## Repository health check

```bash
refract-status --root /path/to/repo
refract-status --root /path/to/repo --json
```

| Flag | Description |
|---|---|
| `--root PATH` | Path to analyse (default: current directory) |
| `--json` | Machine-readable output |

Shows: files per language, raw vs compressed tokens, functions/classes indexed, dangerous calls by category, languages without tree-sitter support.

---

## blast_radius

Ask Claude which functions break if you change a target function.

Example result:

```json
{
  "target": "authenticate",
  "direct_callers": ["login_user"],
  "all_impacted": ["login_user", "verify_session", "admin_access"],
  "impacted_count": 3,
  "risk_level": "MEDIUM"
}
```

Risk levels: LOW (0 to 2 impacted), MEDIUM (3 to 5), HIGH (6 or more).

---

## semantic_diff

Detects breaking API changes by comparing function interfaces, not bodies. Use it as a CI gate.

Example result:

```json
{
  "breaking": ["authenticate"],
  "body_only": ["logout"],
  "added": ["new_function"],
  "removed": [],
  "unchanged": ["hash_password"],
  "is_breaking": true
}
```

If is_breaking is true, the PR changes the public API and must be reviewed.

---

## security_surface

Maps every function that calls dangerous primitives across your repo.

HIGH risk: subprocess, os.system, eval, exec, pickle, ctypes

MEDIUM risk: open (write mode), socket, requests, httpx, urllib

Example result:

```json
{
  "high_risk": [
    {
      "file": "src/refract_server.py",
      "function": "_git_show",
      "calls": ["subprocess.run"],
      "line_hint": "line 801"
    }
  ],
  "medium_risk": [
    {
      "file": "src/compression_cache.py",
      "function": "store_cached",
      "calls": ["open"],
      "line_hint": "line 90"
    },
    {
      "file": "src/refract_proxy.py",
      "function": "_check_url_reachable",
      "calls": ["urllib.request.urlopen"],
      "line_hint": "line 430"
    }
  ],
  "summary": {
    "high_risk_count": 1,
    "medium_risk_count": 2,
    "total_functions_scanned": 789,
    "total_files_scanned": 41,
    "clean_files": 37
  }
}
```

---

## Languages supported

Python (via ast module), JavaScript, TypeScript, JSX, TSX (via tree-sitter). fastembed and tree-sitter are installed by default.

Language is auto-detected from file extension.

---

## Built-in Anthropic caching

Refract integrates with Anthropic prompt caching. `as_anthropic_tools()` returns compressed tools in Anthropic API format with `cache_control` pre-injected on the last tool, so the Anthropic API caches the full tool list from the second call onward (price: $0.30/M vs $3.00/M for cache hits).

```python
from refract_proxy import RefractProxy

proxy = RefractProxy(target_url="https://my-mcp-server.com/mcp", use_cache=True)
await proxy.connect()

# Pass directly to the Anthropic client:
response = anthropic_client.messages.create(
    model="claude-sonnet-4-6",
    tools=proxy.as_anthropic_tools(),   # cache_control injected automatically
    messages=[{"role": "user", "content": "..."}],
)
```

---

## Troubleshooting

**"Failed to spawn process: No such file or directory" in Claude Desktop**

Claude Desktop cannot find refract-proxy in its PATH. Find the absolute path and use it directly:

```bash
which refract-proxy
```

Then use the full path in claude_desktop_config.json:

```json
{
  "mcpServers": {
    "my-tool-via-refract": {
      "command": "/full/path/to/refract-proxy",
      "args": [
        "--target",
        "npx @modelcontextprotocol/server-filesystem /path/to/folder"
      ]
    }
  }
}
```

---

## Works with

Claude Desktop, Cursor, any client that follows the MCP standard, any existing MCP server.

---

## License

MIT — free to use, including commercially.

TDQS

A3.8/5.0

Scored across 5 tools

Disambiguation5/5

Each tool has a distinct purpose: impact analysis, source retrieval, file compression, repo indexing, and security scanning. No two tools overlap in functionality.

Naming Consistency4/5

All tools use snake_case, but 'expand' is a lone verb while others follow a verb_noun pattern (e.g., get_compressed, index_repo). Minor inconsistency but still clear.

Tool Count5/5

5 tools is a well-scoped set for a static analysis toolkit. Neither too few nor too many, each tool serves a clear need.

Completeness4/5

Covers core static analysis needs (impact, source, compression, indexing, security). Missing a dedicated diff or test analysis tool, but the surface is reasonably complete for the domain.

Maintenance

ActivityMaintained
ResponsivenessNo issues