AthelaPerk commited on
Commit
0065b9f
·
verified ·
1 Parent(s): 6aadba1

Update mcp_server.py with smart injection and real embeddings

Browse files
Files changed (1) hide show
  1. mcp_server.py +345 -0
mcp_server.py ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Mnemo MCP Server
4
+ Provides memory capabilities to Claude and other MCP-compatible LLMs
5
+
6
+ Usage:
7
+ uvx mnemo-memory # Run as MCP server
8
+
9
+ Or in Claude config:
10
+ {
11
+ "mcpServers": {
12
+ "mnemo": {"command": "uvx", "args": ["mnemo-memory"]}
13
+ }
14
+ }
15
+ """
16
+
17
+ import json
18
+ import sys
19
+ from typing import Any, Dict, List
20
+
21
+ try:
22
+ from mcp.server import Server
23
+ from mcp.types import Tool, TextContent
24
+ HAS_MCP = True
25
+ except ImportError:
26
+ HAS_MCP = False
27
+ print("Warning: MCP not installed. Install with: pip install mcp", file=sys.stderr)
28
+
29
+ from mnemo import Mnemo, should_inject_memory
30
+
31
+ # Global memory instance
32
+ memory = Mnemo(use_real_embeddings=True)
33
+
34
+ # Tool definitions
35
+ TOOLS = [
36
+ {
37
+ "name": "mnemo_add",
38
+ "description": "Store a new memory. Use this to save important information for later retrieval.",
39
+ "inputSchema": {
40
+ "type": "object",
41
+ "properties": {
42
+ "content": {
43
+ "type": "string",
44
+ "description": "The content to remember"
45
+ },
46
+ "metadata": {
47
+ "type": "object",
48
+ "description": "Optional metadata (tags, source, etc.)"
49
+ }
50
+ },
51
+ "required": ["content"]
52
+ }
53
+ },
54
+ {
55
+ "name": "mnemo_search",
56
+ "description": "Search stored memories. Returns relevant memories ranked by similarity.",
57
+ "inputSchema": {
58
+ "type": "object",
59
+ "properties": {
60
+ "query": {
61
+ "type": "string",
62
+ "description": "Search query"
63
+ },
64
+ "top_k": {
65
+ "type": "integer",
66
+ "description": "Number of results (default: 5)",
67
+ "default": 5
68
+ }
69
+ },
70
+ "required": ["query"]
71
+ }
72
+ },
73
+ {
74
+ "name": "mnemo_should_inject",
75
+ "description": "Check if memory should be injected for this query. Uses context-check algorithm with 90% accuracy.",
76
+ "inputSchema": {
77
+ "type": "object",
78
+ "properties": {
79
+ "query": {
80
+ "type": "string",
81
+ "description": "The query to check"
82
+ },
83
+ "context": {
84
+ "type": "string",
85
+ "description": "Optional additional context"
86
+ }
87
+ },
88
+ "required": ["query"]
89
+ }
90
+ },
91
+ {
92
+ "name": "mnemo_get_context",
93
+ "description": "Get formatted memory context for injection into prompts.",
94
+ "inputSchema": {
95
+ "type": "object",
96
+ "properties": {
97
+ "query": {
98
+ "type": "string",
99
+ "description": "Search query for finding relevant context"
100
+ },
101
+ "top_k": {
102
+ "type": "integer",
103
+ "description": "Number of memories to include (default: 3)",
104
+ "default": 3
105
+ }
106
+ },
107
+ "required": ["query"]
108
+ }
109
+ },
110
+ {
111
+ "name": "mnemo_list",
112
+ "description": "List all stored memories.",
113
+ "inputSchema": {
114
+ "type": "object",
115
+ "properties": {}
116
+ }
117
+ },
118
+ {
119
+ "name": "mnemo_delete",
120
+ "description": "Delete a specific memory by ID.",
121
+ "inputSchema": {
122
+ "type": "object",
123
+ "properties": {
124
+ "memory_id": {
125
+ "type": "string",
126
+ "description": "ID of the memory to delete"
127
+ }
128
+ },
129
+ "required": ["memory_id"]
130
+ }
131
+ },
132
+ {
133
+ "name": "mnemo_stats",
134
+ "description": "Get memory system statistics.",
135
+ "inputSchema": {
136
+ "type": "object",
137
+ "properties": {}
138
+ }
139
+ },
140
+ {
141
+ "name": "mnemo_clear",
142
+ "description": "Clear all stored memories. Use with caution!",
143
+ "inputSchema": {
144
+ "type": "object",
145
+ "properties": {
146
+ "confirm": {
147
+ "type": "boolean",
148
+ "description": "Must be true to confirm deletion"
149
+ }
150
+ },
151
+ "required": ["confirm"]
152
+ }
153
+ }
154
+ ]
155
+
156
+
157
+ def handle_tool_call(name: str, arguments: Dict[str, Any]) -> str:
158
+ """Handle a tool call and return result"""
159
+
160
+ if name == "mnemo_add":
161
+ content = arguments.get("content", "")
162
+ metadata = arguments.get("metadata", {})
163
+ mem_id = memory.add(content, metadata)
164
+ return json.dumps({
165
+ "status": "success",
166
+ "memory_id": mem_id,
167
+ "message": f"Memory stored successfully"
168
+ })
169
+
170
+ elif name == "mnemo_search":
171
+ query = arguments.get("query", "")
172
+ top_k = arguments.get("top_k", 5)
173
+ results = memory.search(query, top_k=top_k)
174
+
175
+ return json.dumps({
176
+ "status": "success",
177
+ "count": len(results),
178
+ "results": [
179
+ {
180
+ "id": r.id,
181
+ "content": r.content,
182
+ "score": round(r.score, 3),
183
+ "metadata": r.metadata
184
+ }
185
+ for r in results
186
+ ]
187
+ })
188
+
189
+ elif name == "mnemo_should_inject":
190
+ query = arguments.get("query", "")
191
+ context = arguments.get("context", "")
192
+ should, reason = should_inject_memory(query, context)
193
+
194
+ return json.dumps({
195
+ "should_inject": should,
196
+ "reason": reason,
197
+ "recommendation": "Inject memory context" if should else "Skip memory - standalone query"
198
+ })
199
+
200
+ elif name == "mnemo_get_context":
201
+ query = arguments.get("query", "")
202
+ top_k = arguments.get("top_k", 3)
203
+ context = memory.get_context(query, top_k=top_k)
204
+
205
+ return json.dumps({
206
+ "status": "success",
207
+ "context": context if context else None,
208
+ "message": "Context retrieved" if context else "No relevant context found"
209
+ })
210
+
211
+ elif name == "mnemo_list":
212
+ memories = memory.list_all()
213
+ return json.dumps({
214
+ "status": "success",
215
+ "count": len(memories),
216
+ "memories": [
217
+ {
218
+ "id": m.id,
219
+ "content": m.content[:200] + "..." if len(m.content) > 200 else m.content,
220
+ "created_at": m.created_at,
221
+ "metadata": m.metadata
222
+ }
223
+ for m in memories
224
+ ]
225
+ })
226
+
227
+ elif name == "mnemo_delete":
228
+ memory_id = arguments.get("memory_id", "")
229
+ success = memory.delete(memory_id)
230
+
231
+ return json.dumps({
232
+ "status": "success" if success else "error",
233
+ "message": f"Memory {memory_id} deleted" if success else f"Memory {memory_id} not found"
234
+ })
235
+
236
+ elif name == "mnemo_stats":
237
+ stats = memory.get_stats()
238
+ return json.dumps({
239
+ "status": "success",
240
+ "stats": stats
241
+ })
242
+
243
+ elif name == "mnemo_clear":
244
+ if arguments.get("confirm", False):
245
+ memory.clear()
246
+ return json.dumps({
247
+ "status": "success",
248
+ "message": "All memories cleared"
249
+ })
250
+ else:
251
+ return json.dumps({
252
+ "status": "error",
253
+ "message": "Must set confirm=true to clear all memories"
254
+ })
255
+
256
+ else:
257
+ return json.dumps({
258
+ "status": "error",
259
+ "message": f"Unknown tool: {name}"
260
+ })
261
+
262
+
263
+ def run_mcp_server():
264
+ """Run as MCP server"""
265
+ if not HAS_MCP:
266
+ print("Error: MCP not installed. Install with: pip install mcp", file=sys.stderr)
267
+ sys.exit(1)
268
+
269
+ server = Server("mnemo-memory")
270
+
271
+ @server.list_tools()
272
+ async def list_tools() -> List[Tool]:
273
+ return [
274
+ Tool(
275
+ name=tool["name"],
276
+ description=tool["description"],
277
+ inputSchema=tool["inputSchema"]
278
+ )
279
+ for tool in TOOLS
280
+ ]
281
+
282
+ @server.call_tool()
283
+ async def call_tool(name: str, arguments: Dict[str, Any]) -> List[TextContent]:
284
+ result = handle_tool_call(name, arguments)
285
+ return [TextContent(type="text", text=result)]
286
+
287
+ # Run server
288
+ import asyncio
289
+ asyncio.run(server.run())
290
+
291
+
292
+ def run_cli():
293
+ """Simple CLI for testing"""
294
+ print("Mnemo Memory System - CLI Mode")
295
+ print("Commands: add, search, inject, context, list, stats, clear, quit")
296
+ print("-" * 50)
297
+
298
+ while True:
299
+ try:
300
+ cmd = input("\n> ").strip().lower()
301
+
302
+ if cmd == "quit":
303
+ break
304
+ elif cmd == "add":
305
+ content = input("Content: ")
306
+ result = handle_tool_call("mnemo_add", {"content": content})
307
+ print(result)
308
+ elif cmd == "search":
309
+ query = input("Query: ")
310
+ result = handle_tool_call("mnemo_search", {"query": query})
311
+ print(result)
312
+ elif cmd == "inject":
313
+ query = input("Query: ")
314
+ result = handle_tool_call("mnemo_should_inject", {"query": query})
315
+ print(result)
316
+ elif cmd == "context":
317
+ query = input("Query: ")
318
+ result = handle_tool_call("mnemo_get_context", {"query": query})
319
+ print(result)
320
+ elif cmd == "list":
321
+ result = handle_tool_call("mnemo_list", {})
322
+ print(result)
323
+ elif cmd == "stats":
324
+ result = handle_tool_call("mnemo_stats", {})
325
+ print(result)
326
+ elif cmd == "clear":
327
+ confirm = input("Are you sure? (yes/no): ")
328
+ if confirm.lower() == "yes":
329
+ result = handle_tool_call("mnemo_clear", {"confirm": True})
330
+ print(result)
331
+ else:
332
+ print("Unknown command. Use: add, search, inject, context, list, stats, clear, quit")
333
+
334
+ except KeyboardInterrupt:
335
+ print("\nBye!")
336
+ break
337
+ except Exception as e:
338
+ print(f"Error: {e}")
339
+
340
+
341
+ if __name__ == "__main__":
342
+ if len(sys.argv) > 1 and sys.argv[1] == "--cli":
343
+ run_cli()
344
+ else:
345
+ run_mcp_server()