@postnesia/mcp 0.1.8 → 0.1.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
@@ -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 content. Returns all matching memories.",
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 results = queries.vectorSearch(db).all(Buffer.from(embedding.buffer), limit);
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.prepare(`
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,7 +276,7 @@ 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)"),
279
+ learnedAboutRye: z.string().optional().describe("What I learned about the user (optional)"),
216
280
  keyMoments: z.string().optional().describe("Key moments (optional)"),
217
281
  mood: z.string().optional().describe("Mood/feeling (optional)"),
218
282
  },
@@ -0,0 +1 @@
1
+ import 'dotenv/config';
@@ -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.8",
3
+ "version": "0.1.12",
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
- "@postnesia/db": "workspace:*",
21
- "zod": "^4.3.6"
16
+ "zod": "^4.3.6",
17
+ "@postnesia/db": "0.1.12"
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
+ }