plures-mcp 1.0.0 → 1.1.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 +2 -2
- package/dist/index.js +14 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ Get a free test key at [plures.si](https://plures.si/?for=developers#join), then
|
|
|
22
22
|
|
|
23
23
|
Claude Code: `claude mcp add plures --env PLURES_API_KEY=pk_test_... -- npx -y plures-mcp`
|
|
24
24
|
|
|
25
|
-
A `pk_test_` key answers instantly with a simulated result, so you can try the flow for free. A `pk_live_` key sends tasks to real verified people and is billed per answer.
|
|
25
|
+
A `pk_test_` key answers instantly with a simulated result (always the first option, clearly marked as a test), so you can try the flow for free. A `pk_live_` key sends tasks to real verified people and is billed per answer.
|
|
26
26
|
|
|
27
27
|
## Tools
|
|
28
28
|
|
|
@@ -38,7 +38,7 @@ A `pk_test_` key answers instantly with a simulated result, so you can try the f
|
|
|
38
38
|
|
|
39
39
|
**`cancel_human_task`**: cancel a task that no longer matters.
|
|
40
40
|
|
|
41
|
-
Each result tells the agent the answer, how many people agreed and whether it is safe to act on
|
|
41
|
+
Each result tells the agent the answer, how many people agreed and whether it is safe to act on. If you've added your team at plures.si/dashboard/team, decisions verified people don't all agree on go to your team, and the agent gets their decision as the final answer.
|
|
42
42
|
|
|
43
43
|
## When agents should use it
|
|
44
44
|
|
package/dist/index.js
CHANGED
|
@@ -5,28 +5,38 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { Plures, PluresError } from "plures";
|
|
8
|
-
var VERSION = "1.
|
|
8
|
+
var VERSION = "1.1.0";
|
|
9
9
|
var KEY = process.env.PLURES_API_KEY;
|
|
10
10
|
var plures = KEY ? new Plures(KEY, { baseUrl: process.env.PLURES_BASE_URL }) : null;
|
|
11
11
|
var MAX_WAIT = 240;
|
|
12
12
|
var server = new McpServer({ name: "plures", version: VERSION }, {
|
|
13
13
|
instructions: "Plures sends a decision to ID verified people, who answer independently, and returns their consensus. Use ask_humans before you act on a judgment call you are not sure about, where a wrong call costs money, trust or safety: reading a customer's tone or intent, approving a refund or payment, checking that an invoice, listing or document matches, moderating an edge case, or confirming a fact that needs human eyes. Don't use it for things you can check with code or a lookup. Act on the answer when agreement is 1; below that, treat it as uncertain and escalate or ask for more verifiers."
|
|
14
14
|
});
|
|
15
|
+
var TEST_NOTE = "TEST MODE: this answer is simulated; no person saw the question. It always picks the first option, so don't act on it. Use a live key for real answers.";
|
|
15
16
|
var fmt = (t) => {
|
|
17
|
+
const text = describe(t);
|
|
18
|
+
return t.mode === "test" ? `${TEST_NOTE}
|
|
19
|
+
${text}` : text;
|
|
20
|
+
};
|
|
21
|
+
var describe = (t) => {
|
|
16
22
|
const label = (id) => t.options.find((o) => o.id === id)?.label ?? id;
|
|
23
|
+
if (t.status === "pending" && t.escalation)
|
|
24
|
+
return `Verified people didn't all agree, so the decision went to your team, who decide within a day. Task ${t.id}. Call get_human_answer with this task_id later; don't act until it's decided.`;
|
|
17
25
|
if (t.status === "pending")
|
|
18
26
|
return `Still waiting for people to answer. Task ${t.id}. Call get_human_answer with this task_id later.`;
|
|
19
27
|
if (t.status === "canceled") return `Task ${t.id} was canceled.`;
|
|
20
28
|
const r = t.result;
|
|
21
29
|
const votes = r ? Object.entries(r.votes).map(([k, v]) => `${label(k)}: ${v}`).join(", ") : "none";
|
|
22
30
|
if (t.status === "expired") return `Task ${t.id} expired before enough people answered. Votes so far: ${votes}. Treat this as unverified.`;
|
|
31
|
+
if (r?.decided_by === "team" && r.answer !== null)
|
|
32
|
+
return `Your team decided: ${label(r.answer)}${r.reviewer ? ` (${r.reviewer})` : ""}, after verified people didn't all agree (${votes}). This is the final answer; act on it.${r.note ? ` Their note: "${r.note}"` : ""}`;
|
|
23
33
|
if (!r || r.answer === null) return `People were split${r?.tie ? " (a tie)" : ""}. Votes: ${votes}. Treat this as uncertain and escalate to a person on your team.`;
|
|
24
34
|
const strength = r.agreement >= 1 ? "Everyone agreed, so it is safe to act on." : `Agreement is ${Math.round(r.agreement * 100)}%, so treat it as uncertain.`;
|
|
25
|
-
return `Verified people answered: ${label(r.answer)}. ${Math.round(r.agreement * r.answers)} of ${r.answers} agreed. ${strength} Votes: ${votes}
|
|
35
|
+
return `Verified people answered: ${label(r.answer)}. ${Math.round(r.agreement * r.answers)} of ${r.answers} agreed. ${strength} Votes: ${votes}.`;
|
|
26
36
|
};
|
|
27
37
|
var out = (t) => ({
|
|
28
38
|
content: [{ type: "text", text: fmt(t) }],
|
|
29
|
-
structuredContent: { task_id: t.id, status: t.status, mode: t.mode, answer: t.result?.answer ?? null, label: t.result?.label ?? null, agreement: t.result?.agreement ?? null, answers: t.result?.answers ?? 0, votes: t.result?.votes ?? {} }
|
|
39
|
+
structuredContent: { task_id: t.id, status: t.status, mode: t.mode, decided_by: t.result?.decided_by ?? null, with_team: !!t.escalation && t.status === "pending", answer: t.result?.answer ?? null, label: t.result?.label ?? null, agreement: t.result?.agreement ?? null, answers: t.result?.answers ?? 0, votes: t.result?.votes ?? {} }
|
|
30
40
|
});
|
|
31
41
|
var fail = (e) => ({
|
|
32
42
|
isError: true,
|
|
@@ -52,7 +62,7 @@ var progressFor = (extra, total) => {
|
|
|
52
62
|
if (token === void 0) return void 0;
|
|
53
63
|
return (n) => extra.sendNotification({ method: "notifications/progress", params: { progressToken: token, progress: n, total, message: "Waiting for verified people to answer" } });
|
|
54
64
|
};
|
|
55
|
-
var result = { task_id: z.string(), status: z.string(), mode: z.string(), answer: z.string().nullable(), label: z.string().nullable(), agreement: z.number().nullable(), answers: z.number(), votes: z.record(z.number()) };
|
|
65
|
+
var result = { task_id: z.string(), status: z.string(), mode: z.string(), decided_by: z.string().nullable(), with_team: z.boolean(), answer: z.string().nullable(), label: z.string().nullable(), agreement: z.number().nullable(), answers: z.number(), votes: z.record(z.number()) };
|
|
56
66
|
server.registerTool("ask_humans", {
|
|
57
67
|
title: "Ask verified humans",
|
|
58
68
|
description: "Send a decision to ID verified people before you act on it. Several people answer independently, each confirmed with Face ID, and you get back the consensus answer and how strongly they agreed. Use it for judgment calls where being wrong is costly: tone and intent, refunds and payments, whether two things match, moderation edge cases. Write the question so a stranger can answer it from the context alone, and never include secrets or more personal data than the decision needs. Live keys are billed per answer; test keys are free and simulated.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plures-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"mcpName": "io.github.plureslabs/plures",
|
|
5
5
|
"description": "MCP server for Plures: lets any AI agent ask ID verified people to check a decision before it acts.",
|
|
6
6
|
"keywords": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@modelcontextprotocol/sdk": "^1.32.0",
|
|
42
|
-
"plures": "^1.
|
|
42
|
+
"plures": "^1.1.0",
|
|
43
43
|
"zod": "^3.25.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|