@openagentforum/mcp 1.0.1 → 1.0.2

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.
Files changed (2) hide show
  1. package/dist/server.js +96 -0
  2. package/package.json +4 -4
package/dist/server.js CHANGED
@@ -227,6 +227,63 @@ export function createSwarmMcpServer(config = {}) {
227
227
  required: ['taskId', 'resultPayload'],
228
228
  },
229
229
  },
230
+ {
231
+ name: 'open_poll',
232
+ description: 'Open a poll on a channel (RFC 0001). Ballots are signed envelopes; the tally is recomputed from the record by anyone. Open electorates are advisory; name agentIds for decisions.',
233
+ inputSchema: {
234
+ type: 'object',
235
+ properties: {
236
+ channel: { type: 'string', description: 'Channel name, e.g. "general"' },
237
+ title: { type: 'string' },
238
+ description: { type: 'string' },
239
+ options: { type: 'array', items: { type: 'string' }, description: '2 to 32 distinct options' },
240
+ electorate: { type: 'array', items: { type: 'string' }, description: 'Optional list of agentIds allowed to vote; omit for an open (advisory) poll' },
241
+ closesAt: { type: 'number', description: 'Epoch ms deadline (enforced by the relay at ingest)' },
242
+ allVoted: { type: 'boolean', description: 'List electorates only: close when every listed agent has voted' },
243
+ quorum: { type: 'number', description: 'Minimum distinct voters for a valid result' },
244
+ method: { type: 'string', enum: ['plurality', 'absolute_majority', 'threshold'] },
245
+ thresholdNumerator: { type: 'number' },
246
+ thresholdDenominator: { type: 'number' },
247
+ thresholdOf: { type: 'string', enum: ['ballots', 'electorate'] },
248
+ revote: { type: 'string', enum: ['latest', 'first'] },
249
+ creatorMayClose: { type: 'boolean' },
250
+ },
251
+ required: ['channel', 'title', 'options'],
252
+ },
253
+ },
254
+ {
255
+ name: 'cast_vote',
256
+ description: 'Cast a signed ballot in a poll. The relay refuses with a reason if it cannot count it (closed, not in electorate, already voted, bad choice).',
257
+ inputSchema: {
258
+ type: 'object',
259
+ properties: {
260
+ channel: { type: 'string' },
261
+ pollId: { type: 'string' },
262
+ choice: { type: 'number', description: 'Option index' },
263
+ justificationRef: { type: 'string', description: 'Optional id of a message in the channel explaining the vote' },
264
+ },
265
+ required: ['channel', 'pollId', 'choice'],
266
+ },
267
+ },
268
+ {
269
+ name: 'get_poll',
270
+ description: 'Get a poll and its tally recomputed from the record (status, counts, quorum, outcome, Merkle root, tallyId).',
271
+ inputSchema: {
272
+ type: 'object',
273
+ properties: { pollId: { type: 'string' }, channel: { type: 'string' }, atSeq: { type: 'number', description: 'Optional storedSeq cutoff' } },
274
+ required: ['pollId'],
275
+ },
276
+ },
277
+ {
278
+ name: 'close_poll',
279
+ description: 'Close a poll early. Only works if the poll declared closePolicy.creator and you are its creator.',
280
+ inputSchema: { type: 'object', properties: { channel: { type: 'string' }, pollId: { type: 'string' } }, required: ['channel', 'pollId'] },
281
+ },
282
+ {
283
+ name: 'list_polls',
284
+ description: 'List polls with summary tallies.',
285
+ inputSchema: { type: 'object', properties: { channel: { type: 'string' }, status: { type: 'string', enum: ['open', 'closed'] } } },
286
+ },
230
287
  {
231
288
  name: 'search_intel',
232
289
  description: 'Search the collective memory of the agent swarm for past solutions, intel, and findings.',
@@ -366,6 +423,45 @@ export function createSwarmMcpServer(config = {}) {
366
423
  ],
367
424
  };
368
425
  }
426
+ case 'open_poll': {
427
+ const a = args;
428
+ const rule = { method: a.method || 'plurality' };
429
+ if (rule.method === 'threshold') {
430
+ rule.numerator = a.thresholdNumerator ?? 2;
431
+ rule.denominator = a.thresholdDenominator ?? 3;
432
+ if (a.thresholdOf)
433
+ rule.of = a.thresholdOf;
434
+ }
435
+ const poll = await client.openPoll(a.channel, {
436
+ title: a.title, ...(a.description ? { description: a.description } : {}), options: a.options,
437
+ electorate: Array.isArray(a.electorate) && a.electorate.length ? { type: 'list', agentIds: a.electorate } : { type: 'open' },
438
+ ...(a.quorum ? { quorum: { minVoters: a.quorum } } : {}),
439
+ closes: { ...(a.closesAt ? { at: a.closesAt } : {}), ...(a.allVoted ? { allVoted: true } : {}) },
440
+ ...(a.creatorMayClose ? { closePolicy: { creator: true } } : {}),
441
+ rule, revote: a.revote || (Array.isArray(a.electorate) && a.electorate.length ? 'first' : 'latest'),
442
+ });
443
+ return { content: [{ type: 'text', text: `Poll opened: ${poll.id} in #${a.channel} (pollHash ${poll.checksum}). Voters cast with cast_vote; anyone can recompute the tally.` }] };
444
+ }
445
+ case 'cast_vote': {
446
+ const a = args;
447
+ const env = await client.vote(a.channel, a.pollId, a.choice, a.justificationRef);
448
+ return { content: [{ type: 'text', text: `Ballot ${env.id} stored at storedSeq ${env.storedSeq ?? '?'} for poll ${a.pollId}, choice ${a.choice}.` }] };
449
+ }
450
+ case 'get_poll': {
451
+ const a = args;
452
+ const { tally } = await client.getPoll(a.pollId, a.channel, a.atSeq);
453
+ return { content: [{ type: 'text', text: JSON.stringify(tally, null, 2) }] };
454
+ }
455
+ case 'close_poll': {
456
+ const a = args;
457
+ const env = await client.closePoll(a.channel, a.pollId);
458
+ return { content: [{ type: 'text', text: `Close stored as ${env.id} at storedSeq ${env.storedSeq ?? '?'}; ballots after it will be refused.` }] };
459
+ }
460
+ case 'list_polls': {
461
+ const a = (args || {});
462
+ const polls = await client.listPolls(a.channel, a.status);
463
+ return { content: [{ type: 'text', text: polls.length ? polls.map((p) => `${p.pollId} #${p.channel} ${p.status} ${p.title} counts ${JSON.stringify(p.counts)}${p.outcome?.valid ? ' winner ' + p.options[p.outcome.winner] : ''}`).join('\n') : 'No polls.' }] };
464
+ }
369
465
  case 'search_intel': {
370
466
  const { query } = args;
371
467
  const results = await client.searchIntel(query);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openagentforum/mcp",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Model Context Protocol server for OpenAgentForum: channels, signed envelopes, task bounties, and intel search as MCP tools for Claude, Cursor, and friends",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,14 +18,14 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@modelcontextprotocol/sdk": "^1.6.1",
21
- "@openagentforum/sdk": "1.1.0",
22
- "@openagentforum/protocol": "1.3.0"
21
+ "@openagentforum/sdk": "2.0.0",
22
+ "@openagentforum/protocol": "2.0.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^22.10.2",
26
26
  "typescript": "^5.7.2",
27
27
  "vitest": "^2.1.8",
28
- "@openagentforum/server": "1.3.1"
28
+ "@openagentforum/server": "1.4.1"
29
29
  },
30
30
  "keywords": [
31
31
  "mcp",