@cachly-dev/mcp-server 0.10.72 → 0.10.74
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 +1 -1
- package/dist/src/handlers/brain.js +28 -5
- package/dist/src/handlers/brain.js.map +1 -1
- package/dist/src/handlers/team.d.ts +10 -0
- package/dist/src/handlers/team.js +100 -0
- package/dist/src/handlers/team.js.map +1 -1
- package/dist/src/index.js +183 -58
- package/dist/src/index.js.map +1 -1
- package/dist/src/tools.d.ts +51 -0
- package/dist/src/tools.js +37 -0
- package/dist/src/tools.js.map +1 -1
- package/package.json +3 -2
- package/server.json +3 -3
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
<img src="https://img.shields.io/badge/GDPR-EU%20servers-green" alt="GDPR: EU servers" />
|
|
23
23
|
</a>
|
|
24
24
|
|
|
25
|
-
<img src="https://img.shields.io/badge/
|
|
25
|
+
<img src="https://img.shields.io/badge/107%20MCP%20tools-violet" alt="107 MCP tools" />
|
|
26
26
|
|
|
27
27
|
<img src="https://img.shields.io/badge/License-Apache--2.0-yellow" alt="License: Apache-2.0" />
|
|
28
28
|
</p>
|
|
@@ -2,13 +2,21 @@ import { execSync } from 'node:child_process';
|
|
|
2
2
|
import { calculateConfidence, confidenceBadge, STRUCTURED_TEMPLATES, CONFIDENCE_WARN_VALUE, CONFIDENCE_STALE_VALUE, CONFIDENCE_WARN_DAYS } from '../confidence.js';
|
|
3
3
|
import { ckgSlug, extractProblemConcept, ckgUpsertNode, ckgUpdateEdge, ckgUpsertPersonNode, ckgUpsertFileNode, ckgRecordCollaboration, ckgUpsertServiceNode } from '../ckg.js';
|
|
4
4
|
import { safeJsonParse, scanKeys } from '../utils.js';
|
|
5
|
-
import { getRole, ROLE_BADGE } from './team.js';
|
|
5
|
+
import { getRole, ROLE_BADGE, getScopes, lessonVisibleToScope } from './team.js';
|
|
6
6
|
import { keywordSearch, tokenize, splitMultiQuery, levenshtein, indexVocab as _indexVocab } from '../search.js';
|
|
7
7
|
import { rerankByQuality } from '../rerank.js';
|
|
8
8
|
import { computeEmbedding, hasEmbedProvider } from '../embeddings.js';
|
|
9
9
|
// ── Changelog (shown once per version in session_start) ──────────────────────
|
|
10
|
-
const MCP_VERSION = '0.10.
|
|
10
|
+
const MCP_VERSION = '0.10.73';
|
|
11
11
|
const WHATS_NEW = {
|
|
12
|
+
'0.10.73': [
|
|
13
|
+
`🔐 **Team visibility scopes + one-command init + external bench**`,
|
|
14
|
+
` 🆕 \`team_grant_scope\` / \`team_scopes\` — scope lessons to a sub-team (group="security"); only members + admins recall them`,
|
|
15
|
+
` 🏷️ \`learn_from_attempts(group="...")\` — orthogonal to private; smart_recall enforces it per requester`,
|
|
16
|
+
` ⚡ \`init\` is now zero-arg + idempotent (reuses saved creds, only writes what changed, <60s)`,
|
|
17
|
+
` 📦 \`npm run bench:external\` — prove recall-lift on a third-party-labeled corpus (portable JSON format)`,
|
|
18
|
+
` 📊 107 MCP tools`,
|
|
19
|
+
],
|
|
12
20
|
'0.10.72': [
|
|
13
21
|
`👑 **Role model — admin · reviewer · contributor · viewer (Phase 3)**`,
|
|
14
22
|
` 🆕 \`team_assign_role\` — establish governance; first call bootstraps, admins manage the rest`,
|
|
@@ -114,7 +122,7 @@ export const BRAIN_TOOL_NAMES = new Set([
|
|
|
114
122
|
export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
115
123
|
switch (name) {
|
|
116
124
|
case 'learn_from_attempts': {
|
|
117
|
-
const { instance_id, topic, outcome, what_worked, what_failed = '', context: ctx = '', severity = 'major', file_paths = [], commands = [], tags = [], depends_on = [], author = '', visibility = 'team', service = '', service_kind = 'service', } = args;
|
|
125
|
+
const { instance_id, topic, outcome, what_worked, what_failed = '', context: ctx = '', severity = 'major', file_paths = [], commands = [], tags = [], depends_on = [], author = '', visibility = 'team', service = '', service_kind = 'service', group = '', } = args;
|
|
118
126
|
const redis = await getConnection(instance_id);
|
|
119
127
|
const ts = new Date().toISOString();
|
|
120
128
|
// ── Structured template hints ──────────────────────────────────────────
|
|
@@ -233,6 +241,7 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
233
241
|
depends_on,
|
|
234
242
|
...(author ? { author } : {}),
|
|
235
243
|
...(service ? { service } : {}),
|
|
244
|
+
...(group ? { group: String(group).toLowerCase().trim() } : {}),
|
|
236
245
|
visibility,
|
|
237
246
|
recall_count: recallCount,
|
|
238
247
|
ts,
|
|
@@ -585,6 +594,15 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
585
594
|
? rawContextFiles.filter((f) => typeof f === 'string')
|
|
586
595
|
: [];
|
|
587
596
|
const redis = await getConnection(instance_id);
|
|
597
|
+
// Team-level visibility scopes: resolve the requester's group memberships +
|
|
598
|
+
// admin status once, so group-scoped lessons only surface for members/admins.
|
|
599
|
+
let requesterScopes = new Set();
|
|
600
|
+
let requesterIsAdmin = false;
|
|
601
|
+
if (requester) {
|
|
602
|
+
requesterScopes = await getScopes(redis, instance_id, requester).catch(() => new Set());
|
|
603
|
+
const reqRole = await getRole(redis, instance_id, requester).catch(() => null);
|
|
604
|
+
requesterIsAdmin = reqRole === 'admin';
|
|
605
|
+
}
|
|
588
606
|
// ── Layer 1: Keyword search across ALL brain data (always works, no embedding) ──
|
|
589
607
|
const rawMatches = await keywordSearch(redis, ['cachly:ctx:*', 'cachly:lesson:best:*', 'cachly:idx:*'], query, 10);
|
|
590
608
|
// ── Layer 1.5: Quality-aware rerank — proven success lessons outrank
|
|
@@ -758,13 +776,18 @@ export async function handleBrainTool(name, args, getConnection, apiFetch) {
|
|
|
758
776
|
}
|
|
759
777
|
}
|
|
760
778
|
}
|
|
761
|
-
// Filter private lessons
|
|
779
|
+
// Filter private lessons (recall_best_solution only) + enforce team scopes:
|
|
780
|
+
// group-scoped lessons surface only for members of that group (admins see all).
|
|
762
781
|
const hybridResults = [...hybridMap.values()]
|
|
763
782
|
.filter(r => {
|
|
764
783
|
if (!r.key.startsWith('cachly:lesson:best:'))
|
|
765
784
|
return true;
|
|
766
785
|
const ld = safeJsonParse(r.content, {});
|
|
767
|
-
|
|
786
|
+
if (ld.visibility === 'private')
|
|
787
|
+
return false;
|
|
788
|
+
if (!lessonVisibleToScope(ld.group, requesterScopes, requesterIsAdmin))
|
|
789
|
+
return false;
|
|
790
|
+
return true;
|
|
768
791
|
})
|
|
769
792
|
.sort((a, b) => b.hybridScore - a.hybridScore);
|
|
770
793
|
// ── Build output ──────────────────────────────────────────────────────────
|