mem-brain-mcp 1.0.6__py3-none-any.whl → 1.0.7__py3-none-any.whl
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.
- mem_brain_mcp/client.py +26 -54
- mem_brain_mcp/server.py +388 -303
- {mem_brain_mcp-1.0.6.dist-info → mem_brain_mcp-1.0.7.dist-info}/METADATA +1 -1
- mem_brain_mcp-1.0.7.dist-info/RECORD +9 -0
- mem_brain_mcp-1.0.6.dist-info/RECORD +0 -9
- {mem_brain_mcp-1.0.6.dist-info → mem_brain_mcp-1.0.7.dist-info}/WHEEL +0 -0
- {mem_brain_mcp-1.0.6.dist-info → mem_brain_mcp-1.0.7.dist-info}/entry_points.txt +0 -0
mem_brain_mcp/client.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""HTTP client for Mem-Brain API."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from typing import List, Optional, Dict, Any
|
|
4
|
+
from typing import List, Optional, Dict, Any, Union
|
|
5
5
|
import httpx
|
|
6
6
|
|
|
7
7
|
from mem_brain_mcp.config import settings
|
|
@@ -34,12 +34,7 @@ class APIClient:
|
|
|
34
34
|
logger.debug("No authentication token configured for this client instance")
|
|
35
35
|
return headers
|
|
36
36
|
|
|
37
|
-
async def _request(
|
|
38
|
-
self,
|
|
39
|
-
method: str,
|
|
40
|
-
endpoint: str,
|
|
41
|
-
**kwargs
|
|
42
|
-
) -> Dict[str, Any]:
|
|
37
|
+
async def _request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
|
|
43
38
|
"""Make HTTP request to API.
|
|
44
39
|
|
|
45
40
|
Args:
|
|
@@ -56,10 +51,12 @@ class APIClient:
|
|
|
56
51
|
url = f"{self.base_url}/{endpoint.lstrip('/')}"
|
|
57
52
|
headers = self._get_headers()
|
|
58
53
|
headers.update(kwargs.pop("headers", {}))
|
|
59
|
-
|
|
54
|
+
|
|
60
55
|
# Debug logging for request details
|
|
61
56
|
logger.debug(f"Making {method} request to: {url}")
|
|
62
|
-
logger.debug(
|
|
57
|
+
logger.debug(
|
|
58
|
+
f"Headers: {dict((k, v[:20] + '...' if k == 'Authorization' and len(v) > 20 else v) for k, v in headers.items())}"
|
|
59
|
+
)
|
|
63
60
|
if kwargs.get("json"):
|
|
64
61
|
logger.debug(f"Request body: {kwargs.get('json')}")
|
|
65
62
|
if kwargs.get("params"):
|
|
@@ -67,33 +64,29 @@ class APIClient:
|
|
|
67
64
|
|
|
68
65
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
69
66
|
try:
|
|
70
|
-
response = await client.request(
|
|
71
|
-
method=method,
|
|
72
|
-
url=url,
|
|
73
|
-
headers=headers,
|
|
74
|
-
**kwargs
|
|
75
|
-
)
|
|
67
|
+
response = await client.request(method=method, url=url, headers=headers, **kwargs)
|
|
76
68
|
logger.debug(f"Response status: {response.status_code}")
|
|
77
69
|
logger.debug(f"Response headers: {dict(response.headers)}")
|
|
78
|
-
|
|
70
|
+
|
|
79
71
|
try:
|
|
80
72
|
response.raise_for_status()
|
|
81
73
|
result = response.json()
|
|
82
|
-
logger.debug(
|
|
74
|
+
logger.debug(
|
|
75
|
+
f"Response data keys: {list(result.keys()) if isinstance(result, dict) else 'N/A'}"
|
|
76
|
+
)
|
|
83
77
|
return result
|
|
84
78
|
except httpx.HTTPStatusError as e:
|
|
85
79
|
error_detail = e.response.text if e.response else "No response body"
|
|
86
|
-
logger.error(
|
|
80
|
+
logger.error(
|
|
81
|
+
f"API request failed: {e.request.method} {e.request.url} - {e.response.status_code}: {error_detail}"
|
|
82
|
+
)
|
|
87
83
|
raise
|
|
88
84
|
except httpx.RequestError as e:
|
|
89
85
|
logger.error(f"Request error: {type(e).__name__}: {str(e)}")
|
|
90
86
|
raise
|
|
91
87
|
|
|
92
88
|
async def add_memory(
|
|
93
|
-
self,
|
|
94
|
-
content: str,
|
|
95
|
-
tags: Optional[List[str]] = None,
|
|
96
|
-
category: Optional[str] = None
|
|
89
|
+
self, content: str, tags: Optional[List[str]] = None, category: Optional[str] = None
|
|
97
90
|
) -> Dict[str, Any]:
|
|
98
91
|
"""Add a new memory.
|
|
99
92
|
|
|
@@ -114,26 +107,24 @@ class APIClient:
|
|
|
114
107
|
return await self._request("POST", "/memories", json=data)
|
|
115
108
|
|
|
116
109
|
async def search_memories(
|
|
117
|
-
self,
|
|
118
|
-
query: str,
|
|
119
|
-
k: int = 5
|
|
110
|
+
self, query: str, k: int = 5, keyword_filter: Optional[Union[str, List[str]]] = None
|
|
120
111
|
) -> Dict[str, Any]:
|
|
121
112
|
"""Search memories using semantic similarity.
|
|
122
113
|
|
|
123
114
|
Args:
|
|
124
115
|
query: Search query string
|
|
125
116
|
k: Number of results to return (1-100)
|
|
117
|
+
keyword_filter: Optional keyword/tag filter (regex supported)
|
|
126
118
|
|
|
127
119
|
Returns:
|
|
128
120
|
Search results
|
|
129
121
|
"""
|
|
130
122
|
data = {"query": query, "k": k}
|
|
123
|
+
if keyword_filter:
|
|
124
|
+
data["keyword_filter"] = keyword_filter
|
|
131
125
|
return await self._request("POST", "/memories/search", json=data)
|
|
132
126
|
|
|
133
|
-
async def get_memories(
|
|
134
|
-
self,
|
|
135
|
-
memory_ids: List[str]
|
|
136
|
-
) -> Dict[str, Any]:
|
|
127
|
+
async def get_memories(self, memory_ids: List[str]) -> Dict[str, Any]:
|
|
137
128
|
"""Retrieve multiple memories by ID.
|
|
138
129
|
|
|
139
130
|
Args:
|
|
@@ -146,10 +137,7 @@ class APIClient:
|
|
|
146
137
|
return await self._request("POST", "/memories/batch", json=data)
|
|
147
138
|
|
|
148
139
|
async def update_memory(
|
|
149
|
-
self,
|
|
150
|
-
memory_id: str,
|
|
151
|
-
content: Optional[str] = None,
|
|
152
|
-
tags: Optional[List[str]] = None
|
|
140
|
+
self, memory_id: str, content: Optional[str] = None, tags: Optional[List[str]] = None
|
|
153
141
|
) -> Dict[str, Any]:
|
|
154
142
|
"""Update an existing memory.
|
|
155
143
|
|
|
@@ -173,7 +161,7 @@ class APIClient:
|
|
|
173
161
|
self,
|
|
174
162
|
memory_id: Optional[str] = None,
|
|
175
163
|
tags: Optional[str] = None,
|
|
176
|
-
category: Optional[str] = None
|
|
164
|
+
category: Optional[str] = None,
|
|
177
165
|
) -> Dict[str, Any]:
|
|
178
166
|
"""Delete memories by ID or filter.
|
|
179
167
|
|
|
@@ -195,11 +183,7 @@ class APIClient:
|
|
|
195
183
|
|
|
196
184
|
return await self._request("DELETE", "/memories/bulk", params=params)
|
|
197
185
|
|
|
198
|
-
async def unlink_memories(
|
|
199
|
-
self,
|
|
200
|
-
memory_id_1: str,
|
|
201
|
-
memory_id_2: str
|
|
202
|
-
) -> Dict[str, Any]:
|
|
186
|
+
async def unlink_memories(self, memory_id_1: str, memory_id_2: str) -> Dict[str, Any]:
|
|
203
187
|
"""Remove link between two memories.
|
|
204
188
|
|
|
205
189
|
Args:
|
|
@@ -209,10 +193,7 @@ class APIClient:
|
|
|
209
193
|
Returns:
|
|
210
194
|
Unlink response
|
|
211
195
|
"""
|
|
212
|
-
data = {
|
|
213
|
-
"memory_id_1": memory_id_1,
|
|
214
|
-
"memory_id_2": memory_id_2
|
|
215
|
-
}
|
|
196
|
+
data = {"memory_id_1": memory_id_1, "memory_id_2": memory_id_2}
|
|
216
197
|
return await self._request("POST", "/memories/unlink", json=data)
|
|
217
198
|
|
|
218
199
|
async def get_stats(self) -> Dict[str, Any]:
|
|
@@ -224,11 +205,7 @@ class APIClient:
|
|
|
224
205
|
logger.debug("get_stats() called - making request to /stats endpoint")
|
|
225
206
|
return await self._request("GET", "/stats")
|
|
226
207
|
|
|
227
|
-
async def find_path(
|
|
228
|
-
self,
|
|
229
|
-
from_id: str,
|
|
230
|
-
to_id: str
|
|
231
|
-
) -> Dict[str, Any]:
|
|
208
|
+
async def find_path(self, from_id: str, to_id: str) -> Dict[str, Any]:
|
|
232
209
|
"""Find shortest path between two memories.
|
|
233
210
|
|
|
234
211
|
Args:
|
|
@@ -241,11 +218,7 @@ class APIClient:
|
|
|
241
218
|
params = {"from_id": from_id, "to_id": to_id}
|
|
242
219
|
return await self._request("GET", "/graph/path", params=params)
|
|
243
220
|
|
|
244
|
-
async def get_neighborhood(
|
|
245
|
-
self,
|
|
246
|
-
memory_id: str,
|
|
247
|
-
hops: int = 2
|
|
248
|
-
) -> Dict[str, Any]:
|
|
221
|
+
async def get_neighborhood(self, memory_id: str, hops: int = 2) -> Dict[str, Any]:
|
|
249
222
|
"""Get all memories within N hops of a given memory.
|
|
250
223
|
|
|
251
224
|
Args:
|
|
@@ -257,4 +230,3 @@ class APIClient:
|
|
|
257
230
|
"""
|
|
258
231
|
params = {"memory_id": memory_id, "hops": hops}
|
|
259
232
|
return await self._request("GET", "/graph/neighborhood", params=params)
|
|
260
|
-
|