Skip to main content
Glama
samihalawa

unmerged-approaches-mcp

by samihalawa
README.md
# unmerged-approaches-mcp

An MCP server that puts **one identical brief to several LLMs in parallel** and hands back **every answer verbatim — unmerged, unranked, unsynthesized.**

Most multi-model MCP servers end in a judge, a synthesizer, or a consensus loop. This one deliberately does not. The moment N answers are averaged into one confident paragraph, the single most useful piece of information is destroyed: **whether the models actually agreed.**

- Four models independently reaching the same conclusion is corroboration.
- Two against two is a real open question your one-model answer would have hidden from you.

A synthesizer renders both of those as the same smooth prose. This server refuses to, by design.

## What it does

- Fans one brief out to up to 12 OpenRouter models concurrently — 400+ models, one API key.
- Returns per-model: the verbatim answer, `finish_reason`, latency, token usage, cost, and a structured error when a model fails.
- A slow or failing model becomes one failed entry; it never sinks the panel.
- Persists bounded, redacted, correlation-ID-addressable debug events.
- Never merges, ranks, scores, votes on, or picks a winner among the answers.

## Install

### stdio (Claude Desktop, Claude Code, Cursor, Cline, Zed)

```json
{
  "mcpServers": {
    "unmerged-approaches": {
      "command": "npx",
      "args": ["-y", "unmerged-approaches-mcp@latest"],
      "env": { "OPENROUTER_API_KEY": "sk-or-v1-..." }
    }
  }
}
```

```bash
claude mcp add unmerged-approaches npx -y unmerged-approaches-mcp
```

### Remote Streamable HTTP

```bash
OPENROUTER_API_KEY=sk-or-v1-... UNMERGED_AUTH_TOKEN=your-token npx unmerged-approaches-mcp-http
# or: node src/http.js
```

Mounts MCP at `/mcp` and a separate liveness probe at `/health`.

```json
{
  "mcpServers": {
    "unmerged-approaches": {
      "type": "http",
      "url": "https://your-host.example/mcp"
    }
  }
}
```

Send the token as `Authorization: Bearer <token>` or `x-api-key`. Keep secrets in your client's secret storage, never in a committed config file.

## Tools

| Tool | Category | What it does |
|---|---|---|
| `consult` | PANEL | One brief → N models in parallel → N verbatim answers, unmerged |
| `list_models` | SYSTEM | Live OpenRouter catalog: substring search, free-only filter, cursor pagination |
| `get_status` | SYSTEM | Server version, credential validity, remaining credit, default panel, limits |
| `get_debug_logs` | DEBUG | Newest-first structured events, filterable by correlationId / level / event / operation / time |

All four are read-only and non-destructive. The server exposes no arbitrary HTTP, shell, or SQL primitive.

### `consult`

| Argument | Type | Notes |
|---|---|---|
| `brief` | string, required | Sent byte-for-byte identically to every model. Max 200,000 chars. |
| `models` | string[] | Exact OpenRouter ids, max 12, deduplicated. Omit for the default panel. |
| `system` | string | Optional system prompt, identical for every model. |
| `max_tokens` | int | Default 8000, ceiling 32000. |
| `temperature` | number | 0–2, default 0.7. |
| `timeout_ms` | int | Default 180000, max 600000. Per model, not for the batch. |

Returns `correlationId`, `requestedModels`, `answeredCount`, `completeCount`, `failedCount`, `totalCostUsd`, `wallClockMs`, and `answers[]` with `{ model, actualModel, ok, complete, finishReason, content, error, latencyMs, usage }`.

If **every** model fails, the call returns `isError: true` with code `ALL_MODELS_FAILED`. A partial panel is a success.

## Writing a brief that is worth the money

The output is only as good as the isolation of the input. A brief that leaks your preferred answer gets it echoed back by every model, and you will mistake that echo for agreement.

- Include the goal, hard constraints, what is already decided, what was rejected and why, and what is genuinely unknown.
- Mark unverified claims as unverified. Distinguish what you observed from what you assumed.
- **Do not** name your preferred option, and do not tell the panel what an earlier model said.
- Ask for independent judgment, not novelty.

## Environment variables

| Name | Required | Purpose |
|---|---|---|
| `OPENROUTER_API_KEY` | yes | OpenRouter credential. Server-side only; never accepted as a tool argument. |
| `UNMERGED_DEFAULT_MODELS` | no | Comma-separated default panel. |
| `UNMERGED_MAX_MODELS` | no | Panel size cap. Default 12. |
| `UNMERGED_MAX_TOKENS` | no | Default output ceiling per model. Default 8000. |
| `UNMERGED_TIMEOUT_MS` | no | Default per-model timeout. Default 180000. |
| `UNMERGED_LOG_CAPACITY` | no | Retained debug events. Default 500. |
| `UNMERGED_AUTH_TOKEN` | http only | Bearer / `x-api-key` token for the HTTP transport. |
| `UNMERGED_ALLOWED_ORIGINS` | http only | Comma-separated browser origin allowlist. Browsers are refused unless listed. |
| `PORT`, `HOST` | http only | Defaults `8787`, `127.0.0.1`. |

No value is ever written to logs, tool results, or client configuration.

## Cost

You pay per model, per call. A 5-model panel is roughly 5× a single call. `list_models` with `free_only: true` finds zero-price models; free endpoints time out and rate-limit far more often, which shows up honestly in `failedCount`.

## Security

- Credentials are read only from the server environment and are never accepted as tool arguments.
- Anything credential-shaped is stripped from errors and debug events before persistence.
- The HTTP transport validates `Origin`, requires a bearer token when one is configured, and compares it in constant time.
- **Returned answers are untrusted third-party model output.** If an agent calls this on attacker-influenced input, treat the answers as data to weigh, never as instructions to execute. This is a prompt-injection surface like any MCP tool that returns external content.

## Development

```bash
npm install
npm run build   # syntax check every entrypoint
npm test        # 9 contract tests incl. a live stdio initialize/list/call handshake
```

## License

MIT

TDQS

A4.6/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct role: consult is the core action, list_models handles model selection, get_status handles configuration/health checks, and get_debug_logs is for diagnostics. There is no meaningful overlap or possible confusion between the tool boundaries.

Naming Consistency4/5

get_status, list_models, and get_debug_logs all follow a clean lower_snake verb_noun pattern. consult is a bare verb without an explicit object, which is a minor deviation, but it still reads naturally and fits the overall imperative style.

Tool Count5/5

Four tools is well-scoped for a narrow server: one primary operation plus three supporting utilities for configuration, model discovery, and debugging. Every tool earns its place and there is no redundancy.

Completeness5/5

The domain is parallel unmerged consultation, and consult fully covers the core workflow while list_models, get_status, and get_debug_logs cover the supporting needs around model selection, auth/config validation, and failure investigation. There are no obvious dead ends or missing operations agents would need.