@lesgo/memory-mcp 1.0.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/dist/writer.js ADDED
@@ -0,0 +1,162 @@
1
+ import { cache } from './cache.js';
2
+ import { writeQueue } from './queue.js';
3
+ import { updateIndex } from './index-gen.js';
4
+ import { log } from './logger.js';
5
+ // Maximum file size: 1MB (GitHub API limit for base64 content)
6
+ const MAX_FILE_SIZE = 1 * 1024 * 1024;
7
+ /**
8
+ * Async writer manager - handles non-blocking GitHub operations
9
+ * Updates cache immediately, returns to agent, syncs GitHub in background
10
+ */
11
+ export class AsyncWriter {
12
+ github;
13
+ pendingWrites = new Set();
14
+ constructor(github) {
15
+ this.github = github;
16
+ }
17
+ /**
18
+ * Save a file - updates cache immediately, syncs GitHub async
19
+ * Returns immediately after cache update
20
+ */
21
+ async save(category, name, content) {
22
+ // Validate file size
23
+ const contentSize = Buffer.byteLength(content, 'utf-8');
24
+ if (contentSize > MAX_FILE_SIZE) {
25
+ return {
26
+ success: false,
27
+ path: '',
28
+ error: `File size (${Math.round(contentSize / 1024)}KB) exceeds maximum allowed size (${MAX_FILE_SIZE / 1024}KB)`,
29
+ };
30
+ }
31
+ // Normalize category and name
32
+ const normalizedCategory = category.toLowerCase().replace(/[^a-z0-9-]/g, '-');
33
+ const normalizedName = name.endsWith('.md') || name.endsWith('.yaml') || name.endsWith('.json')
34
+ ? name
35
+ : `${name}.md`;
36
+ const path = `${normalizedCategory}/${normalizedName}`;
37
+ // Get existing SHA if file exists in cache
38
+ const existing = cache.get(path);
39
+ const sha = existing?.sha;
40
+ // Update cache immediately (optimistic)
41
+ cache.set(path, content, sha ?? 'pending');
42
+ // Fire-and-forget GitHub sync
43
+ this.syncToGitHub(path, content, sha);
44
+ return { success: true, path };
45
+ }
46
+ /**
47
+ * Delete a file - removes from cache immediately, syncs GitHub async
48
+ */
49
+ async delete(path) {
50
+ const existing = cache.get(path);
51
+ if (!existing) {
52
+ return { success: false, error: `File not found: ${path}` };
53
+ }
54
+ // Remove from cache immediately
55
+ cache.delete(path);
56
+ // Fire-and-forget GitHub delete
57
+ this.deleteFromGitHub(path, existing.sha);
58
+ return { success: true };
59
+ }
60
+ /**
61
+ * Sync a file to GitHub (async, non-blocking)
62
+ */
63
+ async syncToGitHub(path, content, sha) {
64
+ // Prevent duplicate syncs
65
+ if (this.pendingWrites.has(path)) {
66
+ log('writer', `Sync already pending for: ${path}`);
67
+ return;
68
+ }
69
+ this.pendingWrites.add(path);
70
+ try {
71
+ log('writer', `Starting async sync: ${path}`);
72
+ const newSha = await this.github.putFile(path, content, sha);
73
+ // Update cache with real SHA
74
+ cache.set(path, content, newSha);
75
+ // Update index
76
+ await this.updateIndexSafe();
77
+ // Remove from queue if it was there
78
+ writeQueue.remove(path);
79
+ log('writer', `Sync complete: ${path}`);
80
+ }
81
+ catch (error) {
82
+ log('writer', `Sync failed for ${path}: ${error}`);
83
+ // Add to queue for retry
84
+ writeQueue.enqueue(path, content, 'save');
85
+ }
86
+ finally {
87
+ this.pendingWrites.delete(path);
88
+ }
89
+ }
90
+ /**
91
+ * Delete a file from GitHub (async, non-blocking)
92
+ */
93
+ async deleteFromGitHub(path, sha) {
94
+ if (this.pendingWrites.has(path)) {
95
+ return;
96
+ }
97
+ this.pendingWrites.add(path);
98
+ try {
99
+ log('writer', `Starting async delete: ${path}`);
100
+ await this.github.deleteFile(path, sha);
101
+ // Update index
102
+ await this.updateIndexSafe();
103
+ // Remove from queue if it was there
104
+ writeQueue.remove(path);
105
+ log('writer', `Delete complete: ${path}`);
106
+ }
107
+ catch (error) {
108
+ log('writer', `Delete failed for ${path}: ${error}`);
109
+ // Add to queue for retry
110
+ writeQueue.enqueue(path, '', 'delete');
111
+ }
112
+ finally {
113
+ this.pendingWrites.delete(path);
114
+ }
115
+ }
116
+ /**
117
+ * Update index without blocking on errors
118
+ */
119
+ async updateIndexSafe() {
120
+ try {
121
+ await updateIndex(this.github, cache);
122
+ }
123
+ catch (error) {
124
+ log('writer', `Index update failed: ${error}`);
125
+ }
126
+ }
127
+ /**
128
+ * Drain the write queue - called on startup and after successful operations
129
+ */
130
+ async drainQueue() {
131
+ const items = writeQueue.getAll();
132
+ if (items.length === 0) {
133
+ return;
134
+ }
135
+ log('queue', `Draining ${items.length} queued items`);
136
+ for (const item of items) {
137
+ try {
138
+ if (item.operation === 'save') {
139
+ const existing = cache.get(item.path);
140
+ await this.github.putFile(item.path, item.content, existing?.sha);
141
+ writeQueue.remove(item.path);
142
+ log('queue', `Drained: ${item.path}`);
143
+ }
144
+ else if (item.operation === 'delete') {
145
+ const existing = cache.get(item.path);
146
+ if (existing) {
147
+ await this.github.deleteFile(item.path, existing.sha);
148
+ }
149
+ writeQueue.remove(item.path);
150
+ log('queue', `Drained delete: ${item.path}`);
151
+ }
152
+ }
153
+ catch (error) {
154
+ log('queue', `Failed to drain ${item.path}: ${error}`);
155
+ writeQueue.incrementRetry(item.path);
156
+ }
157
+ }
158
+ // Update index after draining
159
+ await this.updateIndexSafe();
160
+ }
161
+ }
162
+ //# sourceMappingURL=writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writer.js","sourceRoot":"","sources":["../src/writer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAc,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAElC,+DAA+D;AAC/D,MAAM,aAAa,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAEtC;;;GAGG;AACH,MAAM,OAAO,WAAW;IACd,MAAM,CAAe;IACrB,aAAa,GAAgB,IAAI,GAAG,EAAE,CAAC;IAE/C,YAAY,MAAoB;QAC9B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI,CAAC,QAAgB,EAAE,IAAY,EAAE,OAAe;QACxD,qBAAqB;QACrB,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxD,IAAI,WAAW,GAAG,aAAa,EAAE,CAAC;YAChC,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,IAAI,EAAE,EAAE;gBACR,KAAK,EAAE,cAAc,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,qCAAqC,aAAa,GAAG,IAAI,KAAK;aAClH,CAAC;QACJ,CAAC;QAED,8BAA8B;QAC9B,MAAM,kBAAkB,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QAC9E,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YAC7F,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC;QAEjB,MAAM,IAAI,GAAG,GAAG,kBAAkB,IAAI,cAAc,EAAE,CAAC;QAEvD,2CAA2C;QAC3C,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,GAAG,GAAG,QAAQ,EAAE,GAAG,CAAC;QAE1B,wCAAwC;QACxC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,SAAS,CAAC,CAAC;QAE3C,8BAA8B;QAC9B,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;QAEtC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAEjC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,IAAI,EAAE,EAAE,CAAC;QAC9D,CAAC;QAED,gCAAgC;QAChC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEnB,gCAAgC;QAChC,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;QAE1C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,YAAY,CAAC,IAAY,EAAE,OAAe,EAAE,GAAY;QACpE,0BAA0B;QAC1B,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,GAAG,CAAC,QAAQ,EAAE,6BAA6B,IAAI,EAAE,CAAC,CAAC;YACnD,OAAO;QACT,CAAC;QAED,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAE7B,IAAI,CAAC;YACH,GAAG,CAAC,QAAQ,EAAE,wBAAwB,IAAI,EAAE,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;YAE7D,6BAA6B;YAC7B,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;YAEjC,eAAe;YACf,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;YAE7B,oCAAoC;YACpC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAExB,GAAG,CAAC,QAAQ,EAAE,kBAAkB,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,QAAQ,EAAE,mBAAmB,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC;YAEnD,yBAAyB;YACzB,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAC5C,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,gBAAgB,CAAC,IAAY,EAAE,GAAW;QACtD,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,OAAO;QACT,CAAC;QAED,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAE7B,IAAI,CAAC;YACH,GAAG,CAAC,QAAQ,EAAE,0BAA0B,IAAI,EAAE,CAAC,CAAC;YAChD,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAExC,eAAe;YACf,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;YAE7B,oCAAoC;YACpC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAExB,GAAG,CAAC,QAAQ,EAAE,oBAAoB,IAAI,EAAE,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,QAAQ,EAAE,qBAAqB,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC;YAErD,yBAAyB;YACzB,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;QACzC,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,eAAe;QAC3B,IAAI,CAAC;YACH,MAAM,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,QAAQ,EAAE,wBAAwB,KAAK,EAAE,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU;QACd,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC;QAElC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QAED,GAAG,CAAC,OAAO,EAAE,YAAY,KAAK,CAAC,MAAM,eAAe,CAAC,CAAC;QAEtD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,IAAI,IAAI,CAAC,SAAS,KAAK,MAAM,EAAE,CAAC;oBAC9B,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACtC,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;oBAClE,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAC7B,GAAG,CAAC,OAAO,EAAE,YAAY,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;gBACxC,CAAC;qBAAM,IAAI,IAAI,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;oBACvC,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACtC,IAAI,QAAQ,EAAE,CAAC;wBACb,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;oBACxD,CAAC;oBACD,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAC7B,GAAG,CAAC,OAAO,EAAE,mBAAmB,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;gBAC/C,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,GAAG,CAAC,OAAO,EAAE,mBAAmB,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC;gBACvD,UAAU,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;QAED,8BAA8B;QAC9B,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;IAC/B,CAAC;CACF"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@lesgo/memory-mcp",
3
+ "version": "1.0.0",
4
+ "description": "Personal External Brain MCP Server - A stateless MCP server for syncing templates, standards, and knowledge via GitHub",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "memory-mcp": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/clayton-duarte/memory-mcp.git"
16
+ },
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "dev": "tsc --watch",
20
+ "start": "node dist/index.js",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "keywords": [
24
+ "mcp",
25
+ "model-context-protocol",
26
+ "ai",
27
+ "github",
28
+ "knowledge-base"
29
+ ],
30
+ "author": "",
31
+ "license": "MIT",
32
+ "dependencies": {
33
+ "@modelcontextprotocol/sdk": "^1.0.0",
34
+ "@octokit/rest": "^20.0.0",
35
+ "zod": "^3.22.0"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^20.0.0",
39
+ "typescript": "^5.3.0"
40
+ },
41
+ "engines": {
42
+ "node": ">=18.0.0"
43
+ }
44
+ }