Files
basicmachines-co-basic-memory/src/basic_memory/mcp/tools/notes.py
T
2025-01-20 19:05:16 -06:00

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)