Skip to main content
Glama
README.md
# ai-discuss MCP server

An [MCP](https://modelcontextprotocol.io) server that lets a host AI agent
(**Claude Code, opencode, or Codex**) trigger a **multi-agent debate**. The host
calls the `discuss` tool with a topic + code context; the server fans the
question out to several configured AI models, runs an N-round debate where the
agents critique and refine each other's answers, then a synthesizer produces a
consensus recommendation with **ranked, scored options** — and returns it so the
host can keep coding.

Models are reached through OpenAI-compatible **providers** — use **OpenRouter**
(cloud: Claude, GPT, Gemini, DeepSeek, …), **Ollama** (local, keyless), or both
at once in the same debate.

## How it works

```
Claude Code / opencode / Codex ──MCP stdio──► ai-discuss
                                                 │  round loop (fan-out, timeouts, error isolation)
                          ┌──────────────────────┼───────────────────┐
                          ▼                       ▼                   ▼
              OpenAICompatAdapter         OpenAICompatAdapter   Synthesizer
              (OpenRouter / cloud)        (Ollama / local)      (a chosen participant)
                          │                       │                   │
                          └───────────────────────┴───► full markdown transcript on disk
```

- **Round 1:** each participant answers independently.
- **Rounds 2..N:** each participant sees the others' previous answers
  (anonymized by default) and critiques / refines.
- **Synthesis:** the synthesizer scores each option 0–100 and ranks them, with
  reasoning, consensus, and unresolved disagreements.

Output is returned three ways: a concise summary for the host agent, a
`structuredContent` object, and a complete markdown transcript written to disk.

## Install & build

```bash
npm install
npm run build
```

## Configure participants

Copy the example config and edit it:

```bash
cp ai-discuss.config.example.json ai-discuss.config.json
```

The config has a `providers` map (OpenAI-compatible endpoints) and a list of
participants that each pick a `provider` + `model`:

```jsonc
{
  "providers": {
    "openrouter": { "baseURL": "https://openrouter.ai/api/v1", "apiKeyEnv": "OPENROUTER_API_KEY" },
    "ollama":     { "baseURL": "http://localhost:11434/v1",    "apiKeyEnv": null }
  },
  "participants": [
    { "id": "claude",     "provider": "openrouter", "model": "anthropic/claude-sonnet-4" },
    { "id": "qwen-local", "provider": "ollama",     "model": "qwen3.6" },
    { "id": "mock",       "type": "mock", "enabled": false }
  ]
}
```

| participant | how it connects                                       | key fields |
| ----------- | ----------------------------------------------------- | ---------- |
| model       | an OpenAI-compatible provider (default `type`)        | `provider`, `model`, `temperature?`, `maxTokens?` |
| `mock`      | deterministic echo — for credit-free testing          | `reply?` |

- API keys are **never** stored in config — a provider's `apiKeyEnv` names the
  env var that holds the key. `apiKeyEnv: null` marks a **keyless** provider
  (e.g. local Ollama).
- An enabled participant whose provider needs a key that isn't set is skipped at
  runtime — it never crashes the run.
- Add or swap a discussant by editing its `model` (see model ids via the
  `list_models` tool, [openrouter.ai/models](https://openrouter.ai/models), or
  `ollama list`).

Top-level options: `defaultRounds`, `defaultSynthesizer`, `transcriptDir`,
`perParticipantTimeoutMs`, `maxConcurrency`, `anonymizePeers`, `apiRetries`.

## Register with a host

The server is a standard stdio MCP server, so it works with any MCP host. Build
first (`npm run build`), then register. Set `OPENROUTER_API_KEY` if you use
OpenRouter; for Ollama just have `ollama serve` running (no key).

**Claude Code** — `.mcp.json` in the project, or:

```bash
claude mcp add ai-discuss --env OPENROUTER_API_KEY=sk-or-... \
  -- node /absolute/path/to/Ai-discuss-mcp/dist/index.js
```

**opencode** — `opencode.json`:

```jsonc
{
  "mcp": {
    "ai-discuss": {
      "type": "local",
      "command": ["node", "/absolute/path/to/Ai-discuss-mcp/dist/index.js"],
      "enabled": true,
      "environment": { "OPENROUTER_API_KEY": "sk-or-..." }
    }
  }
}
```

**Codex** — `~/.codex/config.toml`:

```toml
[mcp_servers.ai-discuss]
command = "node"
args = ["/absolute/path/to/Ai-discuss-mcp/dist/index.js"]
env = { OPENROUTER_API_KEY = "sk-or-..." }
```

## Tools

### `discuss`

| field            | type       | notes |
| ---------------- | ---------- | ----- |
| `topic`          | string     | required — the question/decision to debate |
| `context`        | string?    | code, constraints, background |
| `options`        | string[]?  | candidate approaches to rank (else participants propose their own) |
| `rounds`         | number?    | 1–6, defaults to config |
| `participants`   | string[]?  | filter to these ids, defaults to all enabled |
| `synthesizer`    | string?    | participant id for synthesis, defaults to config |
| `writeTranscript`| boolean?   | default `true` |

Returns `recommendation`, `rankedOptions[{option, score, reasoning, risks}]`,
`consensus`, `disagreements`, `participantsUsed`, `participantsFailed`, `rounds`,
`synthesizerId`, `degraded`, and `transcriptPath`.

### `list_participants`

Lists configured participants (id, provider, model, enabled/available, default
synthesizer). Cheap — reads config only, no model calls. Useful before calling
`discuss`.

### `list_models`

Queries each configured provider for the model ids it can serve (OpenRouter
`/models`, Ollama `/api/tags`). Useful to discover valid model names. Optional
`provider` arg narrows to one provider.

## Example

> Claude Code, after scaffolding a trading bot, calls:

```json
{
  "name": "discuss",
  "arguments": {
    "topic": "Choose an order-execution strategy for a momentum intraday stock bot to minimize slippage on mid-cap tickers.",
    "context": "Python bot, Alpaca API, ~50 trades/day, $5k-$20k positions, currently naive market orders.",
    "options": ["Market orders", "Marketable limit orders (5bps cap)", "TWAP over 60s", "Adaptive VWAP slices"],
    "rounds": 3,
    "synthesizer": "claude"
  }
}
```

The server returns a ranked recommendation and a transcript path, and the host
continues editing the execution module.

## Development

```bash
npm run dev        # tsx watch (no rebuild loop)
npm test           # vitest unit suite (no network / no credits)
npm run inspect    # MCP Inspector against the built server
npm run typecheck  # tsc --noEmit
```

### Credit-free end-to-end

Set every participant (including the synthesizer) to `type: "mock"` and run the
server through `npm run inspect` or any MCP client. The full pipeline runs,
writes a transcript, and returns valid `structuredContent` without any API
calls. (With mock participants the synthesizer can't emit JSON, so you'll see
`degraded: true` — that exercises the fallback path.)

## Design notes

- **Adapter pattern** — the orchestrator only ever calls `participant.ask()`; it
  never knows whether a participant is a real model or a mock. One
  `OpenAICompatAdapter` serves every provider (OpenRouter, Ollama, …), differing
  only by `baseURL`, optional key, and headers.
- **Error isolation** — `ask()` never throws; failures are encoded in the
  result. Each round fans out with `Promise.allSettled` + per-participant
  timeout/abort, so one dead participant degrades but never aborts the run. A
  participant that fails one round is still invited to the next.
- **Always-valid output** — the synthesizer is asked for strict JSON, retried
  once, and finally falls back to a mechanical synthesis so the tool always
  returns schema-valid structured content.
- **stdout is sacred** — all logging goes to stderr only; stdout carries the MCP
  JSON-RPC stream.

TDQS

A4.2/5.0

Scored across 3 tools

Disambiguation5/5

Each tool serves a distinct purpose: discuss runs the debate, list_models discovers available models, and list_participants shows configured participants. No functional overlap.

Naming Consistency5/5

All tools use consistent snake_case verb_noun naming (discuss, list_models, list_participants), with a clear pattern.

Tool Count5/5

Three tools is well-scoped for this focused server; each tool plays an essential role without redundancy.

Completeness3/5

Tools cover the main discussion task and listing of resources, but lack create/update/delete operations for participants or models, leaving configuration management to external means.

Maintenance

ActivityInactive
ResponsivenessNo issues