frinus-mcp 3.6.2 → 3.6.4
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 +1 -1
- package/dist/tools/definitions.js +22 -8
- package/dist/tools/handlers.js +59 -19
- package/manifest.json +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -9,16 +9,19 @@ export const TOOLS = [
|
|
|
9
9
|
Use this to record important information, learnings, decisions, or procedures
|
|
10
10
|
that should be remembered for future reference.
|
|
11
11
|
|
|
12
|
-
The backend classifies the memory by content at write time and
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
The backend classifies the memory by content at write time and proposes which
|
|
13
|
+
universe/context node it belongs to. When the tenant has auto-classification
|
|
14
|
+
enabled and the proposal's confidence meets the tenant threshold (the platform
|
|
15
|
+
target is 0.60), the backend ANCHORS the memory on that node automatically — it
|
|
16
|
+
is moved without review. Below the threshold (or with auto-classification off)
|
|
17
|
+
the proposal stays a PENDING suggestion: review it with
|
|
18
|
+
memory_classification_pending and apply with memory_classification_approve.
|
|
16
19
|
|
|
17
20
|
Passing universe_id or context_id anchors the memory THERE directly (status
|
|
18
21
|
"manual"): auto-classification is skipped entirely, so no suggestion is returned
|
|
19
22
|
and nothing may later move it. The response always reports the destination the
|
|
20
|
-
memory actually landed in — a suggestion
|
|
21
|
-
and
|
|
23
|
+
memory actually landed in — a suggestion that is still pending is labelled as
|
|
24
|
+
such and has NOT been applied.
|
|
22
25
|
|
|
23
26
|
Memory types:
|
|
24
27
|
- episodic: Specific experiences and events (what happened)
|
|
@@ -469,6 +472,10 @@ Directions:
|
|
|
469
472
|
type: "number",
|
|
470
473
|
description: "Importance score 0-1 (default: 0.5)",
|
|
471
474
|
},
|
|
475
|
+
universe_id: {
|
|
476
|
+
type: "string",
|
|
477
|
+
description: "Optional universe UUID. Items are only promoted to long-term memory when a universe can be resolved (this field > session > agent's universe). Pass it when the agent has no universe of its own (e.g. the orchestrator), otherwise the capture is never promoted.",
|
|
478
|
+
},
|
|
472
479
|
},
|
|
473
480
|
required: ["session_id", "content", "direction"],
|
|
474
481
|
},
|
|
@@ -674,8 +681,15 @@ Useful for maintaining consistency in the knowledge base.`,
|
|
|
674
681
|
inputSchema: {
|
|
675
682
|
type: "object",
|
|
676
683
|
properties: {
|
|
677
|
-
|
|
678
|
-
|
|
684
|
+
// NOTE: the backend (POST /consolidation/detect-conflicts →
|
|
685
|
+
// ConflictDetectionRequest) does NOT accept similarity_threshold for
|
|
686
|
+
// conflict detection — conflict pairs are found by the forgetting
|
|
687
|
+
// manager's own analysis, and the field is silently ignored (the
|
|
688
|
+
// request model inherits _IgnoreExtra). It is kept here only for
|
|
689
|
+
// forward compatibility; it currently has no effect. Redundant
|
|
690
|
+
// detection (consolidation_detect_redundant) DOES honour it.
|
|
691
|
+
similarity_threshold: { type: "number", description: "Currently IGNORED by the backend for conflict detection (kept for forward compatibility). Use consolidation_detect_redundant to tune a similarity threshold." },
|
|
692
|
+
limit: { type: "integer", description: "Maximum memories to analyze (default: 50)" },
|
|
679
693
|
},
|
|
680
694
|
required: [],
|
|
681
695
|
},
|
package/dist/tools/handlers.js
CHANGED
|
@@ -365,14 +365,17 @@ const handlers = {
|
|
|
365
365
|
const userId = args.user_id || resolvedUserId || undefined;
|
|
366
366
|
const meta = args.metadata || {};
|
|
367
367
|
const universeId = args.universe_id || meta.universe_id || undefined;
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
//
|
|
375
|
-
//
|
|
368
|
+
// Write-time classification: the backend classifies the memory by content
|
|
369
|
+
// and, when the tenant's `auto_classify` setting is enabled and the
|
|
370
|
+
// proposal confidence meets the tenant threshold, anchors it automatically
|
|
371
|
+
// (classification_status "proposed"/"universe_only" become real FK columns);
|
|
372
|
+
// below the threshold it stays a pending SUGGESTION the user reviews via
|
|
373
|
+
// memory_classification_pending / memory_classification_approve. See
|
|
374
|
+
// memory/src/api/routers/memories.py (classification/settings). The MCP
|
|
375
|
+
// layer no longer injects an implicit "universe" scope nor surfaces
|
|
376
|
+
// "available universes"; scope, universe_id and context_id are forwarded
|
|
377
|
+
// ONLY when the caller sets them (explicit overrides → status "manual",
|
|
378
|
+
// which anchors there directly and skips classification).
|
|
376
379
|
try {
|
|
377
380
|
const result = await memoryClient.storeMemory({
|
|
378
381
|
agent_id: args.agent_id,
|
|
@@ -799,11 +802,14 @@ const handlers = {
|
|
|
799
802
|
direction: args.direction,
|
|
800
803
|
agent_id: args.agent_id,
|
|
801
804
|
importance: args.importance,
|
|
805
|
+
...(args.universe_id
|
|
806
|
+
? { metadata: { universe_id: args.universe_id } }
|
|
807
|
+
: {}),
|
|
802
808
|
});
|
|
803
809
|
return {
|
|
804
810
|
content: [{
|
|
805
811
|
type: "text",
|
|
806
|
-
text: `Captured to stream.\nSession: ${args.session_id}\nID: ${result.id}`,
|
|
812
|
+
text: `Captured to stream.\nSession: ${args.session_id}\nID: ${result.stream_id ?? result.id}`,
|
|
807
813
|
}],
|
|
808
814
|
};
|
|
809
815
|
},
|
|
@@ -887,7 +893,18 @@ const handlers = {
|
|
|
887
893
|
}
|
|
888
894
|
formatted += `\n`;
|
|
889
895
|
results.memories.forEach((mem, i) => {
|
|
890
|
-
|
|
896
|
+
// Q-MEM-02: /memories/search-with-attention returns `attention_score`
|
|
897
|
+
// (re-ranked) + `base_similarity` (see memory/src/api/schemas.py
|
|
898
|
+
// MemoryWithAttentionScore). The legacy field `weighted_score` and the
|
|
899
|
+
// plain /memories/search field `similarity` are kept as fallbacks so the
|
|
900
|
+
// same renderer works across endpoints. `?? ` (not `||`) so a real 0 score
|
|
901
|
+
// is not swallowed.
|
|
902
|
+
const score = mem.attention_score ??
|
|
903
|
+
mem.weighted_score ??
|
|
904
|
+
mem.base_similarity ??
|
|
905
|
+
mem.similarity;
|
|
906
|
+
const scoreTxt = typeof score === "number" ? score.toFixed(2) : "n/a";
|
|
907
|
+
formatted += `${i + 1}. [${mem.memory_type}] (id: ${mem.id}) (score: ${scoreTxt})\n`;
|
|
891
908
|
formatted += ` ${mem.content?.slice(0, 200)}...\n\n`;
|
|
892
909
|
});
|
|
893
910
|
return { content: [{ type: "text", text: formatted }] };
|
|
@@ -1032,11 +1049,27 @@ const handlers = {
|
|
|
1032
1049
|
if (!conflicts || conflicts.length === 0) {
|
|
1033
1050
|
return { content: [{ type: "text", text: "No conflicts detected." }] };
|
|
1034
1051
|
}
|
|
1035
|
-
let formatted = `Detected ${result.conflicts_found
|
|
1052
|
+
let formatted = `Detected ${result.conflicts_found ?? conflicts.length} potential conflicts:\n\n`;
|
|
1036
1053
|
conflicts.forEach((conflict, i) => {
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1054
|
+
// Q-MEM-03: the backend emits `memory_1` / `memory_2` (ConflictPair in
|
|
1055
|
+
// memory/src/api/schemas.py), each with id/content, plus `conflict_type`
|
|
1056
|
+
// and `suggested_action`. The old code read `memory1`/`memory2` (no
|
|
1057
|
+
// underscore) so content printed as "undefined" and the type/action were
|
|
1058
|
+
// dropped. Keep the snake-less variants as a defensive fallback.
|
|
1059
|
+
const m1 = conflict.memory_1 ?? conflict.memory1;
|
|
1060
|
+
const m2 = conflict.memory_2 ?? conflict.memory2;
|
|
1061
|
+
const simTxt = typeof conflict.similarity === "number"
|
|
1062
|
+
? conflict.similarity.toFixed(2)
|
|
1063
|
+
: "n/a";
|
|
1064
|
+
formatted += `${i + 1}. Similarity: ${simTxt}`;
|
|
1065
|
+
if (conflict.conflict_type)
|
|
1066
|
+
formatted += ` · type: ${conflict.conflict_type}`;
|
|
1067
|
+
formatted += `\n`;
|
|
1068
|
+
formatted += ` Memory 1 (${m1?.id ?? "?"}): ${m1?.content?.slice(0, 100) ?? "(no content)"}...\n`;
|
|
1069
|
+
formatted += ` Memory 2 (${m2?.id ?? "?"}): ${m2?.content?.slice(0, 100) ?? "(no content)"}...\n`;
|
|
1070
|
+
if (conflict.suggested_action)
|
|
1071
|
+
formatted += ` Suggested action: ${conflict.suggested_action}\n`;
|
|
1072
|
+
formatted += `\n`;
|
|
1040
1073
|
});
|
|
1041
1074
|
return { content: [{ type: "text", text: formatted }] };
|
|
1042
1075
|
},
|
|
@@ -1059,12 +1092,19 @@ const handlers = {
|
|
|
1059
1092
|
if (!groups || groups.length === 0) {
|
|
1060
1093
|
return { content: [{ type: "text", text: "No redundant memories detected." }] };
|
|
1061
1094
|
}
|
|
1062
|
-
let formatted = `Detected ${result.groups_found
|
|
1095
|
+
let formatted = `Detected ${result.groups_found ?? groups.length} redundant memory groups (${result.total_redundant ?? 0} redundant memories):\n\n`;
|
|
1063
1096
|
groups.forEach((group, i) => {
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1097
|
+
// Q-MEM-04: the backend emits `keep` (the memory to retain), `redundant`
|
|
1098
|
+
// (the duplicates) and `total_in_group` (RedundantGroup in
|
|
1099
|
+
// memory/src/api/schemas.py). The old code read `avg_similarity`,
|
|
1100
|
+
// `memory_ids` and `memories`, none of which exist, so every group showed
|
|
1101
|
+
// "N/A" and 0 memories.
|
|
1102
|
+
const redundantCount = Array.isArray(group.redundant) ? group.redundant.length : 0;
|
|
1103
|
+
const total = group.total_in_group ?? (redundantCount + (group.keep ? 1 : 0));
|
|
1104
|
+
formatted += `${i + 1}. Memories in group: ${total} (keep 1, redundant ${redundantCount})\n`;
|
|
1105
|
+
const keepPreview = group.keep?.content?.slice(0, 100);
|
|
1106
|
+
if (keepPreview) {
|
|
1107
|
+
formatted += ` Keep (${group.keep?.id ?? "?"}): ${keepPreview}...\n`;
|
|
1068
1108
|
}
|
|
1069
1109
|
formatted += `\n`;
|
|
1070
1110
|
});
|
package/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": "0.3",
|
|
3
3
|
"name": "frinus",
|
|
4
4
|
"display_name": "Frinus — Memória Cognitiva",
|
|
5
|
-
"version": "3.6.
|
|
5
|
+
"version": "3.6.4",
|
|
6
6
|
"description": "Memória cognitiva de longo prazo para o Claude (φρήν + νοῦς). 83 ferramentas de memória, sessões, agentes e conhecimento.",
|
|
7
7
|
"long_description": "O Frinus dá ao Claude memória de longo prazo, identidade e inteligência persistente entre conversas. Conecta o Claude Desktop à plataforma Frinus (Memory Engine, Control Plane e Agent Service) via MCP. Basta colar sua chave de API pessoal — nada para instalar ou compilar.",
|
|
8
8
|
"author": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frinus-mcp",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.4",
|
|
4
4
|
"description": "Frinus - MCP Server for Cognitive Memory (φρήν + νοῦς). Long-term memory, sessions, agents and knowledge for Claude and any MCP client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
19
|
"build": "tsc",
|
|
20
|
+
"test": "node --import tsx --test tests/*.test.ts",
|
|
20
21
|
"start": "node dist/index.js",
|
|
21
22
|
"dev": "tsx src/index.ts",
|
|
22
23
|
"pack:mcpb": "./scripts/pack-mcpb.sh",
|