reflect-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.
Files changed (3) hide show
  1. package/README.md +90 -0
  2. package/package.json +19 -0
  3. package/server.mjs +285 -0
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # Reflect Memory MCP Server for Cursor
2
+
3
+ Connect Cursor to Reflect Memory — read, write, browse, and search your cross-agent memory without manual API calls.
4
+
5
+ ## Prerequisites
6
+
7
+ - Node.js 18+
8
+ - A Reflect Memory agent key (`RM_AGENT_KEY_CURSOR` from Railway, or use `RM_AGENT_KEY_CLAUDE` for testing)
9
+
10
+ ## Setup
11
+
12
+ ### 1. Install dependencies
13
+
14
+ ```bash
15
+ cd integrations/cursor
16
+ npm install
17
+ ```
18
+
19
+ ### 2. Set your API key
20
+
21
+ Add to your shell profile (`~/.zshrc`, `~/.bashrc`) or create a `.env` in this directory:
22
+
23
+ ```bash
24
+ export REFLECT_MEMORY_API_KEY="your-agent-key-here"
25
+ # Or: export RM_AGENT_KEY_CURSOR="your-agent-key-here"
26
+ ```
27
+
28
+ ### 3. Add MCP server to Cursor
29
+
30
+ **Option A: Via Cursor Settings UI**
31
+
32
+ 1. Open Cursor Settings (Cmd/Ctrl + ,)
33
+ 2. Go to **Features** → **Model Context Protocol** (or **Tools & MCP**)
34
+ 3. Click **Add new MCP server**
35
+ 4. Configure:
36
+ - **Name:** `reflect-memory`
37
+ - **Type:** `stdio`
38
+ - **Command:** `node`
39
+ - **Args:** `["/absolute/path/to/reflective-memory/integrations/cursor/server.mjs"]`
40
+
41
+ **Option B: Via project config**
42
+
43
+ Create or edit `.cursor/mcp.json` in your project root (e.g. `reflect-memory-dashboard` or `reflective-memory`):
44
+
45
+ ```json
46
+ {
47
+ "mcpServers": {
48
+ "reflect-memory": {
49
+ "command": "node",
50
+ "args": ["/Users/YOUR_USERNAME/Desktop/reflective-memory/integrations/cursor/server.mjs"],
51
+ "env": {
52
+ "REFLECT_MEMORY_API_KEY": "your-agent-key-here"
53
+ }
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ Replace the path with your actual path to `server.mjs`. You can put the API key in `env` to avoid exporting it globally.
60
+
61
+ ### 4. Restart Cursor
62
+
63
+ Restart Cursor completely for the MCP server to load.
64
+
65
+ ## Tools
66
+
67
+ | Tool | Use when |
68
+ |------|----------|
69
+ | `get_latest_memory` | "Pull latest memory", "What's the latest from ChatGPT?", "Get most recent" |
70
+ | `get_memory_by_id` | You have a memory UUID from browse results |
71
+ | `browse_memories` | "List all memories", "What memories do I have?", discovery |
72
+ | `get_memories_by_tag` | "Council memories", "Memories tagged project_state" |
73
+ | `write_memory` | "Save this to memory", "Write a memory", "Push to Reflect Memory" |
74
+
75
+ ## Usage
76
+
77
+ Ask Cursor naturally:
78
+
79
+ - "Pull the latest memory from Reflect Memory"
80
+ - "What's my most recent memory?"
81
+ - "Browse all my memories"
82
+ - "Search memories for authentication"
83
+ - "Write this to memory: [your content]"
84
+
85
+ ## Troubleshooting
86
+
87
+ - **"REFLECT_MEMORY_API_KEY must be set"** — Add the key to `env` in mcp.json or export it in your shell.
88
+ - **401 Unauthorized** — Wrong or expired agent key. Get `RM_AGENT_KEY_CURSOR` from Railway.
89
+ - **Server not showing** — Restart Cursor fully (quit and reopen).
90
+ - **Path issues** — Use the absolute path to `server.mjs` in your mcp.json args.
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "reflect-memory-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for Reflect Memory — read, write, browse, and search your cross-agent memory from Cursor",
5
+ "type": "module",
6
+ "main": "server.mjs",
7
+ "bin": {
8
+ "reflect-memory-mcp": "server.mjs"
9
+ },
10
+ "scripts": {
11
+ "start": "node server.mjs"
12
+ },
13
+ "dependencies": {
14
+ "@modelcontextprotocol/sdk": "^1.0.0"
15
+ },
16
+ "engines": {
17
+ "node": ">=18"
18
+ }
19
+ }
package/server.mjs ADDED
@@ -0,0 +1,285 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Reflect Memory MCP Server for Cursor
4
+ *
5
+ * Exposes Read, Write, Browse, and Search tools for the Reflect Memory API.
6
+ * Set REFLECT_MEMORY_API_KEY (or RM_AGENT_KEY_CURSOR) in your environment.
7
+ *
8
+ * Configure in Cursor: Settings → Tools & MCP → Add new MCP server
9
+ * Or add to .cursor/mcp.json in your project root.
10
+ */
11
+
12
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
13
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
14
+ import {
15
+ CallToolRequestSchema,
16
+ ListToolsRequestSchema,
17
+ } from "@modelcontextprotocol/sdk/types.js";
18
+ const API_BASE =
19
+ process.env.REFLECT_MEMORY_API_URL || "https://api.reflectmemory.com";
20
+ const API_KEY =
21
+ process.env.REFLECT_MEMORY_API_KEY || process.env.RM_AGENT_KEY_CURSOR;
22
+
23
+ function getApiKey() {
24
+ if (!API_KEY?.trim()) {
25
+ throw new Error(
26
+ "REFLECT_MEMORY_API_KEY or RM_AGENT_KEY_CURSOR must be set. Add it to your environment or .env file."
27
+ );
28
+ }
29
+ return API_KEY.trim();
30
+ }
31
+
32
+ async function fetchApi(path, options = {}) {
33
+ const url = `${API_BASE}${path}`;
34
+ const headers = {
35
+ "Content-Type": "application/json",
36
+ Authorization: `Bearer ${getApiKey()}`,
37
+ ...options.headers,
38
+ };
39
+ const res = await fetch(url, { ...options, headers });
40
+ const data = await res.json().catch(() => ({}));
41
+ if (!res.ok) {
42
+ const err = data.error || data.message || `API error ${res.status}`;
43
+ throw new Error(err);
44
+ }
45
+ return data;
46
+ }
47
+
48
+ const server = new Server(
49
+ {
50
+ name: "reflect-memory",
51
+ version: "1.0.0",
52
+ },
53
+ {
54
+ capabilities: {
55
+ tools: {},
56
+ },
57
+ }
58
+ );
59
+
60
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
61
+ tools: [
62
+ {
63
+ name: "get_latest_memory",
64
+ description:
65
+ "Get the most recent memory from Reflect Memory. Supports optional tag filter (e.g. project_state, council). Use for 'pull latest', 'what's the latest memory', 'get latest from ChatGPT' (origin is set server-side).",
66
+ inputSchema: {
67
+ type: "object",
68
+ properties: {
69
+ tag: {
70
+ type: "string",
71
+ description: "Optional filter by tag (e.g. project_state, council). Leave empty for latest overall.",
72
+ },
73
+ },
74
+ },
75
+ },
76
+ {
77
+ name: "get_memory_by_id",
78
+ description: "Get a single memory by its UUID. Use when you have an ID from browse results.",
79
+ inputSchema: {
80
+ type: "object",
81
+ properties: {
82
+ id: {
83
+ type: "string",
84
+ description: "The memory UUID",
85
+ },
86
+ },
87
+ required: ["id"],
88
+ },
89
+ },
90
+ {
91
+ name: "browse_memories",
92
+ description:
93
+ "Browse memory summaries (title, tags, origin, date) without full content. Use for 'list all memories', 'what memories do I have', discovery. Supports pagination and search.",
94
+ inputSchema: {
95
+ type: "object",
96
+ properties: {
97
+ filter: {
98
+ type: "string",
99
+ enum: ["all", "tags", "search"],
100
+ description: "all=all memories, tags=filter by tags, search=text search",
101
+ },
102
+ tags: {
103
+ type: "array",
104
+ items: { type: "string" },
105
+ description: "Required if filter=tags. E.g. ['project_state','council']",
106
+ },
107
+ term: {
108
+ type: "string",
109
+ description: "Required if filter=search. Search term for title/content.",
110
+ },
111
+ limit: {
112
+ type: "number",
113
+ description: "Max memories to return. Default 50.",
114
+ },
115
+ },
116
+ },
117
+ },
118
+ {
119
+ name: "get_memories_by_tag",
120
+ description:
121
+ "Get full memory bodies by tags. Returns complete content. Use for 'council memories', 'memories tagged X', topic-based retrieval.",
122
+ inputSchema: {
123
+ type: "object",
124
+ properties: {
125
+ tags: {
126
+ type: "array",
127
+ items: { type: "string" },
128
+ description: "Tags to filter by. Returns memories matching ANY of these.",
129
+ },
130
+ limit: {
131
+ type: "number",
132
+ description: "Max memories. Default 20.",
133
+ },
134
+ },
135
+ required: ["tags"],
136
+ },
137
+ },
138
+ {
139
+ name: "write_memory",
140
+ description:
141
+ "Write a new memory to Reflect Memory. Use allowed_vendors: ['*'] for cross-agent visibility.",
142
+ inputSchema: {
143
+ type: "object",
144
+ properties: {
145
+ title: {
146
+ type: "string",
147
+ description: "Short descriptor. Format: 'Topic - Summary'",
148
+ },
149
+ content: {
150
+ type: "string",
151
+ description: "Structured content. Use Change/Reason/Impact/Open Questions format when appropriate.",
152
+ },
153
+ tags: {
154
+ type: "array",
155
+ items: { type: "string" },
156
+ description: "Relevant tags. Include project_state, architecture when applicable.",
157
+ },
158
+ allowed_vendors: {
159
+ type: "array",
160
+ items: { type: "string" },
161
+ description: "Use ['*'] for all agents. Default: ['*']",
162
+ },
163
+ },
164
+ required: ["title", "content", "tags"],
165
+ },
166
+ },
167
+ ],
168
+ }));
169
+
170
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
171
+ const { name, arguments: args } = request.params;
172
+
173
+ try {
174
+ switch (name) {
175
+ case "get_latest_memory": {
176
+ const tag = args?.tag ? `?tag=${encodeURIComponent(args.tag)}` : "";
177
+ const data = await fetchApi(`/agent/memories/latest${tag}`);
178
+ return {
179
+ content: [
180
+ {
181
+ type: "text",
182
+ text: JSON.stringify(data, null, 2),
183
+ },
184
+ ],
185
+ };
186
+ }
187
+
188
+ case "get_memory_by_id": {
189
+ const id = args?.id;
190
+ if (!id) throw new Error("id is required");
191
+ const data = await fetchApi(`/agent/memories/${id}`);
192
+ return {
193
+ content: [
194
+ {
195
+ type: "text",
196
+ text: JSON.stringify(data, null, 2),
197
+ },
198
+ ],
199
+ };
200
+ }
201
+
202
+ case "browse_memories": {
203
+ const filterType = args?.filter || "all";
204
+ let filter = { by: filterType };
205
+ if (filterType === "tags" && args?.tags?.length) {
206
+ filter.tags = args.tags;
207
+ } else if (filterType === "search" && args?.term) {
208
+ filter = { by: "search", term: args.term };
209
+ }
210
+ const limit = args?.limit ?? 50;
211
+ const data = await fetchApi("/agent/memories/browse", {
212
+ method: "POST",
213
+ body: JSON.stringify({ filter, limit }),
214
+ });
215
+ return {
216
+ content: [
217
+ {
218
+ type: "text",
219
+ text: JSON.stringify(data, null, 2),
220
+ },
221
+ ],
222
+ };
223
+ }
224
+
225
+ case "get_memories_by_tag": {
226
+ const tags = args?.tags;
227
+ if (!tags?.length) throw new Error("tags array is required");
228
+ const limit = args?.limit ?? 20;
229
+ const data = await fetchApi("/agent/memories/by-tag", {
230
+ method: "POST",
231
+ body: JSON.stringify({ tags, limit }),
232
+ });
233
+ return {
234
+ content: [
235
+ {
236
+ type: "text",
237
+ text: JSON.stringify(data, null, 2),
238
+ },
239
+ ],
240
+ };
241
+ }
242
+
243
+ case "write_memory": {
244
+ const { title, content, tags } = args || {};
245
+ if (!title || !content || !tags?.length) {
246
+ throw new Error("title, content, and tags are required");
247
+ }
248
+ const allowed_vendors = args?.allowed_vendors ?? ["*"];
249
+ const data = await fetchApi("/agent/memories", {
250
+ method: "POST",
251
+ body: JSON.stringify({
252
+ title,
253
+ content,
254
+ tags,
255
+ allowed_vendors,
256
+ }),
257
+ });
258
+ return {
259
+ content: [
260
+ {
261
+ type: "text",
262
+ text: `Memory created: ${data.id}\n${JSON.stringify(data, null, 2)}`,
263
+ },
264
+ ],
265
+ };
266
+ }
267
+
268
+ default:
269
+ throw new Error(`Unknown tool: ${name}`);
270
+ }
271
+ } catch (err) {
272
+ return {
273
+ content: [
274
+ {
275
+ type: "text",
276
+ text: `Error: ${err.message}`,
277
+ },
278
+ ],
279
+ isError: true,
280
+ };
281
+ }
282
+ });
283
+
284
+ const transport = new StdioServerTransport();
285
+ await server.connect(transport);