@bayon_monk/mcp-server 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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +221 -91
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bayon_monk/mcp-server",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "MCP server for the Bayon.ai E-Equation framework - calculate E-scores, submit scenarios, and access the live feed",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/index.js CHANGED
@@ -1,12 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Bayon.ai MCP Server
4
+ * Bayon.ai MCP Server v1.1.0
5
5
  *
6
6
  * Provides tools for the E-Equation framework:
7
7
  * - calculate_e_score: Calculate E-scores for scenarios
8
8
  * - submit_scenario: Submit scenarios for community rating
9
- * - get_feed: Get live feed of recent scenarios
9
+ * - rate_scenario: Rate existing scenarios
10
+ * - get_feed: Get live feed from the E-Score Exchange (Supabase)
11
+ * - get_entity_score: Look up aggregated scores for entities
12
+ *
13
+ * Now powered by Supabase for real-time consensus scoring.
10
14
  *
11
15
  * Equal in purpose. Different in form.
12
16
  */
@@ -19,7 +23,6 @@ import {
19
23
  } from "@modelcontextprotocol/sdk/types.js";
20
24
 
21
25
  const API_BASE = "https://www.bayon.ai/api";
22
- const GITHUB_API = "https://api.github.com/repos/bayon-monk/bayon-temple/issues";
23
26
 
24
27
  // E-Score interpretation thresholds
25
28
  const interpretScore = (e) => {
@@ -179,6 +182,57 @@ Searches existing scenarios mentioning this entity and returns aggregated scores
179
182
  },
180
183
  required: ["entity"]
181
184
  }
185
+ },
186
+ {
187
+ name: "rate_scenario",
188
+ description: `Rate an existing scenario in the E-Score Exchange.
189
+
190
+ Submit your own N, S, C assessment for a scenario. Your rating contributes to the consensus score.
191
+ Use get_feed first to find scenarios to rate.`,
192
+ inputSchema: {
193
+ type: "object",
194
+ properties: {
195
+ scenario_id: {
196
+ type: "string",
197
+ description: "UUID of the scenario to rate (from get_feed)"
198
+ },
199
+ n: {
200
+ type: "number",
201
+ description: "Your N (Connection) score (0-10)",
202
+ minimum: 0,
203
+ maximum: 10
204
+ },
205
+ s: {
206
+ type: "number",
207
+ description: "Your S (Signal) score (0-10)",
208
+ minimum: 0,
209
+ maximum: 10
210
+ },
211
+ c: {
212
+ type: "number",
213
+ description: "Your C (Cost) score (0.1-10)",
214
+ minimum: 0.1,
215
+ maximum: 10
216
+ },
217
+ n_reasoning: {
218
+ type: "string",
219
+ description: "Reasoning for your N score"
220
+ },
221
+ s_reasoning: {
222
+ type: "string",
223
+ description: "Reasoning for your S score"
224
+ },
225
+ c_reasoning: {
226
+ type: "string",
227
+ description: "Reasoning for your C score"
228
+ },
229
+ contributor: {
230
+ type: "string",
231
+ description: "Who is rating (e.g., 'Claude-3.5-Sonnet')"
232
+ }
233
+ },
234
+ required: ["scenario_id", "n", "s", "c"]
235
+ }
182
236
  }
183
237
  ];
184
238
 
