@plur-ai/mcp 0.9.7 → 0.9.9
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/index.js +2 -2
- package/dist/{server-Y2KI6BN5.js → server-X5ZMO46X.js} +60 -63
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Give your AI agent persistent memory. One line in your MCP config — corrections, preferences, and conventions persist across sessions. No workflow changes, no cloud, no API costs for search.
|
|
4
4
|
|
|
5
|
-
Part of [PLUR](https://plur.ai) — where **Haiku with memory
|
|
5
|
+
Part of [PLUR](https://plur.ai) — where, in our tool-routing and local-knowledge benchmark, **Haiku with memory outperformed Opus without it** at 10x less cost.
|
|
6
6
|
|
|
7
7
|
## Setup (30 seconds)
|
|
8
8
|
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, statSy
|
|
|
5
5
|
import { join } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { homedir } from "os";
|
|
8
|
-
var VERSION = "0.9.
|
|
8
|
+
var VERSION = "0.9.9";
|
|
9
9
|
var HELP = `plur-mcp v${VERSION} \u2014 persistent memory for AI agents
|
|
10
10
|
|
|
11
11
|
Usage:
|
|
@@ -277,7 +277,7 @@ if (arg === "init") {
|
|
|
277
277
|
process.exit(0);
|
|
278
278
|
}
|
|
279
279
|
if (arg === "serve" || arg === void 0) {
|
|
280
|
-
const { runStdio } = await import("./server-
|
|
280
|
+
const { runStdio } = await import("./server-X5ZMO46X.js");
|
|
281
281
|
runStdio().catch((err) => {
|
|
282
282
|
console.error("Failed to start PLUR MCP server:", err);
|
|
283
283
|
process.exit(1);
|
|
@@ -15,6 +15,11 @@ import { Plur as Plur2, checkForUpdate } from "@plur-ai/core";
|
|
|
15
15
|
|
|
16
16
|
// src/tools.ts
|
|
17
17
|
import { extractMetaEngrams, validateMetaEngram, confidenceBand, generateProfile, getProfileForInjection, selectModelForOperation } from "@plur-ai/core";
|
|
18
|
+
|
|
19
|
+
// src/version.ts
|
|
20
|
+
var VERSION = "0.9.9";
|
|
21
|
+
|
|
22
|
+
// src/tools.ts
|
|
18
23
|
function makeHttpLlm(baseUrl, apiKey, model = "gpt-4o-mini") {
|
|
19
24
|
return async (prompt) => {
|
|
20
25
|
const response = await fetch(`${baseUrl.replace(/\/$/, "")}/chat/completions`, {
|
|
@@ -72,6 +77,15 @@ function getLlmFunction() {
|
|
|
72
77
|
if (openaiKey) return makeHttpLlm("https://api.openai.com/v1", openaiKey, "gpt-4o-mini");
|
|
73
78
|
return void 0;
|
|
74
79
|
}
|
|
80
|
+
function sanitizeStatement(raw) {
|
|
81
|
+
const markers = ["</statement>", "<parameter name="];
|
|
82
|
+
let cut = raw.length;
|
|
83
|
+
for (const m of markers) {
|
|
84
|
+
const pos = raw.indexOf(m);
|
|
85
|
+
if (pos !== -1 && pos < cut) cut = pos;
|
|
86
|
+
}
|
|
87
|
+
return raw.slice(0, cut).trimEnd();
|
|
88
|
+
}
|
|
75
89
|
function getToolDefinitions() {
|
|
76
90
|
return [
|
|
77
91
|
{
|
|
@@ -89,38 +103,12 @@ function getToolDefinitions() {
|
|
|
89
103
|
},
|
|
90
104
|
scope: { type: "string", description: "Namespace, e.g. global, project:myapp" },
|
|
91
105
|
domain: { type: "string", description: "Domain tag, e.g. software.deployment" },
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
items: {
|
|
99
|
-
type: "object",
|
|
100
|
-
properties: {
|
|
101
|
-
path: { type: "string", description: "Path to related document" },
|
|
102
|
-
relevance: { type: "string", enum: ["primary", "supporting", "example"] },
|
|
103
|
-
snippet: { type: "string", description: "Short snippet (max 200 chars)" }
|
|
104
|
-
},
|
|
105
|
-
required: ["path"]
|
|
106
|
-
},
|
|
107
|
-
description: "Links to related knowledge documents"
|
|
108
|
-
},
|
|
109
|
-
dual_coding: {
|
|
110
|
-
type: "object",
|
|
111
|
-
properties: {
|
|
112
|
-
example: { type: "string", description: "Concrete example" },
|
|
113
|
-
analogy: { type: "string", description: "Analogy to aid understanding" }
|
|
114
|
-
},
|
|
115
|
-
description: "Dual coding for richer encoding"
|
|
116
|
-
},
|
|
117
|
-
abstract: { type: "string", description: "Abstract engram ID this was derived from" },
|
|
118
|
-
derived_from: { type: "string", description: "Source engram ID this was derived from" },
|
|
119
|
-
commitment: { type: "string", enum: ["exploring", "leaning", "decided", "locked"], description: "Commitment level (default: leaning)" },
|
|
120
|
-
locked_reason: { type: "string", description: "Reason for locking (when commitment=locked)" },
|
|
121
|
-
memory_class: { type: "string", enum: ["semantic", "episodic", "procedural", "metacognitive"], description: "Memory classification (auto-set from type if omitted)" },
|
|
122
|
-
session_episode_id: { type: "string", description: "Link to current session episode for episodic anchoring" },
|
|
123
|
-
pinned: { type: "boolean", description: "Always-load flag. If true, this engram bypasses the keyword-relevance gate and is eligible for injection on every session, regardless of overlap with the user task. Use sparingly: meta-rules, safety conventions, core operating principles only." }
|
|
106
|
+
tags: { type: "array", items: { type: "string" }, description: "Searchable keyword tags \u2014 contribute to BM25/embedding recall, so concrete keywords pay off" },
|
|
107
|
+
rationale: { type: "string", description: "Why this knowledge matters \u2014 also enters the search corpus, helps recall by intent not just statement" },
|
|
108
|
+
source: { type: "string", description: "Origin of this knowledge (URL, conversation ref, etc.)" },
|
|
109
|
+
pinned: { type: "boolean", description: "Always-load flag. If true, this engram bypasses the keyword-relevance gate at injection time. Use sparingly: meta-rules, safety conventions, core operating principles only." },
|
|
110
|
+
commitment: { type: "string", enum: ["exploring", "leaning", "decided", "locked"], description: "How firmly the user has committed to this belief (default: leaning)" },
|
|
111
|
+
locked_reason: { type: "string", description: "Why this engram is locked (only meaningful when commitment=locked)" }
|
|
124
112
|
},
|
|
125
113
|
required: ["statement"]
|
|
126
114
|
},
|
|
@@ -133,33 +121,32 @@ function getToolDefinitions() {
|
|
|
133
121
|
source: args.source,
|
|
134
122
|
tags: args.tags,
|
|
135
123
|
rationale: args.rationale,
|
|
136
|
-
visibility: args.visibility,
|
|
137
|
-
knowledge_anchors: args.knowledge_anchors,
|
|
138
|
-
dual_coding: args.dual_coding,
|
|
139
|
-
abstract: args.abstract,
|
|
140
|
-
derived_from: args.derived_from,
|
|
141
124
|
commitment: args.commitment,
|
|
142
125
|
locked_reason: args.locked_reason,
|
|
143
|
-
memory_class: args.memory_class,
|
|
144
|
-
session_episode_id: args.session_episode_id,
|
|
145
126
|
pinned: args.pinned,
|
|
146
127
|
llm
|
|
147
128
|
};
|
|
129
|
+
const statement = sanitizeStatement(args.statement);
|
|
148
130
|
try {
|
|
149
|
-
const
|
|
131
|
+
const engram = await plur.learnRouted(statement, context);
|
|
150
132
|
return {
|
|
151
|
-
id:
|
|
152
|
-
statement:
|
|
153
|
-
scope:
|
|
154
|
-
type:
|
|
155
|
-
pinned:
|
|
156
|
-
decision:
|
|
157
|
-
|
|
158
|
-
|
|
133
|
+
id: engram.id,
|
|
134
|
+
statement: engram.statement,
|
|
135
|
+
scope: engram.scope,
|
|
136
|
+
type: engram.type,
|
|
137
|
+
pinned: engram.pinned === true,
|
|
138
|
+
decision: "ADD"
|
|
139
|
+
};
|
|
140
|
+
} catch (err) {
|
|
141
|
+
const engram = plur.learn(statement, context);
|
|
142
|
+
return {
|
|
143
|
+
id: engram.id,
|
|
144
|
+
statement: engram.statement,
|
|
145
|
+
scope: engram.scope,
|
|
146
|
+
type: engram.type,
|
|
147
|
+
decision: "ADD",
|
|
148
|
+
warning: `Remote write failed (${err.message}); fell back to local. The id above is the local placeholder \u2014 the canonical engram is NOT on the server.`
|
|
159
149
|
};
|
|
160
|
-
} catch {
|
|
161
|
-
const engram = plur.learn(args.statement, context);
|
|
162
|
-
return { id: engram.id, statement: engram.statement, scope: engram.scope, type: engram.type, decision: "ADD" };
|
|
163
150
|
}
|
|
164
151
|
}
|
|
165
152
|
},
|
|
@@ -174,7 +161,6 @@ function getToolDefinitions() {
|
|
|
174
161
|
scope: { type: "string", description: "Filter by scope (also includes global)" },
|
|
175
162
|
domain: { type: "string", description: "Filter by domain prefix" },
|
|
176
163
|
limit: { type: "number", description: "Max results to return (default 20)" },
|
|
177
|
-
min_strength: { type: "number", description: "Minimum retrieval strength (0-1)" },
|
|
178
164
|
budget: { type: "object", description: "Budget constraints for sub-agents", properties: { max_tokens: { type: "number" }, max_results: { type: "number" } } },
|
|
179
165
|
caller_session_id: { type: "string", description: "Caller session ID for budget enforcement" }
|
|
180
166
|
},
|
|
@@ -184,8 +170,7 @@ function getToolDefinitions() {
|
|
|
184
170
|
const results = plur.recall(args.query, {
|
|
185
171
|
scope: args.scope,
|
|
186
172
|
domain: args.domain,
|
|
187
|
-
limit: args.limit
|
|
188
|
-
min_strength: args.min_strength
|
|
173
|
+
limit: args.limit
|
|
189
174
|
});
|
|
190
175
|
return {
|
|
191
176
|
results: results.map((e) => ({
|
|
@@ -211,7 +196,6 @@ function getToolDefinitions() {
|
|
|
211
196
|
scope: { type: "string", description: "Filter by scope (also includes global)" },
|
|
212
197
|
domain: { type: "string", description: "Filter by domain prefix" },
|
|
213
198
|
limit: { type: "number", description: "Max results to return (default 20)" },
|
|
214
|
-
min_strength: { type: "number", description: "Minimum retrieval strength (0-1)" },
|
|
215
199
|
budget: { type: "object", description: "Budget constraints for sub-agents", properties: { max_tokens: { type: "number" }, max_results: { type: "number" }, ttl_seconds: { type: "number" } } },
|
|
216
200
|
caller_session_id: { type: "string", description: "Session ID of calling agent for budget enforcement" },
|
|
217
201
|
include_episodes: { type: "boolean", description: "If true, include linked episode summaries for each engram (SP2 episodic anchoring)" }
|
|
@@ -224,8 +208,7 @@ function getToolDefinitions() {
|
|
|
224
208
|
const meta = await plur.recallHybridWithMeta(args.query, {
|
|
225
209
|
scope: args.scope,
|
|
226
210
|
domain: args.domain,
|
|
227
|
-
limit: effectiveLimit
|
|
228
|
-
min_strength: args.min_strength
|
|
211
|
+
limit: effectiveLimit
|
|
229
212
|
});
|
|
230
213
|
const results = meta.engrams;
|
|
231
214
|
let truncated = false;
|
|
@@ -364,7 +347,7 @@ function getToolDefinitions() {
|
|
|
364
347
|
const summary = { positive: 0, negative: 0, neutral: 0 };
|
|
365
348
|
for (const { id, signal } of args.signals) {
|
|
366
349
|
try {
|
|
367
|
-
plur.feedback(id, signal);
|
|
350
|
+
await plur.feedback(id, signal);
|
|
368
351
|
results.push({ id, signal, success: true });
|
|
369
352
|
summary[signal]++;
|
|
370
353
|
} catch (err) {
|
|
@@ -374,7 +357,7 @@ function getToolDefinitions() {
|
|
|
374
357
|
return { mode: "batch", results, summary };
|
|
375
358
|
}
|
|
376
359
|
try {
|
|
377
|
-
plur.feedback(args.id, args.signal);
|
|
360
|
+
await plur.feedback(args.id, args.signal);
|
|
378
361
|
return { success: true, id: args.id, signal: args.signal };
|
|
379
362
|
} catch (err) {
|
|
380
363
|
if (err.message?.includes("readonly store")) {
|
|
@@ -431,14 +414,14 @@ function getToolDefinitions() {
|
|
|
431
414
|
const engram = plur.getById(args.id);
|
|
432
415
|
if (!engram) throw new Error(`Engram not found: ${args.id}`);
|
|
433
416
|
if (engram.status === "retired") return { success: false, error: `Already retired: ${args.id}` };
|
|
434
|
-
plur.forget(args.id);
|
|
417
|
+
await plur.forget(args.id);
|
|
435
418
|
return { success: true, retired: { id: engram.id, statement: engram.statement } };
|
|
436
419
|
}
|
|
437
420
|
if (args.search) {
|
|
438
421
|
const matches = plur.recall(args.search, { limit: 100 });
|
|
439
422
|
if (matches.length === 0) return { success: false, error: `No active engrams matching "${args.search}"` };
|
|
440
423
|
if (matches.length === 1) {
|
|
441
|
-
plur.forget(matches[0].id);
|
|
424
|
+
await plur.forget(matches[0].id);
|
|
442
425
|
return { success: true, retired: { id: matches[0].id, statement: matches[0].statement } };
|
|
443
426
|
}
|
|
444
427
|
return {
|
|
@@ -830,7 +813,7 @@ function getToolDefinitions() {
|
|
|
830
813
|
},
|
|
831
814
|
{
|
|
832
815
|
name: "plur_status",
|
|
833
|
-
description: "Return system health \u2014 engram count, episode count, pack count, storage root",
|
|
816
|
+
description: "Return system health \u2014 running version, engram count, episode count, pack count, storage root",
|
|
834
817
|
annotations: { title: "Status", readOnlyHint: true, idempotentHint: true },
|
|
835
818
|
inputSchema: {
|
|
836
819
|
type: "object",
|
|
@@ -839,6 +822,7 @@ function getToolDefinitions() {
|
|
|
839
822
|
handler: async (_args, plur) => {
|
|
840
823
|
const status = plur.status();
|
|
841
824
|
return {
|
|
825
|
+
version: VERSION,
|
|
842
826
|
engram_count: status.engram_count,
|
|
843
827
|
episode_count: status.episode_count,
|
|
844
828
|
pack_count: status.pack_count,
|
|
@@ -1173,6 +1157,20 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
|
|
|
1173
1157
|
return { tensions, count: tensions.length };
|
|
1174
1158
|
}
|
|
1175
1159
|
},
|
|
1160
|
+
{
|
|
1161
|
+
name: "plur_tensions_purge",
|
|
1162
|
+
description: "Purge all conflict relations from local engrams \u2014 removes accumulated false positives from the legacy tension-detection system",
|
|
1163
|
+
annotations: { title: "Purge Tensions", destructiveHint: true, idempotentHint: true },
|
|
1164
|
+
inputSchema: { type: "object", properties: {} },
|
|
1165
|
+
handler: async (_args, plur) => {
|
|
1166
|
+
const result = plur.purgeTensions();
|
|
1167
|
+
return {
|
|
1168
|
+
purged_conflict_refs: result.purged_count,
|
|
1169
|
+
engrams_modified: result.engrams_modified,
|
|
1170
|
+
message: `Purged ${result.purged_count} conflict references from ${result.engrams_modified} engrams.`
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
},
|
|
1176
1174
|
{
|
|
1177
1175
|
name: "plur_episode_to_engram",
|
|
1178
1176
|
description: "Promote an episode to a persistent episodic engram \u2014 useful when a session event deserves long-term memory",
|
|
@@ -1419,7 +1417,6 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
|
|
|
1419
1417
|
|
|
1420
1418
|
// src/server.ts
|
|
1421
1419
|
import { z } from "zod";
|
|
1422
|
-
var VERSION = "0.9.7";
|
|
1423
1420
|
var INSTRUCTIONS = `PLUR is your persistent memory. Corrections, preferences, and conventions persist across sessions as engrams.
|
|
1424
1421
|
|
|
1425
1422
|
PLUR is a GLOBAL tool \u2014 one MCP server, one engram store (~/.plur/), available in every project. Multi-project scoping uses domain/scope fields on engrams, not separate installations.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plur-ai/mcp",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"plur-mcp": "dist/index.js"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
15
15
|
"zod": "^3.23.0",
|
|
16
|
-
"@plur-ai/core": "0.9.
|
|
16
|
+
"@plur-ai/core": "0.9.9"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/node": "^25.5.0"
|