mem-brain-mcp 1.0.2__py3-none-any.whl → 1.0.4__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.2"
3
+ __version__ = "1.0.4"
4
4
 
mem_brain_mcp/server.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """MCP Server for Mem-Brain API using FastMCP."""
2
2
 
3
+ import json
3
4
  import logging
4
5
  from typing import Any, Dict, List, Optional, Union
5
6
  import httpx
@@ -407,11 +408,13 @@ async def add_memory(
407
408
  - Example: "User prefers Python over JavaScript"
408
409
  - Example: "User prefers dark mode interfaces"
409
410
 
410
- tags (list[str], optional): List of tags to categorize the memory.
411
- - Can be None (default) or a list of strings
411
+ tags (list[str] or str, optional): Tags to categorize the memory.
412
+ - Can be None (default), a list of strings, a comma-separated string, or a JSON array string
413
+ - If omitted, the system will auto-generate tags based on content
412
414
  - Example: ["coding", "preferences"]
413
- - Example: ["personal", "pets", "animals"]
414
- - If you pass a single string, it will be converted to a list
415
+ - Example: "coding,preferences" (comma-separated)
416
+ - Example: '["coding", "preferences"]' (JSON string)
417
+ - Note: The system auto-generates relevant tags, so providing tags is optional
415
418
 
416
419
  category (str, optional): Category name for the memory.
417
420
  - Can be None (default) or a non-empty string
@@ -480,7 +483,7 @@ async def add_memory(
480
483
  logger.info(f"add_memory called - content length: {len(content_str)}, tags: {tags}, category: {category}")
481
484
  logger.debug(f"add_memory full content: {content_str[:100]}...")
482
485
 
483
- # Normalize tags: convert empty list to None, ensure it's a list if provided
486
+ # Normalize tags: handle various input formats and convert to list of strings
484
487
  normalized_tags = None
485
488
  if tags is not None:
486
489
  if isinstance(tags, list):
@@ -495,13 +498,31 @@ async def add_memory(
495
498
  )
496
499
  normalized_tags = tags if tags else None # Empty list becomes None
497
500
  elif isinstance(tags, str):
498
- # Handle case where tags might be passed as a single string
499
- normalized_tags = [tags]
501
+ tags_str = tags.strip()
502
+ if not tags_str:
503
+ normalized_tags = None
504
+ else:
505
+ # Try to parse as JSON array first (e.g., '["tag1", "tag2"]')
506
+ try:
507
+ parsed = json.loads(tags_str)
508
+ if isinstance(parsed, list):
509
+ normalized_tags = [str(item).strip() for item in parsed if str(item).strip()]
510
+ else:
511
+ # If JSON but not a list, treat as single tag
512
+ normalized_tags = [tags_str]
513
+ except (json.JSONDecodeError, ValueError):
514
+ # Not JSON, try comma-separated string
515
+ if ',' in tags_str:
516
+ normalized_tags = [tag.strip() for tag in tags_str.split(',') if tag.strip()]
517
+ else:
518
+ # Single tag string
519
+ normalized_tags = [tags_str]
500
520
  else:
501
521
  raise ToolError(
502
- f"The 'tags' parameter must be a list of strings or None, but got {type(tags).__name__}.\n"
522
+ f"The 'tags' parameter must be a list of strings, a comma-separated string, or None, but got {type(tags).__name__}.\n"
503
523
  f"Received: {repr(tags)}\n"
504
524
  "Example: add_memory(content=\"...\", tags=[\"coding\", \"preferences\"])\n"
525
+ "Example: add_memory(content=\"...\", tags=\"coding,preferences\")\n"
505
526
  "Example: add_memory(content=\"...\", tags=None) # or omit tags parameter"
506
527
  )
507
528
 
@@ -738,10 +759,13 @@ async def update_memory(
738
759
  - If provided, must not be empty or whitespace-only
739
760
  - Example: "User no longer likes TypeScript, prefers Python"
740
761
 
741
- tags (list[str], optional): New tags for the memory.
742
- - Can be None (to keep existing tags) or a list of strings
762
+ tags (list[str] or str, optional): New tags for the memory.
763
+ - Can be None (to keep existing tags), a list of strings, a comma-separated string, or a JSON array string
764
+ - If provided, replaces existing tags
743
765
  - Example: ["coding", "python"]
744
- - Example: ["preferences", "updated"]
766
+ - Example: "coding,python" (comma-separated)
767
+ - Example: '["coding", "python"]' (JSON string)
768
+ - Note: The system can auto-generate tags if you omit this parameter
745
769
 
746
770
  Returns:
747
771
  str: A formatted string with the updated memory details.
@@ -819,7 +843,7 @@ async def update_memory(
819
843
  else:
820
844
  content_str = None
821
845
 
822
- # Validate tags if provided
846
+ # Validate tags if provided - handle various input formats
823
847
  normalized_tags = None
824
848
  if tags is not None:
825
849
  if isinstance(tags, list):
@@ -834,13 +858,31 @@ async def update_memory(
834
858
  )
835
859
  normalized_tags = tags if tags else None # Empty list becomes None
836
860
  elif isinstance(tags, str):
837
- # Handle case where tags might be passed as a single string
838
- normalized_tags = [tags]
861
+ tags_str = tags.strip()
862
+ if not tags_str:
863
+ normalized_tags = None
864
+ else:
865
+ # Try to parse as JSON array first (e.g., '["tag1", "tag2"]')
866
+ try:
867
+ parsed = json.loads(tags_str)
868
+ if isinstance(parsed, list):
869
+ normalized_tags = [str(item).strip() for item in parsed if str(item).strip()]
870
+ else:
871
+ # If JSON but not a list, treat as single tag
872
+ normalized_tags = [tags_str]
873
+ except (json.JSONDecodeError, ValueError):
874
+ # Not JSON, try comma-separated string
875
+ if ',' in tags_str:
876
+ normalized_tags = [tag.strip() for tag in tags_str.split(',') if tag.strip()]
877
+ else:
878
+ # Single tag string
879
+ normalized_tags = [tags_str]
839
880
  else:
840
881
  raise ToolError(
841
- f"The 'tags' parameter must be a list of strings or None, but got {type(tags).__name__}.\n"
882
+ f"The 'tags' parameter must be a list of strings, a comma-separated string, or None, but got {type(tags).__name__}.\n"
842
883
  f"Received: {repr(tags)}\n"
843
884
  "Example: update_memory(memory_id=\"...\", tags=[\"coding\", \"preferences\"])\n"
885
+ "Example: update_memory(memory_id=\"...\", tags=\"coding,preferences\")\n"
844
886
  "Example: update_memory(memory_id=\"...\", tags=None) # or omit tags parameter"
845
887
  )
846
888
 
@@ -1101,16 +1143,27 @@ async def unlink_memories(memory_id_1: str, memory_id_2: str) -> str:
1101
1143
 
1102
1144
 
1103
1145
  @mcp.tool()
1104
- async def get_stats() -> str:
1146
+ async def get_stats(_placeholder: Optional[bool] = None) -> str:
1105
1147
  """Get memory system statistics including total memories, links, and top tags. Use this when user asks 'how much do you remember?' or wants an overview of their memory system.
1106
1148
 
1149
+ Parameters:
1150
+ _placeholder (bool, optional): Placeholder parameter for OpenCode compatibility. This parameter is ignored and can be omitted or set to any value. The function takes no actual parameters.
1151
+ - This is a workaround for MCP clients that incorrectly require a parameter for parameterless tools
1152
+ - Can be safely omitted or set to None/True/False
1153
+ - Example: get_stats() or get_stats(_placeholder=True)
1154
+
1107
1155
  Returns:
1108
1156
  str: Formatted statistics including total memories, links, and top tags.
1109
1157
 
1110
1158
  Examples:
1111
- # Get statistics
1159
+ # Get statistics (preferred - no parameters needed)
1112
1160
  get_stats()
1161
+
1162
+ # Get statistics (OpenCode workaround - parameter is ignored)
1163
+ get_stats(_placeholder=True)
1113
1164
  """
1165
+ # _placeholder parameter is ignored - this is a workaround for OpenCode compatibility
1166
+ # The function actually takes no parameters, but some MCP clients incorrectly require one
1114
1167
  try:
1115
1168
  logger.info("get_stats called")
1116
1169
  client = await _get_api_client()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mem-brain-mcp
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: MCP Server for Mem-Brain API - Exposes memory operations as MCP tools
5
5
  Keywords: ai,claude,cursor,llm,mcp,memory,model-context-protocol
6
6
  Classifier: Development Status :: 4 - Beta
@@ -0,0 +1,9 @@
1
+ mem_brain_mcp/__init__.py,sha256=IxDcMoheWmVbcdZEJSXj5fAx0WlYMjVonvvG0boqsec,89
2
+ mem_brain_mcp/__main__.py,sha256=H_mwoKm1FBmu4KzAcQcq-TXZqeNvlrAekAxB1s4F4hA,712
3
+ mem_brain_mcp/client.py,sha256=7KFGcLoPDaOOLiuG2lygQK7xH5Kio-YifDjuSpDoDJ8,6993
4
+ mem_brain_mcp/config.py,sha256=xx2lBkCIeT85t0HxtORwZHSU3hZT_EdsThpfjwPJhbQ,1261
5
+ mem_brain_mcp/server.py,sha256=Yw92d-Qy5kRDAaIC8VjEKBOYKCy2p6neZ9OSfxsThdY,69640
6
+ mem_brain_mcp-1.0.4.dist-info/METADATA,sha256=7N3lTIQRHbFLfYdZyf2GZIowXEbmofVxO8QOrPFUCkE,5228
7
+ mem_brain_mcp-1.0.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
8
+ mem_brain_mcp-1.0.4.dist-info/entry_points.txt,sha256=NH6QYQ-Sd8eJn5crpe_DL1PvGeUlL3y65968xPhmwG8,62
9
+ mem_brain_mcp-1.0.4.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- mem_brain_mcp/__init__.py,sha256=NMoDpXlRhGO858M6lrLqPN0fz7hKpqler4e96WniWxg,89
2
- mem_brain_mcp/__main__.py,sha256=H_mwoKm1FBmu4KzAcQcq-TXZqeNvlrAekAxB1s4F4hA,712
3
- mem_brain_mcp/client.py,sha256=7KFGcLoPDaOOLiuG2lygQK7xH5Kio-YifDjuSpDoDJ8,6993
4
- mem_brain_mcp/config.py,sha256=xx2lBkCIeT85t0HxtORwZHSU3hZT_EdsThpfjwPJhbQ,1261
5
- mem_brain_mcp/server.py,sha256=qnevDzJkt1rH-slV2r0pP_SeFkknZVqWRnZEKH_IeVo,66308
6
- mem_brain_mcp-1.0.2.dist-info/METADATA,sha256=zLQMVD-sIRSH03dYAP6RovZH_49XqREG061Yf87ROW4,5228
7
- mem_brain_mcp-1.0.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
8
- mem_brain_mcp-1.0.2.dist-info/entry_points.txt,sha256=NH6QYQ-Sd8eJn5crpe_DL1PvGeUlL3y65968xPhmwG8,62
9
- mem_brain_mcp-1.0.2.dist-info/RECORD,,