ai-consensus-connector 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -0
- package/index.js +96 -0
- package/package.json +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# AI Consensus — coding-agent connector
|
|
2
|
+
|
|
3
|
+
Make AI Consensus a **standing part of your development model**. Your coding
|
|
4
|
+
agent — Claude Code, Cursor, Cline, any MCP client — routes every decision of
|
|
5
|
+
consequence through three frontier AIs (Claude, GPT, Gemini) that answer
|
|
6
|
+
**independently**, then **cross-examine each other**, and return one
|
|
7
|
+
recommendation **plus the strongest dissent**. You keep working inside your
|
|
8
|
+
agent; the panel reviews as you go.
|
|
9
|
+
|
|
10
|
+
This is not a tool you reach for occasionally. It's a review layer you switch
|
|
11
|
+
on once, and from then on everything that matters gets debated by three
|
|
12
|
+
independent models before it ships. (Honest bill: a run takes a few minutes
|
|
13
|
+
and, on the bring-your-own-keys plan, costs roughly $0.40–$1 of your own
|
|
14
|
+
provider tokens.)
|
|
15
|
+
|
|
16
|
+
## Setup — Claude Code (about 2 minutes)
|
|
17
|
+
|
|
18
|
+
1. Get an API key: sign in at https://ai-consensus.ai → **Developer API** → create a key (copy it; it's shown once).
|
|
19
|
+
2. Add the connector:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
claude mcp add ai-consensus --scope user \
|
|
23
|
+
--env AI_CONSENSUS_API_KEY=aic_live_your_key_here \
|
|
24
|
+
-- npx -y ai-consensus-connector
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
(Self-hosting? add `--env AI_CONSENSUS_API_URL=https://your-instance`.)
|
|
28
|
+
|
|
29
|
+
3. Verify it's connected (free — no run is started):
|
|
30
|
+
|
|
31
|
+
> "Use the check_ai_consensus_connection tool."
|
|
32
|
+
|
|
33
|
+
4. Make it your standing rule — add to your project's `CLAUDE.md`:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
STANDING RULE: This project develops with AI Consensus as its review layer.
|
|
37
|
+
Route every decision of consequence — architecture, design, implementation
|
|
38
|
+
approach, tricky bugs, anything whose cost of being wrong is real — through
|
|
39
|
+
consult_ai_consensus as a matter of course, and weigh its recommendation and
|
|
40
|
+
strongest dissent before acting. Purely mechanical edits (formatting, typos,
|
|
41
|
+
exact instructed changes) don't need a run. When in doubt, consult.
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Teams that want *everything* routed can use the strict variant instead:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
STRICT MODE: Consult consult_ai_consensus before every non-trivial change of
|
|
48
|
+
any kind, and weigh the dissent before acting.
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Setup — Cursor / Cline / other MCP clients
|
|
52
|
+
|
|
53
|
+
Add a stdio MCP server with command `npx -y ai-consensus-connector` and the
|
|
54
|
+
`AI_CONSENSUS_API_KEY` environment variable. For Cursor, in `mcp.json`:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"ai-consensus": {
|
|
60
|
+
"command": "npx",
|
|
61
|
+
"args": ["-y", "ai-consensus-connector"],
|
|
62
|
+
"env": { "AI_CONSENSUS_API_KEY": "aic_live_your_key_here" }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Then put the standing rule in your project's agent rules file (`.cursorrules`,
|
|
69
|
+
`.clinerules`, etc.).
|
|
70
|
+
|
|
71
|
+
## Tools
|
|
72
|
+
- `consult_ai_consensus` — route a decision/task through the panel and wait for the result (a few minutes).
|
|
73
|
+
- `start_ai_consensus` / `get_ai_consensus_result` — fire-and-forget + collect later.
|
|
74
|
+
- `check_ai_consensus_connection` — zero-cost setup check (reachability, key accepted, provider keys present).
|
|
75
|
+
- `cancel_ai_consensus_run` — stop a run.
|
|
76
|
+
|
|
77
|
+
Your key authenticates to your account and bills your plan (unlimited on your
|
|
78
|
+
own keys, or prepaid credits). Keep it secret — and note that pasting the
|
|
79
|
+
`--env` form above stores the key in your shell history; use your client's
|
|
80
|
+
config file if that concerns you.
|
package/index.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// AI Consensus — MCP connector. Makes AI Consensus a standing review layer in
|
|
3
|
+
// your development loop: your coding agent (Claude Code, Cursor, Cline — any
|
|
4
|
+
// MCP client) routes decisions of consequence through three frontier AIs that
|
|
5
|
+
// answer independently, cross-examine each other, and return one
|
|
6
|
+
// recommendation plus the strongest dissent. Config via env:
|
|
7
|
+
// AI_CONSENSUS_API_KEY (required) aic_live_...
|
|
8
|
+
// AI_CONSENSUS_API_URL (optional) defaults to the hosted API
|
|
9
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
13
|
+
const API_BASE = (process.env.AI_CONSENSUS_API_URL || "https://ai-consensus.ai").replace(/\/$/, "");
|
|
14
|
+
const API_KEY = process.env.AI_CONSENSUS_API_KEY || "";
|
|
15
|
+
|
|
16
|
+
async function api(path, opts = {}) {
|
|
17
|
+
const res = await fetch(API_BASE + path, {
|
|
18
|
+
...opts,
|
|
19
|
+
headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json", ...(opts.headers || {}) },
|
|
20
|
+
});
|
|
21
|
+
let json;
|
|
22
|
+
try { json = JSON.parse(await res.text()); } catch { json = {}; }
|
|
23
|
+
return { status: res.status, json };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function render(r) {
|
|
27
|
+
if (r?.result?.artifact) return r.result.artifact;
|
|
28
|
+
const v = r?.result?.verdict;
|
|
29
|
+
if (v) {
|
|
30
|
+
const out = [v.consensusReached ? "RECOMMENDATION:" : "A JUDGEMENT CALL — WHAT TO WEIGH:", v.consensusReached ? v.consensusDecision : v.escalation];
|
|
31
|
+
if (v.dissent) out.push("", "STRONGEST DISSENT:", v.dissent);
|
|
32
|
+
return out.join("\n");
|
|
33
|
+
}
|
|
34
|
+
return `Status: ${r?.status}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const server = new McpServer({ name: "ai-consensus", version: "0.12.0" });
|
|
38
|
+
|
|
39
|
+
server.tool(
|
|
40
|
+
"consult_ai_consensus",
|
|
41
|
+
"Route a decision, question, or task through three frontier AIs (Claude, GPT, Gemini) that answer independently, then cross-examine each other — returning one recommendation plus the strongest dissent. Intended as a STANDING part of the development loop: consult it as a matter of course for any decision of consequence (architecture, design, approach, tricky bugs, anything whose cost of being wrong is real) — not just rare emergencies. Purely mechanical edits do not need a run. A run takes a few minutes.",
|
|
42
|
+
{ prompt: z.string().describe("The decision, question, or task to run through AI Consensus.") },
|
|
43
|
+
async ({ prompt }) => {
|
|
44
|
+
if (!API_KEY) return { content: [{ type: "text", text: "AI_CONSENSUS_API_KEY is not set." }], isError: true };
|
|
45
|
+
const sub = await api("/api/v1/runs", { method: "POST", body: JSON.stringify({ question: prompt }) });
|
|
46
|
+
if (sub.status !== 202) return { content: [{ type: "text", text: `Could not start the run: ${JSON.stringify(sub.json)}` }], isError: true };
|
|
47
|
+
const runId = sub.json.run_id;
|
|
48
|
+
for (let i = 0; i < 30; i++) {
|
|
49
|
+
const w = await api(`/api/v1/runs/${runId}/wait`, { method: "POST" });
|
|
50
|
+
if (w.json?.status === "succeeded" || w.json?.status === "failed") return { content: [{ type: "text", text: render(w.json) }] };
|
|
51
|
+
}
|
|
52
|
+
return { content: [{ type: "text", text: `Still running — collect it later with get_ai_consensus_result and run id ${runId}.` }] };
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
server.tool("start_ai_consensus", "Start an AI Consensus run without waiting; returns a run id to collect later.",
|
|
57
|
+
{ prompt: z.string() },
|
|
58
|
+
async ({ prompt }) => {
|
|
59
|
+
const sub = await api("/api/v1/runs", { method: "POST", body: JSON.stringify({ question: prompt }) });
|
|
60
|
+
return { content: [{ type: "text", text: sub.status === 202 ? `Started. run_id: ${sub.json.run_id}` : `Error: ${JSON.stringify(sub.json)}` }] };
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
server.tool("get_ai_consensus_result", "Fetch the status/result of an AI Consensus run by id.",
|
|
64
|
+
{ run_id: z.string() },
|
|
65
|
+
async ({ run_id }) => {
|
|
66
|
+
const r = await api(`/api/v1/runs/${run_id}`);
|
|
67
|
+
return { content: [{ type: "text", text: r.json?.status === "succeeded" ? render(r.json) : `Status: ${r.json?.status ?? r.status}` }] };
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
server.tool("cancel_ai_consensus_run", "Cancel an in-progress AI Consensus run by id.",
|
|
71
|
+
{ run_id: z.string() },
|
|
72
|
+
async ({ run_id }) => {
|
|
73
|
+
const r = await api(`/api/v1/runs/${run_id}/cancel`, { method: "POST" });
|
|
74
|
+
return { content: [{ type: "text", text: JSON.stringify(r.json) }] };
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
server.tool(
|
|
78
|
+
"check_ai_consensus_connection",
|
|
79
|
+
"Zero-cost setup check: verifies the API is reachable, your AI Consensus key is accepted, and (on bring-your-own-keys plans) which provider keys are present. Does NOT start a deliberation and costs nothing. Use right after installing the connector.",
|
|
80
|
+
{},
|
|
81
|
+
async () => {
|
|
82
|
+
if (!API_KEY) return { content: [{ type: "text", text: "AI_CONSENSUS_API_KEY is not set." }], isError: true };
|
|
83
|
+
const r = await api("/api/v1/status");
|
|
84
|
+
if (r.status !== 200 || !r.json?.ok) {
|
|
85
|
+
return { content: [{ type: "text", text: `Not connected (HTTP ${r.status}): ${JSON.stringify(r.json)}` }], isError: true };
|
|
86
|
+
}
|
|
87
|
+
const lines = [`Connected ✓ — key accepted, plan: ${r.json.plan_type ?? "unknown"}.`];
|
|
88
|
+
if (r.json.provider_keys_present) {
|
|
89
|
+
const p = r.json.provider_keys_present;
|
|
90
|
+
lines.push(`Provider keys present: anthropic=${p.anthropic}, openai=${p.openai}, gemini=${p.gemini} (presence only — validated on your first real run).`);
|
|
91
|
+
}
|
|
92
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
await server.connect(new StdioServerTransport());
|
package/package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ai-consensus-connector",
|
|
3
|
+
"version": "0.12.0",
|
|
4
|
+
"mcpName": "io.github.thestevenjohnstone/ai-consensus",
|
|
5
|
+
"description": "Make AI Consensus a standing review layer for your coding agent — Claude, GPT and Gemini answer independently, cross-examine each other, and return one recommendation plus the strongest dissent.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": { "ai-consensus-connector": "index.js" },
|
|
8
|
+
"files": ["index.js", "README.md"],
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
11
|
+
"zod": "^3.23.8"
|
|
12
|
+
},
|
|
13
|
+
"engines": { "node": ">=18" },
|
|
14
|
+
"license": "UNLICENSED"
|
|
15
|
+
}
|