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