@postnesia/mcp 0.1.8 → 0.1.13
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 +1 -1
- package/dist/index.js +74 -11
- package/dist/install.d.ts +1 -0
- package/dist/install.js +98 -0
- package/package.json +12 -10
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ Add to your MCP config (e.g. `.claude/settings.json` or Claude Desktop):
|
|
|
45
45
|
|
|
46
46
|
| Tool | Required | Optional |
|
|
47
47
|
|---|---|---|
|
|
48
|
-
| `journal_add` | `date` (YYYY-MM-DD), `content` | `learned`, `
|
|
48
|
+
| `journal_add` | `date` (YYYY-MM-DD), `content` | `learned`, `keyMoments`, `mood` |
|
|
49
49
|
| `journal_recent` | — | `days` (default: 7) |
|
|
50
50
|
|
|
51
51
|
### Tasks
|
package/dist/index.js
CHANGED
|
@@ -20,17 +20,37 @@ const server = new McpServer({
|
|
|
20
20
|
// memory tools
|
|
21
21
|
// ---------------------------------------------------------------------------
|
|
22
22
|
server.registerTool("memory_search", {
|
|
23
|
-
description: "Search memories by
|
|
23
|
+
description: "Search memories by semantic similarity. Optionally filter by type.",
|
|
24
24
|
inputSchema: {
|
|
25
25
|
query: z.string().describe("Search query"),
|
|
26
26
|
limit: z.number().optional().describe("Maximum results to return (default: 10)"),
|
|
27
|
+
type: z.enum(["event", "decision", "lesson", "preference", "person", "technical"]).optional().describe("Filter by memory type"),
|
|
27
28
|
},
|
|
28
|
-
}, async ({ query, limit = 10 }) => {
|
|
29
|
+
}, async ({ query, limit = 10, type }) => {
|
|
29
30
|
const [embedding] = await embed(query);
|
|
30
31
|
if (!embedding) {
|
|
31
32
|
throw new ReferenceError('Unable to create query embedding');
|
|
32
33
|
}
|
|
33
|
-
const
|
|
34
|
+
const embeddingBuffer = Buffer.from(embedding.buffer);
|
|
35
|
+
const results = type
|
|
36
|
+
? queries.vectorSearchByType(db).all(embeddingBuffer, limit, type)
|
|
37
|
+
: queries.vectorSearch(db).all(embeddingBuffer, limit);
|
|
38
|
+
for (const result of results) {
|
|
39
|
+
logAccess(result.id, "search");
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
server.registerTool("memory_search_keyword", {
|
|
46
|
+
description: "Keyword search memories by content (use when semantic search is not appropriate).",
|
|
47
|
+
inputSchema: {
|
|
48
|
+
query: z.string().describe("Keyword or phrase to search for"),
|
|
49
|
+
limit: z.number().optional().describe("Maximum results to return (default: 10)"),
|
|
50
|
+
},
|
|
51
|
+
}, async ({ query, limit = 10 }) => {
|
|
52
|
+
const pattern = `%${query}%`;
|
|
53
|
+
const results = queries.searchMemories(db).all(pattern, pattern, limit);
|
|
34
54
|
for (const result of results) {
|
|
35
55
|
logAccess(result.id, "search");
|
|
36
56
|
}
|
|
@@ -38,6 +58,54 @@ server.registerTool("memory_search", {
|
|
|
38
58
|
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
39
59
|
};
|
|
40
60
|
});
|
|
61
|
+
server.registerTool("memory_by_context", {
|
|
62
|
+
description: "Find memories by context string (LIKE match).",
|
|
63
|
+
inputSchema: {
|
|
64
|
+
context: z.string().describe("Context string to search for"),
|
|
65
|
+
limit: z.number().optional().describe("Maximum results (default: 10)"),
|
|
66
|
+
},
|
|
67
|
+
}, async ({ context, limit = 10 }) => {
|
|
68
|
+
const results = queries.getMemoriesByContext(db).all(`%${context}%`, limit);
|
|
69
|
+
for (const r of results)
|
|
70
|
+
logAccess(r.id, "search");
|
|
71
|
+
return {
|
|
72
|
+
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
server.registerTool("memory_by_tag", {
|
|
76
|
+
description: "Find memories by tag (exact match).",
|
|
77
|
+
inputSchema: {
|
|
78
|
+
tag: z.string().describe("Tag to filter by"),
|
|
79
|
+
limit: z.number().optional().describe("Maximum results (default: 10)"),
|
|
80
|
+
},
|
|
81
|
+
}, async ({ tag, limit = 10 }) => {
|
|
82
|
+
const results = queries.getMemoriesByTag(db).all(tag, limit);
|
|
83
|
+
for (const r of results)
|
|
84
|
+
logAccess(r.id, "search");
|
|
85
|
+
return {
|
|
86
|
+
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
server.registerTool("memory_supersede_chain", {
|
|
90
|
+
description: "Trace the supersede chain for a memory (shows the full replacement history).",
|
|
91
|
+
inputSchema: {
|
|
92
|
+
memoryId: z.number().describe("Memory ID to trace"),
|
|
93
|
+
},
|
|
94
|
+
}, async ({ memoryId }) => {
|
|
95
|
+
const chain = queries.getSupersedeChain(db).all(memoryId);
|
|
96
|
+
return {
|
|
97
|
+
content: [{ type: "text", text: JSON.stringify(chain, null, 2) }],
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
server.registerTool("memory_core", {
|
|
101
|
+
description: "Get all core memories (foundational memories that are always loaded).",
|
|
102
|
+
inputSchema: {},
|
|
103
|
+
}, async () => {
|
|
104
|
+
const results = queries.coreMemories(db).all();
|
|
105
|
+
return {
|
|
106
|
+
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
107
|
+
};
|
|
108
|
+
});
|
|
41
109
|
server.registerTool("memory_add", {
|
|
42
110
|
description: "Add a new memory to the database",
|
|
43
111
|
inputSchema: {
|
|
@@ -92,11 +160,7 @@ server.registerTool("memory_update_core", {
|
|
|
92
160
|
if (!embedding) {
|
|
93
161
|
throw new ReferenceError('Unable to create query embedding');
|
|
94
162
|
}
|
|
95
|
-
db.
|
|
96
|
-
UPDATE memory
|
|
97
|
-
SET content = ?, content_l1 = ?, updated_at = datetime('now')
|
|
98
|
-
WHERE id = ?
|
|
99
|
-
`).run(content, contentL1, memoryId);
|
|
163
|
+
queries.updateCoreMemory(db).run(content, contentL1, memoryId);
|
|
100
164
|
db.prepare("DELETE FROM vec_memories WHERE memory_id = ?").run(BigInt(memoryId));
|
|
101
165
|
db.prepare("INSERT INTO vec_memories(memory_id, embedding) VALUES (?, ?)").run(BigInt(memoryId), Buffer.from(embedding.buffer));
|
|
102
166
|
return {
|
|
@@ -212,12 +276,11 @@ server.registerTool("journal_add", {
|
|
|
212
276
|
date: z.string().describe("Date YYYY-MM-DD"),
|
|
213
277
|
content: z.string().describe("Full journal narrative"),
|
|
214
278
|
learned: z.string().optional().describe("What I learned (optional)"),
|
|
215
|
-
learnedAboutRye: z.string().optional().describe("What I learned about Rye (optional)"),
|
|
216
279
|
keyMoments: z.string().optional().describe("Key moments (optional)"),
|
|
217
280
|
mood: z.string().optional().describe("Mood/feeling (optional)"),
|
|
218
281
|
},
|
|
219
|
-
}, async ({ date, content, learned,
|
|
220
|
-
const combinedLearned = [learned
|
|
282
|
+
}, async ({ date, content, learned, keyMoments, mood }) => {
|
|
283
|
+
const combinedLearned = [learned].filter(Boolean).join("\n\n") || null;
|
|
221
284
|
queries.insertJournal(db).run(date, content, combinedLearned, keyMoments || null, mood || null);
|
|
222
285
|
return {
|
|
223
286
|
content: [{ type: "text", text: `Journal entry created for ${date}` }],
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import 'dotenv/config';
|
package/dist/install.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
var TypeEnum;
|
|
3
|
+
(function (TypeEnum) {
|
|
4
|
+
TypeEnum["claude"] = "claude";
|
|
5
|
+
TypeEnum["opencode"] = "opencode";
|
|
6
|
+
})(TypeEnum || (TypeEnum = {}));
|
|
7
|
+
;
|
|
8
|
+
var ScopeEnum;
|
|
9
|
+
(function (ScopeEnum) {
|
|
10
|
+
ScopeEnum["user"] = "user";
|
|
11
|
+
ScopeEnum["project"] = "project";
|
|
12
|
+
ScopeEnum["local"] = "local";
|
|
13
|
+
})(ScopeEnum || (ScopeEnum = {}));
|
|
14
|
+
;
|
|
15
|
+
if (!process.env['DATABASE_URL']) {
|
|
16
|
+
console.error('DATABASE_URL (absolute path) environment variable is not configured');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
if (!process.env['GEMINI_API_KEY']) {
|
|
20
|
+
console.error('GEMINI_API_KEY environment variable is not configured');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const config = {
|
|
24
|
+
claude: {
|
|
25
|
+
mcpServers: {
|
|
26
|
+
postnesia: {
|
|
27
|
+
type: "stdio",
|
|
28
|
+
enabled: true,
|
|
29
|
+
command: "npx",
|
|
30
|
+
args: [
|
|
31
|
+
"pn-mcp"
|
|
32
|
+
],
|
|
33
|
+
env: {
|
|
34
|
+
DATABASE_URL: process.env['DATABASE_URL'],
|
|
35
|
+
GEMINI_API_KEY: process.env['GEMINI_API_KEY']
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
opencode: {
|
|
41
|
+
mcp: {
|
|
42
|
+
postnesia: {
|
|
43
|
+
type: "local",
|
|
44
|
+
enabled: false,
|
|
45
|
+
command: [
|
|
46
|
+
"npx",
|
|
47
|
+
"pn-mcp"
|
|
48
|
+
],
|
|
49
|
+
environment: {
|
|
50
|
+
DATABASE_URL: process.env['DATABASE_URL'],
|
|
51
|
+
GEMINI_API_KEY: process.env['GEMINI_API_KEY']
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const [_, __, type, scope] = process.argv;
|
|
58
|
+
if (type === 'claude') {
|
|
59
|
+
const msg = 'Add the following to your $file configuration:';
|
|
60
|
+
if (scope === 'project') {
|
|
61
|
+
console.log(msg.replace('$file', `${process.cwd()}/.mcp.json`));
|
|
62
|
+
console.log(JSON.stringify(config.claude, null, 2));
|
|
63
|
+
process.exit(0);
|
|
64
|
+
}
|
|
65
|
+
if (scope === 'local') {
|
|
66
|
+
console.log(msg.replace('$file', `${process.env.HOME}/.claude.json for the current project`));
|
|
67
|
+
console.log(JSON.stringify({ projects: { [process.cwd()]: config.claude } }, null, 2));
|
|
68
|
+
process.exit(0);
|
|
69
|
+
}
|
|
70
|
+
if (scope === 'user' || !scope) {
|
|
71
|
+
console.log(msg.replace('$file', `${process.env.HOME}/.claude.json`));
|
|
72
|
+
console.log(JSON.stringify(config.claude, null, 2));
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
console.error('Invalid scope argument provided: "project", "user" or "local"');
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
if (type === 'opencode') {
|
|
79
|
+
const msg = 'Add the following to your $file configuration:';
|
|
80
|
+
if (scope === 'project') {
|
|
81
|
+
console.log(msg.replace('$file', `${process.cwd()}/.opencode.json`));
|
|
82
|
+
console.log(JSON.stringify(config.opencode, null, 2));
|
|
83
|
+
process.exit(0);
|
|
84
|
+
}
|
|
85
|
+
if (!scope || scope === 'user') {
|
|
86
|
+
console.log(msg.replace('$file', `${process.env.HOME}/.config/opencode/opencode.json`));
|
|
87
|
+
console.log(JSON.stringify(config.opencode, null, 2));
|
|
88
|
+
process.exit(0);
|
|
89
|
+
}
|
|
90
|
+
if (scope === 'local') {
|
|
91
|
+
console.log('Local scope does not exist for opencode');
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
console.error('Invalid scope argument provided: "project", "user"');
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
console.error('Argument type opencode or claude is required');
|
|
98
|
+
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@postnesia/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "An MCP server to interact with the Postnesia database",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -8,21 +8,23 @@
|
|
|
8
8
|
"dist"
|
|
9
9
|
],
|
|
10
10
|
"bin": {
|
|
11
|
-
"pn-mcp": "./dist/index.js"
|
|
12
|
-
|
|
13
|
-
"scripts": {
|
|
14
|
-
"build": "pnpm clean && tsc -p tsconfig.json",
|
|
15
|
-
"clean": "rm -rf ./dist",
|
|
16
|
-
"start": "tsx src/index.ts"
|
|
11
|
+
"pn-mcp": "./dist/index.js",
|
|
12
|
+
"pn-mcp-install": "./dist/install.js"
|
|
17
13
|
},
|
|
18
14
|
"dependencies": {
|
|
19
15
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
16
|
+
"zod": "^4.3.6",
|
|
17
|
+
"@postnesia/db": "0.1.13"
|
|
22
18
|
},
|
|
23
19
|
"devDependencies": {
|
|
24
20
|
"@types/node": "^22.10.5",
|
|
21
|
+
"dotenv": "^17.3.1",
|
|
25
22
|
"tsx": "^4.19.2",
|
|
26
23
|
"typescript": "^5.7.3"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "pnpm clean && tsc -p tsconfig.json",
|
|
27
|
+
"clean": "rm -rf ./dist",
|
|
28
|
+
"start": "tsx src/index.ts"
|
|
27
29
|
}
|
|
28
|
-
}
|
|
30
|
+
}
|