@plur-ai/mcp 0.5.2 → 0.6.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 +22 -23
- package/dist/index.js +108 -40
- package/dist/{server-4WAIYC37.js → server-5BXYBMHX.js} +23 -8
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,16 +8,14 @@ Part of [PLUR](https://plur.ai) — where **Haiku with memory outperforms Opus w
|
|
|
8
8
|
|
|
9
9
|
### Claude Code
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
One command — sets up storage, MCP config, and hooks:
|
|
12
12
|
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
"mcpServers": {
|
|
16
|
-
"plur": { "command": "npx", "args": ["-y", "@plur-ai/mcp"] }
|
|
17
|
-
}
|
|
18
|
-
}
|
|
13
|
+
```bash
|
|
14
|
+
npx @plur-ai/mcp init
|
|
19
15
|
```
|
|
20
16
|
|
|
17
|
+
Restart Claude Code. Done. Your agent now has persistent memory with automatic injection.
|
|
18
|
+
|
|
21
19
|
### Cursor
|
|
22
20
|
|
|
23
21
|
Add to `.cursor/mcp.json`:
|
|
@@ -52,31 +50,32 @@ Your agent gets these tools automatically:
|
|
|
52
50
|
|
|
53
51
|
| Tool | What it does |
|
|
54
52
|
|------|-------------|
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
53
|
+
| `plur_session_start` | Start a session — injects relevant engrams for your task |
|
|
54
|
+
| `plur_learn` | Store a memory — correction, preference, convention, or decision |
|
|
55
|
+
| `plur_recall_hybrid` | **Best default** — BM25 + embeddings merged via RRF. Zero cost. |
|
|
56
|
+
| `plur_recall` | Keyword search (BM25 only, instant) |
|
|
57
|
+
| `plur_inject_hybrid` | Load relevant memories for the current task |
|
|
58
|
+
| `plur_feedback` | Rate a memory — trains relevance over time |
|
|
59
|
+
| `plur_forget` | Retire a memory (history preserved) |
|
|
60
|
+
| `plur_session_end` | End a session — captures summary and new learnings |
|
|
61
|
+
| `plur_capture` | Record a session event |
|
|
62
|
+
| `plur_timeline` | Query session history |
|
|
63
|
+
| `plur_ingest` | Extract learnings from text |
|
|
64
|
+
| `plur_sync` | Sync memory across machines via git |
|
|
65
|
+
| `plur_packs_install` | Install a shareable memory pack |
|
|
66
|
+
| `plur_packs_list` | List installed packs |
|
|
67
|
+
| `plur_status` | System health |
|
|
69
68
|
|
|
70
69
|
## Sync across machines
|
|
71
70
|
|
|
72
71
|
Your agent can sync memory to any git remote:
|
|
73
72
|
|
|
74
73
|
```
|
|
75
|
-
Agent:
|
|
74
|
+
Agent: plur_sync({ remote: "git@github.com:you/plur-memory.git" })
|
|
76
75
|
→ "Initialized and pushed."
|
|
77
76
|
|
|
78
77
|
# On another machine, same remote:
|
|
79
|
-
Agent:
|
|
78
|
+
Agent: plur_sync()
|
|
80
79
|
→ "Synced. Pulled 12 remote commits."
|
|
81
80
|
```
|
|
82
81
|
|
package/dist/index.js
CHANGED
|
@@ -1,81 +1,149 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
var VERSION = "0.6.0";
|
|
5
8
|
var HELP = `plur-mcp v${VERSION} \u2014 persistent memory for AI agents
|
|
6
9
|
|
|
7
10
|
Usage:
|
|
8
11
|
plur-mcp Start the MCP server (stdio transport)
|
|
9
|
-
plur-mcp init
|
|
12
|
+
plur-mcp init Set up PLUR: storage + MCP config + Claude Code hooks
|
|
10
13
|
plur-mcp --help Show this help message
|
|
11
14
|
plur-mcp --version Show version
|
|
12
15
|
|
|
13
16
|
Environment:
|
|
14
17
|
PLUR_PATH Storage location (default: ~/.plur/)
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
{
|
|
20
|
-
"mcpServers": {
|
|
21
|
-
"plur": { "command": "npx", "args": ["-y", "@plur-ai/mcp@latest"] }
|
|
22
|
-
}
|
|
23
|
-
}
|
|
19
|
+
Quick start:
|
|
20
|
+
npx @plur-ai/mcp init
|
|
24
21
|
|
|
25
22
|
Docs: https://plur.ai \xB7 https://github.com/plur-ai/plur
|
|
26
23
|
`;
|
|
27
|
-
var
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
var MCP_SERVER_CONFIG = {
|
|
25
|
+
command: "npx",
|
|
26
|
+
args: ["-y", "@plur-ai/mcp@latest"]
|
|
27
|
+
};
|
|
28
|
+
var PLUR_HOOKS = {
|
|
29
|
+
UserPromptSubmit: [{
|
|
30
|
+
hooks: [{ type: "command", command: "npx @plur-ai/cli hook-inject", timeout: 15 }]
|
|
31
|
+
}],
|
|
32
|
+
PostCompact: [{
|
|
33
|
+
matcher: "auto|manual",
|
|
34
|
+
hooks: [{ type: "command", command: "npx @plur-ai/cli hook-inject --rehydrate", timeout: 15 }]
|
|
35
|
+
}]
|
|
36
|
+
};
|
|
37
|
+
function findMcpConfig() {
|
|
38
|
+
const projectMcp = join(process.cwd(), ".mcp.json");
|
|
39
|
+
if (existsSync(projectMcp)) return projectMcp;
|
|
40
|
+
const globalMcp = join(homedir(), ".claude", "mcp.json");
|
|
41
|
+
if (existsSync(globalMcp)) return globalMcp;
|
|
42
|
+
return projectMcp;
|
|
31
43
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
function writeMcpConfig(configPath) {
|
|
45
|
+
let config = {};
|
|
46
|
+
if (existsSync(configPath)) {
|
|
47
|
+
try {
|
|
48
|
+
config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const servers = config.mcpServers ?? {};
|
|
53
|
+
if (servers.plur) {
|
|
54
|
+
return `already configured in ${configPath}`;
|
|
55
|
+
}
|
|
56
|
+
servers.plur = MCP_SERVER_CONFIG;
|
|
57
|
+
config.mcpServers = servers;
|
|
58
|
+
const dir = join(configPath, "..");
|
|
59
|
+
mkdirSync(dir, { recursive: true });
|
|
60
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
61
|
+
return `added to ${configPath}`;
|
|
36
62
|
}
|
|
37
|
-
|
|
63
|
+
function installHooks() {
|
|
64
|
+
const projectSettings = join(process.cwd(), ".claude", "settings.json");
|
|
65
|
+
const globalSettings = join(homedir(), ".claude", "settings.json");
|
|
66
|
+
const settingsPath = existsSync(join(process.cwd(), ".claude")) ? projectSettings : globalSettings;
|
|
67
|
+
let settings = {};
|
|
68
|
+
if (existsSync(settingsPath)) {
|
|
69
|
+
try {
|
|
70
|
+
settings = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
71
|
+
} catch {
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const hooks = settings.hooks ?? {};
|
|
75
|
+
for (const entries of Object.values(hooks)) {
|
|
76
|
+
for (const entry of entries) {
|
|
77
|
+
for (const h of entry.hooks ?? []) {
|
|
78
|
+
if (h.command.includes("@plur-ai/cli")) {
|
|
79
|
+
return `already installed in ${settingsPath}`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const existing = settings.hooks ?? {};
|
|
85
|
+
const merged = { ...existing };
|
|
86
|
+
for (const [event, newEntries] of Object.entries(PLUR_HOOKS)) {
|
|
87
|
+
merged[event] = [...merged[event] ?? [], ...newEntries];
|
|
88
|
+
}
|
|
89
|
+
settings.hooks = merged;
|
|
90
|
+
const dir = join(settingsPath, "..");
|
|
91
|
+
mkdirSync(dir, { recursive: true });
|
|
92
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
93
|
+
return `installed in ${settingsPath}`;
|
|
94
|
+
}
|
|
95
|
+
async function runInit() {
|
|
96
|
+
const results = [];
|
|
38
97
|
const { detectPlurStorage } = await import("@plur-ai/core");
|
|
39
98
|
const paths = detectPlurStorage();
|
|
40
|
-
|
|
99
|
+
results.push(`Storage: ${paths.root}`);
|
|
100
|
+
let searchMode = "BM25 keyword search";
|
|
41
101
|
try {
|
|
42
102
|
const mod = "@huggingface/transformers";
|
|
43
103
|
await import(
|
|
44
104
|
/* @vite-ignore */
|
|
45
105
|
mod
|
|
46
106
|
);
|
|
47
|
-
|
|
107
|
+
searchMode = "hybrid (BM25 + embeddings)";
|
|
48
108
|
} catch {
|
|
49
109
|
}
|
|
50
|
-
|
|
110
|
+
results.push(`Search: ${searchMode}`);
|
|
111
|
+
const mcpConfigPath = findMcpConfig();
|
|
112
|
+
const mcpStatus = writeMcpConfig(mcpConfigPath);
|
|
113
|
+
results.push(`MCP: ${mcpStatus}`);
|
|
114
|
+
const hooksStatus = installHooks();
|
|
115
|
+
results.push(`Hooks: ${hooksStatus}`);
|
|
51
116
|
process.stdout.write(`PLUR initialized.
|
|
52
117
|
|
|
53
|
-
|
|
54
|
-
Search: ${searchMode}
|
|
118
|
+
${results.join("\n ")}
|
|
55
119
|
|
|
56
|
-
|
|
120
|
+
`);
|
|
121
|
+
if (mcpStatus.includes("added") || hooksStatus.includes("installed")) {
|
|
122
|
+
process.stdout.write(` Restart Claude Code to activate.
|
|
57
123
|
|
|
58
|
-
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
"plur": { "command": "npx", "args": ["-y", "@plur-ai/mcp@latest"] }
|
|
62
|
-
}
|
|
63
|
-
}
|
|
124
|
+
`);
|
|
125
|
+
} else {
|
|
126
|
+
process.stdout.write(` Everything is set up. Start a new conversation to use PLUR.
|
|
64
127
|
|
|
65
|
-
|
|
66
|
-
{
|
|
67
|
-
"mcpServers": {
|
|
68
|
-
"plur": { "command": "npx", "args": ["-y", "@plur-ai/mcp@latest"] }
|
|
69
|
-
}
|
|
128
|
+
`);
|
|
70
129
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
130
|
+
}
|
|
131
|
+
var arg = process.argv[2];
|
|
132
|
+
if (arg === "--help" || arg === "-h") {
|
|
133
|
+
process.stdout.write(HELP);
|
|
134
|
+
process.exit(0);
|
|
135
|
+
}
|
|
136
|
+
if (arg === "--version" || arg === "-v") {
|
|
137
|
+
process.stdout.write(`${VERSION}
|
|
74
138
|
`);
|
|
75
139
|
process.exit(0);
|
|
76
140
|
}
|
|
141
|
+
if (arg === "init") {
|
|
142
|
+
await runInit();
|
|
143
|
+
process.exit(0);
|
|
144
|
+
}
|
|
77
145
|
if (arg === "serve" || arg === void 0) {
|
|
78
|
-
const { runStdio } = await import("./server-
|
|
146
|
+
const { runStdio } = await import("./server-5BXYBMHX.js");
|
|
79
147
|
runStdio().catch((err) => {
|
|
80
148
|
console.error("Failed to start PLUR MCP server:", err);
|
|
81
149
|
process.exit(1);
|
|
@@ -297,8 +297,15 @@ function getToolDefinitions() {
|
|
|
297
297
|
}
|
|
298
298
|
return { mode: "batch", results, summary };
|
|
299
299
|
}
|
|
300
|
-
|
|
301
|
-
|
|
300
|
+
try {
|
|
301
|
+
plur.feedback(args.id, args.signal);
|
|
302
|
+
return { success: true, id: args.id, signal: args.signal };
|
|
303
|
+
} catch (err) {
|
|
304
|
+
if (err.message?.includes("readonly store")) {
|
|
305
|
+
return { success: false, id: args.id, signal: args.signal, note: "Engram is in a readonly store. Feedback noted for this session but not persisted." };
|
|
306
|
+
}
|
|
307
|
+
throw err;
|
|
308
|
+
}
|
|
302
309
|
}
|
|
303
310
|
},
|
|
304
311
|
{
|
|
@@ -742,27 +749,35 @@ You have ${store_stats.engram_count} engrams but none matched this task. Call pl
|
|
|
742
749
|
},
|
|
743
750
|
{
|
|
744
751
|
name: "plur_session_end",
|
|
745
|
-
description:
|
|
752
|
+
description: `End a session. BEFORE calling this tool, review the conversation and extract learnings:
|
|
753
|
+
|
|
754
|
+
1. Corrections the user made ("no, use X not Y") \u2192 type: behavioral
|
|
755
|
+
2. Preferences stated ("always X", "never Y") \u2192 type: behavioral
|
|
756
|
+
3. Codebase patterns discovered (naming, structure, conventions) \u2192 type: architectural
|
|
757
|
+
4. Technical facts learned (API quirks, config, gotchas) \u2192 type: procedural
|
|
758
|
+
5. Terminology defined or clarified \u2192 type: terminological
|
|
759
|
+
|
|
760
|
+
Include at least one engram_suggestion if ANYTHING was learned. An empty suggestions array means nothing worth remembering happened \u2014 this should be rare.`,
|
|
746
761
|
annotations: { title: "Session End", destructiveHint: false, idempotentHint: false },
|
|
747
762
|
inputSchema: {
|
|
748
763
|
type: "object",
|
|
749
764
|
properties: {
|
|
750
|
-
summary: { type: "string", description: "What happened in this session" },
|
|
765
|
+
summary: { type: "string", description: "What happened in this session (1-3 sentences)" },
|
|
751
766
|
session_id: { type: "string", description: "Session ID from plur_session_start" },
|
|
752
767
|
engram_suggestions: {
|
|
753
768
|
type: "array",
|
|
754
769
|
items: {
|
|
755
770
|
type: "object",
|
|
756
771
|
properties: {
|
|
757
|
-
statement: { type: "string", description: "
|
|
772
|
+
statement: { type: "string", description: "A concise, reusable assertion. Write it as advice to your future self." },
|
|
758
773
|
type: { type: "string", enum: ["behavioral", "terminological", "procedural", "architectural"] }
|
|
759
774
|
},
|
|
760
775
|
required: ["statement"]
|
|
761
776
|
},
|
|
762
|
-
description: "
|
|
777
|
+
description: "Learnings from this session. Review the conversation for corrections, preferences, patterns, and technical facts before calling."
|
|
763
778
|
}
|
|
764
779
|
},
|
|
765
|
-
required: ["summary"]
|
|
780
|
+
required: ["summary", "engram_suggestions"]
|
|
766
781
|
},
|
|
767
782
|
handler: async (args, plur) => {
|
|
768
783
|
const summary = args.summary;
|
|
@@ -896,7 +911,7 @@ You have ${store_stats.engram_count} engrams but none matched this task. Call pl
|
|
|
896
911
|
|
|
897
912
|
// src/server.ts
|
|
898
913
|
import { z } from "zod";
|
|
899
|
-
var VERSION = "0.
|
|
914
|
+
var VERSION = "0.6.0";
|
|
900
915
|
var INSTRUCTIONS = `PLUR is your persistent memory. Corrections, preferences, and conventions persist across sessions as engrams.
|
|
901
916
|
|
|
902
917
|
REQUIRED at session boundaries:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plur-ai/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
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.
|
|
15
|
+
"@plur-ai/core": "0.6.0"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@types/node": "^25.5.0"
|