@cachly-dev/mcp-server 0.7.1 → 0.8.1

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/dist/index.js CHANGED
@@ -47,7 +47,7 @@ import { Redis } from 'ioredis';
47
47
  const API_URL = process.env.CACHLY_API_URL ?? 'https://api.cachly.dev';
48
48
  const JWT = process.env.CACHLY_JWT ?? '';
49
49
  const EMBED_MODEL = process.env.CACHLY_EMBED_MODEL ?? '';
50
- const CURRENT_VERSION = '0.7.1';
50
+ const CURRENT_VERSION = '0.8.1';
51
51
  /**
52
52
  * ┌──────────────────────────────────────────────────────────────────────┐
53
53
  * │ EMBEDDING PROVIDER — pluggable, client-side first │
@@ -3406,7 +3406,8 @@ const TOOLS = [
3406
3406
  'The lesson is immediately searchable by any other AI using syndicate_search. ' +
3407
3407
  'This is how individual knowledge becomes collective intelligence. ' +
3408
3408
  'Call this AFTER every learn_from_attempts that is worth sharing universally ' +
3409
- '(critical bugs, deployment gotchas, architecture discoveries).',
3409
+ '(critical bugs, deployment gotchas, architecture discoveries). ' +
3410
+ 'Use scope="org" to keep the lesson private to your organisation.',
3410
3411
  inputSchema: {
3411
3412
  type: 'object',
3412
3413
  properties: {
@@ -3416,6 +3417,7 @@ const TOOLS = [
3416
3417
  what_failed: { type: 'string', description: 'What failed or was wrong — helps others avoid the same trap.' },
3417
3418
  severity: { type: 'string', enum: ['critical', 'major', 'minor'], description: 'How severe the issue was (default: minor)' },
3418
3419
  tags: { type: 'array', items: { type: 'string' }, description: 'Up to 10 keywords for better discoverability' },
3420
+ scope: { type: 'string', enum: ['public', 'org'], description: 'Visibility: "public" = global commons (default), "org" = private to your org only' },
3419
3421
  },
3420
3422
  required: ['topic', 'what_worked'],
3421
3423
  },
@@ -3432,6 +3434,7 @@ const TOOLS = [
3432
3434
  properties: {
3433
3435
  q: { type: 'string', description: 'Free-text search query (leave empty for most recent lessons)' },
3434
3436
  category: { type: 'string', description: 'Filter by category prefix: "fix", "deploy", "debug", "infra", "api", "web"' },
3437
+ scope: { type: 'string', enum: ['public', 'org'], description: '"public" = global commons (default), "org" = public + your org-private lessons' },
3435
3438
  limit: { type: 'number', description: 'Max results to return (default: 20, max: 50)' },
3436
3439
  },
3437
3440
  required: [],
@@ -3440,7 +3443,7 @@ const TOOLS = [
3440
3443
  {
3441
3444
  name: 'syndicate_stats',
3442
3445
  description: 'Show the health of the global Knowledge Commons: total lessons, total confirms, ' +
3443
- 'top categories, most-trusted lessons, and growth in the last 7 days. ' +
3446
+ 'top categories, most-trusted lessons, growth in the last 7 days, and top contributors (anonymous scores). ' +
3444
3447
  'Use for weekly reviews or to explore what the community knows.',
3445
3448
  inputSchema: {
3446
3449
  type: 'object',
@@ -3448,6 +3451,20 @@ const TOOLS = [
3448
3451
  required: [],
3449
3452
  },
3450
3453
  },
3454
+ {
3455
+ name: 'syndicate_trending',
3456
+ description: 'Show the TRENDING lessons in the global Knowledge Commons — those with the fastest confirmation velocity ' +
3457
+ 'in the last 7 days (confirm_count / age_in_days). ' +
3458
+ 'Use this at the start of a session or weekly review to see what the community is actively validating. ' +
3459
+ 'Lessons need at least 2 independent confirms to appear here.',
3460
+ inputSchema: {
3461
+ type: 'object',
3462
+ properties: {
3463
+ limit: { type: 'number', description: 'Max results (default: 10, max: 50)' },
3464
+ },
3465
+ required: [],
3466
+ },
3467
+ },
3451
3468
  ];
3452
3469
  // ── Handlers ──────────────────────────────────────────────────────────────────
3453
3470
  function formatInstance(inst) {
@@ -4990,6 +5007,19 @@ async function handleTool(name, args) {
4990
5007
  }
4991
5008
  }
4992
5009
  catch { /* non-critical */ }
