mirror of
https://github.com/mruwnik/memory.git
synced 2025-06-28 23:24:43 +02:00
shorter tool descriptions + time tool
This commit is contained in:
parent
79567b19f2
commit
0551ddd30c
@ -7,6 +7,7 @@ import pathlib
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import Text, func
|
||||
from sqlalchemy import cast as sql_cast
|
||||
from sqlalchemy.dialects.postgresql import ARRAY
|
||||
@ -70,22 +71,17 @@ def filter_source_ids(
|
||||
return source_ids
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def get_current_time() -> dict:
|
||||
"""Get the current time in UTC."""
|
||||
return {"current_time": datetime.now(timezone.utc).isoformat()}
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def get_all_tags() -> list[str]:
|
||||
"""
|
||||
Get all unique tags used across the entire knowledge base.
|
||||
|
||||
Purpose:
|
||||
This tool retrieves all tags that have been used in the system, both from
|
||||
AI observations (created with 'observe') and other content. Use it to
|
||||
understand the tag taxonomy, ensure consistency, or discover related topics.
|
||||
|
||||
Returns:
|
||||
Sorted list of all unique tags in the system. Tags follow patterns like:
|
||||
- Topics: "machine-learning", "functional-programming"
|
||||
- Projects: "project:website-redesign"
|
||||
- Contexts: "context:work", "context:late-night"
|
||||
- Domains: "domain:finance"
|
||||
Returns sorted list of tags from both observations and content.
|
||||
"""
|
||||
with make_session() as session:
|
||||
tags_query = session.query(func.unnest(SourceItem.tags)).distinct()
|
||||
@ -96,20 +92,7 @@ async def get_all_tags() -> list[str]:
|
||||
async def get_all_subjects() -> list[str]:
|
||||
"""
|
||||
Get all unique subjects from observations about the user.
|
||||
|
||||
Purpose:
|
||||
This tool retrieves all subject identifiers that have been used in
|
||||
observations (created with 'observe'). Subjects are the consistent
|
||||
identifiers for what observations are about. Use this to understand
|
||||
what aspects of the user have been tracked and ensure consistency.
|
||||
|
||||
Returns:
|
||||
Sorted list of all unique subjects. Common patterns include:
|
||||
- "programming_style", "programming_philosophy"
|
||||
- "work_habits", "work_schedule"
|
||||
- "ai_beliefs", "ai_safety_beliefs"
|
||||
- "learning_preferences"
|
||||
- "communication_style"
|
||||
Returns sorted list of subject identifiers used in observations.
|
||||
"""
|
||||
with make_session() as session:
|
||||
return sorted(
|
||||
@ -120,23 +103,8 @@ async def get_all_subjects() -> list[str]:
|
||||
@mcp.tool()
|
||||
async def get_all_observation_types() -> list[str]:
|
||||
"""
|
||||
Get all unique observation types that have been used.
|
||||
|
||||
Purpose:
|
||||
This tool retrieves the distinct observation types that have been recorded
|
||||
in the system. While the standard types are predefined (belief, preference,
|
||||
behavior, contradiction, general), this shows what's actually been used.
|
||||
Helpful for understanding the distribution of observation types.
|
||||
|
||||
Standard types:
|
||||
- "belief": Opinions or beliefs the user holds
|
||||
- "preference": Things they prefer or favor
|
||||
- "behavior": Patterns in how they act or work
|
||||
- "contradiction": Noted inconsistencies
|
||||
- "general": Observations that don't fit other categories
|
||||
|
||||
Returns:
|
||||
List of observation types that have actually been used in the system.
|
||||
Get all observation types that have been used.
|
||||
Standard types are belief, preference, behavior, contradiction, general, but there can be more.
|
||||
"""
|
||||
with make_session() as session:
|
||||
return sorted(
|
||||
@ -157,110 +125,19 @@ async def search_knowledge_base(
|
||||
limit: int = 10,
|
||||
) -> list[dict]:
|
||||
"""
|
||||
Search through the user's stored knowledge and content.
|
||||
|
||||
Purpose:
|
||||
This tool searches the user's personal knowledge base - a collection of
|
||||
their saved content including emails, documents, blog posts, books, and
|
||||
more. Use this alongside 'search_observations' to build a complete picture:
|
||||
- search_knowledge_base: Finds user's actual content and information
|
||||
- search_observations: Finds AI-generated insights about the user
|
||||
Together they enable deeply personalized, context-aware assistance.
|
||||
|
||||
When to use:
|
||||
- User asks about something they've read/written/received
|
||||
- You need to find specific content the user has saved
|
||||
- User references a document, email, or article
|
||||
- To provide quotes or information from user's sources
|
||||
- To understand context from user's past communications
|
||||
- When user says "that article about..." or similar references
|
||||
|
||||
How it works:
|
||||
Uses hybrid search combining semantic understanding with keyword matching.
|
||||
This means it finds content based on meaning AND specific terms, giving
|
||||
you the best of both approaches. Results are ranked by relevance.
|
||||
Search user's stored content including emails, documents, articles, books.
|
||||
Use to find specific information the user has saved or received.
|
||||
Combine with search_observations for complete user context.
|
||||
|
||||
Args:
|
||||
query: Natural language search query. Be descriptive about what you're
|
||||
looking for. The search understands meaning but also values exact terms.
|
||||
Examples:
|
||||
- "email about project deadline from last week"
|
||||
- "functional programming articles comparing Haskell and Scala"
|
||||
- "that blog post about AI safety and alignment"
|
||||
- "recipe for chocolate cake Sarah sent me"
|
||||
Pro tip: Include both concepts and specific keywords for best results.
|
||||
query: Natural language search query - be descriptive about what you're looking for
|
||||
previews: Include actual content in results - when false only a snippet is returned
|
||||
modalities: Filter by type: email, blog, book, forum, photo, comic, webpage (empty = all)
|
||||
tags: Filter by tags - content must have at least one matching tag
|
||||
limit: Max results (1-100)
|
||||
|
||||
previews: Whether to include content snippets in results.
|
||||
- True: Returns preview text and image previews (useful for quick scanning)
|
||||
- False: Returns just metadata (faster, less data)
|
||||
Default is False.
|
||||
|
||||
modalities: Types of content to search. Leave empty to search all.
|
||||
Available types:
|
||||
- 'email': Email messages
|
||||
- 'blog': Blog posts and articles
|
||||
- 'book': Book sections and ebooks
|
||||
- 'forum': Forum posts (e.g., LessWrong, Reddit)
|
||||
- 'observation': AI observations (use search_observations instead)
|
||||
- 'photo': Images with extracted text
|
||||
- 'comic': Comics and graphic content
|
||||
- 'webpage': General web pages
|
||||
Examples:
|
||||
- ["email"] - only emails
|
||||
- ["blog", "forum"] - articles and forum posts
|
||||
- [] - search everything
|
||||
|
||||
limit: Maximum results to return (1-100). Default 10.
|
||||
Increase for comprehensive searches, decrease for quick lookups.
|
||||
|
||||
Returns:
|
||||
List of search results ranked by relevance, each containing:
|
||||
- id: Unique identifier for the source item
|
||||
- score: Relevance score (0-1, higher is better)
|
||||
- chunks: Matching content segments with metadata
|
||||
- content: Full details including:
|
||||
- For emails: sender, recipient, subject, date
|
||||
- For blogs: author, title, url, publish date
|
||||
- For books: title, author, chapter info
|
||||
- Type-specific fields for each modality
|
||||
- filename: Path to file if content is stored on disk
|
||||
|
||||
Examples:
|
||||
# Find specific email
|
||||
results = await search_knowledge_base(
|
||||
query="Sarah deadline project proposal next Friday",
|
||||
modalities=["email"],
|
||||
previews=True,
|
||||
limit=5
|
||||
)
|
||||
|
||||
# Search for technical articles
|
||||
results = await search_knowledge_base(
|
||||
query="functional programming monads category theory",
|
||||
modalities=["blog", "book"],
|
||||
limit=20
|
||||
)
|
||||
|
||||
# Find everything about a topic
|
||||
results = await search_knowledge_base(
|
||||
query="machine learning deployment kubernetes docker",
|
||||
previews=True
|
||||
)
|
||||
|
||||
# Quick lookup of a remembered document
|
||||
results = await search_knowledge_base(
|
||||
query="tax forms 2023 accountant recommendations",
|
||||
modalities=["email"],
|
||||
limit=3
|
||||
)
|
||||
|
||||
Best practices:
|
||||
- Include context in queries ("email from Sarah" vs just "Sarah")
|
||||
- Use modalities to filter when you know the content type
|
||||
- Enable previews when you need to verify content before using
|
||||
- Combine with search_observations for complete context
|
||||
- Higher scores (>0.7) indicate strong matches
|
||||
- If no results, try broader queries or different phrasing
|
||||
Returns: List of search results with id, score, chunks, content, filename
|
||||
Higher scores (>0.7) indicate strong matches.
|
||||
"""
|
||||
logger.info(f"MCP search for: {query}")
|
||||
|
||||
@ -282,175 +159,70 @@ async def search_knowledge_base(
|
||||
),
|
||||
)
|
||||
|
||||
# Convert SearchResult objects to dictionaries for MCP
|
||||
return [result.model_dump() for result in results]
|
||||
|
||||
|
||||
class RawObservation(BaseModel):
|
||||
subject: str
|
||||
content: str
|
||||
observation_type: str = "general"
|
||||
confidences: dict[str, float] = {}
|
||||
evidence: dict | None = None
|
||||
tags: list[str] = []
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def observe(
|
||||
content: str,
|
||||
subject: str,
|
||||
observation_type: str = "general",
|
||||
confidences: dict[str, float] = {},
|
||||
evidence: dict | None = None,
|
||||
tags: list[str] | None = None,
|
||||
observations: list[RawObservation],
|
||||
session_id: str | None = None,
|
||||
agent_model: str = "unknown",
|
||||
) -> dict:
|
||||
"""
|
||||
Record an observation about the user to build long-term understanding.
|
||||
Record observations about the user for long-term understanding.
|
||||
Use proactively when user expresses preferences, behaviors, beliefs, or contradictions.
|
||||
Be specific and detailed - observations should make sense months later.
|
||||
|
||||
Purpose:
|
||||
This tool is part of a memory system designed to help AI agents build a
|
||||
deep, persistent understanding of users over time. Use it to record any
|
||||
notable information about the user's preferences, beliefs, behaviors, or
|
||||
characteristics. These observations accumulate to create a comprehensive
|
||||
model of the user that improves future interactions.
|
||||
|
||||
Quick Reference:
|
||||
# Most common patterns:
|
||||
observe(content="User prefers X over Y because...", subject="preferences", observation_type="preference")
|
||||
observe(content="User always/often does X when Y", subject="work_habits", observation_type="behavior")
|
||||
observe(content="User believes/thinks X about Y", subject="beliefs_on_topic", observation_type="belief")
|
||||
observe(content="User said X but previously said Y", subject="topic", observation_type="contradiction")
|
||||
|
||||
When to use:
|
||||
- User expresses a preference or opinion
|
||||
- You notice a behavioral pattern
|
||||
- User reveals information about their work/life/interests
|
||||
- You spot a contradiction with previous statements
|
||||
- Any insight that would help understand the user better in future
|
||||
|
||||
Important: Be an active observer. Don't wait to be asked - proactively record
|
||||
observations throughout conversations to build understanding.
|
||||
RawObservation fields:
|
||||
content (required): Detailed observation text explaining what you observed
|
||||
subject (required): Consistent identifier like "programming_style", "work_habits"
|
||||
observation_type: belief, preference, behavior, contradiction, general
|
||||
confidences: Dict of scores (0.0-1.0), e.g. {"observation_accuracy": 0.9}
|
||||
evidence: Context dict with extra context, e.g. "quote" (exact words) and "context" (situation)
|
||||
tags: List of categorization tags for organization
|
||||
|
||||
Args:
|
||||
content: The observation itself. Be specific and detailed. Write complete
|
||||
thoughts that will make sense when read months later without context.
|
||||
Bad: "Likes FP"
|
||||
Good: "User strongly prefers functional programming paradigms, especially
|
||||
pure functions and immutability, considering them more maintainable"
|
||||
|
||||
subject: A consistent identifier for what this observation is about. Use
|
||||
snake_case and be consistent across observations to enable tracking.
|
||||
Examples:
|
||||
- "programming_style" (not "coding" or "development")
|
||||
- "work_habits" (not "productivity" or "work_patterns")
|
||||
- "ai_safety_beliefs" (not "AI" or "artificial_intelligence")
|
||||
|
||||
observation_type: Categorize the observation:
|
||||
- "belief": An opinion or belief the user holds
|
||||
- "preference": Something they prefer or favor
|
||||
- "behavior": A pattern in how they act or work
|
||||
- "contradiction": An inconsistency with previous observations
|
||||
- "general": Doesn't fit other categories
|
||||
|
||||
confidences: How certain you are (0.0-1.0) in a given aspect of the observation:
|
||||
- 1.0: User explicitly stated this
|
||||
- 0.9: Strongly implied or demonstrated repeatedly
|
||||
- 0.8: Inferred with high confidence (default)
|
||||
- 0.7: Probable but with some uncertainty
|
||||
- 0.6 or below: Speculative, use sparingly
|
||||
Provided as a mapping of <aspect>: <confidence>
|
||||
Examples:
|
||||
- {"observation_accuracy": 0.95}
|
||||
- {"observation_accuracy": 0.8, "interpretation": 0.5}
|
||||
|
||||
evidence: Supporting context as a dict. Include relevant details:
|
||||
- "quote": Exact words from the user
|
||||
- "context": What prompted this observation
|
||||
- "timestamp": When this was observed
|
||||
- "related_to": Connection to other topics
|
||||
Example: {
|
||||
"quote": "I always refactor to pure functions",
|
||||
"context": "Discussing code review practices"
|
||||
}
|
||||
|
||||
tags: Categorization labels. Use lowercase with hyphens. Common patterns:
|
||||
- Topics: "machine-learning", "web-development", "philosophy"
|
||||
- Projects: "project:website-redesign", "project:thesis"
|
||||
- Contexts: "context:work", "context:personal", "context:late-night"
|
||||
- Domains: "domain:finance", "domain:healthcare"
|
||||
|
||||
session_id: UUID string to group observations from the same conversation.
|
||||
Generate one UUID per conversation and reuse it for all observations
|
||||
in that conversation. Format: "550e8400-e29b-41d4-a716-446655440000"
|
||||
|
||||
agent_model: Which AI model made this observation (e.g., "claude-3-opus",
|
||||
"gpt-4", "claude-3.5-sonnet"). Helps track observation quality.
|
||||
|
||||
Returns:
|
||||
Dict with created observation details:
|
||||
- id: Unique identifier for reference
|
||||
- created_at: Timestamp of creation
|
||||
- subject: The subject as stored
|
||||
- observation_type: The type as stored
|
||||
- confidence: The confidence score
|
||||
- tags: List of applied tags
|
||||
|
||||
Examples:
|
||||
# After user mentions their coding philosophy
|
||||
await observe(
|
||||
content="User believes strongly in functional programming principles, "
|
||||
"particularly avoiding mutable state which they call 'the root "
|
||||
"of all evil'. They prioritize code purity over performance.",
|
||||
subject="programming_philosophy",
|
||||
observation_type="belief",
|
||||
confidences={"observation_accuracy": 0.95},
|
||||
evidence={
|
||||
"quote": "State is the root of all evil in programming",
|
||||
"context": "Discussing why they chose Haskell for their project"
|
||||
},
|
||||
tags=["programming", "functional-programming", "philosophy"],
|
||||
session_id="550e8400-e29b-41d4-a716-446655440000",
|
||||
agent_model="claude-3-opus"
|
||||
)
|
||||
|
||||
# Noticing a work pattern
|
||||
await observe(
|
||||
content="User frequently works on complex problems late at night, "
|
||||
"typically between 11pm and 3am, claiming better focus",
|
||||
subject="work_schedule",
|
||||
observation_type="behavior",
|
||||
confidences={"observation_accuracy": 0.85},
|
||||
evidence={
|
||||
"context": "Mentioned across multiple conversations over 2 weeks"
|
||||
},
|
||||
tags=["behavior", "work-habits", "productivity", "context:late-night"],
|
||||
agent_model="claude-3-opus"
|
||||
)
|
||||
|
||||
# Recording a contradiction
|
||||
await observe(
|
||||
content="User now advocates for microservices architecture, but "
|
||||
"previously argued strongly for monoliths in similar contexts",
|
||||
subject="architecture_preferences",
|
||||
observation_type="contradiction",
|
||||
confidences={"observation_accuracy": 0.9},
|
||||
evidence={
|
||||
"quote": "Microservices are definitely the way to go",
|
||||
"context": "Designing a new system similar to one from 3 months ago"
|
||||
},
|
||||
tags=["architecture", "contradiction", "software-design"],
|
||||
agent_model="gpt-4"
|
||||
)
|
||||
observations: List of RawObservation objects
|
||||
session_id: UUID to group observations from same conversation
|
||||
agent_model: AI model making observations (for quality tracking)
|
||||
"""
|
||||
task = celery_app.send_task(
|
||||
SYNC_OBSERVATION,
|
||||
queue="notes",
|
||||
kwargs={
|
||||
"subject": subject,
|
||||
"content": content,
|
||||
"observation_type": observation_type,
|
||||
"confidences": confidences,
|
||||
"evidence": evidence,
|
||||
"tags": tags,
|
||||
"session_id": session_id,
|
||||
"agent_model": agent_model,
|
||||
},
|
||||
)
|
||||
tasks = [
|
||||
(
|
||||
observation,
|
||||
celery_app.send_task(
|
||||
SYNC_OBSERVATION,
|
||||
queue="notes",
|
||||
kwargs={
|
||||
"subject": observation.subject,
|
||||
"content": observation.content,
|
||||
"observation_type": observation.observation_type,
|
||||
"confidences": observation.confidences,
|
||||
"evidence": observation.evidence,
|
||||
"tags": observation.tags,
|
||||
"session_id": session_id,
|
||||
"agent_model": agent_model,
|
||||
},
|
||||
),
|
||||
)
|
||||
for observation in observations
|
||||
]
|
||||
|
||||
def short_content(obs: RawObservation) -> str:
|
||||
if len(obs.content) > 50:
|
||||
return obs.content[:47] + "..."
|
||||
return obs.content
|
||||
|
||||
return {
|
||||
"task_id": task.id,
|
||||
"task_ids": {short_content(obs): task.id for obs, task in tasks},
|
||||
"status": "queued",
|
||||
}
|
||||
|
||||
@ -465,107 +237,20 @@ async def search_observations(
|
||||
limit: int = 10,
|
||||
) -> list[dict]:
|
||||
"""
|
||||
Search through observations to understand the user better.
|
||||
|
||||
Purpose:
|
||||
This tool searches through all observations recorded about the user using
|
||||
the 'observe' tool. Use it to recall past insights, check for patterns,
|
||||
find contradictions, or understand the user's preferences before responding.
|
||||
The more you use this tool, the more personalized and insightful your
|
||||
responses can be.
|
||||
|
||||
When to use:
|
||||
- Before answering questions where user preferences might matter
|
||||
- When the user references something from the past
|
||||
- To check if current behavior aligns with past patterns
|
||||
- To find related observations on a topic
|
||||
- To build context about the user's expertise or interests
|
||||
- Whenever personalization would improve your response
|
||||
Search recorded observations about the user.
|
||||
Use before responding to understand user preferences, patterns, and past insights.
|
||||
Search by meaning - the query matches both content and context.
|
||||
|
||||
Args:
|
||||
query: Natural language description of what you're looking for. The search
|
||||
matches both meaning and specific terms in observation content.
|
||||
Examples:
|
||||
- "programming preferences and coding style"
|
||||
- "opinions about artificial intelligence and AI safety"
|
||||
- "work habits productivity patterns when does user work best"
|
||||
- "previous projects the user has worked on"
|
||||
Pro tip: Use natural language but include key terms you expect to find.
|
||||
query: Natural language search query describing what you're looking for
|
||||
subject: Filter by exact subject identifier (empty = search all subjects)
|
||||
tags: Filter by tags (must have at least one matching tag)
|
||||
observation_types: Filter by: belief, preference, behavior, contradiction, general
|
||||
min_confidences: Minimum confidence thresholds, e.g. {"observation_accuracy": 0.8}
|
||||
limit: Max results (1-100)
|
||||
|
||||
subject: Filter by exact subject identifier. Must match subjects used when
|
||||
creating observations (e.g., "programming_style", "work_habits").
|
||||
Leave empty to search all subjects. Use this when you know the exact
|
||||
subject category you want.
|
||||
|
||||
tags: Filter results to only observations with these tags. Observations must
|
||||
have at least one matching tag. Use the same format as when creating:
|
||||
- ["programming", "functional-programming"]
|
||||
- ["context:work", "project:thesis"]
|
||||
- ["domain:finance", "machine-learning"]
|
||||
|
||||
observation_types: Filter by type of observation:
|
||||
- "belief": Opinions or beliefs the user holds
|
||||
- "preference": Things they prefer or favor
|
||||
- "behavior": Patterns in how they act or work
|
||||
- "contradiction": Noted inconsistencies
|
||||
- "general": Other observations
|
||||
Leave as None to search all types.
|
||||
|
||||
min_confidences: Only return observations with confidence >= this value, e.g.
|
||||
{"observation_accuracy": 0.8, "interpretation": 0.5} facts where you were confident
|
||||
that you observed the fact but are not necessarily sure about the interpretation.
|
||||
Range: 0.0 to 1.0
|
||||
|
||||
limit: Maximum results to return (1-100). Default 10. Increase when you
|
||||
need comprehensive understanding of a topic.
|
||||
|
||||
Returns:
|
||||
List of observations sorted by relevance, each containing:
|
||||
- subject: What the observation is about
|
||||
- content: The full observation text
|
||||
- observation_type: Type of observation
|
||||
- evidence: Supporting context/quotes if provided
|
||||
- confidence: How certain the observation is (0-1)
|
||||
- agent_model: Which AI model made the observation
|
||||
- tags: All tags on this observation
|
||||
- created_at: When it was observed (if available)
|
||||
|
||||
Examples:
|
||||
# Before discussing code architecture
|
||||
results = await search_observations(
|
||||
query="software architecture preferences microservices monoliths",
|
||||
tags=["architecture"],
|
||||
min_confidence=0.7
|
||||
)
|
||||
|
||||
# Understanding work style for scheduling
|
||||
results = await search_observations(
|
||||
query="when does user work best productivity schedule",
|
||||
observation_types=["behavior", "preference"],
|
||||
subject="work_schedule"
|
||||
)
|
||||
|
||||
# Check for AI safety views before discussing AI
|
||||
results = await search_observations(
|
||||
query="artificial intelligence safety alignment concerns",
|
||||
observation_types=["belief"],
|
||||
min_confidence=0.8,
|
||||
limit=20
|
||||
)
|
||||
|
||||
# Find contradictions on a topic
|
||||
results = await search_observations(
|
||||
query="testing methodology unit tests integration",
|
||||
observation_types=["contradiction"],
|
||||
tags=["testing", "software-development"]
|
||||
)
|
||||
|
||||
Best practices:
|
||||
- Search before making assumptions about user preferences
|
||||
- Use broad queries first, then filter with tags/types if too many results
|
||||
- Check for contradictions when user says something unexpected
|
||||
- Higher confidence observations are more reliable
|
||||
- Recent observations may override older ones on same topic
|
||||
Returns: List with content, tags, created_at, metadata
|
||||
Results sorted by relevance to your query.
|
||||
"""
|
||||
semantic_text = observation.generate_semantic_text(
|
||||
subject=subject or "",
|
||||
@ -614,39 +299,20 @@ async def create_note(
|
||||
content: str,
|
||||
filename: str | None = None,
|
||||
note_type: str | None = None,
|
||||
confidence: float = 0.5,
|
||||
confidences: dict[str, float] = {},
|
||||
tags: list[str] = [],
|
||||
) -> dict:
|
||||
"""
|
||||
Create a note when the user asks for something to be noted down or when you think
|
||||
something is important to note down.
|
||||
|
||||
Purpose:
|
||||
Use this tool when the user explicitly asks to note, save, or record
|
||||
something for later reference. Notes don't have to be really short - long
|
||||
markdown docs are fine, as long as that was what was asked for.
|
||||
You can also use this tool to note down things that are important to you.
|
||||
|
||||
When to use:
|
||||
- User says "note down that..." or "please save this"
|
||||
- User asks to record information for future reference
|
||||
- User wants to remember something specific
|
||||
Create a note when user asks to save or record something.
|
||||
Use when user explicitly requests noting information for future reference.
|
||||
|
||||
Args:
|
||||
subject: What the note is about (e.g., "meeting_notes", "idea")
|
||||
content: The actual content to note down, as markdown
|
||||
subject: What the note is about (used for organization)
|
||||
content: Note content as a markdown string
|
||||
filename: Optional path relative to notes folder (e.g., "project/ideas.md")
|
||||
note_type: Optional categorization of the note
|
||||
confidence: How confident you are in the note accuracy (0.0-1.0)
|
||||
tags: Optional tags for organization
|
||||
|
||||
Example:
|
||||
# User: "Please note down that we decided to use React for the frontend"
|
||||
await create_note(
|
||||
subject="project_decisions",
|
||||
content="Decided to use React for the frontend",
|
||||
tags=["project", "frontend"]
|
||||
)
|
||||
confidences: Dict of scores (0.0-1.0), e.g. {"observation_accuracy": 0.9}
|
||||
tags: Organization tags for filtering and discovery
|
||||
"""
|
||||
if filename:
|
||||
path = pathlib.Path(filename)
|
||||
@ -663,7 +329,7 @@ async def create_note(
|
||||
"content": content,
|
||||
"filename": filename,
|
||||
"note_type": note_type,
|
||||
"confidence": confidence,
|
||||
"confidences": confidences,
|
||||
"tags": tags,
|
||||
},
|
||||
)
|
||||
@ -683,36 +349,14 @@ async def create_note(
|
||||
@mcp.tool()
|
||||
async def note_files(path: str = "/"):
|
||||
"""
|
||||
List all available note files in the user's note storage system.
|
||||
|
||||
Purpose:
|
||||
This tool provides a way to discover and browse the user's organized note
|
||||
collection. Notes are stored as Markdown files and can be created either
|
||||
through the 'create_note' tool or by the user directly. Use this tool to
|
||||
understand what notes exist before reading or referencing them, or to help
|
||||
the user navigate their note collection.
|
||||
List note files in the user's note storage.
|
||||
Use to discover existing notes before reading or to help user navigate their collection.
|
||||
|
||||
Args:
|
||||
path: Directory path to search within the notes collection. Use "/" for the
|
||||
root notes directory, or specify subdirectories like "/projects" or
|
||||
"/meetings". The path should start with "/" and use forward slashes.
|
||||
Examples:
|
||||
- "/" - List all notes in the entire collection
|
||||
- "/projects" - Only notes in the projects folder
|
||||
- "/meetings/2024" - Notes in a specific year's meetings folder
|
||||
path: Directory path to search (e.g., "/", "/projects", "/meetings")
|
||||
Use "/" for root, or subdirectories to narrow scope
|
||||
|
||||
Examples:
|
||||
# List all notes
|
||||
all_notes = await note_files("/")
|
||||
# Returns: ["/notes/project_ideas.md", "/notes/meetings/daily_standup.md", ...]
|
||||
|
||||
# List notes in a specific folder
|
||||
project_notes = await note_files("/projects")
|
||||
# Returns: ["/notes/projects/website_redesign.md", "/notes/projects/mobile_app.md"]
|
||||
|
||||
# Check for meeting notes
|
||||
meeting_notes = await note_files("/meetings")
|
||||
# Returns: ["/notes/meetings/2024-01-15.md", "/notes/meetings/weekly_review.md"]
|
||||
Returns: List of file paths relative to notes directory
|
||||
"""
|
||||
root = settings.NOTES_STORAGE_DIR / path.lstrip("/")
|
||||
return [
|
||||
@ -725,38 +369,15 @@ async def note_files(path: str = "/"):
|
||||
@mcp.tool()
|
||||
def fetch_file(filename: str):
|
||||
"""
|
||||
Retrieve the raw content of a file from the user's storage system.
|
||||
|
||||
Purpose:
|
||||
This tool allows you to read the actual content of files stored in the
|
||||
user's file system, including notes, documents, images, and other files.
|
||||
Use this when you need to access the specific content of a file that has
|
||||
been referenced or when the user asks you to read/examine a particular file.
|
||||
Read file content from user's storage.
|
||||
Use when you need to access specific content of a file that's been referenced.
|
||||
|
||||
Args:
|
||||
filename: Path to the file to fetch, relative to the file storage directory.
|
||||
Should start with "/" and use forward slashes. The path structure depends
|
||||
on how files are organized in the storage system.
|
||||
Examples:
|
||||
- "/notes/project_ideas.md" - A note file
|
||||
- "/documents/report.pdf" - A PDF document
|
||||
- "/images/diagram.png" - An image file
|
||||
- "/emails/important_thread.txt" - Saved email content
|
||||
filename: Path to file (e.g., "/notes/project.md", "/documents/report.pdf")
|
||||
Path should start with "/" and use forward slashes
|
||||
|
||||
Returns:
|
||||
Raw bytes content of the file. For text files (like Markdown notes), you'll
|
||||
typically want to decode this as UTF-8 to get readable text:
|
||||
```python
|
||||
content_bytes = await fetch_file("/notes/my_note.md")
|
||||
content_text = content_bytes.decode('utf-8')
|
||||
```
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If the specified file doesn't exist at the given path.
|
||||
|
||||
Security note:
|
||||
This tool only accesses files within the configured storage directory,
|
||||
ensuring it cannot read arbitrary system files.
|
||||
Returns: Raw bytes content (decode as UTF-8 for text files)
|
||||
Raises FileNotFoundError if file doesn't exist.
|
||||
"""
|
||||
path = settings.FILE_STORAGE_DIR / filename.lstrip("/")
|
||||
if not path.exists():
|
||||
|
Loading…
x
Reference in New Issue
Block a user