mem-brain-mcp 1.0.5__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/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """Mem-Brain MCP Server - Exposes Mem-Brain API as MCP tools."""
2
2
 
3
- __version__ = "1.0.5"
3
+ __version__ = "1.0.6"
4
4
 
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:
@@ -57,26 +52,41 @@ class APIClient:
57
52
  headers = self._get_headers()
58
53
  headers.update(kwargs.pop("headers", {}))
59
54
 
55
+ # Debug logging for request details
56
+ logger.debug(f"Making {method} request to: {url}")
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
+ )
60
+ if kwargs.get("json"):
61
+ logger.debug(f"Request body: {kwargs.get('json')}")
62
+ if kwargs.get("params"):
63
+ logger.debug(f"Request params: {kwargs.get('params')}")
64
+
60
65
  async with httpx.AsyncClient(timeout=30.0) as client:
61
- response = await client.request(
62
- method=method,
63
- url=url,
64
- headers=headers,
65
- **kwargs
66
- )
67
66
  try:
68
- response.raise_for_status()
69
- return response.json()
70
- except httpx.HTTPStatusError as e:
71
- error_detail = e.response.text if e.response else "No response body"
72
- logger.error(f"API request failed: {e.request.method} {e.request.url} - {e.response.status_code}: {error_detail}")
67
+ response = await client.request(method=method, url=url, headers=headers, **kwargs)
68
+ logger.debug(f"Response status: {response.status_code}")
69
+ logger.debug(f"Response headers: {dict(response.headers)}")
70
+
71
+ try:
72
+ response.raise_for_status()
73
+ result = response.json()
74
+ logger.debug(
75
+ f"Response data keys: {list(result.keys()) if isinstance(result, dict) else 'N/A'}"
76
+ )
77
+ return result
78
+ except httpx.HTTPStatusError as e:
79
+ error_detail = e.response.text if e.response else "No response body"
80
+ logger.error(
81
+ f"API request failed: {e.request.method} {e.request.url} - {e.response.status_code}: {error_detail}"
82
+ )
83
+ raise
84
+ except httpx.RequestError as e:
85
+ logger.error(f"Request error: {type(e).__name__}: {str(e)}")
73
86
  raise
74
87
 
75
88
  async def add_memory(
76
- self,
77
- content: str,
78
- tags: Optional[List[str]] = None,
79
- category: Optional[str] = None
89
+ self, content: str, tags: Optional[List[str]] = None, category: Optional[str] = None
80
90
  ) -> Dict[str, Any]:
81
91
  """Add a new memory.
82
92
 
@@ -97,26 +107,24 @@ class APIClient:
97
107
  return await self._request("POST", "/memories", json=data)
98
108
 
99
109
  async def search_memories(
100
- self,
101
- query: str,
102
- k: int = 5
110
+ self, query: str, k: int = 5, keyword_filter: Optional[Union[str, List[str]]] = None
103
111
  ) -> Dict[str, Any]:
104
112
  """Search memories using semantic similarity.
105
113
 
106
114
  Args:
107
115
  query: Search query string
108
116
  k: Number of results to return (1-100)
117
+ keyword_filter: Optional keyword/tag filter (regex supported)
109
118
 
110
119
  Returns:
111
120
  Search results
112
121
  """
113
122
  data = {"query": query, "k": k}
123
+ if keyword_filter:
124
+ data["keyword_filter"] = keyword_filter
114
125
  return await self._request("POST", "/memories/search", json=data)
115
126
 
116
- async def get_memories(
117
- self,
118
- memory_ids: List[str]
119
- ) -> Dict[str, Any]:
127
+ async def get_memories(self, memory_ids: List[str]) -> Dict[str, Any]:
120
128
  """Retrieve multiple memories by ID.
121
129
 
122
130
  Args:
@@ -129,10 +137,7 @@ class APIClient:
129
137
  return await self._request("POST", "/memories/batch", json=data)
130
138
 
131
139
  async def update_memory(
132
- self,
133
- memory_id: str,
134
- content: Optional[str] = None,
135
- tags: Optional[List[str]] = None
140
+ self, memory_id: str, content: Optional[str] = None, tags: Optional[List[str]] = None
136
141
  ) -> Dict[str, Any]:
137
142
  """Update an existing memory.
138
143
 
@@ -156,7 +161,7 @@ class APIClient:
156
161
  self,
157
162
  memory_id: Optional[str] = None,
158
163
  tags: Optional[str] = None,
159
- category: Optional[str] = None
164
+ category: Optional[str] = None,
160
165
  ) -> Dict[str, Any]:
161
166
  """Delete memories by ID or filter.
162
167
 
@@ -178,11 +183,7 @@ class APIClient:
178
183
 
179
184
  return await self._request("DELETE", "/memories/bulk", params=params)
180
185
 
181
- async def unlink_memories(
182
- self,
183
- memory_id_1: str,
184
- memory_id_2: str
185
- ) -> Dict[str, Any]:
186
+ async def unlink_memories(self, memory_id_1: str, memory_id_2: str) -> Dict[str, Any]:
186
187
  """Remove link between two memories.
187
188
 
188
189
  Args:
@@ -192,10 +193,7 @@ class APIClient:
192
193
  Returns:
193
194
  Unlink response
194
195
  """
195
- data = {
196
- "memory_id_1": memory_id_1,
197
- "memory_id_2": memory_id_2
198
- }
196
+ data = {"memory_id_1": memory_id_1, "memory_id_2": memory_id_2}
199
197
  return await self._request("POST", "/memories/unlink", json=data)
200
198
 
201
199
  async def get_stats(self) -> Dict[str, Any]:
@@ -204,13 +202,10 @@ class APIClient:
204
202
  Returns:
205
203
  Statistics response
206
204
  """
205
+ logger.debug("get_stats() called - making request to /stats endpoint")
207
206
  return await self._request("GET", "/stats")
208
207
 
209
- async def find_path(
210
- self,
211
- from_id: str,
212
- to_id: str
213
- ) -> Dict[str, Any]:
208
+ async def find_path(self, from_id: str, to_id: str) -> Dict[str, Any]:
214
209
  """Find shortest path between two memories.
215
210
 
216
211
  Args:
@@ -223,11 +218,7 @@ class APIClient:
223
218
  params = {"from_id": from_id, "to_id": to_id}
224
219
  return await self._request("GET", "/graph/path", params=params)
225
220
 
226
- async def get_neighborhood(
227
- self,
228
- memory_id: str,
229
- hops: int = 2
230
- ) -> Dict[str, Any]:
221
+ async def get_neighborhood(self, memory_id: str, hops: int = 2) -> Dict[str, Any]:
231
222
  """Get all memories within N hops of a given memory.
232
223
 
233
224
  Args:
@@ -239,4 +230,3 @@ class APIClient:
239
230
  """
240
231
  params = {"memory_id": memory_id, "hops": hops}
241
232
  return await self._request("GET", "/graph/neighborhood", params=params)
242
-