mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
5d528e5619
- Created docs/mcp-tool-usage.md with complete reference for all 16 MCP tools - Added detailed examples and usage patterns for each tool - Included function signatures, parameters, and return types - Organized tools into categories (Content, Navigation, Search, Project, Viz) - Added common workflows section showing real-world usage patterns - Created Python script for automated doc generation (scripts/generate_tool_docs.py) - Updated README.md with link to new tool documentation Resolves #404 Co-authored-by: Paul Hernandez <phernandez@users.noreply.github.com>
273 lines
8.8 KiB
Python
273 lines
8.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Generate markdown documentation for Basic Memory MCP tools.
|
|
|
|
This script extracts tool documentation and usage examples from MCP tool files
|
|
and generates a comprehensive markdown document that users can easily reference
|
|
when creating instructions for their LLMs.
|
|
"""
|
|
|
|
import ast
|
|
import inspect
|
|
from pathlib import Path
|
|
from typing import Dict, List, Optional, Tuple
|
|
|
|
|
|
def extract_function_signature(node: ast.FunctionDef) -> str:
|
|
"""Extract a clean function signature from an AST node."""
|
|
args = []
|
|
for arg in node.args.args:
|
|
arg_name = arg.arg
|
|
# Skip self, cls, context parameters
|
|
if arg_name in ["self", "cls", "context"]:
|
|
continue
|
|
# Add type annotation if available
|
|
if arg.annotation:
|
|
type_str = ast.unparse(arg.annotation)
|
|
args.append(f"{arg_name}: {type_str}")
|
|
else:
|
|
args.append(arg_name)
|
|
return f"({', '.join(args)})"
|
|
|
|
|
|
def extract_tool_info(file_path: Path) -> Optional[Dict]:
|
|
"""Extract tool information from a Python file."""
|
|
try:
|
|
with open(file_path, "r") as f:
|
|
content = f.read()
|
|
|
|
tree = ast.parse(content)
|
|
|
|
# Find functions decorated with @mcp.tool
|
|
for node in ast.walk(tree):
|
|
if isinstance(node, ast.FunctionDef):
|
|
# Check if function has @mcp.tool decorator
|
|
for decorator in node.decorator_list:
|
|
if isinstance(decorator, ast.Call):
|
|
if isinstance(decorator.func, ast.Attribute):
|
|
if (
|
|
decorator.func.attr == "tool"
|
|
and isinstance(decorator.func.value, ast.Name)
|
|
and decorator.func.value.id == "mcp"
|
|
):
|
|
# Extract description from decorator
|
|
description = ""
|
|
for keyword in decorator.keywords:
|
|
if keyword.arg == "description":
|
|
if isinstance(keyword.value, ast.Constant):
|
|
description = keyword.value.value
|
|
|
|
# Extract docstring
|
|
docstring = ast.get_docstring(node) or ""
|
|
|
|
# Extract function name and signature
|
|
func_name = node.name
|
|
signature = extract_function_signature(node)
|
|
|
|
return {
|
|
"name": func_name,
|
|
"signature": signature,
|
|
"description": description.strip(),
|
|
"docstring": docstring.strip(),
|
|
"file": file_path.name,
|
|
}
|
|
return None
|
|
except Exception as e:
|
|
print(f"Error processing {file_path}: {e}")
|
|
return None
|
|
|
|
|
|
def format_tool_section(tool_info: Dict) -> str:
|
|
"""Format a tool's documentation as a markdown section."""
|
|
lines = []
|
|
|
|
# Tool header
|
|
lines.append(f"## {tool_info['name']}")
|
|
lines.append("")
|
|
|
|
# Short description from decorator
|
|
if tool_info['description']:
|
|
lines.append(f"**{tool_info['description']}**")
|
|
lines.append("")
|
|
|
|
# Function signature
|
|
lines.append("### Function Signature")
|
|
lines.append("```python")
|
|
lines.append(f"{tool_info['name']}{tool_info['signature']}")
|
|
lines.append("```")
|
|
lines.append("")
|
|
|
|
# Full docstring with examples
|
|
if tool_info['docstring']:
|
|
lines.append("### Documentation")
|
|
lines.append("")
|
|
|
|
# Process docstring to maintain formatting
|
|
docstring = tool_info['docstring']
|
|
|
|
# Split into sections
|
|
sections = docstring.split('\n\n')
|
|
in_code_block = False
|
|
|
|
for section in sections:
|
|
# Check if this is a code example section
|
|
if 'Examples:' in section or 'Example:' in section:
|
|
lines.append(section)
|
|
lines.append("")
|
|
elif section.strip().startswith('```'):
|
|
lines.append(section)
|
|
lines.append("")
|
|
else:
|
|
# Regular text section
|
|
lines.append(section)
|
|
lines.append("")
|
|
|
|
lines.append("---")
|
|
lines.append("")
|
|
|
|
return '\n'.join(lines)
|
|
|
|
|
|
def generate_table_of_contents(tools: List[Dict]) -> str:
|
|
"""Generate a table of contents for all tools."""
|
|
lines = ["## Table of Contents", ""]
|
|
|
|
# Group tools by category
|
|
content_mgmt = []
|
|
knowledge_graph = []
|
|
search = []
|
|
project_mgmt = []
|
|
visualization = []
|
|
other = []
|
|
|
|
for tool in tools:
|
|
name = tool['name']
|
|
link = f"[{name}](#{name.replace('_', '-')})"
|
|
|
|
# Categorize based on tool name
|
|
if name in ['write_note', 'read_note', 'read_content', 'view_note', 'edit_note', 'move_note', 'delete_note']:
|
|
content_mgmt.append(f"- {link}")
|
|
elif name in ['build_context', 'recent_activity', 'list_directory']:
|
|
knowledge_graph.append(f"- {link}")
|
|
elif name in ['search_notes']:
|
|
search.append(f"- {link}")
|
|
elif name in ['list_memory_projects', 'create_memory_project', 'delete_project', 'get_current_project', 'sync_status']:
|
|
project_mgmt.append(f"- {link}")
|
|
elif name in ['canvas']:
|
|
visualization.append(f"- {link}")
|
|
else:
|
|
other.append(f"- {link}")
|
|
|
|
if content_mgmt:
|
|
lines.append("### Content Management")
|
|
lines.extend(content_mgmt)
|
|
lines.append("")
|
|
|
|
if knowledge_graph:
|
|
lines.append("### Knowledge Graph Navigation")
|
|
lines.extend(knowledge_graph)
|
|
lines.append("")
|
|
|
|
if search:
|
|
lines.append("### Search & Discovery")
|
|
lines.extend(search)
|
|
lines.append("")
|
|
|
|
if project_mgmt:
|
|
lines.append("### Project Management")
|
|
lines.extend(project_mgmt)
|
|
lines.append("")
|
|
|
|
if visualization:
|
|
lines.append("### Visualization")
|
|
lines.extend(visualization)
|
|
lines.append("")
|
|
|
|
if other:
|
|
lines.append("### Other Tools")
|
|
lines.extend(other)
|
|
lines.append("")
|
|
|
|
lines.append("---")
|
|
lines.append("")
|
|
|
|
return '\n'.join(lines)
|
|
|
|
|
|
def main():
|
|
"""Main function to generate tool documentation."""
|
|
# Get the project root
|
|
script_dir = Path(__file__).parent
|
|
project_root = script_dir.parent
|
|
tools_dir = project_root / "src" / "basic_memory" / "mcp" / "tools"
|
|
output_file = project_root / "docs" / "mcp-tool-usage.md"
|
|
|
|
print(f"Scanning tools directory: {tools_dir}")
|
|
|
|
# Extract tool information from all tool files
|
|
tools = []
|
|
for tool_file in sorted(tools_dir.glob("*.py")):
|
|
# Skip __init__.py and utils.py
|
|
if tool_file.name in ["__init__.py", "utils.py", "chatgpt_tools.py"]:
|
|
continue
|
|
|
|
print(f"Processing: {tool_file.name}")
|
|
tool_info = extract_tool_info(tool_file)
|
|
if tool_info:
|
|
tools.append(tool_info)
|
|
print(f" Found tool: {tool_info['name']}")
|
|
|
|
# Sort tools alphabetically by name
|
|
tools.sort(key=lambda x: x['name'])
|
|
|
|
print(f"\nGenerating documentation for {len(tools)} tools...")
|
|
|
|
# Generate the markdown document
|
|
doc_lines = [
|
|
"# Basic Memory MCP Tool Usage Guide",
|
|
"",
|
|
"This document provides comprehensive documentation and usage examples for all Basic Memory MCP tools.",
|
|
"Use this as a reference when creating instructions for your LLM or integrating Basic Memory into your workflows.",
|
|
"",
|
|
f"**Total Tools:** {len(tools)}",
|
|
"",
|
|
"---",
|
|
"",
|
|
]
|
|
|
|
# Add table of contents
|
|
doc_lines.append(generate_table_of_contents(tools))
|
|
|
|
# Add individual tool sections
|
|
for tool in tools:
|
|
doc_lines.append(format_tool_section(tool))
|
|
|
|
# Add footer
|
|
doc_lines.extend([
|
|
"---",
|
|
"",
|
|
"## Additional Resources",
|
|
"",
|
|
"- [Basic Memory README](../README.md)",
|
|
"- [CLAUDE.md Project Guide](../CLAUDE.md)",
|
|
"- [MCP Server Implementation](../src/basic_memory/mcp/)",
|
|
"",
|
|
"---",
|
|
"",
|
|
"*This documentation was automatically generated from the MCP tool source code.*",
|
|
"*Last updated: (run `just generate-tool-docs` to regenerate)*",
|
|
"",
|
|
])
|
|
|
|
# Write the output file
|
|
output_content = '\n'.join(doc_lines)
|
|
output_file.write_text(output_content)
|
|
|
|
print(f"\n✓ Documentation generated: {output_file}")
|
|
print(f" Total size: {len(output_content)} characters")
|
|
print(f" Total lines: {len(doc_lines)}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|