plures-mcp 1.0.1 → 1.2.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 CHANGED
@@ -31,6 +31,8 @@ A `pk_test_` key answers instantly with a simulated result (always the first opt
31
31
  - `options` (required): 2 to 8 possible answers
32
32
  - `context`: the material to judge, as plain text
33
33
  - `image_url`, `instructions`
34
+ - `audio_url` or `video_url` with `media_seconds`: a clip up to 3 minutes that people play through before answering
35
+ - `sensitive`: disturbing content, shown only to people who opted in
34
36
  - `verifiers`: 1, 3, 5, 7 or 9 (default 3)
35
37
  - `wait_seconds`: how long to wait for the answer, 0 to 240 (default 45). If people haven't answered by then, the tool returns a `task_id` to check later.
36
38
 
@@ -38,7 +40,7 @@ A `pk_test_` key answers instantly with a simulated result (always the first opt
38
40
 
39
41
  **`cancel_human_task`**: cancel a task that no longer matters.
40
42
 
41
- Each result tells the agent the answer, how many people agreed and whether it is safe to act on: unanimous answers are, anything less should be escalated.
43
+ 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
44
 
43
45
  ## When agents should use it
44
46
 
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ 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.0.1";
8
+ var VERSION = "1.2.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;
@@ -20,19 +20,23 @@ ${text}` : text;
20
20
  };
21
21
  var describe = (t) => {
22
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.`;
23
25
  if (t.status === "pending")
24
26
  return `Still waiting for people to answer. Task ${t.id}. Call get_human_answer with this task_id later.`;
25
27
  if (t.status === "canceled") return `Task ${t.id} was canceled.`;
26
28
  const r = t.result;
27
29
  const votes = r ? Object.entries(r.votes).map(([k, v]) => `${label(k)}: ${v}`).join(", ") : "none";
28
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}"` : ""}`;
29
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.`;
30
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.`;
31
35
  return `Verified people answered: ${label(r.answer)}. ${Math.round(r.agreement * r.answers)} of ${r.answers} agreed. ${strength} Votes: ${votes}.`;
32
36
  };
33
37
  var out = (t) => ({
34
38
  content: [{ type: "text", text: fmt(t) }],
35
- 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 ?? {} }
36
40
  });
37
41
  var fail = (e) => ({
38
42
  isError: true,
@@ -58,7 +62,7 @@ var progressFor = (extra, total) => {
58
62
  if (token === void 0) return void 0;
59
63
  return (n) => extra.sendNotification({ method: "notifications/progress", params: { progressToken: token, progress: n, total, message: "Waiting for verified people to answer" } });
60
64
  };
61
- 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()) };
62
66
  server.registerTool("ask_humans", {
63
67
  title: "Ask verified humans",
64
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.",
@@ -67,6 +71,10 @@ server.registerTool("ask_humans", {
67
71
  options: z.array(z.string().min(1).max(200)).min(2).max(8).describe('2 to 8 possible answers, e.g. ["Yes", "No"]'),
68
72
  context: z.string().max(2e4).optional().describe("The material to judge, as plain text: the message, the invoice lines, the listing"),
69
73
  image_url: z.string().url().optional().describe("An https image shown with the question"),
74
+ audio_url: z.string().url().optional().describe("An https recording people listen to in full before answering, such as a call your voice agent handled. Up to 3 minutes; needs media_seconds"),
75
+ video_url: z.string().url().optional().describe("An https video (MP4) people watch in full before answering. Up to 3 minutes; needs media_seconds. Not with audio_url"),
76
+ media_seconds: z.number().int().min(1).max(180).optional().describe("Length of the audio or video in seconds. Required with a clip"),
77
+ sensitive: z.boolean().optional().describe("Set true for violent, graphic or disturbing content, so only people who opted in see it, behind a warning"),
70
78
  instructions: z.string().max(2e3).optional().describe("Short guidance shown under the question"),
71
79
  verifiers: z.union([z.literal(1), z.literal(3), z.literal(5), z.literal(7), z.literal(9)]).optional().describe("How many people answer. Default 3; use 5 for higher stakes"),
72
80
  wait_seconds: z.number().int().min(0).max(MAX_WAIT).optional().describe(`How long to wait for the answer before returning a task_id to check later. Default 45, up to ${MAX_WAIT}`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plures-mcp",
3
- "version": "1.0.1",
3
+ "version": "1.2.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.0.0",
42
+ "plures": "^1.2.0",
43
43
  "zod": "^3.25.0"
44
44
  },
45
45
  "devDependencies": {