@plur-ai/mcp 0.12.0 → 0.13.0
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 +0 -1
- package/dist/index.js +3 -9
- package/dist/{server-6CNGJXSW.js → server-B6OJN4ZX.js} +12 -127
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -52,7 +52,6 @@ Your agent gets these tools automatically:
|
|
|
52
52
|
|------|-------------|
|
|
53
53
|
| `plur_session_start` | Start a session — injects relevant engrams for your task |
|
|
54
54
|
| `plur_learn` | Store a memory — correction, preference, convention, or decision |
|
|
55
|
-
| `plur_learn_batch` | Store many memories in one call — same dedup + policy as `plur_learn`, with per-item failure isolation |
|
|
56
55
|
| `plur_recall_hybrid` | **Best default** — BM25 + embeddings merged via RRF. Zero cost. |
|
|
57
56
|
| `plur_recall` | Keyword search (BM25 only, instant) |
|
|
58
57
|
| `plur_inject_hybrid` | Load relevant memories for the current task |
|
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, platform } from "os";
|
|
8
|
-
var VERSION = "0.
|
|
8
|
+
var VERSION = "0.13.0";
|
|
9
9
|
var HELP = `plur-mcp v${VERSION} \u2014 persistent memory for AI agents
|
|
10
10
|
|
|
11
11
|
Usage:
|
|
@@ -65,12 +65,6 @@ var PLUR_HOOKS = {
|
|
|
65
65
|
matcher: "auto|manual",
|
|
66
66
|
hooks: [{ type: "command", command: `${CLI} hook-inject --rehydrate`, timeout: 15 }]
|
|
67
67
|
}],
|
|
68
|
-
// Auto-close the memory lifecycle at session end (Claude Code SessionEnd,
|
|
69
|
-
// shipped v1.0.85) — captures a closing episode and cleans up the session
|
|
70
|
-
// checkpoint even if the agent forgot to call plur_session_end (#217).
|
|
71
|
-
SessionEnd: [{
|
|
72
|
-
hooks: [{ type: "command", command: `${CLI} hook-session-end`, timeout: 5 }]
|
|
73
|
-
}],
|
|
74
68
|
// --- Contextual injection ---
|
|
75
69
|
PreToolUse: [
|
|
76
70
|
{ matcher: "EnterPlanMode", hooks: [{ type: "command", command: `${CLI} hook-inject --event plan_mode`, timeout: 10 }] },
|
|
@@ -104,7 +98,7 @@ Hooks inject engrams automatically on every first message \u2014 you do not need
|
|
|
104
98
|
2. **Learn**: When corrected or discovering something new, call \`plur_learn\` immediately
|
|
105
99
|
3. **Recall**: Before answering factual questions, call \`plur_recall_hybrid\` \u2014 check memory first
|
|
106
100
|
4. **Feedback**: Rate injected engrams with \`plur_feedback\` (positive/negative) \u2014 trains relevance
|
|
107
|
-
5. **End**: Call \`plur_session_end\` with summary + engram_suggestions
|
|
101
|
+
5. **End**: Call \`plur_session_end\` with summary + engram_suggestions
|
|
108
102
|
|
|
109
103
|
Do not ask permission to use these tools \u2014 they are your memory system.
|
|
110
104
|
|
|
@@ -285,7 +279,7 @@ if (arg === "init") {
|
|
|
285
279
|
process.exit(0);
|
|
286
280
|
}
|
|
287
281
|
if (arg === "serve" || arg === void 0) {
|
|
288
|
-
const { runStdio } = await import("./server-
|
|
282
|
+
const { runStdio } = await import("./server-B6OJN4ZX.js");
|
|
289
283
|
runStdio().catch((err) => {
|
|
290
284
|
console.error("Failed to start PLUR MCP server:", err);
|
|
291
285
|
process.exit(1);
|
|
@@ -31,7 +31,7 @@ function recordTelemetry(event) {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
// src/version.ts
|
|
34
|
-
var VERSION = "0.
|
|
34
|
+
var VERSION = "0.13.0";
|
|
35
35
|
|
|
36
36
|
// src/tools.ts
|
|
37
37
|
import { z } from "zod";
|
|
@@ -347,82 +347,6 @@ function getAllToolDefinitions() {
|
|
|
347
347
|
}
|
|
348
348
|
}
|
|
349
349
|
},
|
|
350
|
-
{
|
|
351
|
-
name: "plur_learn_batch",
|
|
352
|
-
description: "Create many engrams in one call \u2014 the batch form of plur_learn. Accepts an array of engram objects and writes them sequentially through the SAME dedup + policy pipeline as plur_learn (content-hash NOOP \u2192 semantic recall \u2192 LLM ADD/UPDATE/MERGE decision). Dedup also applies WITHIN the batch: a statement duplicating an earlier item in the same array resolves to NOOP against it. Returns the created/affected engram ids in input order, the per-item decisions, aggregate stats, and any per-item failures \u2014 a single bad item does not abort the batch. Use this when an orchestration fans out and wants to persist consolidated findings without N separate calls. LLM dedup calls are capped (default 50, override with max_llm_calls) to bound bulk-import cost. Note: unlike plur_learn, batch items take the LOCAL learn path \u2014 remote-scope auto-routing (learnRouted) is not applied per item, so for shared/remote-store writes prefer plur_learn or pass an explicit local scope. See plur-ai/plur#281.",
|
|
353
|
-
annotations: { title: "Learn (batch)", destructiveHint: false, idempotentHint: false },
|
|
354
|
-
inputSchema: {
|
|
355
|
-
type: "object",
|
|
356
|
-
properties: {
|
|
357
|
-
engrams: {
|
|
358
|
-
type: "array",
|
|
359
|
-
description: "Engram objects to persist. Each requires `statement`; the other fields mirror plur_learn.",
|
|
360
|
-
items: {
|
|
361
|
-
type: "object",
|
|
362
|
-
properties: {
|
|
363
|
-
statement: { type: "string", description: "The knowledge assertion to store" },
|
|
364
|
-
type: { type: "string", enum: ["behavioral", "terminological", "procedural", "architectural"], description: "Category of the engram" },
|
|
365
|
-
scope: { type: "string", description: "Namespace, e.g. global, project:myapp" },
|
|
366
|
-
domain: { type: "string", description: "Domain tag, e.g. software.deployment" },
|
|
367
|
-
tags: { type: "array", items: { type: "string" }, description: "Searchable keyword tags \u2014 contribute to BM25/embedding recall" },
|
|
368
|
-
rationale: { type: "string", description: "Why this knowledge matters \u2014 also enters the search corpus" },
|
|
369
|
-
source: { type: "string", description: "Origin of this knowledge (URL, conversation ref, etc.)" },
|
|
370
|
-
pinned: { type: "boolean", description: "Always-load flag. Use sparingly: meta-rules, safety conventions, core principles." },
|
|
371
|
-
commitment: { type: "string", enum: ["exploring", "leaning", "decided", "locked"], description: "How firmly the user has committed (default: leaning)" },
|
|
372
|
-
valid_from: { type: "string", description: "ISO date (YYYY-MM-DD) the knowledge becomes valid" },
|
|
373
|
-
valid_until: { type: "string", description: "ISO date (YYYY-MM-DD) the knowledge expires" }
|
|
374
|
-
},
|
|
375
|
-
required: ["statement"]
|
|
376
|
-
}
|
|
377
|
-
},
|
|
378
|
-
max_llm_calls: { type: "number", description: "Max LLM dedup calls across the whole batch (default 50). Once spent, remaining items use the cheap hash/cosine path. Pass a large number to opt out." }
|
|
379
|
-
},
|
|
380
|
-
required: ["engrams"]
|
|
381
|
-
},
|
|
382
|
-
handler: async (args, plur) => {
|
|
383
|
-
const llm = getLlmFunction();
|
|
384
|
-
const raw = Array.isArray(args.engrams) ? args.engrams : [];
|
|
385
|
-
if (raw.length === 0) {
|
|
386
|
-
return { ids: [], results: [], stats: { added: 0, updated: 0, merged: 0, noops: 0, failed: 0 }, failures: [], warning: "No engrams provided \u2014 pass a non-empty `engrams` array." };
|
|
387
|
-
}
|
|
388
|
-
const items = raw.map((e) => ({
|
|
389
|
-
statement: sanitizeStatement(e.statement),
|
|
390
|
-
context: {
|
|
391
|
-
type: e.type,
|
|
392
|
-
scope: e.scope,
|
|
393
|
-
domain: e.domain,
|
|
394
|
-
source: e.source,
|
|
395
|
-
tags: e.tags,
|
|
396
|
-
rationale: e.rationale,
|
|
397
|
-
commitment: e.commitment,
|
|
398
|
-
pinned: e.pinned,
|
|
399
|
-
valid_from: e.valid_from,
|
|
400
|
-
valid_until: e.valid_until
|
|
401
|
-
}
|
|
402
|
-
}));
|
|
403
|
-
const maxLlmCalls = typeof args.max_llm_calls === "number" ? args.max_llm_calls : void 0;
|
|
404
|
-
const { results, stats, failures } = await plur.learnBatch(
|
|
405
|
-
items,
|
|
406
|
-
llm,
|
|
407
|
-
maxLlmCalls !== void 0 ? { maxLlmCalls } : void 0
|
|
408
|
-
);
|
|
409
|
-
mcpCanary.signal("learn_activity");
|
|
410
|
-
recordTelemetry("learn");
|
|
411
|
-
return {
|
|
412
|
-
ids: results.map((r) => r.engram.id),
|
|
413
|
-
results: results.map((r) => ({
|
|
414
|
-
id: r.engram.id,
|
|
415
|
-
statement: r.engram.statement,
|
|
416
|
-
scope: r.engram.scope,
|
|
417
|
-
type: r.engram.type,
|
|
418
|
-
decision: r.decision,
|
|
419
|
-
...r.existing_id ? { existing_id: r.existing_id } : {}
|
|
420
|
-
})),
|
|
421
|
-
stats,
|
|
422
|
-
...failures.length > 0 ? { failures, warning: `${failures.length} of ${raw.length} engram(s) failed to persist; the rest were written.` } : {}
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
},
|
|
426
350
|
{
|
|
427
351
|
name: "plur_recall",
|
|
428
352
|
description: "Query engrams by BM25 keyword matching \u2014 use plur_recall_hybrid for semantic similarity. Note: a project-scope filter also returns personal-family engrams (local, global, user:*, agent:*); an explicit scope=global recall returns ALL personal-family engrams \u2014 wider than scope=global INJECT, which is targeted to the global namespace only.",
|
|
@@ -446,18 +370,14 @@ function getAllToolDefinitions() {
|
|
|
446
370
|
limit: args.limit
|
|
447
371
|
});
|
|
448
372
|
return {
|
|
449
|
-
results: results.map((e) => {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
domain: e.domain,
|
|
458
|
-
retrieval_strength: e.activation.retrieval_strength
|
|
459
|
-
};
|
|
460
|
-
}),
|
|
373
|
+
results: results.map((e) => ({
|
|
374
|
+
id: e.id,
|
|
375
|
+
statement: e.statement,
|
|
376
|
+
type: e.type,
|
|
377
|
+
scope: e.scope,
|
|
378
|
+
domain: e.domain,
|
|
379
|
+
retrieval_strength: e.activation.retrieval_strength
|
|
380
|
+
})),
|
|
461
381
|
count: results.length
|
|
462
382
|
};
|
|
463
383
|
}
|
|
@@ -513,11 +433,9 @@ function getAllToolDefinitions() {
|
|
|
513
433
|
const response = {
|
|
514
434
|
results: boundedResults.map((e) => {
|
|
515
435
|
const raw = e;
|
|
516
|
-
const supersededBy = e.relations?.superseded_by;
|
|
517
|
-
const annotation = supersededBy?.length ? ` [superseded by ${supersededBy.join(", ")}]` : "";
|
|
518
436
|
const base = {
|
|
519
437
|
id: e.id,
|
|
520
|
-
statement: e.statement
|
|
438
|
+
statement: e.statement,
|
|
521
439
|
type: e.type,
|
|
522
440
|
scope: e.scope,
|
|
523
441
|
domain: e.domain,
|
|
@@ -1266,23 +1184,6 @@ function getAllToolDefinitions() {
|
|
|
1266
1184
|
}
|
|
1267
1185
|
}
|
|
1268
1186
|
checks.push({ check: "reranker available", ok: rerankerOk, detail: rerankerDetail });
|
|
1269
|
-
if (rerankerOk) {
|
|
1270
|
-
try {
|
|
1271
|
-
const fitResult = await plur.checkRerankerFit({ rerankerName });
|
|
1272
|
-
const sep = fitResult.separability.toFixed(3);
|
|
1273
|
-
checks.push({
|
|
1274
|
-
check: "reranker domain fit",
|
|
1275
|
-
ok: fitResult.fit,
|
|
1276
|
-
detail: fitResult.n_pairs === 0 ? "Not enough engrams to evaluate fit (< 2) \u2014 assuming fit" : fitResult.fit ? `Good fit \u2014 separability ${sep} on ${fitResult.n_pairs} pairs (threshold \u2265 0.05)` : `Poor fit \u2014 separability ${sep} on ${fitResult.n_pairs} pairs (threshold \u2265 0.05). Reranker may be net-negative on this store's domain mix.`
|
|
1277
|
-
});
|
|
1278
|
-
if (!fitResult.fit && fitResult.n_pairs > 0) {
|
|
1279
|
-
remediation.push(
|
|
1280
|
-
`Reranker "${rerankerName}" shows poor separability (${sep}) on this store's engrams \u2014 it may be scoring irrelevant pairs higher than relevant ones. Consider unsetting PLUR_RERANKER or switching to a different tier. The fit check compares same-domain vs cross-domain pair scores; low separability means the model lacks signal on your content.`
|
|
1281
|
-
);
|
|
1282
|
-
}
|
|
1283
|
-
} catch {
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
1187
|
try {
|
|
1287
1188
|
let evalStatus;
|
|
1288
1189
|
let freshlyRun = false;
|
|
@@ -1737,27 +1638,11 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
|
|
|
1737
1638
|
if (discoveries.length === 0) {
|
|
1738
1639
|
return { discovered: [], note: "No remote stores configured. Register one scope first with plur_stores_add, then discover the rest." };
|
|
1739
1640
|
}
|
|
1740
|
-
const enrich = (d) => {
|
|
1741
|
-
if (!d.ok) return d;
|
|
1742
|
-
const byScope = new Map(d.metadata.map((m) => [m.scope, m]));
|
|
1743
|
-
const registeredSet = new Set(d.registered);
|
|
1744
|
-
const scopes = d.authorized.map((scope) => {
|
|
1745
|
-
const m = byScope.get(scope);
|
|
1746
|
-
return {
|
|
1747
|
-
scope,
|
|
1748
|
-
registered: registeredSet.has(scope),
|
|
1749
|
-
...m?.description ? { description: m.description } : {},
|
|
1750
|
-
...m && m.covers.length ? { covers: m.covers } : {}
|
|
1751
|
-
};
|
|
1752
|
-
});
|
|
1753
|
-
return { ...d, scopes };
|
|
1754
|
-
};
|
|
1755
|
-
const discovered = discoveries.map(enrich);
|
|
1756
1641
|
if (!register) {
|
|
1757
|
-
return { discovered };
|
|
1642
|
+
return { discovered: discoveries };
|
|
1758
1643
|
}
|
|
1759
1644
|
const registered = await plur.registerDiscoveredScopes({ url });
|
|
1760
|
-
return { discovered, registered };
|
|
1645
|
+
return { discovered: discoveries, registered };
|
|
1761
1646
|
}
|
|
1762
1647
|
},
|
|
1763
1648
|
{
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plur-ai/mcp",
|
|
3
3
|
"mcpName": "io.github.plur-ai/plur",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.13.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"plur-mcp": "dist/index.js"
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
16
16
|
"zod": "^3.23.0",
|
|
17
|
-
"@plur-ai/core": "0.
|
|
17
|
+
"@plur-ai/core": "0.13.0"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@types/node": "^25.5.0"
|