@plur-ai/mcp 0.9.8 → 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-4IHAOMXN.js → server-X5ZMO46X.js} +45 -53
- 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,20 +121,14 @@ 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 engram = await plur.learnRouted(
|
|
131
|
+
const engram = await plur.learnRouted(statement, context);
|
|
150
132
|
return {
|
|
151
133
|
id: engram.id,
|
|
152
134
|
statement: engram.statement,
|
|
@@ -156,7 +138,7 @@ function getToolDefinitions() {
|
|
|
156
138
|
decision: "ADD"
|
|
157
139
|
};
|
|
158
140
|
} catch (err) {
|
|
159
|
-
const engram = plur.learn(
|
|
141
|
+
const engram = plur.learn(statement, context);
|
|
160
142
|
return {
|
|
161
143
|
id: engram.id,
|
|
162
144
|
statement: engram.statement,
|
|
@@ -179,7 +161,6 @@ function getToolDefinitions() {
|
|
|
179
161
|
scope: { type: "string", description: "Filter by scope (also includes global)" },
|
|
180
162
|
domain: { type: "string", description: "Filter by domain prefix" },
|
|
181
163
|
limit: { type: "number", description: "Max results to return (default 20)" },
|
|
182
|
-
min_strength: { type: "number", description: "Minimum retrieval strength (0-1)" },
|
|
183
164
|
budget: { type: "object", description: "Budget constraints for sub-agents", properties: { max_tokens: { type: "number" }, max_results: { type: "number" } } },
|
|
184
165
|
caller_session_id: { type: "string", description: "Caller session ID for budget enforcement" }
|
|
185
166
|
},
|
|
@@ -189,8 +170,7 @@ function getToolDefinitions() {
|
|
|
189
170
|
const results = plur.recall(args.query, {
|
|
190
171
|
scope: args.scope,
|
|
191
172
|
domain: args.domain,
|
|
192
|
-
limit: args.limit
|
|
193
|
-
min_strength: args.min_strength
|
|
173
|
+
limit: args.limit
|
|
194
174
|
});
|
|
195
175
|
return {
|
|
196
176
|
results: results.map((e) => ({
|
|
@@ -216,7 +196,6 @@ function getToolDefinitions() {
|
|
|
216
196
|
scope: { type: "string", description: "Filter by scope (also includes global)" },
|
|
217
197
|
domain: { type: "string", description: "Filter by domain prefix" },
|
|
218
198
|
limit: { type: "number", description: "Max results to return (default 20)" },
|
|
219
|
-
min_strength: { type: "number", description: "Minimum retrieval strength (0-1)" },
|
|
220
199
|
budget: { type: "object", description: "Budget constraints for sub-agents", properties: { max_tokens: { type: "number" }, max_results: { type: "number" }, ttl_seconds: { type: "number" } } },
|
|
221
200
|
caller_session_id: { type: "string", description: "Session ID of calling agent for budget enforcement" },
|
|
222
201
|
include_episodes: { type: "boolean", description: "If true, include linked episode summaries for each engram (SP2 episodic anchoring)" }
|
|
@@ -229,8 +208,7 @@ function getToolDefinitions() {
|
|
|
229
208
|
const meta = await plur.recallHybridWithMeta(args.query, {
|
|
230
209
|
scope: args.scope,
|
|
231
210
|
domain: args.domain,
|
|
232
|
-
limit: effectiveLimit
|
|
233
|
-
min_strength: args.min_strength
|
|
211
|
+
limit: effectiveLimit
|
|
234
212
|
});
|
|
235
213
|
const results = meta.engrams;
|
|
236
214
|
let truncated = false;
|
|
@@ -369,7 +347,7 @@ function getToolDefinitions() {
|
|
|
369
347
|
const summary = { positive: 0, negative: 0, neutral: 0 };
|
|
370
348
|
for (const { id, signal } of args.signals) {
|
|
371
349
|
try {
|
|
372
|
-
plur.feedback(id, signal);
|
|
350
|
+
await plur.feedback(id, signal);
|
|
373
351
|
results.push({ id, signal, success: true });
|
|
374
352
|
summary[signal]++;
|
|
375
353
|
} catch (err) {
|
|
@@ -379,7 +357,7 @@ function getToolDefinitions() {
|
|
|
379
357
|
return { mode: "batch", results, summary };
|
|
380
358
|
}
|
|
381
359
|
try {
|
|
382
|
-
plur.feedback(args.id, args.signal);
|
|
360
|
+
await plur.feedback(args.id, args.signal);
|
|
383
361
|
return { success: true, id: args.id, signal: args.signal };
|
|
384
362
|
} catch (err) {
|
|
385
363
|
if (err.message?.includes("readonly store")) {
|
|
@@ -436,14 +414,14 @@ function getToolDefinitions() {
|
|
|
436
414
|
const engram = plur.getById(args.id);
|
|
437
415
|
if (!engram) throw new Error(`Engram not found: ${args.id}`);
|
|
438
416
|
if (engram.status === "retired") return { success: false, error: `Already retired: ${args.id}` };
|
|
439
|
-
plur.forget(args.id);
|
|
417
|
+
await plur.forget(args.id);
|
|
440
418
|
return { success: true, retired: { id: engram.id, statement: engram.statement } };
|
|
441
419
|
}
|
|
442
420
|
if (args.search) {
|
|
443
421
|
const matches = plur.recall(args.search, { limit: 100 });
|
|
444
422
|
if (matches.length === 0) return { success: false, error: `No active engrams matching "${args.search}"` };
|
|
445
423
|
if (matches.length === 1) {
|
|
446
|
-
plur.forget(matches[0].id);
|
|
424
|
+
await plur.forget(matches[0].id);
|
|
447
425
|
return { success: true, retired: { id: matches[0].id, statement: matches[0].statement } };
|
|
448
426
|
}
|
|
449
427
|
return {
|
|
@@ -835,7 +813,7 @@ function getToolDefinitions() {
|
|
|
835
813
|
},
|
|
836
814
|
{
|
|
837
815
|
name: "plur_status",
|
|
838
|
-
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",
|
|
839
817
|
annotations: { title: "Status", readOnlyHint: true, idempotentHint: true },
|
|
840
818
|
inputSchema: {
|
|
841
819
|
type: "object",
|
|
@@ -844,6 +822,7 @@ function getToolDefinitions() {
|
|
|
844
822
|
handler: async (_args, plur) => {
|
|
845
823
|
const status = plur.status();
|
|
846
824
|
return {
|
|
825
|
+
version: VERSION,
|
|
847
826
|
engram_count: status.engram_count,
|
|
848
827
|
episode_count: status.episode_count,
|
|
849
828
|
pack_count: status.pack_count,
|
|
@@ -1178,6 +1157,20 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
|
|
|
1178
1157
|
return { tensions, count: tensions.length };
|
|
1179
1158
|
}
|
|
1180
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
|
+
},
|
|
1181
1174
|
{
|
|
1182
1175
|
name: "plur_episode_to_engram",
|
|
1183
1176
|
description: "Promote an episode to a persistent episodic engram \u2014 useful when a session event deserves long-term memory",
|
|
@@ -1424,7 +1417,6 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
|
|
|
1424
1417
|
|
|
1425
1418
|
// src/server.ts
|
|
1426
1419
|
import { z } from "zod";
|
|
1427
|
-
var VERSION = "0.9.8";
|
|
1428
1420
|
var INSTRUCTIONS = `PLUR is your persistent memory. Corrections, preferences, and conventions persist across sessions as engrams.
|
|
1429
1421
|
|
|
1430
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"
|