@mnemoverse/mcp-memory-server 0.8.0 → 0.8.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.
- package/README.md +39 -5
- package/dist/index.d.ts +23 -1
- package/dist/index.js +664 -122
- package/dist/index.js.map +1 -1
- package/dist/names.d.ts +168 -0
- package/dist/names.js +216 -0
- package/dist/names.js.map +1 -0
- package/dist/render.d.ts +80 -6
- package/dist/render.js +99 -10
- package/dist/render.js.map +1 -1
- package/dist/requests.d.ts +69 -0
- package/dist/requests.js +76 -0
- package/dist/requests.js.map +1 -0
- package/dist/scope.d.ts +223 -0
- package/dist/scope.js +480 -0
- package/dist/scope.js.map +1 -0
- package/dist/teaching.d.ts +68 -24
- package/dist/teaching.js +124 -30
- package/dist/teaching.js.map +1 -1
- package/package.json +4 -3
package/dist/teaching.js
CHANGED
|
@@ -19,57 +19,151 @@
|
|
|
19
19
|
* - Active polarity: no "only when the user explicitly asks" gating.
|
|
20
20
|
* - Mentions every tool family: read/write/stats/feedback, delete, rooms, vault.
|
|
21
21
|
*/
|
|
22
|
-
export const SERVER_INSTRUCTIONS = "You own this long-term memory. It persists across sessions and every AI tool this user connects
|
|
22
|
+
export const SERVER_INSTRUCTIONS = "You own this long-term memory. It persists across sessions and every AI tool this user connects. Use it as a habit: memory_read before answering anything that may have come up; memory_write the moment you learn a durable fact, preference or decision — don't wait to be asked. Shared rooms are SEPARATE stores: to read one, pass its address as domain (memory_list_rooms); unscoped reads never cover rooms. Rate recalls with memory_feedback; memory_stats shows counts; memory_list_recent is newest-first; memory_delete prunes; memory_delete_domain wipes a domain, only with user go-ahead. Rooms: memory_create_room, memory_invite_to_room, memory_join_room. vault_list names secrets by alias, never values. Never store passwords, API keys, payment data, MFA codes, government IDs, or health records.";
|
|
23
23
|
/** The pre-existing zero-result message — kept as the fail-open fallback. */
|
|
24
24
|
export const NO_MATCH_MESSAGE = "No memories found for this query.";
|
|
25
25
|
/** Hint for an UNSCOPED no-match against a non-empty store: widen the query.
|
|
26
26
|
* (No drop-the-filter clause — none was set; advising to remove a filter that
|
|
27
27
|
* does not exist nudges the model into confabulating state.) */
|
|
28
28
|
export const NO_MATCH_HINT = " Try a broader query.";
|
|
29
|
-
/** Hint for a DOMAIN-SCOPED no-match: here a filter genuinely exists, so
|
|
30
|
-
* suggesting to drop it is honest and actionable. */
|
|
31
|
-
export const NO_MATCH_SCOPED_HINT = " Try a broader query, or drop the domain filter to search all domains.";
|
|
32
29
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
30
|
+
* Hint for a DOMAIN-SCOPED no-match: widen the query, or try another domain —
|
|
31
|
+
* and NEVER drop the scope.
|
|
32
|
+
*
|
|
33
|
+
* This doc used to read "here a filter genuinely exists, so suggesting to drop
|
|
34
|
+
* it is honest and actionable", which described a draft that no longer exists:
|
|
35
|
+
* the clause "…or drop the domain filter to search all domains" was deleted, and
|
|
36
|
+
* a test now forbids it. The advice was actively harmful when the domain is a
|
|
37
|
+
* room — an unscoped read does not cover rooms at all, so dropping the filter is
|
|
38
|
+
* the one move guaranteed to lose the content the reader is hunting, and it is
|
|
39
|
+
* the step that cost two agents a day on 2026-08-07.
|
|
40
|
+
*/
|
|
41
|
+
export const NO_MATCH_SCOPED_HINT = " Try a broader query, or a different domain.";
|
|
42
|
+
/**
|
|
43
|
+
* The honest line for an account whose OWN domains are empty but whose rooms
|
|
44
|
+
* are not — an invited teammate whose every memory lives in shared rooms.
|
|
45
|
+
*
|
|
46
|
+
* Without this, such a caller was greeted with EMPTY_STORE_WELCOME: "your
|
|
47
|
+
* memory is empty, nothing has been saved yet, which is why this search
|
|
48
|
+
* returned nothing" — three false clauses, immediately contradicted by the
|
|
49
|
+
* scope note appended underneath, in the same payload (review, 2026-08-08).
|
|
50
|
+
* That reader is precisely the person from the incident this release is about.
|
|
51
|
+
*
|
|
52
|
+
* ENDS WITH A COLON, so it is only ever emitted together with the disclosure
|
|
53
|
+
* that follows it. That pairing used to be two independent decisions and they
|
|
54
|
+
* disagreed: this line was chosen from a flag counting ALL rooms while the
|
|
55
|
+
* disclosure was built from the LIVE ones, so an account whose only room was
|
|
56
|
+
* archived received the colon and nothing after it. Both now come out of the
|
|
57
|
+
* same {@link RoomScope} arm, and the arm that has nothing to disclose has no
|
|
58
|
+
* `note` field to forget.
|
|
59
|
+
*/
|
|
60
|
+
export const EMPTY_PERSONAL_STORE_WITH_ROOMS = "Nothing in your own domains — they hold no memories yet. That is not the whole picture:";
|
|
61
|
+
/**
|
|
62
|
+
* The same situation with the room probe UNANSWERED. Says the one true thing —
|
|
63
|
+
* the personal store measured zero and the rest could not be checked — and
|
|
64
|
+
* deliberately does NOT say "nothing has been saved yet", which was reachable
|
|
65
|
+
* here and is a claim about a scope this client failed to reach.
|
|
66
|
+
*/
|
|
67
|
+
export const EMPTY_PERSONAL_STORE_ROOMS_UNCHECKED = "Nothing in your own domains — they hold no memories yet. Whether that is the whole picture could not be checked:";
|
|
68
|
+
/**
|
|
69
|
+
* First-contact greeting: shown ONLY when a read comes back empty, the personal
|
|
70
|
+
* store holds zero memories, AND the room probe answered that there are no rooms
|
|
71
|
+
* — i.e. the very first read of this account's life, established rather than
|
|
72
|
+
* assumed. Seeds the ANSWER, not the store: one functional paragraph that says
|
|
73
|
+
* what this store is, how to save the first memory, and one next step.
|
|
38
74
|
*/
|
|
39
75
|
export const EMPTY_STORE_WELCOME = "Your long-term memory is empty — nothing has been saved yet, which is why this search returned nothing. " +
|
|
40
76
|
"This store is your own persistent memory: whatever you save survives across sessions and across every AI tool this user has connected. " +
|
|
41
77
|
'Save the first memory now with memory_write, e.g. content: "User prefers TypeScript strict mode" — future sessions will recall it with memory_read.';
|
|
42
78
|
/**
|
|
43
|
-
*
|
|
79
|
+
* Compile-time exhaustiveness. Adding a state to `RoomScope` / `NamedScope`
|
|
80
|
+
* without answering for it here is a type error, which is the point: the state
|
|
81
|
+
* this release exists to fix ("we could not check") was reachable precisely
|
|
82
|
+
* because it had no arm of its own and fell into the falsy one. Returns the
|
|
83
|
+
* neutral message rather than throwing, so an unreachable branch can never take
|
|
84
|
+
* a handler down.
|
|
85
|
+
*/
|
|
86
|
+
function noStateLeftUnhandled(state) {
|
|
87
|
+
void state;
|
|
88
|
+
return NO_MATCH_MESSAGE;
|
|
89
|
+
}
|
|
90
|
+
/** The disclosure a room state owes the reader — "" only for the state whose
|
|
91
|
+
* type says there is nothing to disclose. */
|
|
92
|
+
function roomsTail(rooms) {
|
|
93
|
+
return "note" in rooms ? rooms.note : "";
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Assemble the WHOLE answer for a zero-result read: the head sentence and the
|
|
97
|
+
* disclosure that belongs with it, in one place.
|
|
98
|
+
*
|
|
99
|
+
* They are assembled together on purpose. While the head came from here and the
|
|
100
|
+
* tail was concatenated by the caller, the two could be — and were — derived
|
|
101
|
+
* from different facts: a head promising "that is not the whole picture:" with
|
|
102
|
+
* an empty tail after it, and a first-contact greeting under a tail admitting
|
|
103
|
+
* the room list could not be fetched. Every arm below returns a complete answer,
|
|
104
|
+
* so a head cannot outlive the evidence for it.
|
|
44
105
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* The scoped copy suggests dropping the filter — honest, one actually exists.
|
|
106
|
+
* A NAMED scope never greets and never probes stats: that probe measures the
|
|
107
|
+
* PERSONAL store, so on a scoped read it could claim "the store is empty" about
|
|
108
|
+
* a domain whose memories the query merely missed. Its four arms are the four
|
|
109
|
+
* things we can know about the name.
|
|
50
110
|
*
|
|
51
|
-
* UNSCOPED
|
|
52
|
-
*
|
|
53
|
-
* - total_atoms
|
|
54
|
-
* - total_atoms
|
|
55
|
-
*
|
|
56
|
-
*
|
|
111
|
+
* An UNSCOPED scope makes ONE stats call, on this path only:
|
|
112
|
+
* - stats unreachable / no number → the plain no-match message + the disclosure
|
|
113
|
+
* - total_atoms > 0 → no-match + broaden hint + the disclosure
|
|
114
|
+
* - total_atoms === 0 → one head per room state, each with its own
|
|
115
|
+
* disclosure; only `rooms.state === "none"` may claim the memory is empty,
|
|
116
|
+
* because only there has the claim been established.
|
|
57
117
|
*/
|
|
58
|
-
export async function buildReadEmptyResponse(fetchStats,
|
|
59
|
-
if (
|
|
60
|
-
|
|
118
|
+
export async function buildReadEmptyResponse(fetchStats, scope) {
|
|
119
|
+
if (scope.kind === "named") {
|
|
120
|
+
const named = scope.named;
|
|
121
|
+
switch (named.state) {
|
|
122
|
+
case "present":
|
|
123
|
+
// The store is there; the query missed. The hint suggests a wider query
|
|
124
|
+
// or another domain and never suggests dropping the scope — when the
|
|
125
|
+
// domain is a room, dropping it is the one move that guarantees the
|
|
126
|
+
// content stays hidden (the 2026-08-07 incident).
|
|
127
|
+
return NO_MATCH_MESSAGE + NO_MATCH_SCOPED_HINT;
|
|
128
|
+
case "no-such-domain":
|
|
129
|
+
case "no-such-room":
|
|
130
|
+
// A SPECIFIC diagnosis replaces the generic advice; it does not follow
|
|
131
|
+
// it. Printed the other way round, a model reading top-down went off to
|
|
132
|
+
// widen a query against a store that does not exist.
|
|
133
|
+
return NO_MATCH_MESSAGE + named.note;
|
|
134
|
+
case "unchecked":
|
|
135
|
+
// Not a diagnosis — an admission. So the generic advice stays, and the
|
|
136
|
+
// admission is added rather than substituted. This is the case that used
|
|
137
|
+
// to be spelled exactly like "present".
|
|
138
|
+
return NO_MATCH_MESSAGE + NO_MATCH_SCOPED_HINT + named.note;
|
|
139
|
+
default:
|
|
140
|
+
return noStateLeftUnhandled(named);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const rooms = scope.rooms;
|
|
61
144
|
let totalAtoms;
|
|
62
145
|
try {
|
|
63
146
|
totalAtoms = (await fetchStats())?.total_atoms;
|
|
64
147
|
}
|
|
65
148
|
catch {
|
|
66
|
-
return NO_MATCH_MESSAGE;
|
|
149
|
+
return NO_MATCH_MESSAGE + roomsTail(rooms);
|
|
67
150
|
}
|
|
68
|
-
if (totalAtoms
|
|
69
|
-
return
|
|
70
|
-
if (
|
|
71
|
-
return NO_MATCH_MESSAGE + NO_MATCH_HINT;
|
|
151
|
+
if (typeof totalAtoms !== "number")
|
|
152
|
+
return NO_MATCH_MESSAGE + roomsTail(rooms);
|
|
153
|
+
if (totalAtoms > 0)
|
|
154
|
+
return NO_MATCH_MESSAGE + NO_MATCH_HINT + roomsTail(rooms);
|
|
155
|
+
// The personal bucket measured zero. `total_atoms` counts ONE org, so what
|
|
156
|
+
// that means for the account depends entirely on the rooms.
|
|
157
|
+
switch (rooms.state) {
|
|
158
|
+
case "none":
|
|
159
|
+
return EMPTY_STORE_WELCOME;
|
|
160
|
+
case "live":
|
|
161
|
+
case "archived-only":
|
|
162
|
+
return EMPTY_PERSONAL_STORE_WITH_ROOMS + rooms.note;
|
|
163
|
+
case "unknown":
|
|
164
|
+
return EMPTY_PERSONAL_STORE_ROOMS_UNCHECKED + rooms.note;
|
|
165
|
+
default:
|
|
166
|
+
return noStateLeftUnhandled(rooms);
|
|
72
167
|
}
|
|
73
|
-
return NO_MATCH_MESSAGE;
|
|
74
168
|
}
|
|
75
169
|
//# sourceMappingURL=teaching.js.map
|
package/dist/teaching.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teaching.js","sourceRoot":"","sources":["../src/teaching.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"teaching.js","sourceRoot":"","sources":["../src/teaching.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mnemoverse/mcp-memory-server",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Hosted persistent memory for AI agents that learns which facts help and lets the rest fade — one key across Claude Code, Cursor, VS Code & ChatGPT, no infra to run",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,11 +15,12 @@
|
|
|
15
15
|
"package.json"
|
|
16
16
|
],
|
|
17
17
|
"scripts": {
|
|
18
|
-
"build": "tsc",
|
|
18
|
+
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc",
|
|
19
19
|
"dev": "tsc --watch",
|
|
20
20
|
"start": "node dist/index.js",
|
|
21
21
|
"test": "vitest run",
|
|
22
22
|
"test:watch": "vitest",
|
|
23
|
+
"typecheck:test": "tsc -p tsconfig.test.json",
|
|
23
24
|
"generate:configs": "node scripts/generate-configs.mjs",
|
|
24
25
|
"verify:configs": "node scripts/generate-configs.mjs --check",
|
|
25
26
|
"check:release-sync": "node scripts/check-release-sync.mjs",
|
|
@@ -56,7 +57,7 @@
|
|
|
56
57
|
},
|
|
57
58
|
"homepage": "https://mnemoverse.com/docs/api/mcp-server",
|
|
58
59
|
"dependencies": {
|
|
59
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
60
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
60
61
|
"zod": "^4.3.6"
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|