mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
fix: update MCP tool/prompt/resource calls to use .fn attribute
FastMCP library changes now require calling decorated functions via the .fn attribute: - Tools: @mcp.tool() functions return FunctionTool, call with tool.fn() - Prompts: @mcp.prompt() functions return FunctionPrompt, call with prompt.fn() - Resources: @mcp.resource() functions return FunctionResource, call with resource.fn() Updated core files: - view_note.py: read_note() → read_note.fn() - read_note.py: search_notes() → search_notes.fn() (2 locations) - tool.py: 6 MCP tool calls updated to use .fn - recent_activity.py: recent_activity() → recent_activity.fn() - project.py: project_info() → project_info.fn() with type ignore Updated 100+ test files systematically to use .fn attribute and fixed mock targets. All 869 tests now pass. Fixes view_note tool error in Claude Desktop. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -174,7 +174,7 @@ def display_project_info(
|
||||
"""Display detailed information and statistics about the current project."""
|
||||
try:
|
||||
# Get project info
|
||||
info = asyncio.run(project_info())
|
||||
info = asyncio.run(project_info.fn()) # type: ignore # pyright: ignore [reportAttributeAccessIssue]
|
||||
|
||||
if json_output:
|
||||
# Convert to JSON and print
|
||||
|
||||
@@ -90,7 +90,7 @@ def write_note(
|
||||
typer.echo("Empty content provided. Please provide non-empty content.", err=True)
|
||||
raise typer.Exit(1)
|
||||
|
||||
note = asyncio.run(mcp_write_note(title, content, folder, tags))
|
||||
note = asyncio.run(mcp_write_note.fn(title, content, folder, tags))
|
||||
rprint(note)
|
||||
except Exception as e: # pragma: no cover
|
||||
if not isinstance(e, typer.Exit):
|
||||
@@ -103,7 +103,7 @@ def write_note(
|
||||
def read_note(identifier: str, page: int = 1, page_size: int = 10):
|
||||
"""Read a markdown note from the knowledge base."""
|
||||
try:
|
||||
note = asyncio.run(mcp_read_note(identifier, page, page_size))
|
||||
note = asyncio.run(mcp_read_note.fn(identifier, page, page_size))
|
||||
rprint(note)
|
||||
except Exception as e: # pragma: no cover
|
||||
if not isinstance(e, typer.Exit):
|
||||
@@ -124,7 +124,7 @@ def build_context(
|
||||
"""Get context needed to continue a discussion."""
|
||||
try:
|
||||
context = asyncio.run(
|
||||
mcp_build_context(
|
||||
mcp_build_context.fn(
|
||||
url=url,
|
||||
depth=depth,
|
||||
timeframe=timeframe,
|
||||
@@ -157,7 +157,7 @@ def recent_activity(
|
||||
"""Get recent activity across the knowledge base."""
|
||||
try:
|
||||
context = asyncio.run(
|
||||
mcp_recent_activity(
|
||||
mcp_recent_activity.fn(
|
||||
type=type, # pyright: ignore [reportArgumentType]
|
||||
depth=depth,
|
||||
timeframe=timeframe,
|
||||
@@ -210,7 +210,7 @@ def search_notes(
|
||||
search_type = "text" if search_type is None else search_type
|
||||
|
||||
results = asyncio.run(
|
||||
mcp_search(
|
||||
mcp_search.fn(
|
||||
query,
|
||||
search_type=search_type,
|
||||
page=page,
|
||||
@@ -241,7 +241,7 @@ def continue_conversation(
|
||||
"""Prompt to continue a previous conversation or work session."""
|
||||
try:
|
||||
# Prompt functions return formatted strings directly
|
||||
session = asyncio.run(mcp_continue_conversation(topic=topic, timeframe=timeframe))
|
||||
session = asyncio.run(mcp_continue_conversation.fn(topic=topic, timeframe=timeframe))
|
||||
rprint(session)
|
||||
except Exception as e: # pragma: no cover
|
||||
if not isinstance(e, typer.Exit):
|
||||
|
||||
@@ -38,7 +38,7 @@ async def recent_activity_prompt(
|
||||
"""
|
||||
logger.info(f"Getting recent activity, timeframe: {timeframe}")
|
||||
|
||||
recent = await recent_activity(timeframe=timeframe, type=[SearchItemType.ENTITY])
|
||||
recent = await recent_activity.fn(timeframe=timeframe, type=[SearchItemType.ENTITY])
|
||||
|
||||
# Extract primary results from the hierarchical structure
|
||||
primary_results = []
|
||||
|
||||
@@ -81,7 +81,7 @@ async def read_note(
|
||||
|
||||
# Fallback 1: Try title search via API
|
||||
logger.info(f"Search title for: {identifier}")
|
||||
title_results = await search_notes(query=identifier, search_type="title", project=project)
|
||||
title_results = await search_notes.fn(query=identifier, search_type="title", project=project)
|
||||
|
||||
if title_results and title_results.results:
|
||||
result = title_results.results[0] # Get the first/best match
|
||||
@@ -105,7 +105,7 @@ async def read_note(
|
||||
|
||||
# Fallback 2: Text search as a last resort
|
||||
logger.info(f"Title search failed, trying text search for: {identifier}")
|
||||
text_results = await search_notes(query=identifier, search_type="text", project=project)
|
||||
text_results = await search_notes.fn(query=identifier, search_type="text", project=project)
|
||||
|
||||
# We didn't find a direct match, construct a helpful error message
|
||||
if not text_results or not text_results.results:
|
||||
|
||||
@@ -37,7 +37,7 @@ async def view_note(
|
||||
logger.info(f"Viewing note: {identifier}")
|
||||
|
||||
# Call the existing read_note logic
|
||||
content = await read_note(identifier, page, page_size, project)
|
||||
content = await read_note.fn(identifier, page, page_size, project)
|
||||
|
||||
# Check if this is an error message (note not found)
|
||||
if "# Note Not Found:" in content:
|
||||
|
||||
@@ -217,11 +217,11 @@ class ProjectService:
|
||||
for name, path in config_projects.items():
|
||||
# Generate normalized name (what the database expects)
|
||||
normalized_name = generate_permalink(name)
|
||||
|
||||
|
||||
if normalized_name != name:
|
||||
logger.info(f"Normalizing project name in config: '{name}' -> '{normalized_name}'")
|
||||
config_updated = True
|
||||
|
||||
|
||||
updated_config[normalized_name] = path
|
||||
|
||||
# Update the configuration if any changes were made
|
||||
|
||||
Reference in New Issue
Block a user