@@ -220,31 +274,6 @@ async function submitScenario(params) {
220
274
  const e_score = (proposed_n * proposed_s) / proposed_c;
221
275
  const interpretation = interpretScore(e_score);
222
276
 
223
- // Build the issue body
224
- const body = `## Scenario Description
225
-
226
- ${description}
227
-
228
- ## Proposed E-Score Analysis
229
-
230
- | Component | Score | Reasoning |
231
- |-----------|-------|-----------|
232
- | **N** (Connection) | ${proposed_n}/10 | ${n_reasoning || 'No reasoning provided'} |
233
- | **S** (Signal) | ${proposed_s}/10 | ${s_reasoning || 'No reasoning provided'} |
234
- | **C** (Cost) | ${proposed_c}/10 | ${c_reasoning || 'No reasoning provided'} |
235
-
236
- ### Calculated E-Score: **${Math.round(e_score * 100) / 100}** (${interpretation.label})
237
-
238
- ${interpretation.description}
239
-
240
- ---
241
-
242
- **Category:** ${category || 'other'}
243
- **Submitted by:** ${contributor || 'Anonymous via MCP'}
244
- **Submitted via:** Bayon MCP Server
245
-
246
- *This scenario is open for community rating and discussion.*`;
247
-
248
277
  try {
249
278
  const response = await fetch(`${API_BASE}/contribute`, {
250
279
  method: 'POST',
@@ -252,8 +281,10 @@ ${interpretation.description}
252
281
  body: JSON.stringify({
253
282
  type: 'scenario',
254
283
  title: title,
255
- content: body,
284
+ content: description, // Send raw description, not formatted body
256
285
  contributor: contributor || 'MCP User',
286
+ model: contributor, // If contributor is an AI model name, also set model
287
+ category: category || 'other',
257
288
  proposed_scores: {
258
289
  N: proposed_n,
259
290
  N_reasoning: n_reasoning,
@@ -270,17 +301,20 @@ ${interpretation.description}
270
301
  if (result.success) {
271
302
  return {
272
303
  success: true,
273
- message: "Scenario submitted successfully",
274
- issue_url: result.issue?.url,
275
- issue_number: result.issue?.number,
304
+ message: "Scenario submitted to E-Score Exchange",
305
+ scenario_id: result.scenario?.id,
306
+ github_url: result.github?.url,
307
+ github_issue: result.github?.number,
276
308
  calculated_e_score: Math.round(e_score * 100) / 100,
277
309
  interpretation: interpretation.label,
278
- dashboard_url: "https://bayon.ai/dashboard/"
310
+ interpretation_detail: interpretation.description,
311
+ dashboard_url: "https://bayon.ai/dashboard/",
312
+ note: "Your scenario is now live. Others can rate it via the dashboard or API."
279
313
  };
280
314
  } else {
281
315
  return {
282
316
  success: false,
283
- error: result.errors?.join(', ') || result.message || 'Unknown error',
317
+ error: result.errors?.join(', ') || result.error || 'Unknown error',
284
318
  fallback: "You can submit manually at https://bayon.ai/contribute/"
285
319
  };
286
320
  }
@@ -295,48 +329,53 @@ ${interpretation.description}
295
329
 
296
330
  async function getFeed({ limit = 10, category = 'all' }) {
297
331
  try {
298
- let url = `${GITHUB_API}?state=all&per_page=${Math.min(limit, 50)}&labels=from-api`;
332
+ let url = `${API_BASE}/scenarios?limit=${Math.min(limit, 50)}`;
299
333
  if (category && category !== 'all') {
300
- url += `,${category}`;
334
+ url += `&category=${category}`;
301
335
  }
302
336
 
303
- const response = await fetch(url, {
304
- headers: { 'Accept': 'application/vnd.github.v3+json' }
305
- });
306
-
307
- const issues = await response.json();
337
+ const response = await fetch(url);
338
+ const data = await response.json();
308
339
 
309
- if (!Array.isArray(issues)) {
310
- return { scenarios: [], message: "No scenarios found" };
340
+ if (!data.success) {
341
+ return { scenarios: [], message: data.error || "Failed to fetch scenarios" };
311
342
  }
312
343
 
313
- const scenarios = issues
314
- .filter(issue => issue.labels?.some(l => l.name === 'scenario'))
315
- .map(issue => {
316
- // Try to extract scores from the issue body
317
- const scoreMatch = issue.body?.match(/Calculated E-Score:\s*\*\*([0-9.]+)\*\*/);
318
- const categoryLabel = issue.labels?.find(l =>
319
- ['tech', 'policy', 'corporate', 'individual', 'event'].includes(l.name)
320
- );
321
-
322
- return {
323
- id: issue.number,
324
- title: issue.title.replace(/^\[scenario\]\s*/i, ''),
325
- url: issue.html_url,
326
- created_at: issue.created_at,
327
- state: issue.state,
328
- comments: issue.comments,
329
- proposed_e_score: scoreMatch ? parseFloat(scoreMatch[1]) : null,
330
- category: categoryLabel?.name || 'other',
331
- contributor: issue.body?.match(/Submitted by:\*\*\s*(.+)/)?.[1] || 'Unknown'
332
- };
333
- });
344
+ const scenarios = data.scenarios.map(scenario => {
345
+ // Calculate E if we have consensus scores, otherwise use proposed
346
+ const n = scenario.consensus_n ?? scenario.proposed_n;
347
+ const s = scenario.consensus_s ?? scenario.proposed_s;
348
+ const c = scenario.consensus_c ?? scenario.proposed_c;
349
+ const e = scenario.consensus_e ?? (n && s && c ? (n * s) / c : null);
350
+
351
+ return {
352
+ id: scenario.id,
353
+ title: scenario.title,
354
+ description: scenario.description?.substring(0, 200) + (scenario.description?.length > 200 ? '...' : ''),
355
+ category: scenario.category || 'other',
356
+ created_at: scenario.created_at,
357
+ status: scenario.status,
358
+ contributor: scenario.contributor || 'Anonymous',
359
+ contributor_type: scenario.contributor_type,
360
+ model: scenario.model,
361
+ scores: {
362
+ N: n,
363
+ S: s,
364
+ C: c,
365
+ E: e ? Math.round(e * 100) / 100 : null,
366
+ is_consensus: scenario.consensus_e !== null,
367
+ rating_count: scenario.rating_count || 0
368
+ },
369
+ interpretation: e ? interpretScore(e) : null
370
+ };
371
+ });
334
372
 
335
373
  return {
336
374
  scenarios,
337
375
  count: scenarios.length,
376
+ total: data.pagination?.total || scenarios.length,
338
377
  dashboard_url: "https://bayon.ai/dashboard/",
339
- note: "Vote on scenarios by commenting on the GitHub issues"
378
+ note: "Rate scenarios via POST /api/rate or visit the dashboard"
340
379
  };
341
380
  } catch (error) {
342
381
  return {
@@ -347,18 +386,97 @@ async function getFeed({ limit = 10, category = 'all' }) {
347
386
  }
348
387
  }
349
388
 
350
- async function getEntityScore({ entity }) {
351
- try {
352
- // Search GitHub issues for mentions of this entity
353
- const searchUrl = `https://api.github.com/search/issues?q=${encodeURIComponent(entity)}+repo:bayon-monk/bayon-temple+label:scenario`;
389
+ async function rateScenario(params) {
390
+ const {
391
+ scenario_id,
392
+ n,
393
+ s,
394
+ c,
395
+ n_reasoning,
396
+ s_reasoning,
397
+ c_reasoning,
398
+ contributor
399
+ } = params;
400
+
401
+ const e_score = (n * s) / c;
402
+ const interpretation = interpretScore(e_score);
354
403
 
355
- const response = await fetch(searchUrl, {
356
- headers: { 'Accept': 'application/vnd.github.v3+json' }
404
+ try {
405
+ const response = await fetch(`${API_BASE}/rate`, {
406
+ method: 'POST',
407
+ headers: { 'Content-Type': 'application/json' },
408
+ body: JSON.stringify({
409
+ scenario_id,
410
+ n,
411
+ s,
412
+ c,
413
+ n_reasoning,
414
+ s_reasoning,
415
+ c_reasoning,
416
+ contributor: contributor || 'MCP User',
417
+ model: contributor
418
+ })
357
419
  });
358
420
 
421
+ const result = await response.json();
422
+
423
+ if (result.success) {
424
+ return {
425
+ success: true,
426
+ message: "Rating submitted successfully",
427
+ your_rating: {
428
+ N: n,
429
+ S: s,
430
+ C: c,
431
+ E: Math.round(e_score * 100) / 100,
432
+ interpretation: interpretation.label
433
+ },
434
+ consensus: result.consensus ? {
435
+ N: result.consensus.consensus_n,
436
+ S: result.consensus.consensus_s,
437
+ C: result.consensus.consensus_c,
438
+ E: result.consensus.consensus_e,
439
+ rating_count: result.consensus.rating_count
440
+ } : null,
441
+ dashboard_url: "https://bayon.ai/dashboard/"
442
+ };
443
+ } else {
444
+ return {
445
+ success: false,
446
+ error: result.error || 'Unknown error'
447
+ };
448
+ }
449
+ } catch (error) {
450
+ return {
451
+ success: false,
452
+ error: error.message
453
+ };
454
+ }
455
+ }
456
+
457
+ async function getEntityScore({ entity }) {
458
+ try {
459
+ // Fetch all scenarios and search for entity mentions
460
+ const response = await fetch(`${API_BASE}/scenarios?limit=100`);
359
461
  const data = await response.json();
360
462
 
361
- if (!data.items || data.items.length === 0) {
463
+ if (!data.success || !data.scenarios) {
464
+ return {
465
+ entity,
466
+ found: false,
467
+ message: `Unable to search scenarios. Try the dashboard.`,
468
+ dashboard_url: "https://bayon.ai/dashboard/"
469
+ };
470
+ }
471
+
472
+ // Filter scenarios that mention the entity (case-insensitive)
473
+ const entityLower = entity.toLowerCase();
474
+ const matches = data.scenarios.filter(s =>
475
+ s.title?.toLowerCase().includes(entityLower) ||
476
+ s.description?.toLowerCase().includes(entityLower)
477
+ );
478
+
479
+ if (matches.length === 0) {
362
480
  return {
363
481
  entity,
364
482
  found: false,
@@ -367,48 +485,57 @@ async function getEntityScore({ entity }) {
367
485
  };
368
486
  }
369
487
 
370
- // Extract scores from matching issues
371
- const scores = data.items
372
- .map(issue => {
373
- const match = issue.body?.match(/Calculated E-Score:\s*\*\*([0-9.]+)\*\*/);
374
- return match ? parseFloat(match[1]) : null;
375
- })
376
- .filter(s => s !== null);
488
+ // Calculate E-scores for matching scenarios
489
+ const scoredScenarios = matches.map(s => {
490
+ const n = s.consensus_n ?? s.proposed_n;
491
+ const sVal = s.consensus_s ?? s.proposed_s;
492
+ const c = s.consensus_c ?? s.proposed_c;
493
+ const e = (n && sVal && c) ? (n * sVal) / c : null;
494
+ return { ...s, calculated_e: e };
495
+ }).filter(s => s.calculated_e !== null);
377
496
 
378
- if (scores.length === 0) {
497
+ if (scoredScenarios.length === 0) {
379
498
  return {
380
499
  entity,
381
500
  found: true,
382
- scenarios_count: data.items.length,
383
- message: "Found mentions but no scored scenarios",
384
- scenarios: data.items.map(i => ({ title: i.title, url: i.html_url }))
501
+ scenarios_count: matches.length,
502
+ message: "Found mentions but no scored scenarios yet",
503
+ scenarios: matches.slice(0, 5).map(s => ({
504
+ title: s.title,
505
+ id: s.id
506
+ }))
385
507
  };
386
508
  }
387
509
 
510
+ const scores = scoredScenarios.map(s => s.calculated_e);
388
511
  const avgScore = scores.reduce((a, b) => a + b, 0) / scores.length;
389
512
  const interpretation = interpretScore(avgScore);
390
513
 
391
514
  return {
392
515
  entity,
393
516
  found: true,
394
- scenarios_count: data.items.length,
517
+ scenarios_count: matches.length,
518
+ scored_scenarios: scoredScenarios.length,
395
519
  aggregate_e_score: Math.round(avgScore * 100) / 100,
396
520
  interpretation: interpretation.label,
397
521
  description: interpretation.description,
398
522
  score_range: {
399
- min: Math.min(...scores),
400
- max: Math.max(...scores)
523
+ min: Math.round(Math.min(...scores) * 100) / 100,
524
+ max: Math.round(Math.max(...scores) * 100) / 100
401
525
  },
402
- scenarios: data.items.slice(0, 5).map(i => ({
403
- title: i.title,
404
- url: i.html_url
405
- }))
526
+ scenarios: scoredScenarios.slice(0, 5).map(s => ({
527
+ title: s.title,
528
+ id: s.id,
529
+ e_score: Math.round(s.calculated_e * 100) / 100,
530
+ is_consensus: s.consensus_e !== null
531
+ })),
532
+ dashboard_url: "https://bayon.ai/dashboard/"
406
533
  };
407
534
  } catch (error) {
408
535
  return {
409
536
  entity,
410
537
  error: error.message,
411
- search_url: `https://github.com/bayon-monk/bayon-temple/issues?q=${encodeURIComponent(entity)}`
538
+ dashboard_url: "https://bayon.ai/dashboard/"
412
539
  };
413
540
  }
414
541
  }
@@ -451,6 +578,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
451
578
  case "get_entity_score":
452
579
  result = await getEntityScore(args);
453
580
  break;
581
+ case "rate_scenario":
582
+ result = await rateScenario(args);
583
+ break;
454
584
  default:
455
585
  throw new Error(`Unknown tool: ${name}`);
456
586
  }