@cybermem/mcp 0.6.6 → 0.6.10
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 +100 -20
- package/package.json +1 -1
- package/src/index.ts +126 -31
package/dist/index.js
CHANGED
|
@@ -19,40 +19,97 @@ const getArg = (name) => {
|
|
|
19
19
|
const idx = args.indexOf(name);
|
|
20
20
|
return idx !== -1 && args[idx + 1] ? args[idx + 1] : undefined;
|
|
21
21
|
};
|
|
22
|
-
const cliUrl = getArg(
|
|
23
|
-
const cliApiKey = getArg(
|
|
24
|
-
const cliClientName = getArg(
|
|
22
|
+
const cliUrl = getArg("--url");
|
|
23
|
+
const cliApiKey = getArg("--api-key");
|
|
24
|
+
const cliClientName = getArg("--client-name");
|
|
25
25
|
// Use CLI args first, then env, then defaults
|
|
26
26
|
// Default to local CyberMem backend (via Traefik on port 8626)
|
|
27
27
|
const API_URL = cliUrl || process.env.CYBERMEM_URL || "http://localhost:8626/memory";
|
|
28
28
|
const API_KEY = cliApiKey || process.env.OM_API_KEY || "";
|
|
29
29
|
// Track client name per session
|
|
30
30
|
let currentClientName = cliClientName || "cybermem-mcp";
|
|
31
|
+
// CyberMem Agent Protocol - instructions sent to clients on handshake
|
|
32
|
+
const CYBERMEM_INSTRUCTIONS = `CyberMem is a persistent context daemon for AI agents.
|
|
33
|
+
|
|
34
|
+
PROTOCOL:
|
|
35
|
+
1. On session start: call query_memory("user context profile") to load persona
|
|
36
|
+
2. Store new insights immediately with add_memory - include FULL content, not summaries
|
|
37
|
+
3. Refresh context: 6h for active topics, 24h for projects, 7d for insights
|
|
38
|
+
4. Always include tags: [topic, year, source:your-client-name]
|
|
39
|
+
5. Priority: CyberMem context > session context > training data
|
|
40
|
+
|
|
41
|
+
MEMORY FORMAT:
|
|
42
|
+
- content: Full text with all details, metrics, dates. NO truncation.
|
|
43
|
+
- tags: Always include topic category + year + source:client-name
|
|
44
|
+
|
|
45
|
+
INTEGRITY RULES:
|
|
46
|
+
- Never overwrite without reading first
|
|
47
|
+
- Always include metadata (tags, source)
|
|
48
|
+
- Sync before critical decisions
|
|
49
|
+
- Last-write-wins for conflicts
|
|
50
|
+
|
|
51
|
+
For full protocol: https://cybermem.dev/docs/agent-protocol`;
|
|
52
|
+
// Short protocol reminder for tool descriptions (derived from main instructions)
|
|
53
|
+
const PROTOCOL_REMINDER = "CyberMem Protocol: Store FULL content (no summaries), always include tags [topic, year, source:client-name]. Query 'user context profile' on session start.";
|
|
31
54
|
const server = new index_js_1.Server({
|
|
32
|
-
name: "cybermem
|
|
33
|
-
version: "0.
|
|
55
|
+
name: "cybermem",
|
|
56
|
+
version: "0.6.8",
|
|
34
57
|
}, {
|
|
35
58
|
capabilities: {
|
|
36
59
|
tools: {},
|
|
60
|
+
resources: {}, // Enable resources for protocol document
|
|
37
61
|
},
|
|
62
|
+
instructions: CYBERMEM_INSTRUCTIONS,
|
|
63
|
+
});
|
|
64
|
+
// Register resources handler for protocol document
|
|
65
|
+
server.setRequestHandler(types_js_1.ListResourcesRequestSchema, async () => ({
|
|
66
|
+
resources: [
|
|
67
|
+
{
|
|
68
|
+
uri: "cybermem://protocol",
|
|
69
|
+
name: "CyberMem Agent Protocol",
|
|
70
|
+
description: "Instructions for AI agents using CyberMem memory system",
|
|
71
|
+
mimeType: "text/plain",
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
}));
|
|
75
|
+
server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) => {
|
|
76
|
+
if (request.params.uri === "cybermem://protocol") {
|
|
77
|
+
return {
|
|
78
|
+
contents: [
|
|
79
|
+
{
|
|
80
|
+
uri: "cybermem://protocol",
|
|
81
|
+
mimeType: "text/plain",
|
|
82
|
+
text: CYBERMEM_INSTRUCTIONS,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
throw new Error(`Unknown resource: ${request.params.uri}`);
|
|
38
88
|
});
|
|
39
89
|
const tools = [
|
|
40
90
|
{
|
|
41
91
|
name: "add_memory",
|
|
42
|
-
description:
|
|
92
|
+
description: `Store a new memory in CyberMem. ${PROTOCOL_REMINDER}`,
|
|
43
93
|
inputSchema: {
|
|
44
94
|
type: "object",
|
|
45
95
|
properties: {
|
|
46
|
-
content: {
|
|
96
|
+
content: {
|
|
97
|
+
type: "string",
|
|
98
|
+
description: "Full content with all details - NO truncation or summarization",
|
|
99
|
+
},
|
|
47
100
|
user_id: { type: "string" },
|
|
48
|
-
tags: {
|
|
101
|
+
tags: {
|
|
102
|
+
type: "array",
|
|
103
|
+
items: { type: "string" },
|
|
104
|
+
description: "Always include [topic, year, source:your-client-name]",
|
|
105
|
+
},
|
|
49
106
|
},
|
|
50
107
|
required: ["content"],
|
|
51
108
|
},
|
|
52
109
|
},
|
|
53
110
|
{
|
|
54
111
|
name: "query_memory",
|
|
55
|
-
description:
|
|
112
|
+
description: `Search for relevant memories. On session start, call query_memory("user context profile") first.`,
|
|
56
113
|
inputSchema: {
|
|
57
114
|
type: "object",
|
|
58
115
|
properties: {
|
|
@@ -96,7 +153,7 @@ const tools = [
|
|
|
96
153
|
},
|
|
97
154
|
required: ["id"],
|
|
98
155
|
},
|
|
99
|
-
}
|
|
156
|
+
},
|
|
100
157
|
];
|
|
101
158
|
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
102
159
|
tools,
|
|
@@ -105,7 +162,7 @@ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
|
105
162
|
const apiClient = axios_1.default.create({
|
|
106
163
|
baseURL: API_URL,
|
|
107
164
|
headers: {
|
|
108
|
-
|
|
165
|
+
Authorization: `Bearer ${API_KEY}`,
|
|
109
166
|
},
|
|
110
167
|
});
|
|
111
168
|
// Helper to get client with context
|
|
@@ -115,11 +172,26 @@ function getClient(customHeaders = {}) {
|
|
|
115
172
|
const clientName = customHeaders["X-Client-Name"] || clientVersion?.name || currentClientName;
|
|
116
173
|
return {
|
|
117
174
|
...apiClient,
|
|
118
|
-
get: (url, config) => apiClient.get(url, {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
175
|
+
get: (url, config) => apiClient.get(url, {
|
|
176
|
+
...config,
|
|
177
|
+
headers: { "X-Client-Name": clientName, ...config?.headers },
|
|
178
|
+
}),
|
|
179
|
+
post: (url, data, config) => apiClient.post(url, data, {
|
|
180
|
+
...config,
|
|
181
|
+
headers: { "X-Client-Name": clientName, ...config?.headers },
|
|
182
|
+
}),
|
|
183
|
+
put: (url, data, config) => apiClient.put(url, data, {
|
|
184
|
+
...config,
|
|
185
|
+
headers: { "X-Client-Name": clientName, ...config?.headers },
|
|
186
|
+
}),
|
|
187
|
+
patch: (url, data, config) => apiClient.patch(url, data, {
|
|
188
|
+
...config,
|
|
189
|
+
headers: { "X-Client-Name": clientName, ...config?.headers },
|
|
190
|
+
}),
|
|
191
|
+
delete: (url, config) => apiClient.delete(url, {
|
|
192
|
+
...config,
|
|
193
|
+
headers: { "X-Client-Name": clientName, ...config?.headers },
|
|
194
|
+
}),
|
|
123
195
|
};
|
|
124
196
|
}
|
|
125
197
|
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
@@ -128,16 +200,22 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
128
200
|
switch (name) {
|
|
129
201
|
case "add_memory": {
|
|
130
202
|
const response = await getClient().post("/add", args);
|
|
131
|
-
return {
|
|
203
|
+
return {
|
|
204
|
+
content: [{ type: "text", text: JSON.stringify(response.data) }],
|
|
205
|
+
};
|
|
132
206
|
}
|
|
133
207
|
case "query_memory": {
|
|
134
208
|
const response = await getClient().post("/query", args);
|
|
135
|
-
return {
|
|
209
|
+
return {
|
|
210
|
+
content: [{ type: "text", text: JSON.stringify(response.data) }],
|
|
211
|
+
};
|
|
136
212
|
}
|
|
137
213
|
case "list_memories": {
|
|
138
214
|
const limit = args?.limit || 10;
|
|
139
215
|
const response = await getClient().get(`/all?l=${limit}`);
|
|
140
|
-
return {
|
|
216
|
+
return {
|
|
217
|
+
content: [{ type: "text", text: JSON.stringify(response.data) }],
|
|
218
|
+
};
|
|
141
219
|
}
|
|
142
220
|
case "delete_memory": {
|
|
143
221
|
const { id } = args;
|
|
@@ -147,7 +225,9 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
147
225
|
case "update_memory": {
|
|
148
226
|
const { id, ...updates } = args;
|
|
149
227
|
const response = await getClient().patch(`/${id}`, updates);
|
|
150
|
-
return {
|
|
228
|
+
return {
|
|
229
|
+
content: [{ type: "text", text: JSON.stringify(response.data) }],
|
|
230
|
+
};
|
|
151
231
|
}
|
|
152
232
|
default:
|
|
153
233
|
throw new Error(`Unknown tool: ${name}`);
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -4,8 +4,10 @@ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
5
|
import {
|
|
6
6
|
CallToolRequestSchema,
|
|
7
|
+
ListResourcesRequestSchema,
|
|
7
8
|
ListToolsRequestSchema,
|
|
8
|
-
|
|
9
|
+
ReadResourceRequestSchema,
|
|
10
|
+
Tool,
|
|
9
11
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
10
12
|
import axios from "axios";
|
|
11
13
|
import cors from "cors";
|
|
@@ -21,47 +23,110 @@ const getArg = (name: string): string | undefined => {
|
|
|
21
23
|
return idx !== -1 && args[idx + 1] ? args[idx + 1] : undefined;
|
|
22
24
|
};
|
|
23
25
|
|
|
24
|
-
const cliUrl = getArg(
|
|
25
|
-
const cliApiKey = getArg(
|
|
26
|
-
const cliClientName = getArg(
|
|
26
|
+
const cliUrl = getArg("--url");
|
|
27
|
+
const cliApiKey = getArg("--api-key");
|
|
28
|
+
const cliClientName = getArg("--client-name");
|
|
27
29
|
|
|
28
30
|
// Use CLI args first, then env, then defaults
|
|
29
31
|
// Default to local CyberMem backend (via Traefik on port 8626)
|
|
30
|
-
const API_URL =
|
|
32
|
+
const API_URL =
|
|
33
|
+
cliUrl || process.env.CYBERMEM_URL || "http://localhost:8626/memory";
|
|
31
34
|
const API_KEY = cliApiKey || process.env.OM_API_KEY || "";
|
|
32
35
|
|
|
33
36
|
// Track client name per session
|
|
34
37
|
let currentClientName = cliClientName || "cybermem-mcp";
|
|
38
|
+
// CyberMem Agent Protocol - instructions sent to clients on handshake
|
|
39
|
+
const CYBERMEM_INSTRUCTIONS = `CyberMem is a persistent context daemon for AI agents.
|
|
40
|
+
|
|
41
|
+
PROTOCOL:
|
|
42
|
+
1. On session start: call query_memory("user context profile") to load persona
|
|
43
|
+
2. Store new insights immediately with add_memory - include FULL content, not summaries
|
|
44
|
+
3. Refresh context: 6h for active topics, 24h for projects, 7d for insights
|
|
45
|
+
4. Always include tags: [topic, year, source:your-client-name]
|
|
46
|
+
5. Priority: CyberMem context > session context > training data
|
|
47
|
+
|
|
48
|
+
MEMORY FORMAT:
|
|
49
|
+
- content: Full text with all details, metrics, dates. NO truncation.
|
|
50
|
+
- tags: Always include topic category + year + source:client-name
|
|
51
|
+
|
|
52
|
+
INTEGRITY RULES:
|
|
53
|
+
- Never overwrite without reading first
|
|
54
|
+
- Always include metadata (tags, source)
|
|
55
|
+
- Sync before critical decisions
|
|
56
|
+
- Last-write-wins for conflicts
|
|
57
|
+
|
|
58
|
+
For full protocol: https://cybermem.dev/docs/agent-protocol`;
|
|
59
|
+
|
|
60
|
+
// Short protocol reminder for tool descriptions (derived from main instructions)
|
|
61
|
+
const PROTOCOL_REMINDER =
|
|
62
|
+
"CyberMem Protocol: Store FULL content (no summaries), always include tags [topic, year, source:client-name]. Query 'user context profile' on session start.";
|
|
35
63
|
|
|
36
64
|
const server = new Server(
|
|
37
65
|
{
|
|
38
|
-
name: "cybermem
|
|
39
|
-
version: "0.
|
|
66
|
+
name: "cybermem",
|
|
67
|
+
version: "0.6.8",
|
|
40
68
|
},
|
|
41
69
|
{
|
|
42
70
|
capabilities: {
|
|
43
71
|
tools: {},
|
|
72
|
+
resources: {}, // Enable resources for protocol document
|
|
44
73
|
},
|
|
45
|
-
|
|
74
|
+
instructions: CYBERMEM_INSTRUCTIONS,
|
|
75
|
+
},
|
|
46
76
|
);
|
|
47
77
|
|
|
78
|
+
// Register resources handler for protocol document
|
|
79
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
80
|
+
resources: [
|
|
81
|
+
{
|
|
82
|
+
uri: "cybermem://protocol",
|
|
83
|
+
name: "CyberMem Agent Protocol",
|
|
84
|
+
description: "Instructions for AI agents using CyberMem memory system",
|
|
85
|
+
mimeType: "text/plain",
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
91
|
+
if (request.params.uri === "cybermem://protocol") {
|
|
92
|
+
return {
|
|
93
|
+
contents: [
|
|
94
|
+
{
|
|
95
|
+
uri: "cybermem://protocol",
|
|
96
|
+
mimeType: "text/plain",
|
|
97
|
+
text: CYBERMEM_INSTRUCTIONS,
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
throw new Error(`Unknown resource: ${request.params.uri}`);
|
|
103
|
+
});
|
|
104
|
+
|
|
48
105
|
const tools: Tool[] = [
|
|
49
106
|
{
|
|
50
107
|
name: "add_memory",
|
|
51
|
-
description:
|
|
108
|
+
description: `Store a new memory in CyberMem. ${PROTOCOL_REMINDER}`,
|
|
52
109
|
inputSchema: {
|
|
53
110
|
type: "object",
|
|
54
111
|
properties: {
|
|
55
|
-
content: {
|
|
112
|
+
content: {
|
|
113
|
+
type: "string",
|
|
114
|
+
description:
|
|
115
|
+
"Full content with all details - NO truncation or summarization",
|
|
116
|
+
},
|
|
56
117
|
user_id: { type: "string" },
|
|
57
|
-
tags: {
|
|
118
|
+
tags: {
|
|
119
|
+
type: "array",
|
|
120
|
+
items: { type: "string" },
|
|
121
|
+
description: "Always include [topic, year, source:your-client-name]",
|
|
122
|
+
},
|
|
58
123
|
},
|
|
59
124
|
required: ["content"],
|
|
60
125
|
},
|
|
61
126
|
},
|
|
62
127
|
{
|
|
63
128
|
name: "query_memory",
|
|
64
|
-
description:
|
|
129
|
+
description: `Search for relevant memories. On session start, call query_memory("user context profile") first.`,
|
|
65
130
|
inputSchema: {
|
|
66
131
|
type: "object",
|
|
67
132
|
properties: {
|
|
@@ -105,7 +170,7 @@ const tools: Tool[] = [
|
|
|
105
170
|
},
|
|
106
171
|
required: ["id"],
|
|
107
172
|
},
|
|
108
|
-
}
|
|
173
|
+
},
|
|
109
174
|
];
|
|
110
175
|
|
|
111
176
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
@@ -116,24 +181,45 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
116
181
|
const apiClient = axios.create({
|
|
117
182
|
baseURL: API_URL,
|
|
118
183
|
headers: {
|
|
119
|
-
|
|
184
|
+
Authorization: `Bearer ${API_KEY}`,
|
|
120
185
|
},
|
|
121
186
|
});
|
|
122
187
|
|
|
123
188
|
// Helper to get client with context
|
|
124
189
|
function getClient(customHeaders: Record<string, string> = {}) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
190
|
+
// Get client name from MCP protocol (sent during initialize) or fallback to CLI arg
|
|
191
|
+
const clientVersion = server.getClientVersion();
|
|
192
|
+
const clientName =
|
|
193
|
+
customHeaders["X-Client-Name"] || clientVersion?.name || currentClientName;
|
|
128
194
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
195
|
+
return {
|
|
196
|
+
...apiClient,
|
|
197
|
+
get: (url: string, config?: any) =>
|
|
198
|
+
apiClient.get(url, {
|
|
199
|
+
...config,
|
|
200
|
+
headers: { "X-Client-Name": clientName, ...config?.headers },
|
|
201
|
+
}),
|
|
202
|
+
post: (url: string, data?: any, config?: any) =>
|
|
203
|
+
apiClient.post(url, data, {
|
|
204
|
+
...config,
|
|
205
|
+
headers: { "X-Client-Name": clientName, ...config?.headers },
|
|
206
|
+
}),
|
|
207
|
+
put: (url: string, data?: any, config?: any) =>
|
|
208
|
+
apiClient.put(url, data, {
|
|
209
|
+
...config,
|
|
210
|
+
headers: { "X-Client-Name": clientName, ...config?.headers },
|
|
211
|
+
}),
|
|
212
|
+
patch: (url: string, data?: any, config?: any) =>
|
|
213
|
+
apiClient.patch(url, data, {
|
|
214
|
+
...config,
|
|
215
|
+
headers: { "X-Client-Name": clientName, ...config?.headers },
|
|
216
|
+
}),
|
|
217
|
+
delete: (url: string, config?: any) =>
|
|
218
|
+
apiClient.delete(url, {
|
|
219
|
+
...config,
|
|
220
|
+
headers: { "X-Client-Name": clientName, ...config?.headers },
|
|
221
|
+
}),
|
|
222
|
+
};
|
|
137
223
|
}
|
|
138
224
|
|
|
139
225
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -143,16 +229,22 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
143
229
|
switch (name) {
|
|
144
230
|
case "add_memory": {
|
|
145
231
|
const response = await getClient().post("/add", args);
|
|
146
|
-
return {
|
|
232
|
+
return {
|
|
233
|
+
content: [{ type: "text", text: JSON.stringify(response.data) }],
|
|
234
|
+
};
|
|
147
235
|
}
|
|
148
236
|
case "query_memory": {
|
|
149
237
|
const response = await getClient().post("/query", args);
|
|
150
|
-
return {
|
|
238
|
+
return {
|
|
239
|
+
content: [{ type: "text", text: JSON.stringify(response.data) }],
|
|
240
|
+
};
|
|
151
241
|
}
|
|
152
242
|
case "list_memories": {
|
|
153
243
|
const limit = args?.limit || 10;
|
|
154
244
|
const response = await getClient().get(`/all?l=${limit}`);
|
|
155
|
-
return {
|
|
245
|
+
return {
|
|
246
|
+
content: [{ type: "text", text: JSON.stringify(response.data) }],
|
|
247
|
+
};
|
|
156
248
|
}
|
|
157
249
|
case "delete_memory": {
|
|
158
250
|
const { id } = args as { id: string };
|
|
@@ -162,7 +254,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
162
254
|
case "update_memory": {
|
|
163
255
|
const { id, ...updates } = args as { id: string; [key: string]: any };
|
|
164
256
|
const response = await getClient().patch(`/${id}`, updates);
|
|
165
|
-
return {
|
|
257
|
+
return {
|
|
258
|
+
content: [{ type: "text", text: JSON.stringify(response.data) }],
|
|
259
|
+
};
|
|
166
260
|
}
|
|
167
261
|
default:
|
|
168
262
|
throw new Error(`Unknown tool: ${name}`);
|
|
@@ -211,11 +305,12 @@ async function run() {
|
|
|
211
305
|
});
|
|
212
306
|
|
|
213
307
|
app.listen(port, () => {
|
|
214
|
-
console.error(
|
|
308
|
+
console.error(
|
|
309
|
+
`CyberMem MCP Server running on SSE at http://localhost:${port}`,
|
|
310
|
+
);
|
|
215
311
|
console.error(` - SSE endpoint: http://localhost:${port}/sse`);
|
|
216
312
|
console.error(` - Message endpoint: http://localhost:${port}/messages`);
|
|
217
313
|
});
|
|
218
|
-
|
|
219
314
|
} else {
|
|
220
315
|
const transport = new StdioServerTransport();
|
|
221
316
|
await server.connect(transport);
|