@plur-ai/mcp 0.12.0 → 0.14.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/dist/index.js +72 -6
- package/dist/{server-6CNGJXSW.js → server-LAXRBVKA.js} +100 -28
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5,14 +5,17 @@ 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.14.0";
|
|
9
9
|
var HELP = `plur-mcp v${VERSION} \u2014 persistent memory for AI agents
|
|
10
10
|
|
|
11
11
|
Usage:
|
|
12
|
-
plur-mcp
|
|
13
|
-
plur-mcp init
|
|
14
|
-
plur-mcp
|
|
15
|
-
plur-mcp
|
|
12
|
+
plur-mcp Start the MCP server (stdio transport)
|
|
13
|
+
plur-mcp init Set up PLUR: storage + MCP config + hooks + CLAUDE.md
|
|
14
|
+
plur-mcp packs install <path> Install a knowledge pack from a local directory
|
|
15
|
+
plur-mcp packs list List installed knowledge packs
|
|
16
|
+
plur-mcp packs uninstall <name> Uninstall a knowledge pack by name
|
|
17
|
+
plur-mcp --help Show this help message
|
|
18
|
+
plur-mcp --version Show version
|
|
16
19
|
|
|
17
20
|
Environment:
|
|
18
21
|
PLUR_PATH Storage location (default: ~/.plur/)
|
|
@@ -270,6 +273,65 @@ async function runInit() {
|
|
|
270
273
|
`);
|
|
271
274
|
}
|
|
272
275
|
}
|
|
276
|
+
async function runPacks() {
|
|
277
|
+
const sub = process.argv[3];
|
|
278
|
+
const plurPath = process.env.PLUR_PATH ?? join(homedir(), ".plur");
|
|
279
|
+
const { Plur } = await import("@plur-ai/core");
|
|
280
|
+
const plur = new Plur({ path: plurPath });
|
|
281
|
+
if (sub === "install") {
|
|
282
|
+
const source = process.argv[4];
|
|
283
|
+
if (!source) {
|
|
284
|
+
process.stderr.write("Usage: plur-mcp packs install <path>\n");
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
const result = plur.installPack(source);
|
|
289
|
+
process.stdout.write(`Installed pack '${result.name}' (${result.installed} engrams)
|
|
290
|
+
`);
|
|
291
|
+
} catch (err) {
|
|
292
|
+
process.stderr.write(`Error: ${err.message}
|
|
293
|
+
`);
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
} else if (sub === "list") {
|
|
297
|
+
const packs = plur.listPacks();
|
|
298
|
+
if (packs.length === 0) {
|
|
299
|
+
process.stdout.write("No packs installed.\n");
|
|
300
|
+
} else {
|
|
301
|
+
for (const pack of packs) {
|
|
302
|
+
const version = pack.manifest?.version ? ` v${pack.manifest.version}` : "";
|
|
303
|
+
process.stdout.write(`${pack.name}${version} (${pack.engram_count} engrams)
|
|
304
|
+
`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
} else if (sub === "uninstall") {
|
|
308
|
+
const name = process.argv[4];
|
|
309
|
+
if (!name) {
|
|
310
|
+
process.stderr.write("Usage: plur-mcp packs uninstall <name>\n");
|
|
311
|
+
process.exit(1);
|
|
312
|
+
}
|
|
313
|
+
try {
|
|
314
|
+
const result = plur.uninstallPack(name);
|
|
315
|
+
if (result.removed) {
|
|
316
|
+
process.stdout.write(`Uninstalled pack '${result.name}' (${result.engram_count} engrams removed)
|
|
317
|
+
`);
|
|
318
|
+
} else {
|
|
319
|
+
process.stderr.write(`Pack '${name}' not found.
|
|
320
|
+
`);
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
} catch (err) {
|
|
324
|
+
process.stderr.write(`Error: ${err.message}
|
|
325
|
+
`);
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
} else {
|
|
329
|
+
process.stderr.write(`Unknown packs subcommand: ${sub ?? "(none)"}
|
|
330
|
+
Available: install, list, uninstall
|
|
331
|
+
`);
|
|
332
|
+
process.exit(1);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
273
335
|
var arg = process.argv[2];
|
|
274
336
|
if (arg === "--help" || arg === "-h") {
|
|
275
337
|
process.stdout.write(HELP);
|
|
@@ -284,8 +346,12 @@ if (arg === "init") {
|
|
|
284
346
|
await runInit();
|
|
285
347
|
process.exit(0);
|
|
286
348
|
}
|
|
349
|
+
if (arg === "packs") {
|
|
350
|
+
await runPacks();
|
|
351
|
+
process.exit(0);
|
|
352
|
+
}
|
|
287
353
|
if (arg === "serve" || arg === void 0) {
|
|
288
|
-
const { runStdio } = await import("./server-
|
|
354
|
+
const { runStdio } = await import("./server-LAXRBVKA.js");
|
|
289
355
|
runStdio().catch((err) => {
|
|
290
356
|
console.error("Failed to start PLUR MCP server:", err);
|
|
291
357
|
process.exit(1);
|
|
@@ -11,6 +11,9 @@ import {
|
|
|
11
11
|
ErrorCode,
|
|
12
12
|
McpError
|
|
13
13
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
14
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync } from "fs";
|
|
15
|
+
import { join as join2 } from "path";
|
|
16
|
+
import { homedir as homedir2 } from "os";
|
|
14
17
|
import { Plur as Plur2, checkForUpdate } from "@plur-ai/core";
|
|
15
18
|
|
|
16
19
|
// src/tools.ts
|
|
@@ -31,7 +34,7 @@ function recordTelemetry(event) {
|
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
// src/version.ts
|
|
34
|
-
var VERSION = "0.
|
|
37
|
+
var VERSION = "0.14.0";
|
|
35
38
|
|
|
36
39
|
// src/tools.ts
|
|
37
40
|
import { z } from "zod";
|
|
@@ -179,6 +182,24 @@ mcpCanary.expect({
|
|
|
179
182
|
description: "Learning from corrections",
|
|
180
183
|
fix: "Call plur_learn when corrected. If using hooks, verify they are installed."
|
|
181
184
|
});
|
|
185
|
+
var _sessionTelemetry = /* @__PURE__ */ new Map();
|
|
186
|
+
var SESSION_TTL_MS = 8 * 60 * 60 * 1e3;
|
|
187
|
+
function _cleanExpiredSessions() {
|
|
188
|
+
const cutoff = Date.now() - SESSION_TTL_MS;
|
|
189
|
+
for (const [id, state] of _sessionTelemetry) {
|
|
190
|
+
if (new Date(state.started_at).getTime() < cutoff) _sessionTelemetry.delete(id);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
var _activeSessionId;
|
|
194
|
+
function _recordInjectionTelemetry(session_id, injected_packs) {
|
|
195
|
+
if (!session_id || !injected_packs) return;
|
|
196
|
+
const state = _sessionTelemetry.get(session_id);
|
|
197
|
+
if (!state) return;
|
|
198
|
+
state.injection_calls++;
|
|
199
|
+
for (const [pack, count] of Object.entries(injected_packs)) {
|
|
200
|
+
state.pack_counts[pack] = (state.pack_counts[pack] ?? 0) + count;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
182
203
|
var CURSOR_CORE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
183
204
|
"plur_session_start",
|
|
184
205
|
"plur_session_end",
|
|
@@ -349,7 +370,7 @@ function getAllToolDefinitions() {
|
|
|
349
370
|
},
|
|
350
371
|
{
|
|
351
372
|
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
|
|
373
|
+
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 `ids` aligned 1:1 with the input array (ids[i] is the engram id for input i, or null if input i failed), the per-item decisions (each carrying its input_index), aggregate stats, and any per-item failures (each with its input index) \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
374
|
annotations: { title: "Learn (batch)", destructiveHint: false, idempotentHint: false },
|
|
354
375
|
inputSchema: {
|
|
355
376
|
type: "object",
|
|
@@ -408,9 +429,14 @@ function getAllToolDefinitions() {
|
|
|
408
429
|
);
|
|
409
430
|
mcpCanary.signal("learn_activity");
|
|
410
431
|
recordTelemetry("learn");
|
|
432
|
+
const ids = raw.map(() => null);
|
|
433
|
+
for (const r of results) {
|
|
434
|
+
if (r.input_index !== void 0) ids[r.input_index] = r.engram.id;
|
|
435
|
+
}
|
|
411
436
|
return {
|
|
412
|
-
ids
|
|
437
|
+
ids,
|
|
413
438
|
results: results.map((r) => ({
|
|
439
|
+
input_index: r.input_index,
|
|
414
440
|
id: r.engram.id,
|
|
415
441
|
statement: r.engram.statement,
|
|
416
442
|
scope: r.engram.scope,
|
|
@@ -565,6 +591,7 @@ function getAllToolDefinitions() {
|
|
|
565
591
|
budget: args.budget,
|
|
566
592
|
scope: args.scope
|
|
567
593
|
});
|
|
594
|
+
_recordInjectionTelemetry(_activeSessionId, result.injected_packs);
|
|
568
595
|
return {
|
|
569
596
|
directives: result.directives,
|
|
570
597
|
consider: result.consider,
|
|
@@ -594,6 +621,7 @@ function getAllToolDefinitions() {
|
|
|
594
621
|
budget: args.budget,
|
|
595
622
|
scope: args.scope
|
|
596
623
|
});
|
|
624
|
+
_recordInjectionTelemetry(_activeSessionId, result.injected_packs);
|
|
597
625
|
return {
|
|
598
626
|
directives: result.directives,
|
|
599
627
|
consider: result.consider,
|
|
@@ -1132,14 +1160,20 @@ function getAllToolDefinitions() {
|
|
|
1132
1160
|
},
|
|
1133
1161
|
{
|
|
1134
1162
|
name: "plur_status",
|
|
1135
|
-
description: "Return system health \u2014 running version, engram count, episode count, pack count, storage root",
|
|
1163
|
+
description: "Return system health \u2014 running version, engram count, episode count, pack count, storage root. Optionally filter engram counts by domain prefix and/or creation date.",
|
|
1136
1164
|
annotations: { title: "Status", readOnlyHint: true, idempotentHint: true },
|
|
1137
1165
|
inputSchema: {
|
|
1138
1166
|
type: "object",
|
|
1139
|
-
properties: {
|
|
1167
|
+
properties: {
|
|
1168
|
+
domain: { type: "string", description: 'Only count engrams whose domain starts with this prefix (e.g. "meridian")' },
|
|
1169
|
+
created_after: { type: "string", description: "ISO-8601 date (YYYY-MM-DD). Only count engrams learned on or after this date." }
|
|
1170
|
+
}
|
|
1140
1171
|
},
|
|
1141
|
-
handler: async (
|
|
1142
|
-
const status = plur.status(
|
|
1172
|
+
handler: async (args, plur) => {
|
|
1173
|
+
const status = plur.status({
|
|
1174
|
+
domain: args.domain,
|
|
1175
|
+
created_after: args.created_after
|
|
1176
|
+
});
|
|
1143
1177
|
const versionCheck = getCachedUpdateCheck("@plur-ai/mcp");
|
|
1144
1178
|
return {
|
|
1145
1179
|
version: VERSION,
|
|
@@ -1385,6 +1419,13 @@ function getAllToolDefinitions() {
|
|
|
1385
1419
|
const session_id = crypto.randomUUID();
|
|
1386
1420
|
const task = args.task;
|
|
1387
1421
|
const tags = args.tags;
|
|
1422
|
+
_cleanExpiredSessions();
|
|
1423
|
+
_activeSessionId = session_id;
|
|
1424
|
+
_sessionTelemetry.set(session_id, {
|
|
1425
|
+
pack_counts: {},
|
|
1426
|
+
injection_calls: 0,
|
|
1427
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1428
|
+
});
|
|
1388
1429
|
let outbox_result;
|
|
1389
1430
|
try {
|
|
1390
1431
|
outbox_result = await plur.flushOutbox();
|
|
@@ -1418,6 +1459,7 @@ function getAllToolDefinitions() {
|
|
|
1418
1459
|
session_id
|
|
1419
1460
|
// stamped on the co_injection provenance event (#452)
|
|
1420
1461
|
});
|
|
1462
|
+
_recordInjectionTelemetry(session_id, result.injected_packs);
|
|
1421
1463
|
if (result.count > 0) {
|
|
1422
1464
|
const lines = [];
|
|
1423
1465
|
if (result.directives) lines.push("## DIRECTIVES\n", result.directives);
|
|
@@ -1430,6 +1472,7 @@ function getAllToolDefinitions() {
|
|
|
1430
1472
|
scope: tags?.length ? `tags:${tags.join(",")}` : void 0,
|
|
1431
1473
|
session_id
|
|
1432
1474
|
});
|
|
1475
|
+
_recordInjectionTelemetry(session_id, result.injected_packs);
|
|
1433
1476
|
if (result.count > 0) {
|
|
1434
1477
|
const lines = [];
|
|
1435
1478
|
if (result.directives) lines.push("## DIRECTIVES\n", result.directives);
|
|
@@ -1615,6 +1658,16 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
|
|
|
1615
1658
|
session_id,
|
|
1616
1659
|
channel: "mcp"
|
|
1617
1660
|
});
|
|
1661
|
+
const telemetry = session_id ? _sessionTelemetry.get(session_id) : void 0;
|
|
1662
|
+
const injection_summary = telemetry && telemetry.injection_calls > 0 ? {
|
|
1663
|
+
pack_counts: { ...telemetry.pack_counts },
|
|
1664
|
+
total_injections: telemetry.injection_calls,
|
|
1665
|
+
session_duration_ms: Date.now() - new Date(telemetry.started_at).getTime()
|
|
1666
|
+
} : void 0;
|
|
1667
|
+
if (session_id) {
|
|
1668
|
+
_sessionTelemetry.delete(session_id);
|
|
1669
|
+
if (_activeSessionId === session_id) _activeSessionId = void 0;
|
|
1670
|
+
}
|
|
1618
1671
|
try {
|
|
1619
1672
|
const plurDir = process.env.PLUR_PATH ?? join(homedir(), ".plur");
|
|
1620
1673
|
const sessionsDir = join(plurDir, "sessions");
|
|
@@ -1633,6 +1686,7 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
|
|
|
1633
1686
|
engrams_created,
|
|
1634
1687
|
episode_id: episode.id,
|
|
1635
1688
|
total_engrams: status.engram_count,
|
|
1689
|
+
...injection_summary ? { injection_summary } : {},
|
|
1636
1690
|
hint: engrams_created === 0 ? "No engrams captured this session. If any corrections, preferences, or patterns came up, consider calling plur_learn before ending." : void 0
|
|
1637
1691
|
};
|
|
1638
1692
|
}
|
|
@@ -2068,9 +2122,9 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
|
|
|
2068
2122
|
if (filterType) {
|
|
2069
2123
|
engrams = engrams.filter((e) => e.type === filterType);
|
|
2070
2124
|
}
|
|
2071
|
-
const { homedir:
|
|
2072
|
-
const { join:
|
|
2073
|
-
const outputDir = args.output_dir ||
|
|
2125
|
+
const { homedir: homedir3 } = await import("os");
|
|
2126
|
+
const { join: join3 } = await import("path");
|
|
2127
|
+
const outputDir = args.output_dir || join3(homedir3(), "plur-packs", name);
|
|
2074
2128
|
const result = plur.exportPack(engrams, outputDir, {
|
|
2075
2129
|
name,
|
|
2076
2130
|
version: "1.0.0",
|
|
@@ -2120,23 +2174,6 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
|
|
|
2120
2174
|
};
|
|
2121
2175
|
}
|
|
2122
2176
|
},
|
|
2123
|
-
{
|
|
2124
|
-
name: "plur_batch_decay",
|
|
2125
|
-
description: "Apply ACT-R decay to all local engrams. Run weekly. Only decays engrams in the local YAML store \u2014 remote-store engrams are not decayed client-side. Returns status transitions only.",
|
|
2126
|
-
annotations: { title: "Batch decay", destructiveHint: false, idempotentHint: false },
|
|
2127
|
-
inputSchema: {
|
|
2128
|
-
type: "object",
|
|
2129
|
-
properties: {
|
|
2130
|
-
context_scope: { type: "string", description: "Scope to skip during decay (engrams in active scope are not decayed)" }
|
|
2131
|
-
}
|
|
2132
|
-
},
|
|
2133
|
-
handler: async (args, plur) => {
|
|
2134
|
-
const result = plur.batchDecay({
|
|
2135
|
-
contextScope: args.context_scope
|
|
2136
|
-
});
|
|
2137
|
-
return result;
|
|
2138
|
-
}
|
|
2139
|
-
},
|
|
2140
2177
|
{
|
|
2141
2178
|
name: "plur_profile",
|
|
2142
2179
|
description: "Generate or retrieve a cognitive profile \u2014 a narrative summary synthesized from stored engrams. Cached for 24h.",
|
|
@@ -2174,6 +2211,28 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
|
|
|
2174
2211
|
}
|
|
2175
2212
|
|
|
2176
2213
|
// src/server.ts
|
|
2214
|
+
function serverPidPath(baseDir) {
|
|
2215
|
+
return join2(baseDir ?? join2(homedir2(), ".plur"), "server.pid");
|
|
2216
|
+
}
|
|
2217
|
+
function readEnterpriseToken(baseDir) {
|
|
2218
|
+
const configPath = join2(baseDir ?? join2(homedir2(), ".plur"), "config.json");
|
|
2219
|
+
if (!existsSync2(configPath)) return void 0;
|
|
2220
|
+
try {
|
|
2221
|
+
const cfg = JSON.parse(readFileSync(configPath, "utf8"));
|
|
2222
|
+
const ent = cfg?.enterprise;
|
|
2223
|
+
if (!ent || typeof ent.url !== "string" || typeof ent.token !== "string") return void 0;
|
|
2224
|
+
return { url: ent.url, token: ent.token, username: ent.username };
|
|
2225
|
+
} catch {
|
|
2226
|
+
return void 0;
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
var _pendingReload = false;
|
|
2230
|
+
function isPendingReload() {
|
|
2231
|
+
return _pendingReload;
|
|
2232
|
+
}
|
|
2233
|
+
function clearPendingReload() {
|
|
2234
|
+
_pendingReload = false;
|
|
2235
|
+
}
|
|
2177
2236
|
var INSTRUCTIONS = `PLUR is your persistent memory. Corrections, preferences, and conventions persist across sessions as engrams.
|
|
2178
2237
|
|
|
2179
2238
|
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.
|
|
@@ -2478,11 +2537,24 @@ async function runStdio() {
|
|
|
2478
2537
|
const profile = process.env.PLUR_TOOL_PROFILE === "cursor" ? "cursor" : "full";
|
|
2479
2538
|
const server = await createServer(void 0, { profile });
|
|
2480
2539
|
registerFlushOnExit({});
|
|
2540
|
+
try {
|
|
2541
|
+
writeFileSync(serverPidPath(), String(process.pid));
|
|
2542
|
+
} catch {
|
|
2543
|
+
}
|
|
2544
|
+
if (process.platform !== "win32") {
|
|
2545
|
+
process.on("SIGUSR1", () => {
|
|
2546
|
+
_pendingReload = true;
|
|
2547
|
+
});
|
|
2548
|
+
}
|
|
2481
2549
|
const transport = new StdioServerTransport();
|
|
2482
2550
|
await server.connect(transport);
|
|
2483
2551
|
}
|
|
2484
2552
|
export {
|
|
2485
2553
|
INSTRUCTIONS,
|
|
2554
|
+
clearPendingReload,
|
|
2486
2555
|
createServer,
|
|
2487
|
-
|
|
2556
|
+
isPendingReload,
|
|
2557
|
+
readEnterpriseToken,
|
|
2558
|
+
runStdio,
|
|
2559
|
+
serverPidPath
|
|
2488
2560
|
};
|
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.14.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.14.0"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@types/node": "^25.5.0"
|