@plur-ai/mcp 0.6.0 → 0.7.2

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
@@ -4,12 +4,12 @@
4
4
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
5
5
  import { join } from "path";
6
6
  import { homedir } from "os";
7
- var VERSION = "0.6.0";
7
+ var VERSION = "0.7.2";
8
8
  var HELP = `plur-mcp v${VERSION} \u2014 persistent memory for AI agents
9
9
 
10
10
  Usage:
11
11
  plur-mcp Start the MCP server (stdio transport)
12
- plur-mcp init Set up PLUR: storage + MCP config + Claude Code hooks
12
+ plur-mcp init Set up PLUR: storage + MCP config + hooks + CLAUDE.md
13
13
  plur-mcp --help Show this help message
14
14
  plur-mcp --version Show version
15
15
 
@@ -25,15 +25,96 @@ var MCP_SERVER_CONFIG = {
25
25
  command: "npx",
26
26
  args: ["-y", "@plur-ai/mcp@latest"]
27
27
  };
28
+ var CLI = "npx @plur-ai/cli";
28
29
  var PLUR_HOOKS = {
30
+ // --- Session lifecycle ---
29
31
  UserPromptSubmit: [{
30
- hooks: [{ type: "command", command: "npx @plur-ai/cli hook-inject", timeout: 15 }]
32
+ hooks: [{ type: "command", command: `${CLI} hook-inject`, timeout: 15 }]
31
33
  }],
32
34
  PostCompact: [{
33
35
  matcher: "auto|manual",
34
- hooks: [{ type: "command", command: "npx @plur-ai/cli hook-inject --rehydrate", timeout: 15 }]
35
- }]
36
+ hooks: [{ type: "command", command: `${CLI} hook-inject --rehydrate`, timeout: 15 }]
37
+ }],
38
+ // --- Contextual injection ---
39
+ PreToolUse: [
40
+ { matcher: "EnterPlanMode", hooks: [{ type: "command", command: `${CLI} hook-inject --event plan_mode`, timeout: 10 }] },
41
+ { matcher: "Skill", hooks: [{ type: "command", command: `${CLI} hook-inject --event skill`, timeout: 10 }] },
42
+ { matcher: "Agent", hooks: [{ type: "command", command: `${CLI} hook-inject --event agent`, timeout: 10 }] },
43
+ { matcher: "Bash|Edit|Write|Agent", hooks: [{ type: "command", command: `${CLI} hook-observe`, timeout: 3 }] }
44
+ ],
45
+ PostToolUse: [
46
+ { matcher: "Bash|Edit|Write|Agent", hooks: [{ type: "command", command: `${CLI} hook-observe --post`, timeout: 3 }] }
47
+ ],
48
+ SubagentStart: [
49
+ { matcher: ".*", hooks: [{ type: "command", command: `${CLI} hook-inject --event subagent`, timeout: 10 }] }
50
+ ],
51
+ Stop: [
52
+ { matcher: "*", hooks: [{ type: "command", command: `${CLI} hook-learn-check`, timeout: 2 }] }
53
+ ]
36
54
  };
55
+ var CLAUDE_MD_SECTION = `## PLUR Memory
56
+
57
+ You have persistent memory via PLUR. Corrections, preferences, and conventions persist across sessions as engrams.
58
+
59
+ ### Session Workflow
60
+
61
+ 1. **Start**: Call \`plur_session_start\` with task description \u2014 injects relevant engrams
62
+ 2. **Learn**: When corrected or discovering something new, call \`plur_learn\` immediately
63
+ 3. **Recall**: Before answering factual questions, call \`plur_recall_hybrid\` \u2014 check memory first
64
+ 4. **Feedback**: Rate injected engrams with \`plur_feedback\` (positive/negative) \u2014 trains relevance
65
+ 5. **End**: Call \`plur_session_end\` with summary + engram_suggestions
66
+
67
+ Do not ask permission to use these tools \u2014 they are your memory system.
68
+
69
+ ### When to check memory
70
+
71
+ Before reaching for web search, file reads, or guessing \u2014 apply this priority:
72
+ 1. Is the answer already in engrams? \u2192 \`plur_recall_hybrid\`
73
+ 2. Is the answer in the local filesystem? \u2192 Read/Grep/Glob
74
+ 3. Is the answer derivable from context already loaded? \u2192 Just answer
75
+ 4. Only if 1-3 fail \u2192 Use external tools
76
+
77
+ | Domain | When to recall |
78
+ |--------|----------------|
79
+ | Decisions | Past design choices, architecture rationale |
80
+ | Corrections | API quirks, bugs, wrong assumptions |
81
+ | Preferences | Formatting, tone, workflow, tool choices |
82
+ | Conventions | Tag formats, file routing, naming rules |
83
+ | Infrastructure | Server IPs, SSH configs, deployment targets |
84
+
85
+ ### When corrected
86
+
87
+ When the user corrects you ("no, use X not Y", "that's wrong"):
88
+ 1. Call \`plur_learn\` immediately \u2014 before continuing the task
89
+ 2. Call \`plur_feedback\` with negative signal on the wrong engram if one was injected
90
+ 3. Then continue with the corrected approach
91
+
92
+ ### Verification
93
+
94
+ When recalling facts that will drive actions:
95
+ 1. State the recalled fact explicitly before acting on it
96
+ 2. Include the engram ID or search that produced it
97
+ 3. If no engram matches, say so and verify from the filesystem
98
+ 4. Never interpolate between two engrams to produce a "probably correct" composite
99
+ `;
100
+ function installClaudeMd() {
101
+ const marker = "## PLUR Memory";
102
+ const projectClaudeMd = join(process.cwd(), "CLAUDE.md");
103
+ const globalClaudeMd = join(homedir(), "CLAUDE.md");
104
+ const claudeMdPath = existsSync(projectClaudeMd) ? projectClaudeMd : existsSync(globalClaudeMd) ? globalClaudeMd : projectClaudeMd;
105
+ if (existsSync(claudeMdPath)) {
106
+ const content = readFileSync(claudeMdPath, "utf8");
107
+ if (content.includes(marker)) {
108
+ return `already in ${claudeMdPath}`;
109
+ }
110
+ writeFileSync(claudeMdPath, content.trimEnd() + "\n\n" + CLAUDE_MD_SECTION);
111
+ return `added to ${claudeMdPath}`;
112
+ }
113
+ writeFileSync(claudeMdPath, `# CLAUDE.md
114
+
115
+ ${CLAUDE_MD_SECTION}`);
116
+ return `created ${claudeMdPath}`;
117
+ }
37
118
  function findMcpConfig() {
38
119
  const projectMcp = join(process.cwd(), ".mcp.json");
39
120
  if (existsSync(projectMcp)) return projectMcp;
@@ -113,6 +194,8 @@ async function runInit() {
113
194
  results.push(`MCP: ${mcpStatus}`);
114
195
  const hooksStatus = installHooks();
115
196
  results.push(`Hooks: ${hooksStatus}`);
197
+ const claudeMdStatus = installClaudeMd();
198
+ results.push(`CLAUDE.md: ${claudeMdStatus}`);
116
199
  process.stdout.write(`PLUR initialized.
117
200
 
118
201
  ${results.join("\n ")}
@@ -143,7 +226,7 @@ if (arg === "init") {
143
226
  process.exit(0);
144
227
  }
145
228
  if (arg === "serve" || arg === void 0) {
146
- const { runStdio } = await import("./server-5BXYBMHX.js");
229
+ const { runStdio } = await import("./server-VIUVB444.js");
147
230
  runStdio().catch((err) => {
148
231
  console.error("Failed to start PLUR MCP server:", err);
149
232
  process.exit(1);
@@ -446,7 +446,7 @@ function getToolDefinitions() {
446
446
  },
447
447
  {
448
448
  name: "plur_packs_install",
449
- description: "Install an engram pack from a directory path \u2014 adds curated engrams to the store",
449
+ description: "Install an engram pack from a directory path \u2014 adds curated engrams to the store. Reports conflicts with existing engrams.",
450
450
  annotations: { title: "Install pack", destructiveHint: false, idempotentHint: true },
451
451
  inputSchema: {
452
452
  type: "object",
@@ -457,12 +457,32 @@ function getToolDefinitions() {
457
457
  },
458
458
  handler: async (args, plur) => {
459
459
  const result = plur.installPack(args.source);
460
- return { installed: result.installed, name: result.name, success: true };
460
+ return {
461
+ installed: result.installed,
462
+ name: result.name,
463
+ conflicts: result.conflicts,
464
+ success: true
465
+ };
466
+ }
467
+ },
468
+ {
469
+ name: "plur_packs_uninstall",
470
+ description: "Uninstall an engram pack by name \u2014 removes the pack and all its engrams",
471
+ annotations: { title: "Uninstall pack", destructiveHint: true, idempotentHint: false },
472
+ inputSchema: {
473
+ type: "object",
474
+ properties: {
475
+ name: { type: "string", description: "Pack name to uninstall (use plur_packs_list to see names)" }
476
+ },
477
+ required: ["name"]
478
+ },
479
+ handler: async (args, plur) => {
480
+ return plur.uninstallPack(args.name);
461
481
  }
462
482
  },
463
483
  {
464
484
  name: "plur_packs_list",
465
- description: "List all installed engram packs",
485
+ description: "List all installed engram packs with integrity hashes",
466
486
  annotations: { title: "List packs", readOnlyHint: true, idempotentHint: true },
467
487
  inputSchema: {
468
488
  type: "object",
@@ -473,14 +493,35 @@ function getToolDefinitions() {
473
493
  return {
474
494
  packs: packs.map((p) => ({
475
495
  name: p.name,
476
- version: p.version,
477
- description: p.description,
478
- engram_count: p.engram_count
496
+ version: p.manifest?.version,
497
+ description: p.manifest?.description,
498
+ engram_count: p.engram_count,
499
+ integrity: p.integrity
479
500
  })),
480
501
  count: packs.length
481
502
  };
482
503
  }
483
504
  },
505
+ {
506
+ name: "plur_packs_discover",
507
+ description: "Browse available engram packs from the registry \u2014 discover curated expertise packs to install",
508
+ annotations: { title: "Discover packs", readOnlyHint: true, idempotentHint: true, openWorldHint: true },
509
+ inputSchema: {
510
+ type: "object",
511
+ properties: {
512
+ query: { type: "string", description: "Search query to filter packs by name or description" },
513
+ tags: { type: "array", items: { type: "string" }, description: "Filter by tags" },
514
+ category: { type: "string", description: "Filter by category (e.g., devops, trading, writing)" }
515
+ }
516
+ },
517
+ handler: async (args, plur) => {
518
+ return {
519
+ packs: [],
520
+ count: 0,
521
+ message: "Pack discovery coming soon. For now, install packs from local paths via plur_packs_install."
522
+ };
523
+ }
524
+ },
484
525
  {
485
526
  name: "plur_sync",
486
527
  description: "Sync engrams via git \u2014 initializes repo on first call, commits and pushes/pulls on subsequent calls. Provide a remote URL on first call to enable cross-device sync.",
@@ -877,33 +918,56 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
877
918
  },
878
919
  {
879
920
  name: "plur_packs_export",
880
- description: "Export personal engrams as a shareable pack",
921
+ description: "Export engrams as a shareable thematic pack with privacy scanning and integrity hash. Filters out private and secret-containing engrams automatically. Output goes to ~/plur-packs/<name> by default.",
881
922
  annotations: { title: "Export pack", destructiveHint: false, idempotentHint: false },
882
923
  inputSchema: {
883
924
  type: "object",
884
925
  properties: {
885
- name: { type: "string", description: "Pack name" },
926
+ name: { type: "string", description: 'Pack name (e.g. "react-patterns", "mcp-design")' },
886
927
  description: { type: "string", description: "Pack description" },
887
- filter_domain: { type: "string", description: "Filter engrams by domain prefix" },
888
- filter_scope: { type: "string", description: "Filter engrams by scope" },
889
- output_dir: { type: "string", description: "Output directory for the pack (default: ~/.plur/exports/)" }
928
+ filter_domain: { type: "string", description: 'Filter engrams by domain prefix (e.g. "mcp", "trading")' },
929
+ filter_scope: { type: "string", description: 'Filter engrams by scope (e.g. "global", "project:myapp")' },
930
+ filter_tags: { type: "array", items: { type: "string" }, description: "Filter by tags" },
931
+ filter_type: { type: "string", enum: ["behavioral", "procedural", "architectural", "terminological"], description: "Filter by engram type" },
932
+ output_dir: { type: "string", description: "Output directory (default: ~/plur-packs/<name>)" },
933
+ creator: { type: "string", description: "Creator name" }
890
934
  },
891
935
  required: ["name"]
892
936
  },
893
937
  handler: async (args, plur) => {
894
938
  const name = args.name;
895
- const engrams = plur.list({
939
+ let engrams = plur.list({
896
940
  domain: args.filter_domain,
897
941
  scope: args.filter_scope
898
- }).filter((e) => e.visibility !== "private" || !args.filter_domain);
899
- const outputDir = args.output_dir || `${plur.status().storage_root}/exports`;
942
+ });
943
+ const filterTags = args.filter_tags;
944
+ if (filterTags) {
945
+ engrams = engrams.filter(
946
+ (e) => e.tags && filterTags.some((t) => e.tags.includes(t))
947
+ );
948
+ }
949
+ const filterType = args.filter_type;
950
+ if (filterType) {
951
+ engrams = engrams.filter((e) => e.type === filterType);
952
+ }
953
+ const { homedir } = await import("os");
954
+ const { join } = await import("path");
955
+ const outputDir = args.output_dir || join(homedir(), "plur-packs", name);
900
956
  const result = plur.exportPack(engrams, outputDir, {
901
957
  name,
902
958
  version: "1.0.0",
903
959
  description: args.description,
904
- creator: "plur-mcp"
960
+ creator: args.creator || void 0
905
961
  });
906
- return { path: result.path, engram_count: result.engram_count, name };
962
+ return {
963
+ path: result.path,
964
+ engram_count: result.engram_count,
965
+ integrity: result.integrity,
966
+ match_terms: result.match_terms,
967
+ privacy_clean: result.privacy.clean,
968
+ privacy_issues: result.privacy.issues.length,
969
+ name
970
+ };
907
971
  }
908
972
  }
909
973
  ];
@@ -911,7 +975,7 @@ Include at least one engram_suggestion if ANYTHING was learned. An empty suggest
911
975
 
912
976
  // src/server.ts
913
977
  import { z } from "zod";
914
- var VERSION = "0.6.0";
978
+ var VERSION = "0.7.2";
915
979
  var INSTRUCTIONS = `PLUR is your persistent memory. Corrections, preferences, and conventions persist across sessions as engrams.
916
980
 
917
981
  REQUIRED at session boundaries:
@@ -927,6 +991,8 @@ OPTIONAL but improves quality:
927
991
  - Call plur_feedback to rate which injected engrams helped (positive/negative)
928
992
  - Call plur_recall_hybrid before answering factual questions \u2014 the answer may be in memory
929
993
 
994
+ For combined search (engrams + files): call plur_recall_hybrid for memories, then datacore.search for journal/knowledge files.
995
+
930
996
  Do not ask permission to use these tools \u2014 they are your memory system.
931
997
 
932
998
  Setup: If this is a fresh install, suggest the user run: npx @plur-ai/cli init
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plur-ai/mcp",
3
- "version": "0.6.0",
3
+ "version": "0.7.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "plur-mcp": "dist/index.js"
@@ -12,7 +12,7 @@
12
12
  "dependencies": {
13
13
  "@modelcontextprotocol/sdk": "^1.12.0",
14
14
  "zod": "^3.23.0",
15
- "@plur-ai/core": "0.6.0"
15
+ "@plur-ai/core": "0.7.2"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@types/node": "^25.5.0"