mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
160 lines
4.8 KiB
Python
160 lines
4.8 KiB
Python
"""Note management tools for Basic Memory MCP server.
|
|
|
|
These tools provide a natural interface for working with markdown notes
|
|
while leveraging the underlying knowledge graph structure.
|
|
"""
|
|
|
|
from typing import Optional, List
|
|
|
|
from loguru import logger
|
|
from mcp.server.fastmcp.exceptions import ToolError
|
|
|
|
from basic_memory.mcp.server import mcp
|
|
from basic_memory.mcp.async_client import client
|
|
from basic_memory.mcp.tools.search import search
|
|
from basic_memory.schemas.request import CreateEntityRequest
|
|
from basic_memory.schemas.base import Entity, Relation
|
|
from basic_memory.schemas.request import CreateRelationsRequest
|
|
from basic_memory.mcp.tools.knowledge import create_entities, create_relations
|
|
from basic_memory.mcp.tools.utils import call_get
|
|
from basic_memory.schemas.search import SearchQuery
|
|
|
|
|
|
@mcp.tool(
|
|
description="Create or update a markdown note. Returns the permalink for referencing.",
|
|
)
|
|
async def write_note(
|
|
title: str,
|
|
content: str,
|
|
tags: Optional[List[str]] = None,
|
|
) -> str:
|
|
"""Write a markdown note to the knowledge base.
|
|
|
|
Args:
|
|
title: The note's title
|
|
content: Markdown content for the note
|
|
tags: Optional list of tags to categorize the note
|
|
|
|
Returns:
|
|
Permalink that can be used to reference the note
|
|
|
|
Examples:
|
|
# Create a simple note
|
|
write_note(
|
|
title="Meeting Notes: Project Planning",
|
|
content="# Key Points\\n\\n- Discussed timeline\\n- Set priorities"
|
|
)
|
|
|
|
# Create note with tags
|
|
write_note(
|
|
title="Security Review",
|
|
content="# Findings\\n\\n1. Updated auth flow\\n2. Added rate limiting",
|
|
tags=["security", "development"]
|
|
)
|
|
"""
|
|
logger.info(f"Writing note: {title}")
|
|
|
|
# Create the entity request
|
|
metadata = {"tags": [f"#{tag}" for tag in tags]} if tags else None
|
|
request = CreateEntityRequest(
|
|
entities=[
|
|
Entity(
|
|
title=title,
|
|
entity_type="note",
|
|
content_type="text/markdown",
|
|
content=content,
|
|
# Convert tags to observations if provided
|
|
entity_metadata=metadata,
|
|
)
|
|
]
|
|
)
|
|
|
|
# Use existing knowledge tool
|
|
result = await create_entities(request)
|
|
return result.entities[0].permalink
|
|
|
|
|
|
@mcp.tool(description="Read a note's content by its title or permalink")
|
|
async def read_note(identifier: str) -> str:
|
|
"""Get the markdown content of a note.
|
|
Uses the resource router to return the actual file content.
|
|
|
|
Args:
|
|
identifier: Note title or permalink
|
|
|
|
Returns:
|
|
The note's markdown content
|
|
|
|
Examples:
|
|
# Read by title
|
|
read_note("Meeting Notes: Project Planning")
|
|
|
|
# Read by permalink
|
|
read_note("notes/project-planning")
|
|
|
|
Raises:
|
|
ValueError: If the note cannot be found
|
|
"""
|
|
try:
|
|
# Try as permalink first
|
|
response = await call_get(client, f"/resource/{identifier}")
|
|
return response.text
|
|
except ToolError as e:
|
|
if "404" in str(e):
|
|
# If not found, try searching by title
|
|
search_response = await search(SearchQuery(text=identifier, entity_types=["note"]))
|
|
|
|
if not search_response.results:
|
|
raise ValueError(f"Note not found: {identifier}")
|
|
|
|
# if we found results, return the first one
|
|
response = await call_get(client, f"/resource/{search_response.results[0].permalink}")
|
|
return response.text
|
|
|
|
raise ValueError(f"Error reading note: {e}")
|
|
except Exception as e:
|
|
raise ValueError(f"Unexpected error reading note: {e}")
|
|
|
|
|
|
@mcp.tool(description="Create a semantic link between two notes")
|
|
async def link_notes(
|
|
from_note: str,
|
|
to_note: str,
|
|
relationship: str = "relates_to",
|
|
context: Optional[str] = None,
|
|
) -> None:
|
|
"""Create a semantic link between two notes.
|
|
|
|
Args:
|
|
from_note: Title or permalink of the source note
|
|
to_note: Title or permalink of the target note
|
|
relationship: Type of relationship (e.g., "relates_to", "implements", "depends_on")
|
|
context: Optional context about the relationship
|
|
|
|
Examples:
|
|
# Create basic link
|
|
link_notes(
|
|
"Architecture Overview",
|
|
"Implementation Details"
|
|
)
|
|
|
|
# Create specific relationship
|
|
link_notes(
|
|
"Project Requirements",
|
|
"Technical Design",
|
|
relationship="informs",
|
|
context="Requirements drive technical decisions"
|
|
)
|
|
"""
|
|
request = CreateRelationsRequest(
|
|
relations=[
|
|
Relation(
|
|
from_id=from_note, # TODO: Add title->permalink lookup
|
|
to_id=to_note,
|
|
relation_type=relationship,
|
|
context=context,
|
|
)
|
|
]
|
|
)
|
|
await create_relations(request)
|