getmnemo-mcp 0.1.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/LICENSE +21 -0
- package/README.md +72 -0
- package/dist/chunk-KXH67NNU.js +261 -0
- package/dist/chunk-KXH67NNU.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +31 -0
- package/dist/cli.js.map +1 -0
- package/dist/http.d.ts +1 -0
- package/dist/http.js +84 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +94 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mnemo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# @ledgermem/mcp-server
|
|
2
|
+
|
|
3
|
+
Model Context Protocol server for [LedgerMem Memory](https://proofly.dev) — exposes long-term memory tools to any MCP client (Claude Desktop, Cursor, Windsurf, VS Code, Zed).
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
| Tool | What it does |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `memory_search` | Hybrid 7-strategy retrieval over the workspace memory store. |
|
|
10
|
+
| `memory_add` | Store an atomic fact with optional metadata. |
|
|
11
|
+
| `memory_update` | Patch an existing memory's content or metadata. |
|
|
12
|
+
| `memory_delete` | Permanently remove a memory by ID. |
|
|
13
|
+
| `memory_list` | Paginate through memories (cursor-based). |
|
|
14
|
+
|
|
15
|
+
All calls are scoped to a workspace and (optionally) an actor.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
### Claude Desktop / Cursor / Windsurf / VS Code / Zed
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx -y @ledgermem/mcp-server
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or wire it into the client config directly. Example for Claude Desktop (`~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"ledgermem": {
|
|
31
|
+
"command": "npx",
|
|
32
|
+
"args": ["-y", "@ledgermem/mcp-server"],
|
|
33
|
+
"env": {
|
|
34
|
+
"LEDGERMEM_API_KEY": "lk_live_...",
|
|
35
|
+
"LEDGERMEM_WORKSPACE_ID": "ws_..."
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Get an API key at <https://app.proofly.dev/settings/api-keys>.
|
|
43
|
+
|
|
44
|
+
### Hosted (HTTP/SSE) at `mcp.proofly.dev`
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx -y install-mcp@latest https://mcp.proofly.dev/mcp --client claude
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
(OAuth flow lands in v0.2 — until then the hosted endpoint accepts `x-ledgermem-api-key` + `x-ledgermem-workspace-id` headers from trusted clients.)
|
|
51
|
+
|
|
52
|
+
## Develop
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm install
|
|
56
|
+
cp .env.example .env # fill in LEDGERMEM_API_KEY + LEDGERMEM_WORKSPACE_ID
|
|
57
|
+
npm run dev # stdio
|
|
58
|
+
npm run dev:http # HTTP/SSE on :8787
|
|
59
|
+
npm run build # bundle to dist/
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Architecture
|
|
63
|
+
|
|
64
|
+
- **stdio** (`src/cli.ts`): one process per MCP client connection, env-configured.
|
|
65
|
+
- **HTTP/SSE** (`src/http.ts`): single long-running process for hosted use, header-or-OAuth auth.
|
|
66
|
+
- Both transports share `src/server.ts` (tool registration + dispatch) and `src/api-client.ts` (typed REST wrapper).
|
|
67
|
+
|
|
68
|
+
The server deliberately does NOT depend on `@ledgermem/memory` (the JS SDK) so it can ship independently.
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
// src/api-client.ts
|
|
2
|
+
var MnemoApiError = class extends Error {
|
|
3
|
+
constructor(message, status, body) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.body = body;
|
|
7
|
+
this.name = "MnemoApiError";
|
|
8
|
+
}
|
|
9
|
+
status;
|
|
10
|
+
body;
|
|
11
|
+
};
|
|
12
|
+
var MnemoApiClient = class {
|
|
13
|
+
baseUrl;
|
|
14
|
+
headers;
|
|
15
|
+
fetchImpl;
|
|
16
|
+
timeoutMs;
|
|
17
|
+
constructor(cfg) {
|
|
18
|
+
if (!cfg.apiKey) throw new Error("apiKey is required");
|
|
19
|
+
if (!cfg.workspaceId) throw new Error("workspaceId is required");
|
|
20
|
+
this.baseUrl = cfg.baseUrl.replace(/\/$/, "");
|
|
21
|
+
this.headers = {
|
|
22
|
+
"authorization": `Bearer ${cfg.apiKey}`,
|
|
23
|
+
"x-workspace-id": cfg.workspaceId,
|
|
24
|
+
"content-type": "application/json",
|
|
25
|
+
"user-agent": "@mnemo/mcp-server",
|
|
26
|
+
...cfg.actorId ? { "x-actor-id": cfg.actorId } : {}
|
|
27
|
+
};
|
|
28
|
+
this.fetchImpl = cfg.fetch ?? fetch;
|
|
29
|
+
this.timeoutMs = cfg.timeoutMs ?? 3e4;
|
|
30
|
+
}
|
|
31
|
+
async search(input) {
|
|
32
|
+
return this.request("POST", "/v1/search", {
|
|
33
|
+
query: input.query,
|
|
34
|
+
limit: input.limit ?? 8,
|
|
35
|
+
...input.actorId !== void 0 ? { actorId: input.actorId } : {}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
async addMemory(input) {
|
|
39
|
+
return this.request("POST", "/v1/memories", {
|
|
40
|
+
content: input.content,
|
|
41
|
+
...input.metadata !== void 0 ? { metadata: input.metadata } : {},
|
|
42
|
+
...input.actorId !== void 0 ? { actorId: input.actorId } : {}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async updateMemory(id, input) {
|
|
46
|
+
return this.request("PATCH", `/v1/memories/${encodeURIComponent(id)}`, input);
|
|
47
|
+
}
|
|
48
|
+
async deleteMemory(id) {
|
|
49
|
+
return this.request(
|
|
50
|
+
"DELETE",
|
|
51
|
+
`/v1/memories/${encodeURIComponent(id)}`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
async listMemories(input) {
|
|
55
|
+
const params = new URLSearchParams();
|
|
56
|
+
if (input?.limit !== void 0) params.set("limit", String(input.limit));
|
|
57
|
+
if (input?.cursor !== void 0) params.set("cursor", input.cursor);
|
|
58
|
+
if (input?.actorId !== void 0) params.set("actorId", input.actorId);
|
|
59
|
+
const qs = params.toString();
|
|
60
|
+
return this.request("GET", `/v1/memories${qs ? `?${qs}` : ""}`);
|
|
61
|
+
}
|
|
62
|
+
async request(method, path, body) {
|
|
63
|
+
const ctrl = new AbortController();
|
|
64
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
65
|
+
try {
|
|
66
|
+
const res = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
67
|
+
method,
|
|
68
|
+
headers: { ...this.headers },
|
|
69
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
70
|
+
signal: ctrl.signal
|
|
71
|
+
});
|
|
72
|
+
const text = await res.text();
|
|
73
|
+
const parsed = text ? safeJson(text) : void 0;
|
|
74
|
+
if (!res.ok) {
|
|
75
|
+
const msg = (parsed && typeof parsed === "object" && "message" in parsed ? String(parsed.message) : null) ?? `HTTP ${res.status} ${res.statusText}`;
|
|
76
|
+
throw new MnemoApiError(msg, res.status, parsed);
|
|
77
|
+
}
|
|
78
|
+
if (parsed !== void 0 && typeof parsed !== "object") {
|
|
79
|
+
throw new MnemoApiError(
|
|
80
|
+
`Expected JSON object response, got: ${typeof parsed}`,
|
|
81
|
+
res.status,
|
|
82
|
+
parsed
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
return parsed;
|
|
86
|
+
} finally {
|
|
87
|
+
clearTimeout(timer);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
function safeJson(s) {
|
|
92
|
+
try {
|
|
93
|
+
return JSON.parse(s);
|
|
94
|
+
} catch {
|
|
95
|
+
return s;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/server.ts
|
|
100
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
101
|
+
import {
|
|
102
|
+
CallToolRequestSchema,
|
|
103
|
+
ErrorCode,
|
|
104
|
+
ListToolsRequestSchema,
|
|
105
|
+
McpError
|
|
106
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
107
|
+
import { z } from "zod";
|
|
108
|
+
var SearchInput = z.object({
|
|
109
|
+
query: z.string().min(1).max(2e3).describe("Natural-language search query."),
|
|
110
|
+
limit: z.number().int().min(1).max(50).default(8).describe("Max number of memories to return."),
|
|
111
|
+
actor_id: z.string().min(1).max(256).optional().describe("Optional actor scope (defaults to the configured actor).")
|
|
112
|
+
});
|
|
113
|
+
var METADATA_MAX_SERIALIZED_BYTES = 16 * 1024;
|
|
114
|
+
var boundedMetadata = z.record(z.unknown()).refine(
|
|
115
|
+
(m) => {
|
|
116
|
+
try {
|
|
117
|
+
return Buffer.byteLength(JSON.stringify(m), "utf8") <= METADATA_MAX_SERIALIZED_BYTES;
|
|
118
|
+
} catch {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
{ message: `metadata exceeds ${METADATA_MAX_SERIALIZED_BYTES} bytes when serialized` }
|
|
123
|
+
);
|
|
124
|
+
var AddInput = z.object({
|
|
125
|
+
content: z.string().min(1).max(1e4).describe("The fact or memory to store."),
|
|
126
|
+
metadata: boundedMetadata.optional().describe("Arbitrary JSON metadata (tags, source, etc.). Max 16KB serialized."),
|
|
127
|
+
actor_id: z.string().min(1).max(256).optional()
|
|
128
|
+
});
|
|
129
|
+
var UpdateInput = z.object({
|
|
130
|
+
id: z.string().min(1).max(256).describe("Memory ID returned by memory_add or memory_search."),
|
|
131
|
+
content: z.string().min(1).max(1e4).optional(),
|
|
132
|
+
metadata: boundedMetadata.optional()
|
|
133
|
+
});
|
|
134
|
+
var DeleteInput = z.object({
|
|
135
|
+
id: z.string().min(1).max(256).describe("Memory ID to delete.")
|
|
136
|
+
});
|
|
137
|
+
var ListInput = z.object({
|
|
138
|
+
limit: z.number().int().min(1).max(100).default(20),
|
|
139
|
+
cursor: z.string().min(1).max(1024).optional(),
|
|
140
|
+
actor_id: z.string().min(1).max(256).optional()
|
|
141
|
+
});
|
|
142
|
+
var TOOLS = [
|
|
143
|
+
{
|
|
144
|
+
name: "memory_search",
|
|
145
|
+
description: "Search the Mnemo memory store for facts relevant to a query. Returns ranked hits with content, score, and source citations. Use this BEFORE answering any question that might require remembered context.",
|
|
146
|
+
inputSchema: {
|
|
147
|
+
type: "object",
|
|
148
|
+
properties: {
|
|
149
|
+
query: { type: "string", description: "Natural-language search query." },
|
|
150
|
+
limit: { type: "integer", minimum: 1, maximum: 50, default: 8 },
|
|
151
|
+
actor_id: { type: "string" }
|
|
152
|
+
},
|
|
153
|
+
required: ["query"]
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: "memory_add",
|
|
158
|
+
description: "Store a new atomic fact in long-term memory. Use this whenever the user reveals durable preferences, facts about themselves, or context that should persist across sessions.",
|
|
159
|
+
inputSchema: {
|
|
160
|
+
type: "object",
|
|
161
|
+
properties: {
|
|
162
|
+
content: { type: "string" },
|
|
163
|
+
metadata: { type: "object" },
|
|
164
|
+
actor_id: { type: "string" }
|
|
165
|
+
},
|
|
166
|
+
required: ["content"]
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: "memory_update",
|
|
171
|
+
description: "Update an existing memory's content or metadata. Use when a previously-stored fact is no longer accurate.",
|
|
172
|
+
inputSchema: {
|
|
173
|
+
type: "object",
|
|
174
|
+
properties: {
|
|
175
|
+
id: { type: "string" },
|
|
176
|
+
content: { type: "string" },
|
|
177
|
+
metadata: { type: "object" }
|
|
178
|
+
},
|
|
179
|
+
required: ["id"]
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: "memory_delete",
|
|
184
|
+
description: "Delete a memory by ID. Use only when the user explicitly asks to forget something or when a fact is permanently invalid.",
|
|
185
|
+
inputSchema: {
|
|
186
|
+
type: "object",
|
|
187
|
+
properties: { id: { type: "string" } },
|
|
188
|
+
required: ["id"]
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: "memory_list",
|
|
193
|
+
description: "List memories in the workspace with cursor pagination. Useful for review/debug; prefer memory_search for retrieval.",
|
|
194
|
+
inputSchema: {
|
|
195
|
+
type: "object",
|
|
196
|
+
properties: {
|
|
197
|
+
limit: { type: "integer", minimum: 1, maximum: 100, default: 20 },
|
|
198
|
+
cursor: { type: "string" },
|
|
199
|
+
actor_id: { type: "string" }
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
];
|
|
204
|
+
function createServer(cfg) {
|
|
205
|
+
const api = new MnemoApiClient(cfg);
|
|
206
|
+
const server = new Server(
|
|
207
|
+
{ name: "getmnemo", version: "0.1.0" },
|
|
208
|
+
{ capabilities: { tools: {} } }
|
|
209
|
+
);
|
|
210
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
211
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
212
|
+
const { name, arguments: args } = req.params;
|
|
213
|
+
try {
|
|
214
|
+
const result = await dispatch(api, name, args ?? {});
|
|
215
|
+
return {
|
|
216
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
217
|
+
};
|
|
218
|
+
} catch (err) {
|
|
219
|
+
if (err instanceof McpError) throw err;
|
|
220
|
+
const message = err instanceof MnemoApiError ? `Mnemo API error (${err.status}): ${err.message}` : err instanceof z.ZodError ? `Invalid arguments: ${err.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}` : err instanceof Error ? err.message : "Unknown error";
|
|
221
|
+
return {
|
|
222
|
+
isError: true,
|
|
223
|
+
content: [{ type: "text", text: message }]
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
return server;
|
|
228
|
+
}
|
|
229
|
+
async function dispatch(api, name, raw) {
|
|
230
|
+
switch (name) {
|
|
231
|
+
case "memory_search": {
|
|
232
|
+
const i = SearchInput.parse(raw);
|
|
233
|
+
return api.search({ query: i.query, limit: i.limit, actorId: i.actor_id });
|
|
234
|
+
}
|
|
235
|
+
case "memory_add": {
|
|
236
|
+
const i = AddInput.parse(raw);
|
|
237
|
+
return api.addMemory({ content: i.content, metadata: i.metadata, actorId: i.actor_id });
|
|
238
|
+
}
|
|
239
|
+
case "memory_update": {
|
|
240
|
+
const i = UpdateInput.parse(raw);
|
|
241
|
+
return api.updateMemory(i.id, { content: i.content, metadata: i.metadata });
|
|
242
|
+
}
|
|
243
|
+
case "memory_delete": {
|
|
244
|
+
const i = DeleteInput.parse(raw);
|
|
245
|
+
return api.deleteMemory(i.id);
|
|
246
|
+
}
|
|
247
|
+
case "memory_list": {
|
|
248
|
+
const i = ListInput.parse(raw);
|
|
249
|
+
return api.listMemories({ limit: i.limit, cursor: i.cursor, actorId: i.actor_id });
|
|
250
|
+
}
|
|
251
|
+
default:
|
|
252
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export {
|
|
257
|
+
MnemoApiError,
|
|
258
|
+
MnemoApiClient,
|
|
259
|
+
createServer
|
|
260
|
+
};
|
|
261
|
+
//# sourceMappingURL=chunk-KXH67NNU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api-client.ts","../src/server.ts"],"sourcesContent":["/**\n * Thin REST client for the Mnemo Memory API.\n *\n * This duplicates the surface area we need for MCP tools — we deliberately\n * do NOT depend on @mnemo/memory here so this server can ship even if\n * the JS SDK lags behind. When the SDK stabilises, swap this for it.\n */\n\nexport type Memory = {\n id: string\n content: string\n metadata?: Record<string, unknown>\n workspaceId: string\n actorId?: string | null\n createdAt: string\n updatedAt: string\n}\n\nexport type SearchHit = {\n memoryId: string\n content: string\n score: number\n metadata?: Record<string, unknown>\n source?: { documentId?: string; chunkId?: string } | null\n}\n\nexport type SearchResponse = {\n hits: SearchHit[]\n query: string\n latencyMs: number\n}\n\nexport type ApiClientConfig = {\n baseUrl: string\n apiKey: string\n workspaceId: string\n actorId?: string\n fetch?: typeof fetch\n /** Per-request timeout in ms (default 30s). */\n timeoutMs?: number\n}\n\nexport class MnemoApiError extends Error {\n constructor(\n message: string,\n readonly status: number,\n readonly body?: unknown,\n ) {\n super(message)\n this.name = 'MnemoApiError'\n }\n}\n\nexport class MnemoApiClient {\n private readonly baseUrl: string\n private readonly headers: Record<string, string>\n private readonly fetchImpl: typeof fetch\n private readonly timeoutMs: number\n\n constructor(cfg: ApiClientConfig) {\n if (!cfg.apiKey) throw new Error('apiKey is required')\n if (!cfg.workspaceId) throw new Error('workspaceId is required')\n this.baseUrl = cfg.baseUrl.replace(/\\/$/, '')\n this.headers = {\n 'authorization': `Bearer ${cfg.apiKey}`,\n 'x-workspace-id': cfg.workspaceId,\n 'content-type': 'application/json',\n 'user-agent': '@mnemo/mcp-server',\n ...(cfg.actorId ? { 'x-actor-id': cfg.actorId } : {}),\n }\n this.fetchImpl = cfg.fetch ?? fetch\n this.timeoutMs = cfg.timeoutMs ?? 30_000\n }\n\n async search(input: {\n query: string\n limit?: number\n actorId?: string\n }): Promise<SearchResponse> {\n return this.request<SearchResponse>('POST', '/v1/search', {\n query: input.query,\n limit: input.limit ?? 8,\n ...(input.actorId !== undefined ? { actorId: input.actorId } : {}),\n })\n }\n\n async addMemory(input: {\n content: string\n metadata?: Record<string, unknown>\n actorId?: string\n }): Promise<Memory> {\n return this.request<Memory>('POST', '/v1/memories', {\n content: input.content,\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n ...(input.actorId !== undefined ? { actorId: input.actorId } : {}),\n })\n }\n\n async updateMemory(\n id: string,\n input: { content?: string; metadata?: Record<string, unknown> },\n ): Promise<Memory> {\n return this.request<Memory>('PATCH', `/v1/memories/${encodeURIComponent(id)}`, input)\n }\n\n async deleteMemory(id: string): Promise<{ id: string; deleted: true }> {\n return this.request<{ id: string; deleted: true }>(\n 'DELETE',\n `/v1/memories/${encodeURIComponent(id)}`,\n )\n }\n\n async listMemories(input?: {\n limit?: number\n cursor?: string\n actorId?: string\n }): Promise<{ items: Memory[]; nextCursor: string | null }> {\n const params = new URLSearchParams()\n if (input?.limit !== undefined) params.set('limit', String(input.limit))\n if (input?.cursor !== undefined) params.set('cursor', input.cursor)\n if (input?.actorId !== undefined) params.set('actorId', input.actorId)\n const qs = params.toString()\n return this.request('GET', `/v1/memories${qs ? `?${qs}` : ''}`)\n }\n\n private async request<T>(\n method: string,\n path: string,\n body?: unknown,\n ): Promise<T> {\n const ctrl = new AbortController()\n const timer = setTimeout(() => ctrl.abort(), this.timeoutMs)\n try {\n const res = await this.fetchImpl(`${this.baseUrl}${path}`, {\n method,\n headers: { ...this.headers },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: ctrl.signal,\n })\n const text = await res.text()\n const parsed = text ? safeJson(text) : undefined\n if (!res.ok) {\n const msg =\n (parsed && typeof parsed === 'object' && 'message' in parsed\n ? String((parsed as { message: unknown }).message)\n : null) ?? `HTTP ${res.status} ${res.statusText}`\n throw new MnemoApiError(msg, res.status, parsed)\n }\n if (parsed !== undefined && typeof parsed !== 'object') {\n throw new MnemoApiError(\n `Expected JSON object response, got: ${typeof parsed}`,\n res.status,\n parsed,\n )\n }\n return parsed as T\n } finally {\n clearTimeout(timer)\n }\n }\n}\n\nfunction safeJson(s: string): unknown {\n try {\n return JSON.parse(s)\n } catch {\n return s\n }\n}\n","/**\n * MCP server factory.\n *\n * Exposes 5 tools to MCP clients (Claude Desktop, Cursor, Windsurf, VS Code,\n * Zed): memory_search, memory_add, memory_update, memory_delete, memory_list.\n *\n * Transport-agnostic — wire to stdio (cli.ts) or HTTP/SSE (http.ts).\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js'\nimport {\n CallToolRequestSchema,\n ErrorCode,\n ListToolsRequestSchema,\n McpError,\n type Tool,\n} from '@modelcontextprotocol/sdk/types.js'\nimport { z } from 'zod'\n\nimport { MnemoApiClient, type ApiClientConfig, MnemoApiError } from './api-client.js'\n\nconst SearchInput = z.object({\n query: z.string().min(1).max(2000).describe('Natural-language search query.'),\n limit: z.number().int().min(1).max(50).default(8).describe('Max number of memories to return.'),\n actor_id: z\n .string()\n .min(1)\n .max(256)\n .optional()\n .describe('Optional actor scope (defaults to the configured actor).'),\n})\n\n// Cap metadata size so a malicious or buggy client cannot push a 10MB blob\n// through the MCP boundary (the upstream API enforces its own limits, but\n// we'd rather reject early than waste a round-trip).\nconst METADATA_MAX_SERIALIZED_BYTES = 16 * 1024\nconst boundedMetadata = z\n .record(z.unknown())\n .refine(\n (m) => {\n try {\n return Buffer.byteLength(JSON.stringify(m), 'utf8') <= METADATA_MAX_SERIALIZED_BYTES\n } catch {\n return false\n }\n },\n { message: `metadata exceeds ${METADATA_MAX_SERIALIZED_BYTES} bytes when serialized` },\n )\n\nconst AddInput = z.object({\n content: z.string().min(1).max(10_000).describe('The fact or memory to store.'),\n metadata: boundedMetadata\n .optional()\n .describe('Arbitrary JSON metadata (tags, source, etc.). Max 16KB serialized.'),\n actor_id: z.string().min(1).max(256).optional(),\n})\n\nconst UpdateInput = z.object({\n id: z.string().min(1).max(256).describe('Memory ID returned by memory_add or memory_search.'),\n content: z.string().min(1).max(10_000).optional(),\n metadata: boundedMetadata.optional(),\n})\n\nconst DeleteInput = z.object({\n id: z.string().min(1).max(256).describe('Memory ID to delete.'),\n})\n\nconst ListInput = z.object({\n limit: z.number().int().min(1).max(100).default(20),\n cursor: z.string().min(1).max(1024).optional(),\n actor_id: z.string().min(1).max(256).optional(),\n})\n\nconst TOOLS: Tool[] = [\n {\n name: 'memory_search',\n description:\n 'Search the Mnemo memory store for facts relevant to a query. Returns ranked hits with content, score, and source citations. Use this BEFORE answering any question that might require remembered context.',\n inputSchema: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Natural-language search query.' },\n limit: { type: 'integer', minimum: 1, maximum: 50, default: 8 },\n actor_id: { type: 'string' },\n },\n required: ['query'],\n },\n },\n {\n name: 'memory_add',\n description:\n 'Store a new atomic fact in long-term memory. Use this whenever the user reveals durable preferences, facts about themselves, or context that should persist across sessions.',\n inputSchema: {\n type: 'object',\n properties: {\n content: { type: 'string' },\n metadata: { type: 'object' },\n actor_id: { type: 'string' },\n },\n required: ['content'],\n },\n },\n {\n name: 'memory_update',\n description:\n \"Update an existing memory's content or metadata. Use when a previously-stored fact is no longer accurate.\",\n inputSchema: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n content: { type: 'string' },\n metadata: { type: 'object' },\n },\n required: ['id'],\n },\n },\n {\n name: 'memory_delete',\n description:\n 'Delete a memory by ID. Use only when the user explicitly asks to forget something or when a fact is permanently invalid.',\n inputSchema: {\n type: 'object',\n properties: { id: { type: 'string' } },\n required: ['id'],\n },\n },\n {\n name: 'memory_list',\n description:\n 'List memories in the workspace with cursor pagination. Useful for review/debug; prefer memory_search for retrieval.',\n inputSchema: {\n type: 'object',\n properties: {\n limit: { type: 'integer', minimum: 1, maximum: 100, default: 20 },\n cursor: { type: 'string' },\n actor_id: { type: 'string' },\n },\n },\n },\n]\n\nexport function createServer(cfg: ApiClientConfig): Server {\n const api = new MnemoApiClient(cfg)\n const server = new Server(\n { name: 'getmnemo', version: '0.1.0' },\n { capabilities: { tools: {} } },\n )\n\n server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }))\n\n server.setRequestHandler(CallToolRequestSchema, async (req) => {\n const { name, arguments: args } = req.params\n try {\n const result = await dispatch(api, name, args ?? {})\n return {\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],\n }\n } catch (err) {\n if (err instanceof McpError) throw err\n const message =\n err instanceof MnemoApiError\n ? `Mnemo API error (${err.status}): ${err.message}`\n : err instanceof z.ZodError\n ? `Invalid arguments: ${err.issues.map((i) => `${i.path.join('.') || '(root)'}: ${i.message}`).join('; ')}`\n : err instanceof Error\n ? err.message\n : 'Unknown error'\n return {\n isError: true,\n content: [{ type: 'text', text: message }],\n }\n }\n })\n\n return server\n}\n\nasync function dispatch(\n api: MnemoApiClient,\n name: string,\n raw: Record<string, unknown>,\n): Promise<unknown> {\n switch (name) {\n case 'memory_search': {\n const i = SearchInput.parse(raw)\n return api.search({ query: i.query, limit: i.limit, actorId: i.actor_id })\n }\n case 'memory_add': {\n const i = AddInput.parse(raw)\n return api.addMemory({ content: i.content, metadata: i.metadata, actorId: i.actor_id })\n }\n case 'memory_update': {\n const i = UpdateInput.parse(raw)\n return api.updateMemory(i.id, { content: i.content, metadata: i.metadata })\n }\n case 'memory_delete': {\n const i = DeleteInput.parse(raw)\n return api.deleteMemory(i.id)\n }\n case 'memory_list': {\n const i = ListInput.parse(raw)\n return api.listMemories({ limit: i.limit, cursor: i.cursor, actorId: i.actor_id })\n }\n default:\n throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`)\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
createServer
|
|
4
|
+
} from "./chunk-KXH67NNU.js";
|
|
5
|
+
|
|
6
|
+
// src/cli.ts
|
|
7
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
+
async function main() {
|
|
9
|
+
const apiKey = process.env.GETMNEMO_API_KEY;
|
|
10
|
+
const workspaceId = process.env.GETMNEMO_WORKSPACE_ID;
|
|
11
|
+
if (!apiKey || !workspaceId) {
|
|
12
|
+
process.stderr.write(
|
|
13
|
+
"Mnemo MCP: missing GETMNEMO_API_KEY and/or GETMNEMO_WORKSPACE_ID env vars.\nGet a key at https://app.getmnemo.xyz/settings/api-keys\n"
|
|
14
|
+
);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const server = createServer({
|
|
18
|
+
baseUrl: process.env.GETMNEMO_API_URL ?? "https://api.getmnemo.xyz",
|
|
19
|
+
apiKey,
|
|
20
|
+
workspaceId,
|
|
21
|
+
actorId: process.env.GETMNEMO_ACTOR_ID
|
|
22
|
+
});
|
|
23
|
+
const transport = new StdioServerTransport();
|
|
24
|
+
await server.connect(transport);
|
|
25
|
+
}
|
|
26
|
+
main().catch((err) => {
|
|
27
|
+
process.stderr.write(`Mnemo MCP fatal: ${err instanceof Error ? err.message : String(err)}
|
|
28
|
+
`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Stdio entry-point for local MCP clients (Claude Desktop, Cursor, Windsurf, Zed).\n *\n * Reads config from env:\n * GETMNEMO_API_URL (default: https://api.getmnemo.xyz)\n * GETMNEMO_API_KEY (required)\n * GETMNEMO_WORKSPACE_ID (required)\n * GETMNEMO_ACTOR_ID (optional)\n */\n\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { createServer } from './server.js'\n\nasync function main(): Promise<void> {\n const apiKey = process.env.GETMNEMO_API_KEY\n const workspaceId = process.env.GETMNEMO_WORKSPACE_ID\n if (!apiKey || !workspaceId) {\n process.stderr.write(\n 'Mnemo MCP: missing GETMNEMO_API_KEY and/or GETMNEMO_WORKSPACE_ID env vars.\\n' +\n 'Get a key at https://app.getmnemo.xyz/settings/api-keys\\n',\n )\n process.exit(1)\n }\n\n const server = createServer({\n baseUrl: process.env.GETMNEMO_API_URL ?? 'https://api.getmnemo.xyz',\n apiKey,\n workspaceId,\n actorId: process.env.GETMNEMO_ACTOR_ID,\n })\n\n const transport = new StdioServerTransport()\n await server.connect(transport)\n // Stays alive until parent process closes stdio.\n}\n\nmain().catch((err) => {\n process.stderr.write(`Mnemo MCP fatal: ${err instanceof Error ? err.message : String(err)}\\n`)\n process.exit(1)\n})\n"],"mappings":";;;;;;AAWA,SAAS,4BAA4B;AAGrC,eAAe,OAAsB;AACnC,QAAM,SAAS,QAAQ,IAAI;AAC3B,QAAM,cAAc,QAAQ,IAAI;AAChC,MAAI,CAAC,UAAU,CAAC,aAAa;AAC3B,YAAQ,OAAO;AAAA,MACb;AAAA,IAEF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,aAAa;AAAA,IAC1B,SAAS,QAAQ,IAAI,oBAAoB;AAAA,IACzC;AAAA,IACA;AAAA,IACA,SAAS,QAAQ,IAAI;AAAA,EACvB,CAAC;AAED,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAEhC;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,OAAO,MAAM,oBAAoB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,CAAI;AAC7F,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
createServer
|
|
4
|
+
} from "./chunk-KXH67NNU.js";
|
|
5
|
+
|
|
6
|
+
// src/http.ts
|
|
7
|
+
import { createServer as createHttpServer } from "http";
|
|
8
|
+
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
9
|
+
var PORT = Number(process.env.PORT ?? 8787);
|
|
10
|
+
var DEFAULT_API_URL = process.env.GETMNEMO_API_URL ?? "https://api.getmnemo.xyz";
|
|
11
|
+
var httpServer = createHttpServer((req, res) => {
|
|
12
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
13
|
+
if (url.pathname === "/healthz") {
|
|
14
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
15
|
+
res.end(JSON.stringify({ status: "ok", service: "getmnemo-mcp" }));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (url.pathname !== "/mcp") {
|
|
19
|
+
res.writeHead(404, { "content-type": "text/plain" });
|
|
20
|
+
res.end("Not found. GET /mcp for the SSE stream, GET /healthz for liveness.\n");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (req.method !== "GET" && req.method !== "POST") {
|
|
24
|
+
res.writeHead(405, { "content-type": "text/plain", allow: "GET, POST" });
|
|
25
|
+
res.end("Method not allowed.\n");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const apiKey = req.headers["x-getmnemo-api-key"] ?? process.env.GETMNEMO_API_KEY;
|
|
29
|
+
const workspaceId = req.headers["x-getmnemo-workspace-id"] ?? process.env.GETMNEMO_WORKSPACE_ID;
|
|
30
|
+
if (!apiKey || !workspaceId) {
|
|
31
|
+
res.writeHead(401, { "content-type": "application/json" });
|
|
32
|
+
res.end(
|
|
33
|
+
JSON.stringify({
|
|
34
|
+
error: "Missing x-getmnemo-api-key and/or x-getmnemo-workspace-id headers.",
|
|
35
|
+
hint: "OAuth flow lands in v0.2; headers work today for trusted clients."
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const server = createServer({
|
|
41
|
+
baseUrl: DEFAULT_API_URL,
|
|
42
|
+
apiKey,
|
|
43
|
+
workspaceId,
|
|
44
|
+
actorId: req.headers["x-getmnemo-actor-id"]
|
|
45
|
+
});
|
|
46
|
+
const transport = new SSEServerTransport("/mcp", res);
|
|
47
|
+
const KEEPALIVE_MS = 25e3;
|
|
48
|
+
let closed = false;
|
|
49
|
+
const keepalive = setInterval(() => {
|
|
50
|
+
if (closed || res.writableEnded || res.destroyed) return;
|
|
51
|
+
try {
|
|
52
|
+
res.write(`: keepalive ${Date.now()}
|
|
53
|
+
|
|
54
|
+
`);
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
}, KEEPALIVE_MS);
|
|
58
|
+
keepalive.unref?.();
|
|
59
|
+
const cleanup = () => {
|
|
60
|
+
if (closed) return;
|
|
61
|
+
closed = true;
|
|
62
|
+
clearInterval(keepalive);
|
|
63
|
+
transport.close().catch(() => void 0);
|
|
64
|
+
server.close().catch(() => void 0);
|
|
65
|
+
};
|
|
66
|
+
res.on("close", cleanup);
|
|
67
|
+
res.on("error", cleanup);
|
|
68
|
+
server.connect(transport).catch((err) => {
|
|
69
|
+
process.stderr.write(`MCP transport error: ${err instanceof Error ? err.message : err}
|
|
70
|
+
`);
|
|
71
|
+
cleanup();
|
|
72
|
+
if (!res.headersSent) {
|
|
73
|
+
res.writeHead(500, { "content-type": "application/json" });
|
|
74
|
+
res.end(JSON.stringify({ error: "MCP transport failed to initialize" }));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
httpServer.listen(PORT, () => {
|
|
79
|
+
process.stdout.write(`Mnemo MCP HTTP listening on :${PORT}
|
|
80
|
+
`);
|
|
81
|
+
});
|
|
82
|
+
process.on("SIGTERM", () => httpServer.close(() => process.exit(0)));
|
|
83
|
+
process.on("SIGINT", () => httpServer.close(() => process.exit(0)));
|
|
84
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/http.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * HTTP/SSE entry-point for hosted MCP at mcp.getmnemo.xyz.\n *\n * Each connecting client supplies its own GETMNEMO_API_KEY and\n * GETMNEMO_WORKSPACE_ID via OAuth (Phase 2) or via custom headers\n * `x-getmnemo-api-key` + `x-getmnemo-workspace-id` (Phase 1, dev-only).\n *\n * Listens on PORT (default 8787). Healthcheck at GET /healthz.\n */\n\nimport { createServer as createHttpServer } from 'node:http'\nimport { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'\nimport { createServer } from './server.js'\n\nconst PORT = Number(process.env.PORT ?? 8787)\nconst DEFAULT_API_URL = process.env.GETMNEMO_API_URL ?? 'https://api.getmnemo.xyz'\n\nconst httpServer = createHttpServer((req, res) => {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`)\n\n if (url.pathname === '/healthz') {\n res.writeHead(200, { 'content-type': 'application/json' })\n res.end(JSON.stringify({ status: 'ok', service: 'getmnemo-mcp' }))\n return\n }\n\n if (url.pathname !== '/mcp') {\n res.writeHead(404, { 'content-type': 'text/plain' })\n res.end('Not found. GET /mcp for the SSE stream, GET /healthz for liveness.\\n')\n return\n }\n\n if (req.method !== 'GET' && req.method !== 'POST') {\n res.writeHead(405, { 'content-type': 'text/plain', allow: 'GET, POST' })\n res.end('Method not allowed.\\n')\n return\n }\n\n const apiKey =\n (req.headers['x-getmnemo-api-key'] as string | undefined) ?? process.env.GETMNEMO_API_KEY\n const workspaceId =\n (req.headers['x-getmnemo-workspace-id'] as string | undefined) ??\n process.env.GETMNEMO_WORKSPACE_ID\n\n if (!apiKey || !workspaceId) {\n res.writeHead(401, { 'content-type': 'application/json' })\n res.end(\n JSON.stringify({\n error: 'Missing x-getmnemo-api-key and/or x-getmnemo-workspace-id headers.',\n hint: 'OAuth flow lands in v0.2; headers work today for trusted clients.',\n }),\n )\n return\n }\n\n const server = createServer({\n baseUrl: DEFAULT_API_URL,\n apiKey,\n workspaceId,\n actorId: req.headers['x-getmnemo-actor-id'] as string | undefined,\n })\n\n const transport = new SSEServerTransport('/mcp', res)\n\n // SSE keepalive: many proxies (CloudFront, ALB, nginx default 60s) close\n // idle connections, which silently breaks long-lived MCP sessions. Emit a\n // comment-frame heartbeat every 25s so the connection stays warm.\n const KEEPALIVE_MS = 25_000\n // Track tear-down so the heartbeat cannot race with cleanup. Without this\n // guard, `setInterval` can fire on the same tick that the client closes the\n // socket: clearInterval is queued, the timer callback is already running,\n // and `res.write` lands on a half-closed transport — Node throws\n // ERR_STREAM_WRITE_AFTER_END (uncaught here, the empty catch only swallows\n // synchronous errors; the actual error fires on the 'error' event).\n let closed = false\n const keepalive = setInterval(() => {\n if (closed || res.writableEnded || res.destroyed) return\n try {\n // SSE comments start with \":\" and are ignored by the client parser.\n res.write(`: keepalive ${Date.now()}\\n\\n`)\n } catch {\n // res may already be closed; cleanup will run via the 'close' handler.\n }\n }, KEEPALIVE_MS)\n // Don't keep the event loop alive solely for the heartbeat.\n keepalive.unref?.()\n\n const cleanup = (): void => {\n if (closed) return\n closed = true\n clearInterval(keepalive)\n transport.close().catch(() => undefined)\n server.close().catch(() => undefined)\n }\n res.on('close', cleanup)\n res.on('error', cleanup)\n\n server.connect(transport).catch((err) => {\n process.stderr.write(`MCP transport error: ${err instanceof Error ? err.message : err}\\n`)\n cleanup()\n if (!res.headersSent) {\n res.writeHead(500, { 'content-type': 'application/json' })\n res.end(JSON.stringify({ error: 'MCP transport failed to initialize' }))\n }\n })\n})\n\nhttpServer.listen(PORT, () => {\n process.stdout.write(`Mnemo MCP HTTP listening on :${PORT}\\n`)\n})\n\nprocess.on('SIGTERM', () => httpServer.close(() => process.exit(0)))\nprocess.on('SIGINT', () => httpServer.close(() => process.exit(0)))\n"],"mappings":";;;;;;AAWA,SAAS,gBAAgB,wBAAwB;AACjD,SAAS,0BAA0B;AAGnC,IAAM,OAAO,OAAO,QAAQ,IAAI,QAAQ,IAAI;AAC5C,IAAM,kBAAkB,QAAQ,IAAI,oBAAoB;AAExD,IAAM,aAAa,iBAAiB,CAAC,KAAK,QAAQ;AAChD,QAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI,QAAQ,QAAQ,WAAW,EAAE;AAE/E,MAAI,IAAI,aAAa,YAAY;AAC/B,QAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,QAAI,IAAI,KAAK,UAAU,EAAE,QAAQ,MAAM,SAAS,eAAe,CAAC,CAAC;AACjE;AAAA,EACF;AAEA,MAAI,IAAI,aAAa,QAAQ;AAC3B,QAAI,UAAU,KAAK,EAAE,gBAAgB,aAAa,CAAC;AACnD,QAAI,IAAI,sEAAsE;AAC9E;AAAA,EACF;AAEA,MAAI,IAAI,WAAW,SAAS,IAAI,WAAW,QAAQ;AACjD,QAAI,UAAU,KAAK,EAAE,gBAAgB,cAAc,OAAO,YAAY,CAAC;AACvE,QAAI,IAAI,uBAAuB;AAC/B;AAAA,EACF;AAEA,QAAM,SACH,IAAI,QAAQ,oBAAoB,KAA4B,QAAQ,IAAI;AAC3E,QAAM,cACH,IAAI,QAAQ,yBAAyB,KACtC,QAAQ,IAAI;AAEd,MAAI,CAAC,UAAU,CAAC,aAAa;AAC3B,QAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,QAAI;AAAA,MACF,KAAK,UAAU;AAAA,QACb,OAAO;AAAA,QACP,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AACA;AAAA,EACF;AAEA,QAAM,SAAS,aAAa;AAAA,IAC1B,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA,SAAS,IAAI,QAAQ,qBAAqB;AAAA,EAC5C,CAAC;AAED,QAAM,YAAY,IAAI,mBAAmB,QAAQ,GAAG;AAKpD,QAAM,eAAe;AAOrB,MAAI,SAAS;AACb,QAAM,YAAY,YAAY,MAAM;AAClC,QAAI,UAAU,IAAI,iBAAiB,IAAI,UAAW;AAClD,QAAI;AAEF,UAAI,MAAM,eAAe,KAAK,IAAI,CAAC;AAAA;AAAA,CAAM;AAAA,IAC3C,QAAQ;AAAA,IAER;AAAA,EACF,GAAG,YAAY;AAEf,YAAU,QAAQ;AAElB,QAAM,UAAU,MAAY;AAC1B,QAAI,OAAQ;AACZ,aAAS;AACT,kBAAc,SAAS;AACvB,cAAU,MAAM,EAAE,MAAM,MAAM,MAAS;AACvC,WAAO,MAAM,EAAE,MAAM,MAAM,MAAS;AAAA,EACtC;AACA,MAAI,GAAG,SAAS,OAAO;AACvB,MAAI,GAAG,SAAS,OAAO;AAEvB,SAAO,QAAQ,SAAS,EAAE,MAAM,CAAC,QAAQ;AACvC,YAAQ,OAAO,MAAM,wBAAwB,eAAe,QAAQ,IAAI,UAAU,GAAG;AAAA,CAAI;AACzF,YAAQ;AACR,QAAI,CAAC,IAAI,aAAa;AACpB,UAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,UAAI,IAAI,KAAK,UAAU,EAAE,OAAO,qCAAqC,CAAC,CAAC;AAAA,IACzE;AAAA,EACF,CAAC;AACH,CAAC;AAED,WAAW,OAAO,MAAM,MAAM;AAC5B,UAAQ,OAAO,MAAM,gCAAgC,IAAI;AAAA,CAAI;AAC/D,CAAC;AAED,QAAQ,GAAG,WAAW,MAAM,WAAW,MAAM,MAAM,QAAQ,KAAK,CAAC,CAAC,CAAC;AACnE,QAAQ,GAAG,UAAU,MAAM,WAAW,MAAM,MAAM,QAAQ,KAAK,CAAC,CAAC,CAAC;","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Thin REST client for the Mnemo Memory API.
|
|
5
|
+
*
|
|
6
|
+
* This duplicates the surface area we need for MCP tools — we deliberately
|
|
7
|
+
* do NOT depend on @mnemo/memory here so this server can ship even if
|
|
8
|
+
* the JS SDK lags behind. When the SDK stabilises, swap this for it.
|
|
9
|
+
*/
|
|
10
|
+
type Memory = {
|
|
11
|
+
id: string;
|
|
12
|
+
content: string;
|
|
13
|
+
metadata?: Record<string, unknown>;
|
|
14
|
+
workspaceId: string;
|
|
15
|
+
actorId?: string | null;
|
|
16
|
+
createdAt: string;
|
|
17
|
+
updatedAt: string;
|
|
18
|
+
};
|
|
19
|
+
type SearchHit = {
|
|
20
|
+
memoryId: string;
|
|
21
|
+
content: string;
|
|
22
|
+
score: number;
|
|
23
|
+
metadata?: Record<string, unknown>;
|
|
24
|
+
source?: {
|
|
25
|
+
documentId?: string;
|
|
26
|
+
chunkId?: string;
|
|
27
|
+
} | null;
|
|
28
|
+
};
|
|
29
|
+
type SearchResponse = {
|
|
30
|
+
hits: SearchHit[];
|
|
31
|
+
query: string;
|
|
32
|
+
latencyMs: number;
|
|
33
|
+
};
|
|
34
|
+
type ApiClientConfig = {
|
|
35
|
+
baseUrl: string;
|
|
36
|
+
apiKey: string;
|
|
37
|
+
workspaceId: string;
|
|
38
|
+
actorId?: string;
|
|
39
|
+
fetch?: typeof fetch;
|
|
40
|
+
/** Per-request timeout in ms (default 30s). */
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
};
|
|
43
|
+
declare class MnemoApiError extends Error {
|
|
44
|
+
readonly status: number;
|
|
45
|
+
readonly body?: unknown | undefined;
|
|
46
|
+
constructor(message: string, status: number, body?: unknown | undefined);
|
|
47
|
+
}
|
|
48
|
+
declare class MnemoApiClient {
|
|
49
|
+
private readonly baseUrl;
|
|
50
|
+
private readonly headers;
|
|
51
|
+
private readonly fetchImpl;
|
|
52
|
+
private readonly timeoutMs;
|
|
53
|
+
constructor(cfg: ApiClientConfig);
|
|
54
|
+
search(input: {
|
|
55
|
+
query: string;
|
|
56
|
+
limit?: number;
|
|
57
|
+
actorId?: string;
|
|
58
|
+
}): Promise<SearchResponse>;
|
|
59
|
+
addMemory(input: {
|
|
60
|
+
content: string;
|
|
61
|
+
metadata?: Record<string, unknown>;
|
|
62
|
+
actorId?: string;
|
|
63
|
+
}): Promise<Memory>;
|
|
64
|
+
updateMemory(id: string, input: {
|
|
65
|
+
content?: string;
|
|
66
|
+
metadata?: Record<string, unknown>;
|
|
67
|
+
}): Promise<Memory>;
|
|
68
|
+
deleteMemory(id: string): Promise<{
|
|
69
|
+
id: string;
|
|
70
|
+
deleted: true;
|
|
71
|
+
}>;
|
|
72
|
+
listMemories(input?: {
|
|
73
|
+
limit?: number;
|
|
74
|
+
cursor?: string;
|
|
75
|
+
actorId?: string;
|
|
76
|
+
}): Promise<{
|
|
77
|
+
items: Memory[];
|
|
78
|
+
nextCursor: string | null;
|
|
79
|
+
}>;
|
|
80
|
+
private request;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* MCP server factory.
|
|
85
|
+
*
|
|
86
|
+
* Exposes 5 tools to MCP clients (Claude Desktop, Cursor, Windsurf, VS Code,
|
|
87
|
+
* Zed): memory_search, memory_add, memory_update, memory_delete, memory_list.
|
|
88
|
+
*
|
|
89
|
+
* Transport-agnostic — wire to stdio (cli.ts) or HTTP/SSE (http.ts).
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
declare function createServer(cfg: ApiClientConfig): Server;
|
|
93
|
+
|
|
94
|
+
export { type ApiClientConfig, type Memory, MnemoApiClient, MnemoApiError, type SearchHit, type SearchResponse, createServer };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "getmnemo-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Model Context Protocol server for Mnemo Memory — exposes search/add/update/delete tools to Claude Desktop, Cursor, Windsurf, VS Code, Zed.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"module": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"bin": {
|
|
11
|
+
"getmnemo-mcp": "dist/cli.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=20"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"dev": "tsx src/cli.ts",
|
|
24
|
+
"dev:http": "tsx src/http.ts",
|
|
25
|
+
"start": "node dist/cli.js",
|
|
26
|
+
"start:http": "node dist/http.js",
|
|
27
|
+
"lint": "eslint src",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"test": "vitest run"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
33
|
+
"zod": "^3.23.8"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^22.10.0",
|
|
37
|
+
"eslint": "^9.17.0",
|
|
38
|
+
"tsup": "^8.3.5",
|
|
39
|
+
"tsx": "^4.19.2",
|
|
40
|
+
"typescript": "^5.7.2",
|
|
41
|
+
"vitest": "^2.1.8"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/ledgermem/getmnemo-mcp.git"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://getmnemo.xyz"
|
|
51
|
+
}
|