parallax-mcp
README.md
# Parallax
**Render-in-the-loop self-verification for coding agents โ and the study of why it works.**
> A second viewpoint reveals structure invisible from the first. Parallax flips a generated text artifact (SQL, a regex, plotting code) into a *rendered representation*, has a critic inspect the render, and reports (or corrects) silent, "looks-right-but-wrong" bugs a quick glance misses.
- **Package (PyPI):** `parallax-verify` ยท **import:** `parallax` ยท **API:** `crosscheck(artifact, render_fn)`
- **Status:** ๐ข **M1 usable.** The `crosscheck` primitive works end-to-end with two adapters (SQL-result-shape, regex-highlight), a CLI, and an MCP server. Offline by default (no API key); upgrades to a multimodal vision critic when one is configured. The *why-it-works* study (M2) is separate and ongoing.
---
## What it catches
Code that LLMs (and people) generate can fail **silently** โ it runs, returns a plausible answer, and is wrong. Nothing throws.
- **SQL:** a dropped/loose `JOIN` predicate inflates row counts (fan-out); a `LEFT`/`INNER` swap silently drops rows.
- **Regex:** a greedy quantifier over-matches into a region it shouldn't; a pattern quietly misses spans it should catch.
Parallax extracts a small set of neutral shape facts (row-count ratio, null fractions, how many matches land in a forbidden region, โฆ), renders them, and has a critic flag anything that looks off. It knows only what you tell it โ there is no hidden oracle.
## Install
```bash
pip install parallax-verify # core: SQL + regex + CLI, offline heuristic critic
pip install "parallax-verify[anthropic]" # + Anthropic vision critic
pip install "parallax-verify[openai]" # + any OpenAI-compatible vision endpoint
pip install "parallax-verify[mcp]" # + the MCP server
```
## Walkthrough (CLI)
Offline by default โ no API key required. Here's a real session; the whole idea fits in about thirty seconds.
A greedy regex over-matches into a region it was supposed to leave alone. `--forbidden-spans 3:9` says *the characters `SECRET` must not be matched*:
```console
$ parallax regex "<.*>" --target "<a>SECRET<b>" --forbidden-spans 3:9
parallax regex: "artifact" - [!] SUSPICIOUS (suspicion 0.65)
critic: parallax-local-heuristic
flagged: matches_in_forbidden_region
note: local heuristic flagged extreme bins: matches_in_forbidden_region
$ echo $?
1
```
Make the quantifier lazy and the bug is gone โ Parallax goes quiet:
```console
$ parallax regex "<.*?>" --target "<a>SECRET<b>" --forbidden-spans 3:9
parallax regex: "artifact" - [ok] looks fine (suspicion 0.05)
critic: parallax-local-heuristic
note: local heuristic saw no extreme bins
$ echo $?
0
```
That flip โ `<.*>` flagged, `<.*?>` clean โ is the whole product in miniature.
SQL works the same way. A join predicate that's too loose multiplies rows (fan-out); you tell Parallax how many rows you expected and it catches the blow-up (3 expected, 12 returned):
```console
$ parallax sql "SELECT o.id, c.region FROM orders o JOIN cust c ON o.cust = c.id" \
--setup "CREATE TABLE orders(id INT, cust INT); INSERT INTO orders VALUES (1,10),(2,10),(3,10);
CREATE TABLE cust(id INT, region VARCHAR); INSERT INTO cust VALUES (10,'A'),(10,'B'),(10,'C'),(10,'D');" \
--expected-rows 3
parallax sql: "artifact" - [!] SUSPICIOUS (suspicion 0.65)
critic: parallax-local-heuristic
flagged: rowcount_ratio_to_expected
note: local heuristic flagged extreme bins: rowcount_ratio_to_expected
```
Exit code is `0` when it looks fine, `1` when flagged, `2` on error โ so it drops straight into CI. Add `--json` for machine-readable output:
```console
$ parallax regex "<.*>" --target "<a>SECRET<b>" --forbidden-spans 3:9 --json
{"kind": "regex", "item_id": "artifact", "caught": true, "suspicion": 0.65, "flagged": ["matches_in_forbidden_region"], "rationale": "local heuristic flagged extreme bins: matches_in_forbidden_region", "corrected": false, "proposed_fix": null, "critic": "parallax-local-heuristic"}
```
Swap the offline critic for a vision model with `--critic anthropic` (needs `ANTHROPIC_API_KEY`) or `--critic openai` โ see [Critics](#critics) below.
## Use from Python
```python
from parallax import Artifact, crosscheck, render_regex
from parallax.clients.heuristic_critic import HeuristicCritic
art = Artifact(kind="regex_highlight", text="<.*>",
context={"target": "<a>SECRET<b>", "forbidden_spans": "3:9"})
result = crosscheck(art, render_regex, HeuristicCritic(), mode="expected")
print(result.critique.caught, result.critique.flagged) # True ('matches_in_forbidden_region',)
```
Run the full demo (SQL + regex, offline): `python examples/demo.py`.
## Use as an MCP tool (for Claude / agents)
`parallax-mcp` starts a server exposing one `crosscheck` tool (`kind` = `sql` | `regex`, `text`, `context`, optional `mode`/`critic`). Point your MCP client at it and an agent can render-verify its own generated SQL/regex before trusting the result.
## The two honest modes
Parallax has no magic oracle. It works from what you give it:
1. **Supplied expectations** โ you provide the expected row count, the spans you meant to match, or the regions that must not match.
2. **Relative anomaly** โ compare against a previous / reference run.
## Critics
Bring your own key โ `local` needs none, and the vision critics accept a key from an env var or the `--api-key` flag:
- **`local`** (default) โ a fast, offline, rule-based sanity check over the same verdict-free features. No API key.
- **`anthropic`** โ an Anthropic multimodal model looks at the rendered image. `PARALLAX_CRITIC=anthropic` + `ANTHROPIC_API_KEY` (default model `claude-opus-4-8`).
- **`openai`** โ any OpenAI-compatible vision endpoint. `PARALLAX_CRITIC=openai` + `OPENAI_API_KEY` (default model `gpt-4o-mini`). Point `--base-url` (or `PARALLAX_BASE_URL`) at OpenAI, Google Gemini's OpenAI-compatible endpoint, OpenRouter, or a local server, and pick the model with `--model`.
> **Which critic to trust.** The offline `local` critic is the zero-config default and is fully tested. The Anthropic vision critic is validated **live on the SQL adapter** โ it passes clean results and catches fan-outs. **Both renders now print each bin's label as text** next to its color, so a vision model reads the bin instead of guessing severity from an ungrounded swatch (a color-only render caused systematic false positives; the regex render has now been brought up to the same grounding as SQL). The OpenAI-compatible critic sends the correct vision request shape (`image_url` data URI + JSON mode). What's still pending is a **full live vision run** on the regex render and on the OpenAI endpoint โ until that's done, treat those two vision verdicts as provisional and lean on `local` for CI.
```bash
# use your own OpenAI key
parallax sql "..." --setup "..." --expected-rows 3 --critic openai --api-key "$OPENAI_API_KEY"
# or a different provider via its OpenAI-compatible endpoint
parallax regex "<.*>" --target "<a>x<b>" --forbidden-spans 3:4 \
--critic openai --base-url https://openrouter.ai/api/v1 --model "google/gemini-2.5-flash"
```
All settings also read from the environment (`PARALLAX_CRITIC` / `PARALLAX_MODEL` / `PARALLAX_API_KEY` / `PARALLAX_BASE_URL`) โ see [.env.example](https://github.com/sani-savaliya/parallax/blob/main/.env.example).
## What this is (and is not)
The render-and-critique *loop* is not new โ it exists in several verticals (maps, motion trajectories, plotting, GUI agents). **Parallax's contribution is different:** a general, artifact-agnostic primitive with SQL-result-shape and regex-highlight adapters, plus a controlled study the field skips.
We do **not** claim "rendering beats text," that images universally help, or that the idea is ours. The honest claim is a **model-conditional representation effect**, scoped to the cells we actually run, with nulls reported. The factorial decomposition that would justify any *why-it-works* statement is a separate, ongoing research program (M2) โ see [docs/NOVELTY.md](https://github.com/sani-savaliya/parallax/blob/main/docs/NOVELTY.md) for the full do-not-claim list and [docs/RED-TEAM.md](https://github.com/sani-savaliya/parallax/blob/main/docs/RED-TEAM.md) for why the design is shaped this way.
## Documentation
| Doc | What |
|---|---|
| [docs/PRD.md](https://github.com/sani-savaliya/parallax/blob/main/docs/PRD.md) | Product requirements, users, scope/non-goals |
| [docs/INTERFACE.md](https://github.com/sani-savaliya/parallax/blob/main/docs/INTERFACE.md) | `crosscheck()` + the adapter contract + worked adapter specs |
| [docs/ARCHITECTURE.md](https://github.com/sani-savaliya/parallax/blob/main/docs/ARCHITECTURE.md) | Stack, package layout, sandboxing, model routing |
| [docs/TASKS.md](https://github.com/sani-savaliya/parallax/blob/main/docs/TASKS.md) | TDD-first task list (M1 library MVP + M2 study), risk register |
| [docs/EVAL.md](https://github.com/sani-savaliya/parallax/blob/main/docs/EVAL.md) | **The M2 crux** โ pre-registration-grade factorial decomposition design |
| [docs/NOVELTY.md](https://github.com/sani-savaliya/parallax/blob/main/docs/NOVELTY.md) | Prior-art verdict, defensible claim, do-not-claim list |
| [docs/RED-TEAM.md](https://github.com/sani-savaliya/parallax/blob/main/docs/RED-TEAM.md) | The adversarial findings that reshaped the contribution |
| [docs/CITATIONS.md](https://github.com/sani-savaliya/parallax/blob/main/docs/CITATIONS.md) | Citation-integrity pass โ all 13 sources verified real; 7 wording/number corrections applied |
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues