@plur-ai/mcp 0.9.10 → 0.9.12
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
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.9.
|
|
8
|
+
var VERSION = "0.9.12";
|
|
9
9
|
var HELP = `plur-mcp v${VERSION} \u2014 persistent memory for AI agents
|
|
10
10
|
|
|
11
11
|
Usage:
|
|
@@ -279,7 +279,7 @@ if (arg === "init") {
|
|
|
279
279
|
process.exit(0);
|
|
280
280
|
}
|
|
281
281
|
if (arg === "serve" || arg === void 0) {
|
|
282
|
-
const { runStdio } = await import("./server-
|
|
282
|
+
const { runStdio } = await import("./server-AZFA22VU.js");
|
|
283
283
|
runStdio().catch((err) => {
|
|
284
284
|
console.error("Failed to start PLUR MCP server:", err);
|
|
285
285
|
process.exit(1);
|
|
@@ -14,10 +14,13 @@ import {
|
|
|
14
14
|
import { Plur as Plur2, checkForUpdate } from "@plur-ai/core";
|
|
15
15
|
|
|
16
16
|
// src/tools.ts
|
|
17
|
-
import {
|
|
17
|
+
import { existsSync, unlinkSync } from "fs";
|
|
18
|
+
import { join } from "path";
|
|
19
|
+
import { homedir } from "os";
|
|
20
|
+
import { extractMetaEngrams, validateMetaEngram, confidenceBand, generateProfile, getProfileForInjection, selectModelForOperation, getCachedUpdateCheck, minorVersionsBehind, scanForTensions, CapabilityCanary, readProjectConfig } from "@plur-ai/core";
|
|
18
21
|
|
|
19
22
|
// src/version.ts
|
|
20
|
-
var VERSION = "0.9.
|
|
23
|
+
var VERSION = "0.9.12";
|
|
21
24
|
|
|
22
25
|
// src/tools.ts
|
|
23
26
|
function makeHttpLlm(baseUrl, apiKey, model = "gpt-4o-mini") {
|
|
@@ -406,7 +409,7 @@ function getToolDefinitions() {
|
|
|
406
409
|
}
|
|
407
410
|
if (!args.id) throw new Error("Provide id (or list:true to list pinned)");
|
|
408
411
|
const target = args.pinned ?? true;
|
|
409
|
-
const updated = plur.
|
|
412
|
+
const updated = await plur.setPinnedAsync(args.id, target);
|
|
410
413
|
if (!updated) throw new Error(`Engram not found: ${args.id}`);
|
|
411
414
|
return {
|
|
412
415
|
id: updated.id,
|
|
@@ -970,7 +973,8 @@ function getToolDefinitions() {
|
|
|
970
973
|
type: "object",
|
|
971
974
|
properties: {
|
|
972
975
|
task: { type: "string", description: "What you are working on (triggers engram injection)" },
|
|
973
|
-
tags: { type: "array", items: { type: "string" }, description: "Tags to filter injected engrams" }
|
|
976
|
+
tags: { type: "array", items: { type: "string" }, description: "Tags to filter injected engrams" },
|
|
977
|
+
default_scope: { type: "string", description: "Default scope for plur_learn calls this session when no explicit scope is provided. Only set this if you want ALL engrams to route to a specific store. Usually, leave unset and pass scope per-engram based on relevance." }
|
|
974
978
|
},
|
|
975
979
|
required: ["task"]
|
|
976
980
|
},
|
|
@@ -986,12 +990,20 @@ function getToolDefinitions() {
|
|
|
986
990
|
outbox_result = await plur.flushOutbox();
|
|
987
991
|
} catch {
|
|
988
992
|
}
|
|
993
|
+
const remote_scopes = plur.getWritableRemoteScopes();
|
|
994
|
+
const projectConfig = readProjectConfig();
|
|
995
|
+
const explicit_default_scope = args.default_scope ?? null;
|
|
996
|
+
const default_scope = explicit_default_scope ?? projectConfig.scope ?? null;
|
|
997
|
+
const scope_source = explicit_default_scope ? "caller" : projectConfig.scope ? "project-config" : "none";
|
|
998
|
+
plur.setSessionScope(default_scope);
|
|
989
999
|
const status = plur.status();
|
|
990
1000
|
const store_stats = {
|
|
991
1001
|
engram_count: status.engram_count,
|
|
992
1002
|
episode_count: status.episode_count,
|
|
993
1003
|
pack_count: status.pack_count
|
|
994
1004
|
};
|
|
1005
|
+
await plur.warmRemoteCaches().catch(() => {
|
|
1006
|
+
});
|
|
995
1007
|
let engrams = null;
|
|
996
1008
|
try {
|
|
997
1009
|
const result = await plur.injectHybrid(task, {
|
|
@@ -1040,11 +1052,31 @@ ${guide}`;
|
|
|
1040
1052
|
version_warning = `Update available: PLUR v${versionCheck.current} \u2192 v${versionCheck.latest}. Run: npx @plur-ai/mcp@latest`;
|
|
1041
1053
|
}
|
|
1042
1054
|
}
|
|
1055
|
+
if (scope_source === "project-config") {
|
|
1056
|
+
guide += `
|
|
1057
|
+
|
|
1058
|
+
Auto-detected project scope: "${default_scope}" (from .plur.yaml in the current project). plur_learn calls without an explicit scope will be tagged with this scope, keeping this project's knowledge separate from your other projects. Pass scope: "global" only for genuinely cross-project knowledge (general coding conventions, language gotchas, tool quirks).`;
|
|
1059
|
+
} else if (scope_source === "none") {
|
|
1060
|
+
guide += `
|
|
1061
|
+
|
|
1062
|
+
\u26A0\uFE0F No project scope detected. plur_learn calls without explicit scope will be tagged "global" and will appear in EVERY project's future sessions. To avoid context bleed across projects, create a .plur.yaml in this project's root with: scope: "project:<your-project-name>"`;
|
|
1063
|
+
}
|
|
1064
|
+
if (remote_scopes.length > 0) {
|
|
1065
|
+
const scopeList = remote_scopes.map((s) => `"${s.scope}"`).join(", ");
|
|
1066
|
+
guide += default_scope ? `
|
|
1067
|
+
|
|
1068
|
+
Session default scope is set to "${default_scope}". To route an engram to a remote enterprise store instead, pass scope explicitly to plur_learn (available remote scopes: ${scopeList}).` : `
|
|
1069
|
+
|
|
1070
|
+
Remote store scopes available: ${scopeList}. When an engram is relevant to the team (engineering patterns, architecture decisions, project conventions), set scope to the matching remote scope in plur_learn. Personal preferences, local project details, and corrections specific to your workflow should stay at default scope (local).`;
|
|
1071
|
+
}
|
|
1043
1072
|
return {
|
|
1044
1073
|
session_id,
|
|
1045
1074
|
engrams: engrams ?? [],
|
|
1046
1075
|
store_stats,
|
|
1047
1076
|
guide,
|
|
1077
|
+
// Remote scope routing info (#229)
|
|
1078
|
+
...remote_scopes.length > 0 ? { remote_scopes } : {},
|
|
1079
|
+
...default_scope ? { default_scope, scope_source } : {},
|
|
1048
1080
|
// Ask LLM to check back — MCP can't push, but we can request a follow-up
|
|
1049
1081
|
follow_up: store_stats.engram_count === 0 ? "This is a fresh store with 0 engrams. After your first exchange with the user, review what you learned and call plur_learn for any corrections, preferences, or patterns. Build the memory from this session." : void 0,
|
|
1050
1082
|
// On fresh install, suggest hook setup for reliable injection
|
|
@@ -1082,14 +1114,21 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
|
|
|
1082
1114
|
engram_suggestions: {
|
|
1083
1115
|
type: "array",
|
|
1084
1116
|
items: {
|
|
1085
|
-
type
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1117
|
+
// Prefer {statement, type} objects. Bare strings are tolerated
|
|
1118
|
+
// and treated as {statement: <string>} (issue #231).
|
|
1119
|
+
anyOf: [
|
|
1120
|
+
{ type: "string" },
|
|
1121
|
+
{
|
|
1122
|
+
type: "object",
|
|
1123
|
+
properties: {
|
|
1124
|
+
statement: { type: "string", description: "A concise, reusable assertion. Write it as advice to your future self." },
|
|
1125
|
+
type: { type: "string", enum: ["behavioral", "terminological", "procedural", "architectural"] }
|
|
1126
|
+
},
|
|
1127
|
+
required: ["statement"]
|
|
1128
|
+
}
|
|
1129
|
+
]
|
|
1091
1130
|
},
|
|
1092
|
-
description:
|
|
1131
|
+
description: 'Learnings from this session. Preferred shape is {statement: "...", type?: "..."}; bare strings are also accepted and treated as the statement. Review the conversation for corrections, preferences, patterns, and technical facts before calling.'
|
|
1093
1132
|
}
|
|
1094
1133
|
},
|
|
1095
1134
|
required: ["summary", "engram_suggestions"]
|
|
@@ -1099,9 +1138,23 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
|
|
|
1099
1138
|
const session_id = args.session_id;
|
|
1100
1139
|
const suggestions = args.engram_suggestions;
|
|
1101
1140
|
let engrams_created = 0;
|
|
1102
|
-
if (suggestions
|
|
1103
|
-
for (
|
|
1104
|
-
|
|
1141
|
+
if (Array.isArray(suggestions) && suggestions.length) {
|
|
1142
|
+
for (let i = 0; i < suggestions.length; i++) {
|
|
1143
|
+
const s = suggestions[i];
|
|
1144
|
+
let statement;
|
|
1145
|
+
let type;
|
|
1146
|
+
if (typeof s === "string") {
|
|
1147
|
+
statement = s;
|
|
1148
|
+
} else if (s && typeof s === "object") {
|
|
1149
|
+
statement = s.statement;
|
|
1150
|
+
type = s.type;
|
|
1151
|
+
}
|
|
1152
|
+
if (typeof statement !== "string" || statement.length === 0) {
|
|
1153
|
+
throw new Error(
|
|
1154
|
+
`engram_suggestions[${i}] must be a string or {statement: string, type?: string}, got ${typeof s}`
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
plur.learn(statement, { type });
|
|
1105
1158
|
engrams_created++;
|
|
1106
1159
|
}
|
|
1107
1160
|
}
|
|
@@ -1109,6 +1162,19 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
|
|
|
1109
1162
|
session_id,
|
|
1110
1163
|
channel: "mcp"
|
|
1111
1164
|
});
|
|
1165
|
+
try {
|
|
1166
|
+
const plurDir = process.env.PLUR_PATH ?? join(homedir(), ".plur");
|
|
1167
|
+
const sessionsDir = join(plurDir, "sessions");
|
|
1168
|
+
const keys = [session_id, process.env.CLAUDE_SESSION_ID, String(process.ppid)].filter(Boolean).map((k) => k.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 64));
|
|
1169
|
+
for (const key of keys) {
|
|
1170
|
+
const cp = join(sessionsDir, `${key}.checkpoint.json`);
|
|
1171
|
+
if (existsSync(cp)) {
|
|
1172
|
+
unlinkSync(cp);
|
|
1173
|
+
break;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
} catch {
|
|
1177
|
+
}
|
|
1112
1178
|
const status = plur.status();
|
|
1113
1179
|
return {
|
|
1114
1180
|
engrams_created,
|
|
@@ -1160,7 +1226,7 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
|
|
|
1160
1226
|
annotations: { title: "List stores", readOnlyHint: true, idempotentHint: true },
|
|
1161
1227
|
inputSchema: { type: "object", properties: {} },
|
|
1162
1228
|
handler: async (_args, plur) => {
|
|
1163
|
-
const stores = plur.
|
|
1229
|
+
const stores = await plur.listStoresAsync();
|
|
1164
1230
|
const outboxCount = plur.outboxCount();
|
|
1165
1231
|
return {
|
|
1166
1232
|
stores,
|
|
@@ -1430,9 +1496,9 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
|
|
|
1430
1496
|
if (filterType) {
|
|
1431
1497
|
engrams = engrams.filter((e) => e.type === filterType);
|
|
1432
1498
|
}
|
|
1433
|
-
const { homedir } = await import("os");
|
|
1434
|
-
const { join } = await import("path");
|
|
1435
|
-
const outputDir = args.output_dir ||
|
|
1499
|
+
const { homedir: homedir2 } = await import("os");
|
|
1500
|
+
const { join: join2 } = await import("path");
|
|
1501
|
+
const outputDir = args.output_dir || join2(homedir2(), "plur-packs", name);
|
|
1436
1502
|
const result = plur.exportPack(engrams, outputDir, {
|
|
1437
1503
|
name,
|
|
1438
1504
|
version: "1.0.0",
|
|
@@ -1641,6 +1707,31 @@ Use \`scope\` to namespace engrams per project:
|
|
|
1641
1707
|
|
|
1642
1708
|
Override with \`PLUR_PATH\` environment variable.
|
|
1643
1709
|
`;
|
|
1710
|
+
function jsonSchemaPropToZod(prop) {
|
|
1711
|
+
if (!prop || typeof prop !== "object") return z.unknown();
|
|
1712
|
+
const variants = prop.anyOf ?? prop.oneOf;
|
|
1713
|
+
if (Array.isArray(variants) && variants.length > 0) {
|
|
1714
|
+
const zodVariants = variants.map(jsonSchemaPropToZod);
|
|
1715
|
+
if (zodVariants.length === 1) return zodVariants[0];
|
|
1716
|
+
return z.union(zodVariants);
|
|
1717
|
+
}
|
|
1718
|
+
if (prop.type === "string") return prop.enum ? z.enum(prop.enum) : z.string();
|
|
1719
|
+
if (prop.type === "number" || prop.type === "integer") return z.number();
|
|
1720
|
+
if (prop.type === "boolean") return z.boolean();
|
|
1721
|
+
if (prop.type === "array") {
|
|
1722
|
+
const itemSchema = prop.items ? jsonSchemaPropToZod(prop.items) : z.unknown();
|
|
1723
|
+
return z.array(itemSchema);
|
|
1724
|
+
}
|
|
1725
|
+
if (prop.type === "object" && prop.properties) {
|
|
1726
|
+
const shape = {};
|
|
1727
|
+
for (const [k, p] of Object.entries(prop.properties)) {
|
|
1728
|
+
const field = jsonSchemaPropToZod(p);
|
|
1729
|
+
shape[k] = prop.required?.includes(k) ? field : field.optional();
|
|
1730
|
+
}
|
|
1731
|
+
return z.object(shape).passthrough();
|
|
1732
|
+
}
|
|
1733
|
+
return z.unknown();
|
|
1734
|
+
}
|
|
1644
1735
|
async function createServer(plur) {
|
|
1645
1736
|
const instance = plur ?? new Plur2();
|
|
1646
1737
|
const tools = getToolDefinitions();
|
|
@@ -1683,12 +1774,7 @@ async function createServer(plur) {
|
|
|
1683
1774
|
if (schema?.properties) {
|
|
1684
1775
|
const shape = {};
|
|
1685
1776
|
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
1686
|
-
|
|
1687
|
-
if (prop.type === "string") field = prop.enum ? z.enum(prop.enum) : z.string();
|
|
1688
|
-
else if (prop.type === "number") field = z.number();
|
|
1689
|
-
else if (prop.type === "boolean") field = z.boolean();
|
|
1690
|
-
else if (prop.type === "array") field = z.array(z.unknown());
|
|
1691
|
-
else field = z.unknown();
|
|
1777
|
+
const field = jsonSchemaPropToZod(prop);
|
|
1692
1778
|
shape[key] = schema.required?.includes(key) ? field : field.optional();
|
|
1693
1779
|
}
|
|
1694
1780
|
const parsed = z.object(shape).passthrough().safeParse(args);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plur-ai/mcp",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.12",
|
|
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.12"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/node": "^25.5.0"
|