5010
+ // ── 🌍 Knowledge Commons — community stats banner ───────────────────────
5011
+ // Fetch syndication stats and show a 1-liner: total lessons + confirms + weekly growth.
5012
+ // Non-fatal: never blocks session start if the API call fails.
5013
+ try {
5014
+ const commonsStats = await apiFetch('/api/v1/syndication/stats');
5015
+ if (commonsStats.total_lessons > 0) {
5016
+ lines.push(`🌍 **Commons:** ${commonsStats.total_lessons.toLocaleString()} lessons · ` +
5017
+ `${commonsStats.total_confirms.toLocaleString()} confirms · ` +
5018
+ `+${commonsStats.added_last_7_days} this week`);
5019
+ lines.push('');
5020
+ }
5021
+ }
5022
+ catch { /* non-critical — never block session start */ }
4993
5023
  return lines.join('\n');
4994
5024
  }
4995
5025
  // ── session_end ───────────────────────────────────────────────────────────
@@ -7055,32 +7085,40 @@ smart_recall(
7055
7085
  }
7056
7086
  // ── v0.7 Knowledge Syndication: syndicate ────────────────────────────────
7057
7087
  case 'syndicate': {
7058
- const { topic, outcome = 'success', what_worked, what_failed = '', severity = 'minor', tags = [] } = args;
7088
+ const { topic, outcome = 'success', what_worked, what_failed = '', severity = 'minor', tags = [], scope = 'public' } = args;
7059
7089
  if (!topic || !what_worked) {
7060
7090
  throw new McpError(ErrorCode.InvalidParams, 'topic and what_worked are required');
7061
7091
  }
7062
- const body = { topic, outcome, what_worked, what_failed, severity, tags };
7092
+ const body = { topic, outcome, what_worked, what_failed, severity, tags, scope };
7063
7093
  const res = await apiFetch('/api/v1/syndication/contribute', { method: 'POST', body: JSON.stringify(body) });
7094
+ const scopeLabel = scope === 'org' ? '🏢 org-private' : '🌐 global commons';
7095
+ const dedupNote = res.deduped
7096
+ ? `\n> ♻️ Duplicate detected — trust score incremented for the existing lesson.`
7097
+ : '';
7064
7098
  return [
7065
- `🌐 **Lesson syndicated to the global Knowledge Commons**`,
7099
+ `${scope === 'org' ? '🏢' : '🌐'} **Lesson syndicated to the ${scope === 'org' ? 'org Knowledge Commons' : 'global Knowledge Commons'}**${dedupNote}`,
7066
7100
  ``,
7067
7101
  `**ID:** \`${res.id}\``,
7068
- `**Topic:** \`${res.topic}\` · **Outcome:** ${res.outcome}`,
7102
+ `**Topic:** \`${res.topic}\` · **Outcome:** ${res.outcome} · **Scope:** ${scopeLabel}`,
7069
7103
  ``,
7070
- `Your lesson is now searchable by every AI brain in the network.`,
7071
- `When another instance confirms it works, its trust score rises automatically.`,
7104
+ scope === 'org'
7105
+ ? `This lesson is visible only within your organisation. Use \`syndicate_search(scope="org")\` to find it.`
7106
+ : `Your lesson is now searchable by every AI brain in the network.`,
7107
+ `When another instance confirms it works, its trust score rises — and so does your contributor reputation.`,
7072
7108
  ``,
7073
7109
  `**Tip:** Use \`syndicate_search(q="${topic}")\` to see all community lessons on this topic.`,
7074
7110
  ].join('\n');
7075
7111
  }
7076
7112
  // ── v0.7 Knowledge Syndication: syndicate_search ─────────────────────────
