mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
fix: Add proper datetime JSON schema format annotations for MCP validation (#312)
Signed-off-by: Claude Code <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
This commit is contained in:
@@ -103,10 +103,17 @@ def format_prompt_context(context: PromptContext) -> str:
|
||||
|
||||
added_permalinks.add(primary_permalink)
|
||||
|
||||
memory_url = normalize_memory_url(primary_permalink)
|
||||
# Use permalink if available, otherwise use file_path
|
||||
if primary_permalink:
|
||||
memory_url = normalize_memory_url(primary_permalink)
|
||||
read_command = f'read_note("{primary_permalink}")'
|
||||
else:
|
||||
memory_url = f"file://{primary.file_path}"
|
||||
read_command = f'read_file("{primary.file_path}")'
|
||||
|
||||
section = dedent(f"""
|
||||
--- {memory_url}
|
||||
|
||||
|
||||
## {primary.title}
|
||||
- **Type**: {primary.type}
|
||||
""")
|
||||
@@ -121,8 +128,8 @@ def format_prompt_context(context: PromptContext) -> str:
|
||||
section += f"\n**Excerpt**:\n{content}\n"
|
||||
|
||||
section += dedent(f"""
|
||||
|
||||
You can read this document with: `read_note("{primary_permalink}")`
|
||||
|
||||
You can read this document with: `{read_command}`
|
||||
""")
|
||||
sections.append(section)
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ def validate_memory_url_path(path: str) -> bool:
|
||||
>>> validate_memory_url_path("invalid://test") # Contains protocol
|
||||
False
|
||||
"""
|
||||
# Empty paths are not valid
|
||||
if not path or not path.strip():
|
||||
return False
|
||||
|
||||
@@ -68,7 +69,13 @@ def normalize_memory_url(url: str | None) -> str:
|
||||
ValueError: Invalid memory URL path: 'memory//test' contains double slashes
|
||||
"""
|
||||
if not url:
|
||||
return ""
|
||||
raise ValueError("Memory URL cannot be empty")
|
||||
|
||||
# Strip whitespace for consistency
|
||||
url = url.strip()
|
||||
|
||||
if not url:
|
||||
raise ValueError("Memory URL cannot be empty or whitespace")
|
||||
|
||||
clean_path = url.removeprefix("memory://")
|
||||
|
||||
@@ -79,8 +86,6 @@ def normalize_memory_url(url: str | None) -> str:
|
||||
raise ValueError(f"Invalid memory URL path: '{clean_path}' contains protocol scheme")
|
||||
elif "//" in clean_path:
|
||||
raise ValueError(f"Invalid memory URL path: '{clean_path}' contains double slashes")
|
||||
elif not clean_path.strip():
|
||||
raise ValueError("Memory URL path cannot be empty or whitespace")
|
||||
else:
|
||||
raise ValueError(f"Invalid memory URL path: '{clean_path}' contains invalid characters")
|
||||
|
||||
@@ -123,7 +128,9 @@ class EntitySummary(BaseModel):
|
||||
title: str
|
||||
content: Optional[str] = None
|
||||
file_path: str
|
||||
created_at: datetime
|
||||
created_at: Annotated[
|
||||
datetime, Field(json_schema_extra={"type": "string", "format": "date-time"})
|
||||
]
|
||||
|
||||
@field_serializer("created_at")
|
||||
def serialize_created_at(self, dt: datetime) -> str:
|
||||
@@ -140,7 +147,9 @@ class RelationSummary(BaseModel):
|
||||
relation_type: str
|
||||
from_entity: Optional[str] = None
|
||||
to_entity: Optional[str] = None
|
||||
created_at: datetime
|
||||
created_at: Annotated[
|
||||
datetime, Field(json_schema_extra={"type": "string", "format": "date-time"})
|
||||
]
|
||||
|
||||
@field_serializer("created_at")
|
||||
def serialize_created_at(self, dt: datetime) -> str:
|
||||
@@ -156,7 +165,9 @@ class ObservationSummary(BaseModel):
|
||||
permalink: str
|
||||
category: str
|
||||
content: str
|
||||
created_at: datetime
|
||||
created_at: Annotated[
|
||||
datetime, Field(json_schema_extra={"type": "string", "format": "date-time"})
|
||||
]
|
||||
|
||||
@field_serializer("created_at")
|
||||
def serialize_created_at(self, dt: datetime) -> str:
|
||||
@@ -170,7 +181,9 @@ class MemoryMetadata(BaseModel):
|
||||
types: Optional[List[SearchItemType]] = None
|
||||
depth: int
|
||||
timeframe: Optional[str] = None
|
||||
generated_at: datetime
|
||||
generated_at: Annotated[
|
||||
datetime, Field(json_schema_extra={"type": "string", "format": "date-time"})
|
||||
]
|
||||
primary_count: Optional[int] = None # Changed field name
|
||||
related_count: Optional[int] = None # Changed field name
|
||||
total_results: Optional[int] = None # For backward compatibility
|
||||
@@ -235,9 +248,9 @@ class ProjectActivity(BaseModel):
|
||||
project_path: str
|
||||
activity: GraphContext = Field(description="The actual activity data for this project")
|
||||
item_count: int = Field(description="Total items in this project's activity")
|
||||
last_activity: Optional[datetime] = Field(
|
||||
default=None, description="Most recent activity timestamp"
|
||||
)
|
||||
last_activity: Optional[
|
||||
Annotated[datetime, Field(json_schema_extra={"type": "string", "format": "date-time"})]
|
||||
] = Field(default=None, description="Most recent activity timestamp")
|
||||
active_folders: List[str] = Field(default_factory=list, description="Most active folders")
|
||||
|
||||
@field_serializer("last_activity")
|
||||
@@ -253,7 +266,9 @@ class ProjectActivitySummary(BaseModel):
|
||||
)
|
||||
summary: ActivityStats
|
||||
timeframe: str = Field(description="The timeframe used for the query")
|
||||
generated_at: datetime
|
||||
generated_at: Annotated[
|
||||
datetime, Field(json_schema_extra={"type": "string", "format": "date-time"})
|
||||
]
|
||||
guidance: Optional[str] = Field(
|
||||
default=None, description="Assistant guidance for project selection and session management"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user