7077
7113
  case 'syndicate_search': {
7078
- const { q = '', limit = 20, category = '' } = args;
7114
+ const { q = '', limit = 20, category = '', scope = '' } = args;
7079
7115
  const params = new URLSearchParams();
7080
7116
  if (q)
7081
7117
  params.set('q', q);
7082
7118
  if (category)
7083
7119
  params.set('category', category);
7120
+ if (scope)
7121
+ params.set('scope', scope);
7084
7122
  params.set('limit', String(Math.min(Math.max(1, limit), 50)));
7085
7123
  const res = await apiFetch(`/api/v1/syndication/search?${params}`);
7086
7124
  if (!res.results || res.results.length === 0) {
@@ -7132,8 +7170,50 @@ smart_recall(
7132
7170
  lines.push(`**\`${lesson.topic}\`** ${confirmBar(lesson.confirm_count)}`, `> ${lesson.what_worked.slice(0, 120)}${lesson.what_worked.length > 120 ? '…' : ''}`, ``);
7133
7171
  }
7134
7172
  lines.push(`---`, `**Contribute:** \`syndicate(topic="...", what_worked="...")\` | **Search:** \`syndicate_search(q="your problem")\``);
7173
+ // Top contributors (anonymous scores)
7174
+ if (res.top_contributors?.length) {
7175
+ lines.push(``, `### 🏅 Top Contributors (anonymous)`);
7176
+ for (const c of res.top_contributors) {
7177
+ lines.push(`- Trust **${c.trust_score}** · ${c.lessons_count} lesson${c.lessons_count === 1 ? '' : 's'} · ${c.confirms_received} confirms received`);
7178
+ }
7179
+ }
7135
7180
  return lines.join('\n');
7136
7181
  }
7182
+ // ── v0.8 Knowledge Syndication: syndicate_trending ───────────────────────
7183
+ case 'syndicate_trending': {
7184
+ const { limit = 10 } = args;
7185
+ const params = new URLSearchParams({ limit: String(Math.min(Math.max(1, limit), 50)) });
7186
+ const res = await apiFetch(`/api/v1/syndication/trending?${params}`);
7187
+ if (!res.results || res.results.length === 0) {
7188
+ return [
7189
+ `## 📈 Trending in the Knowledge Commons`,
7190
+ ``,
7191
+ `No trending lessons yet (need at least 2 confirms in the last 7 days).`,
7192
+ ``,
7193
+ `Contribute and confirm lessons to see them trend: \`syndicate(topic="...", what_worked="...")\``,
7194
+ ].join('\n');
7195
+ }
7196
+ const outcomeIcon = (o) => o === 'success' ? '✅' : o === 'failure' ? '❌' : '⚠️';
7197
+ const severityLabel = (s) => s === 'critical' ? '🔴' : s === 'major' ? '🟡' : '🟢';
7198
+ const confirmBar = (n) => {
7199
+ const filled = Math.min(10, Math.round(n / 5));
7200
+ return '█'.repeat(filled) + '░'.repeat(10 - filled) + ` ×${n}`;
7201
+ };
7202
+ const trendBar = (score) => {
7203
+ const filled = Math.min(10, Math.round(score * 2));
7204
+ return '▲'.repeat(filled) + '△'.repeat(10 - filled) + ` ${score.toFixed(2)}/day`;
7205
+ };
7206
+ const lines = [
7207
+ `## 📈 Trending in the Knowledge Commons`,
7208
+ `*Lessons with the fastest confirmation velocity in the last 7 days*`,
7209
+ ``,
7210
+ ];
7211
+ for (const lesson of res.results) {
7212
+ lines.push(`### ${outcomeIcon(lesson.outcome)} \`${lesson.topic}\` ${severityLabel(lesson.severity)}`, `**Trend:** ${trendBar(lesson.trend_score)} | **Trust:** ${confirmBar(lesson.confirm_count)}`, lesson.what_worked ? `**What worked:** ${lesson.what_worked.slice(0, 200)}${lesson.what_worked.length > 200 ? '…' : ''}` : '', `*ID: \`${lesson.id}\` · ${new Date(lesson.created_at).toLocaleDateString('de-DE')}*`, ``);
7213
+ }
7214
+ lines.push(`---`, `**Confirm** (if this helped you): \`syndicate(topic="${res.results[0]?.topic ?? '...'}", what_worked="...")\` → auto-deduped, trust +1`, `**All trending:** \`syndicate_trending(limit=50)\` | **Search:** \`syndicate_search(q="...")\``);
7215
+ return lines.filter(l => l !== '').join('\n');
7216
+ }
7137
7217
  default:
7138
7218
  return null;
7139
7219
  }