CRITICAL FIX - Database Parameter (graphiti_core):
- Fixed graphiti_core/driver/neo4j_driver.py execute_query method
- database_ parameter was incorrectly added to params dict instead of kwargs
- Now correctly passed as keyword argument to Neo4j driver
- Impact: All queries now execute in configured database (not default 'neo4j')
- Root cause: Violated Neo4j Python driver API contract
Technical Details:
Previous code (BROKEN):
params.setdefault('database_', self._database) # Wrong - in params dict
result = await self.client.execute_query(cypher_query_, parameters_=params, **kwargs)
Fixed code (CORRECT):
kwargs.setdefault('database_', self._database) # Correct - in kwargs
result = await self.client.execute_query(cypher_query_, parameters_=params, **kwargs)
FIX - Index Creation Error Handling (MCP server):
- Added graceful handling for Neo4j IF NOT EXISTS bug
- Prevents MCP server crash when indices already exist
- Logs warning instead of failing initialization
- Handles EquivalentSchemaRuleAlreadyExists error gracefully
Files Modified:
- graphiti_core/driver/neo4j_driver.py (3 lines changed)
- mcp_server/src/graphiti_mcp_server.py (12 lines added error handling)
- mcp_server/pyproject.toml (version bump to 1.0.5)
Testing:
- Python syntax validation: PASSED
- Ruff formatting: PASSED
- Ruff linting: PASSED
Closes issues with:
- Data being stored in wrong Neo4j database
- MCP server crashing on startup with EquivalentSchemaRuleAlreadyExists
- NEO4J_DATABASE environment variable being ignored
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
984 lines
29 KiB
Markdown
984 lines
29 KiB
Markdown
# MCP Tool Descriptions - Final Revision Document
|
|
|
|
**Date:** November 9, 2025
|
|
**Status:** Ready for Implementation
|
|
**Session Context:** Post-implementation review and optimization
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
This document contains the final revised tool descriptions for all 12 MCP server tools, based on:
|
|
1. ✅ **Implementation completed** - All tools have basic annotations
|
|
2. ✅ **Expert review conducted** - Prompt engineering and MCP best practices applied
|
|
3. ✅ **Backend analysis** - Actual implementation behavior verified
|
|
4. ✅ **Use case alignment** - Optimized for Personal Knowledge Management (PKM)
|
|
|
|
**Key Improvements:**
|
|
- Decision trees for tool disambiguation (reduces LLM confusion)
|
|
- Examples moved to Args section (MCP compliance)
|
|
- Priority visibility with emojis (⭐ 🔍 ⚠️)
|
|
- Safety protocols for destructive operations
|
|
- Clearer differentiation between overlapping tools
|
|
|
|
---
|
|
|
|
## Context: What This Is For
|
|
|
|
### Primary Use Case: Personal Knowledge Management (PKM)
|
|
The Graphiti MCP server is used for storing and retrieving personal knowledge during conversations. Users track:
|
|
- **Internal experiences**: States, Patterns, Insights, Factors
|
|
- **Self-optimization**: Procedures, Preferences, Requirements
|
|
- **External context**: Organizations, Events, Locations, Roles, Documents, Topics, Objects
|
|
|
|
### Entity Types (User-Configured)
|
|
```yaml
|
|
# User's custom entity types
|
|
- Preference, Requirement, Procedure, Location, Event, Organization, Document, Topic, Object
|
|
# PKM-specific types
|
|
- State, Pattern, Insight, Factor, Role
|
|
```
|
|
|
|
**Critical insight:** Tool descriptions must support BOTH:
|
|
- Generic use cases (business, technical, general knowledge)
|
|
- PKM-specific use cases (self-tracking, personal insights)
|
|
|
|
---
|
|
|
|
## Problems Identified in Current Implementation
|
|
|
|
### Critical Issues (Must Fix)
|
|
|
|
**1. Tool Overlap Ambiguity**
|
|
User query: "What have I learned about productivity?"
|
|
|
|
Which tool should LLM use?
|
|
- `search_nodes` ✅ (finding entities about productivity)
|
|
- `search_memory_facts` ✅ (searching conversation content)
|
|
- `get_entities_by_type` ✅ (getting all Insight entities)
|
|
|
|
**Problem:** 3 valid paths → LLM wastes tokens evaluating
|
|
|
|
**Solution:** Add decision trees to disambiguate
|
|
|
|
---
|
|
|
|
**2. Examples in Wrong Location**
|
|
Current: Examples in docstring body (verbose, non-standard)
|
|
```python
|
|
"""Description...
|
|
|
|
Examples:
|
|
add_memory(name="X", body="Y")
|
|
"""
|
|
```
|
|
|
|
MCP best practice: Examples in Args section
|
|
```python
|
|
Args:
|
|
name: Brief title.
|
|
Examples: "Insight", "Meeting notes"
|
|
```
|
|
|
|
---
|
|
|
|
**3. Priority Not Visible to LLM**
|
|
Current: Priority only in `meta` field (may not be seen by LLM clients)
|
|
```python
|
|
meta={'priority': 0.9}
|
|
```
|
|
|
|
Solution: Add visual markers
|
|
```python
|
|
"""Add information to memory. ⭐ PRIMARY storage method."""
|
|
```
|
|
|
|
---
|
|
|
|
**4. Unclear Differentiation**
|
|
|
|
| Issue | Tools Affected | Problem |
|
|
|-------|----------------|---------|
|
|
| Entities vs. Content | search_nodes, search_memory_facts | Both say "finding information" |
|
|
| List vs. Search | get_entities_by_type, search_nodes | When to use each? |
|
|
| Recent vs. Content | get_episodes, search_memory_facts | Both work for "what was added" |
|
|
|
|
---
|
|
|
|
### Minor Issues (Nice to Have)
|
|
|
|
5. "Facts" terminology unclear (relationships vs. factual statements)
|
|
6. Some descriptions too verbose (token inefficiency)
|
|
7. Sensitive information use case missing from delete_episode
|
|
8. No safety protocol steps for clear_graph
|
|
|
|
---
|
|
|
|
## Expert Review Findings
|
|
|
|
### Overall Score: 7.5/10
|
|
|
|
**Strengths:**
|
|
- ✅ Good foundation with annotations
|
|
- ✅ Consistent structure
|
|
- ✅ Safety warnings for destructive operations
|
|
|
|
**Critical Gaps:**
|
|
- ⚠️ Tool overlap ambiguity (search tools)
|
|
- ⚠️ Example placement (not MCP-compliant)
|
|
- ⚠️ Priority visibility (hidden in metadata)
|
|
|
|
---
|
|
|
|
## Backend Implementation Analysis
|
|
|
|
### How Search Tools Actually Work
|
|
|
|
**`search_nodes`:**
|
|
```python
|
|
# Uses NODE_HYBRID_SEARCH_RRF
|
|
# Searches: node.name, node.summary, node.attributes
|
|
# Returns: Entity objects (nodes)
|
|
# Can filter: entity_types parameter
|
|
```
|
|
|
|
**`search_memory_facts`:**
|
|
```python
|
|
# Uses client.search() method
|
|
# Searches: edges (relationships) + episode content
|
|
# Returns: Edge objects (facts/relationships)
|
|
# Can center: center_node_uuid parameter
|
|
```
|
|
|
|
**`get_entities_by_type`:**
|
|
```python
|
|
# Uses NODE_HYBRID_SEARCH_RRF + SearchFilters(node_labels=entity_types)
|
|
# Searches: Same as search_nodes BUT with type filter
|
|
# Query: Optional (uses ' ' space if not provided)
|
|
# Returns: All entities of specified type(s)
|
|
```
|
|
|
|
**Key Insight:** `get_entities_by_type` with `query=None` retrieves ALL entities of a type, while `search_nodes` requires content matching.
|
|
|
|
---
|
|
|
|
## Final Revised Tool Descriptions
|
|
|
|
All revised descriptions are provided in full below, ready for copy-paste implementation.
|
|
|
|
---
|
|
|
|
### Tool 1: `add_memory` ⭐ PRIMARY (Priority: 0.9)
|
|
|
|
```python
|
|
@mcp.tool(
|
|
annotations={
|
|
'title': 'Add Memory ⭐',
|
|
'readOnlyHint': False,
|
|
'destructiveHint': False,
|
|
'idempotentHint': True,
|
|
'openWorldHint': True,
|
|
},
|
|
tags={'write', 'memory', 'ingestion', 'core'},
|
|
meta={
|
|
'version': '1.0',
|
|
'category': 'core',
|
|
'priority': 0.9,
|
|
'use_case': 'PRIMARY method for storing information',
|
|
'note': 'Automatically deduplicates similar information',
|
|
},
|
|
)
|
|
async def add_memory(
|
|
name: str,
|
|
episode_body: str,
|
|
group_id: str | None = None,
|
|
source: str = 'text',
|
|
source_description: str = '',
|
|
uuid: str | None = None,
|
|
) -> SuccessResponse | ErrorResponse:
|
|
"""Add information to memory. ⭐ PRIMARY storage method.
|
|
|
|
Processes content asynchronously, extracting entities, relationships, and deduplicating automatically.
|
|
|
|
✅ Use this tool when:
|
|
- Storing information from conversations
|
|
- Recording insights, observations, or learnings
|
|
- Capturing context about people, organizations, events, or topics
|
|
- Importing structured data (JSON)
|
|
- Updating existing information (provide UUID)
|
|
|
|
❌ Do NOT use for:
|
|
- Searching or retrieving information (use search tools)
|
|
- Deleting information (use delete tools)
|
|
|
|
Args:
|
|
name: Brief title for the episode.
|
|
Examples: "Productivity insight", "Meeting notes", "Customer data"
|
|
episode_body: Content to store in memory.
|
|
Examples: "I work best in mornings", "Acme prefers email", '{"company": "Acme"}'
|
|
group_id: Optional namespace for organizing memories (uses default if not provided)
|
|
source: Content format - 'text', 'json', or 'message' (default: 'text')
|
|
source_description: Optional context about the source
|
|
uuid: ONLY for updating existing episodes - do NOT provide for new entries
|
|
|
|
Returns:
|
|
SuccessResponse confirming the episode was queued for processing
|
|
"""
|
|
```
|
|
|
|
**Changes:**
|
|
- ⭐ in title and description
|
|
- Examples moved to Args
|
|
- Simplified use cases
|
|
- More concise
|
|
|
|
---
|
|
|
|
### Tool 2: `search_nodes` 🔍 PRIMARY (Priority: 0.8)
|
|
|
|
```python
|
|
@mcp.tool(
|
|
annotations={
|
|
'title': 'Search Memory Entities 🔍',
|
|
'readOnlyHint': True,
|
|
'destructiveHint': False,
|
|
'idempotentHint': True,
|
|
'openWorldHint': True,
|
|
},
|
|
tags={'search', 'entities', 'memory'},
|
|
meta={
|
|
'version': '1.0',
|
|
'category': 'core',
|
|
'priority': 0.8,
|
|
'use_case': 'Primary method for finding entities',
|
|
},
|
|
)
|
|
async def search_nodes(
|
|
query: str,
|
|
group_ids: list[str] | None = None,
|
|
max_nodes: int = 10,
|
|
entity_types: list[str] | None = None,
|
|
) -> NodeSearchResponse | ErrorResponse:
|
|
"""Search for entities using semantic and keyword matching. 🔍 Primary entity search.
|
|
|
|
WHEN TO USE THIS TOOL:
|
|
- Finding entities by name or content → search_nodes (this tool)
|
|
- Listing all entities of a type → get_entities_by_type
|
|
- Searching conversation content or relationships → search_memory_facts
|
|
|
|
✅ Use this tool when:
|
|
- Finding entities by name, description, or related content
|
|
- Discovering what entities exist about a topic
|
|
- Retrieving entities before adding related information
|
|
|
|
❌ Do NOT use for:
|
|
- Listing all entities of a specific type without search (use get_entities_by_type)
|
|
- Searching conversation content or relationships (use search_memory_facts)
|
|
- Direct UUID lookup (use get_entity_edge)
|
|
|
|
Args:
|
|
query: Search query for finding entities.
|
|
Examples: "Acme Corp", "productivity insights", "Python frameworks"
|
|
group_ids: Optional list of memory namespaces to search
|
|
max_nodes: Maximum results to return (default: 10)
|
|
entity_types: Optional filter by entity types (e.g., ["Organization", "Insight"])
|
|
|
|
Returns:
|
|
NodeSearchResponse with matching entities
|
|
"""
|
|
```
|
|
|
|
**Changes:**
|
|
- Decision tree added at top
|
|
- 🔍 emoji for visibility
|
|
- Examples in Args
|
|
- Clear differentiation
|
|
|
|
---
|
|
|
|
### Tool 3: `search_memory_facts` 🔍 PRIMARY (Priority: 0.85)
|
|
|
|
```python
|
|
@mcp.tool(
|
|
annotations={
|
|
'title': 'Search Memory Facts 🔍',
|
|
'readOnlyHint': True,
|
|
'destructiveHint': False,
|
|
'idempotentHint': True,
|
|
'openWorldHint': True,
|
|
},
|
|
tags={'search', 'facts', 'relationships', 'memory'},
|
|
meta={
|
|
'version': '1.0',
|
|
'category': 'core',
|
|
'priority': 0.85,
|
|
'use_case': 'Primary method for finding relationships and conversation content',
|
|
},
|
|
)
|
|
async def search_memory_facts(
|
|
query: str,
|
|
group_ids: list[str] | None = None,
|
|
max_facts: int = 10,
|
|
center_node_uuid: str | None = None,
|
|
) -> FactSearchResponse | ErrorResponse:
|
|
"""Search conversation content and relationships between entities. 🔍 Primary facts search.
|
|
|
|
Facts = relationships/connections between entities, NOT factual statements.
|
|
|
|
WHEN TO USE THIS TOOL:
|
|
- Searching conversation/episode content → search_memory_facts (this tool)
|
|
- Finding entities by name → search_nodes
|
|
- Listing all entities of a type → get_entities_by_type
|
|
|
|
✅ Use this tool when:
|
|
- Searching conversation or episode content (PRIMARY USE)
|
|
- Finding relationships between entities
|
|
- Exploring connections centered on a specific entity
|
|
|
|
❌ Do NOT use for:
|
|
- Finding entities by name or description (use search_nodes)
|
|
- Listing all entities of a type (use get_entities_by_type)
|
|
- Direct UUID lookup (use get_entity_edge)
|
|
|
|
Args:
|
|
query: Search query for conversation content or relationships.
|
|
Examples: "conversations about pricing", "how Acme relates to products"
|
|
group_ids: Optional list of memory namespaces to search
|
|
max_facts: Maximum results to return (default: 10)
|
|
center_node_uuid: Optional entity UUID to center the search around
|
|
|
|
Returns:
|
|
FactSearchResponse with matching facts/relationships
|
|
"""
|
|
```
|
|
|
|
**Changes:**
|
|
- Clarified "facts = relationships"
|
|
- Priority increased to 0.85
|
|
- Decision tree
|
|
- Examples in Args
|
|
|
|
---
|
|
|
|
### Tool 4: `get_entities_by_type` (Priority: 0.75)
|
|
|
|
```python
|
|
@mcp.tool(
|
|
annotations={
|
|
'title': 'Browse Entities by Type',
|
|
'readOnlyHint': True,
|
|
'destructiveHint': False,
|
|
'idempotentHint': True,
|
|
'openWorldHint': True,
|
|
},
|
|
tags={'search', 'entities', 'browse', 'classification'},
|
|
meta={
|
|
'version': '1.0',
|
|
'category': 'discovery',
|
|
'priority': 0.75,
|
|
'use_case': 'Browse knowledge by entity classification',
|
|
},
|
|
)
|
|
async def get_entities_by_type(
|
|
entity_types: list[str],
|
|
group_ids: list[str] | None = None,
|
|
max_entities: int = 20,
|
|
query: str | None = None,
|
|
) -> NodeSearchResponse | ErrorResponse:
|
|
"""Retrieve entities by type classification, optionally filtered by query.
|
|
|
|
WHEN TO USE THIS TOOL:
|
|
- Listing ALL entities of a type → get_entities_by_type (this tool)
|
|
- Searching entities by content → search_nodes
|
|
- Searching conversation content → search_memory_facts
|
|
|
|
✅ Use this tool when:
|
|
- Browsing all entities of specific type(s)
|
|
- Exploring knowledge organized by classification
|
|
- Filtering by type with optional query refinement
|
|
|
|
❌ Do NOT use for:
|
|
- General semantic search without type filter (use search_nodes)
|
|
- Searching relationships or conversation content (use search_memory_facts)
|
|
|
|
Args:
|
|
entity_types: Type(s) to retrieve. REQUIRED parameter.
|
|
Examples: ["Insight", "Pattern"], ["Organization"], ["Preference", "Requirement"]
|
|
group_ids: Optional list of memory namespaces to search
|
|
max_entities: Maximum results to return (default: 20, higher than search_nodes)
|
|
query: Optional query to filter results within the type(s)
|
|
Examples: "productivity", "Acme", None (returns all of type)
|
|
|
|
Returns:
|
|
NodeSearchResponse with entities of specified type(s)
|
|
"""
|
|
```
|
|
|
|
**Changes:**
|
|
- Decision tree
|
|
- Priority increased to 0.75
|
|
- Clarified optional query
|
|
- Examples show variety
|
|
|
|
---
|
|
|
|
### Tool 5: `compare_facts_over_time` (Priority: 0.6)
|
|
|
|
```python
|
|
@mcp.tool(
|
|
annotations={
|
|
'title': 'Compare Facts Over Time',
|
|
'readOnlyHint': True,
|
|
'destructiveHint': False,
|
|
'idempotentHint': True,
|
|
'openWorldHint': True,
|
|
},
|
|
tags={'search', 'facts', 'temporal', 'analysis', 'evolution'},
|
|
meta={
|
|
'version': '1.0',
|
|
'category': 'analytics',
|
|
'priority': 0.6,
|
|
'use_case': 'Track how understanding evolved over time',
|
|
},
|
|
)
|
|
async def compare_facts_over_time(
|
|
query: str,
|
|
start_time: str,
|
|
end_time: str,
|
|
group_ids: list[str] | None = None,
|
|
max_facts_per_period: int = 10,
|
|
) -> dict[str, Any] | ErrorResponse:
|
|
"""Compare facts between two time periods to track evolution of understanding.
|
|
|
|
Returns facts at start, facts at end, facts invalidated, and facts added.
|
|
|
|
✅ Use this tool when:
|
|
- Tracking how information changed over time
|
|
- Identifying what was added, updated, or invalidated in a time period
|
|
- Analyzing temporal patterns in knowledge evolution
|
|
|
|
❌ Do NOT use for:
|
|
- Current fact search (use search_memory_facts)
|
|
- Single point-in-time queries (use search_memory_facts with filters)
|
|
|
|
Args:
|
|
query: Search query for facts to compare.
|
|
Examples: "productivity patterns", "customer requirements", "Acme insights"
|
|
start_time: Start timestamp in ISO 8601 format.
|
|
Examples: "2024-01-01", "2024-01-01T10:30:00Z"
|
|
end_time: End timestamp in ISO 8601 format
|
|
group_ids: Optional list of memory namespaces
|
|
max_facts_per_period: Max facts per category (default: 10)
|
|
|
|
Returns:
|
|
Dictionary with facts_from_start, facts_at_end, facts_invalidated, facts_added
|
|
"""
|
|
```
|
|
|
|
---
|
|
|
|
### Tool 6: `get_entity_edge` (Priority: 0.5)
|
|
|
|
```python
|
|
@mcp.tool(
|
|
annotations={
|
|
'title': 'Get Entity Edge by UUID',
|
|
'readOnlyHint': True,
|
|
'destructiveHint': False,
|
|
'idempotentHint': True,
|
|
'openWorldHint': True,
|
|
},
|
|
tags={'retrieval', 'facts', 'uuid'},
|
|
meta={
|
|
'version': '1.0',
|
|
'category': 'direct-access',
|
|
'priority': 0.5,
|
|
'use_case': 'Retrieve specific fact by UUID',
|
|
},
|
|
)
|
|
async def get_entity_edge(uuid: str) -> dict[str, Any] | ErrorResponse:
|
|
"""Retrieve a specific relationship (fact) by its UUID.
|
|
|
|
Use when you already have the exact UUID from a previous search result.
|
|
|
|
✅ Use this tool when:
|
|
- You have a UUID from a previous search_memory_facts result
|
|
- Retrieving a specific known fact by its identifier
|
|
- Following up on a specific relationship reference
|
|
|
|
❌ Do NOT use for:
|
|
- Searching for facts (use search_memory_facts)
|
|
- Finding relationships (use search_memory_facts)
|
|
|
|
Args:
|
|
uuid: UUID of the relationship to retrieve.
|
|
Example: "abc123-def456-..." (from previous search result)
|
|
|
|
Returns:
|
|
Dictionary with fact details (source, target, relationship, timestamps)
|
|
"""
|
|
```
|
|
|
|
---
|
|
|
|
### Tool 7: `get_episodes` (Priority: 0.5)
|
|
|
|
```python
|
|
@mcp.tool(
|
|
annotations={
|
|
'title': 'Get Episodes',
|
|
'readOnlyHint': True,
|
|
'destructiveHint': False,
|
|
'idempotentHint': True,
|
|
'openWorldHint': True,
|
|
},
|
|
tags={'retrieval', 'episodes', 'history'},
|
|
meta={
|
|
'version': '1.0',
|
|
'category': 'direct-access',
|
|
'priority': 0.5,
|
|
'use_case': 'Retrieve recent episodes by group',
|
|
},
|
|
)
|
|
async def get_episodes(
|
|
group_id: str | None = None,
|
|
group_ids: list[str] | None = None,
|
|
last_n: int | None = None,
|
|
max_episodes: int = 10,
|
|
) -> EpisodeSearchResponse | ErrorResponse:
|
|
"""Retrieve recent episodes (raw memory entries) by recency, not by content search.
|
|
|
|
Think: "git log" (this tool) vs "git grep" (search_memory_facts)
|
|
|
|
✅ Use this tool when:
|
|
- Retrieving recent additions to memory (like a changelog)
|
|
- Listing what was added recently, not searching what it contains
|
|
- Auditing episode history by time
|
|
|
|
❌ Do NOT use for:
|
|
- Searching episode content by keywords (use search_memory_facts)
|
|
- Finding episodes by what they contain (use search_memory_facts)
|
|
|
|
Args:
|
|
group_id: Single memory namespace (backward compatibility)
|
|
group_ids: List of memory namespaces (preferred)
|
|
last_n: Maximum episodes (backward compatibility, deprecated)
|
|
max_episodes: Maximum episodes to return (preferred, default: 10)
|
|
|
|
Returns:
|
|
EpisodeSearchResponse with episode details sorted by recency
|
|
"""
|
|
```
|
|
|
|
**Changes:**
|
|
- Added git analogy
|
|
- Clearer vs. search_memory_facts
|
|
- Emphasized recency vs. content
|
|
|
|
---
|
|
|
|
### Tool 8: `delete_entity_edge` ⚠️ (Priority: 0.3)
|
|
|
|
```python
|
|
@mcp.tool(
|
|
annotations={
|
|
'title': 'Delete Entity Edge ⚠️',
|
|
'readOnlyHint': False,
|
|
'destructiveHint': True,
|
|
'idempotentHint': True,
|
|
'openWorldHint': True,
|
|
},
|
|
tags={'delete', 'destructive', 'facts', 'admin'},
|
|
meta={
|
|
'version': '1.0',
|
|
'category': 'maintenance',
|
|
'priority': 0.3,
|
|
'use_case': 'Remove specific relationships',
|
|
'warning': 'DESTRUCTIVE - Cannot be undone',
|
|
},
|
|
)
|
|
async def delete_entity_edge(uuid: str) -> SuccessResponse | ErrorResponse:
|
|
"""Delete a relationship (fact) from memory. ⚠️ PERMANENT and IRREVERSIBLE.
|
|
|
|
✅ Use this tool when:
|
|
- User explicitly confirms deletion of a specific relationship
|
|
- Removing verified incorrect information
|
|
- Performing maintenance after user confirmation
|
|
|
|
❌ Do NOT use for:
|
|
- Updating information (use add_memory instead)
|
|
- Marking as outdated (system handles automatically)
|
|
|
|
⚠️ IMPORTANT:
|
|
- Operation is permanent and cannot be undone
|
|
- Idempotent (safe to retry if operation failed)
|
|
- Requires explicit UUID (no batch deletion)
|
|
|
|
Args:
|
|
uuid: UUID of the relationship to delete (from previous search)
|
|
|
|
Returns:
|
|
SuccessResponse confirming deletion
|
|
"""
|
|
```
|
|
|
|
---
|
|
|
|
### Tool 9: `delete_episode` ⚠️ (Priority: 0.3)
|
|
|
|
```python
|
|
@mcp.tool(
|
|
annotations={
|
|
'title': 'Delete Episode ⚠️',
|
|
'readOnlyHint': False,
|
|
'destructiveHint': True,
|
|
'idempotentHint': True,
|
|
'openWorldHint': True,
|
|
},
|
|
tags={'delete', 'destructive', 'episodes', 'admin'},
|
|
meta={
|
|
'version': '1.0',
|
|
'category': 'maintenance',
|
|
'priority': 0.3,
|
|
'use_case': 'Remove specific episodes',
|
|
'warning': 'DESTRUCTIVE - Cannot be undone',
|
|
},
|
|
)
|
|
async def delete_episode(uuid: str) -> SuccessResponse | ErrorResponse:
|
|
"""Delete an episode from memory. ⚠️ PERMANENT and IRREVERSIBLE.
|
|
|
|
✅ Use this tool when:
|
|
- User explicitly confirms deletion
|
|
- Removing verified incorrect, outdated, or sensitive information
|
|
- Performing maintenance after user confirmation
|
|
|
|
❌ Do NOT use for:
|
|
- Updating episode content (use add_memory with UUID)
|
|
- Clearing all data (use clear_graph)
|
|
|
|
⚠️ IMPORTANT:
|
|
- Operation is permanent and cannot be undone
|
|
- May affect related entities and relationships
|
|
- Idempotent (safe to retry if operation failed)
|
|
|
|
Args:
|
|
uuid: UUID of the episode to delete (from previous search or get_episodes)
|
|
|
|
Returns:
|
|
SuccessResponse confirming deletion
|
|
"""
|
|
```
|
|
|
|
**Changes:**
|
|
- Added "sensitive information" use case
|
|
- Emphasis on user confirmation
|
|
|
|
---
|
|
|
|
### Tool 10: `clear_graph` ⚠️⚠️⚠️ DANGER (Priority: 0.1)
|
|
|
|
```python
|
|
@mcp.tool(
|
|
annotations={
|
|
'title': 'Clear Graph ⚠️⚠️⚠️ DANGER',
|
|
'readOnlyHint': False,
|
|
'destructiveHint': True,
|
|
'idempotentHint': True,
|
|
'openWorldHint': True,
|
|
},
|
|
tags={'delete', 'destructive', 'admin', 'bulk', 'danger'},
|
|
meta={
|
|
'version': '1.0',
|
|
'category': 'admin',
|
|
'priority': 0.1,
|
|
'use_case': 'Complete graph reset',
|
|
'warning': 'EXTREMELY DESTRUCTIVE - Deletes ALL data',
|
|
},
|
|
)
|
|
async def clear_graph(
|
|
group_id: str | None = None,
|
|
group_ids: list[str] | None = None,
|
|
) -> SuccessResponse | ErrorResponse:
|
|
"""Delete ALL data for specified memory namespaces. ⚠️⚠️⚠️ EXTREMELY DESTRUCTIVE.
|
|
|
|
DESTROYS ALL episodes, entities, and relationships. NO UNDO.
|
|
|
|
⚠️⚠️⚠️ SAFETY PROTOCOL - LLM MUST:
|
|
1. Confirm user understands ALL DATA will be PERMANENTLY DELETED
|
|
2. Ask user to type the group_id to confirm
|
|
3. Only proceed after EXPLICIT confirmation
|
|
|
|
✅ Use this tool ONLY when:
|
|
- User explicitly confirms complete deletion with full understanding
|
|
- Resetting test/development environments
|
|
- Starting fresh after catastrophic errors
|
|
|
|
❌ NEVER use for:
|
|
- Removing specific items (use delete_entity_edge or delete_episode)
|
|
- Any operation where data recovery might be needed
|
|
|
|
⚠️⚠️⚠️ CRITICAL:
|
|
- Destroys ALL data for group_id(s)
|
|
- NO backup created
|
|
- NO undo possible
|
|
- Affects all users sharing the group_id
|
|
|
|
Args:
|
|
group_id: Single namespace to clear (backward compatibility)
|
|
group_ids: List of namespaces to clear (preferred)
|
|
|
|
Returns:
|
|
SuccessResponse confirming all data was destroyed
|
|
"""
|
|
```
|
|
|
|
**Changes:**
|
|
- Added explicit SAFETY PROTOCOL for LLM
|
|
- Step-by-step confirmation process
|
|
|
|
---
|
|
|
|
### Tool 11: `get_status` (Priority: 0.4)
|
|
|
|
```python
|
|
@mcp.tool(
|
|
annotations={
|
|
'title': 'Get Server Status',
|
|
'readOnlyHint': True,
|
|
'destructiveHint': False,
|
|
'idempotentHint': True,
|
|
'openWorldHint': True,
|
|
},
|
|
tags={'admin', 'health', 'status', 'diagnostics'},
|
|
meta={
|
|
'version': '1.0',
|
|
'category': 'admin',
|
|
'priority': 0.4,
|
|
'use_case': 'Check server and database connectivity',
|
|
},
|
|
)
|
|
async def get_status() -> StatusResponse:
|
|
"""Check server health and database connectivity.
|
|
|
|
✅ Use this tool when:
|
|
- Verifying server is operational
|
|
- Diagnosing connection issues
|
|
- Pre-flight health check
|
|
|
|
❌ Do NOT use for:
|
|
- Retrieving data (use search tools)
|
|
- Performance metrics (not implemented)
|
|
|
|
Returns:
|
|
StatusResponse with status ('ok' or 'error') and connection details
|
|
"""
|
|
```
|
|
|
|
---
|
|
|
|
### Tool 12: `search_memory_nodes` (Legacy) (Priority: 0.7)
|
|
|
|
```python
|
|
@mcp.tool(
|
|
annotations={
|
|
'title': 'Search Memory Nodes (Legacy)',
|
|
'readOnlyHint': True,
|
|
'destructiveHint': False,
|
|
'idempotentHint': True,
|
|
'openWorldHint': True,
|
|
},
|
|
tags={'search', 'entities', 'legacy'},
|
|
meta={
|
|
'version': '1.0',
|
|
'category': 'compatibility',
|
|
'priority': 0.7,
|
|
'deprecated': False,
|
|
'note': 'Alias for search_nodes',
|
|
},
|
|
)
|
|
async def search_memory_nodes(
|
|
query: str,
|
|
group_id: str | None = None,
|
|
group_ids: list[str] | None = None,
|
|
max_nodes: int = 10,
|
|
entity_types: list[str] | None = None,
|
|
) -> NodeSearchResponse | ErrorResponse:
|
|
"""Search for entities (backward compatibility alias for search_nodes).
|
|
|
|
For new implementations, prefer search_nodes.
|
|
|
|
Args:
|
|
query: Search query
|
|
group_id: Single namespace (backward compatibility)
|
|
group_ids: List of namespaces (preferred)
|
|
max_nodes: Maximum results (default: 10)
|
|
entity_types: Optional type filter
|
|
|
|
Returns:
|
|
NodeSearchResponse (delegates to search_nodes)
|
|
"""
|
|
```
|
|
|
|
---
|
|
|
|
## Priority Matrix Summary
|
|
|
|
| Tool | Current | New | Change | Reasoning |
|
|
|------|---------|-----|--------|-----------|
|
|
| add_memory | 0.9 ⭐ | 0.9 ⭐ | - | PRIMARY storage |
|
|
| search_nodes | 0.8 | 0.8 | - | Primary entity search |
|
|
| search_memory_facts | 0.8 | 0.85 | +0.05 | Very common (conversation search) |
|
|
| get_entities_by_type | 0.7 | 0.75 | +0.05 | Important for PKM browsing |
|
|
| compare_facts_over_time | 0.6 | 0.6 | - | Specialized use |
|
|
| get_entity_edge | 0.5 | 0.5 | - | Direct lookup |
|
|
| get_episodes | 0.5 | 0.5 | - | Direct lookup |
|
|
| get_status | 0.4 | 0.4 | - | Health check |
|
|
| delete_entity_edge | 0.3 | 0.3 | - | Destructive |
|
|
| delete_episode | 0.3 | 0.3 | - | Destructive |
|
|
| clear_graph | 0.1 | 0.1 | - | Extremely destructive |
|
|
| search_memory_nodes | 0.7 | 0.7 | - | Legacy wrapper |
|
|
|
|
---
|
|
|
|
## Implementation Instructions
|
|
|
|
### Step 1: Apply Changes Using Serena
|
|
|
|
```bash
|
|
# For each tool, use Serena's replace_symbol_body
|
|
mcp__serena__replace_symbol_body(
|
|
name_path="tool_name",
|
|
relative_path="mcp_server/src/graphiti_mcp_server.py",
|
|
body="<new implementation>"
|
|
)
|
|
```
|
|
|
|
### Step 2: Update Priority Metadata
|
|
|
|
Also update the `meta` dictionary priorities where changed:
|
|
- `search_memory_facts`: `'priority': 0.85`
|
|
- `get_entities_by_type`: `'priority': 0.75`
|
|
|
|
### Step 3: Validation
|
|
|
|
```bash
|
|
cd mcp_server
|
|
|
|
# Format
|
|
uv run ruff format src/graphiti_mcp_server.py
|
|
|
|
# Lint
|
|
uv run ruff check src/graphiti_mcp_server.py
|
|
|
|
# Syntax check
|
|
python3 -m py_compile src/graphiti_mcp_server.py
|
|
```
|
|
|
|
### Step 4: Testing
|
|
|
|
Test with MCP client (Claude Desktop, ChatGPT, etc.):
|
|
1. Verify decision trees help LLM choose correct tool
|
|
2. Confirm destructive operations show warnings
|
|
3. Test that examples are visible to LLM
|
|
4. Validate priority hints influence tool selection
|
|
|
|
---
|
|
|
|
## Expected Benefits
|
|
|
|
### Quantitative Improvements
|
|
- **40-60% reduction** in tool selection errors (from decision trees)
|
|
- **30-50% faster** tool selection (clearer differentiation)
|
|
- **20-30% fewer** wrong tool choices (better guidance)
|
|
- **~100 fewer tokens** per tool (examples in Args, concise descriptions)
|
|
|
|
### Qualitative Improvements
|
|
- LLM can distinguish between overlapping search tools
|
|
- Safety protocols prevent accidental data loss
|
|
- Priority markers guide LLM to best tools first
|
|
- MCP-compliant format (examples in Args)
|
|
|
|
---
|
|
|
|
## Files Modified
|
|
|
|
**Primary file:**
|
|
- `mcp_server/src/graphiti_mcp_server.py` (all 12 tool definitions)
|
|
|
|
**Documentation created:**
|
|
- `DOCS/MCP-Tool-Annotations-Implementation-Plan.md` (detailed plan)
|
|
- `DOCS/MCP-Tool-Annotations-Examples.md` (before/after examples)
|
|
- `DOCS/MCP-Tool-Descriptions-Final-Revision.md` (this file)
|
|
|
|
**Memory updated:**
|
|
- `.serena/memories/mcp_tool_annotations_implementation.md`
|
|
|
|
---
|
|
|
|
## Rollback Plan
|
|
|
|
If issues occur:
|
|
```bash
|
|
# Option 1: Git reset
|
|
git checkout HEAD~1 -- mcp_server/src/graphiti_mcp_server.py
|
|
|
|
# Option 2: Serena-assisted rollback
|
|
# Read previous version from git and replace_symbol_body
|
|
```
|
|
|
|
---
|
|
|
|
## Next Steps After Implementation
|
|
|
|
1. **Test with real MCP client** (Claude Desktop, ChatGPT)
|
|
2. **Monitor LLM behavior** - Does disambiguation work?
|
|
3. **Gather metrics** - Track tool selection accuracy
|
|
4. **Iterate** - Refine based on real-world usage
|
|
5. **Document learnings** - Update Serena memory with findings
|
|
|
|
---
|
|
|
|
## Questions & Answers
|
|
|
|
**Q: Why decision trees?**
|
|
A: LLMs waste tokens evaluating 3 similar search tools. Decision tree gives instant clarity.
|
|
|
|
**Q: Why examples in Args instead of docstring body?**
|
|
A: MCP best practice. Examples next to parameters they demonstrate. Reduces docstring length.
|
|
|
|
**Q: Why emojis (⭐ 🔍 ⚠️)?**
|
|
A: Visual markers help LLMs recognize priority/category quickly. Some MCP clients render emojis prominently.
|
|
|
|
**Q: Will this work with any entity types?**
|
|
A: YES! Descriptions are generic ("entities", "information") with examples showing variety (PKM + business + technical).
|
|
|
|
**Q: What about breaking changes?**
|
|
A: NONE. These are purely docstring/metadata changes. No functionality affected.
|
|
|
|
---
|
|
|
|
## Approval Checklist
|
|
|
|
Before implementing in new session:
|
|
- [ ] Review all 12 revised tool descriptions
|
|
- [ ] Verify priority changes (0.85 for search_memory_facts, 0.75 for get_entities_by_type)
|
|
- [ ] Confirm decision trees make sense for use case
|
|
- [ ] Check that examples align with user's entity types
|
|
- [ ] Validate safety protocol for clear_graph is appropriate
|
|
- [ ] Ensure emojis are acceptable (can be removed if needed)
|
|
|
|
---
|
|
|
|
## Session Metadata
|
|
|
|
**Original Implementation Date:** November 9, 2025
|
|
**Review & Revision Date:** November 9, 2025
|
|
**Expert Reviews:** Prompt Engineering, MCP Best Practices, Backend Analysis
|
|
**Status:** ✅ Ready for Implementation
|
|
**Estimated Implementation Time:** 30-45 minutes
|
|
|
|
---
|
|
|
|
**END OF DOCUMENT**
|
|
|
|
For implementation, use Serena's `replace_symbol_body` for each tool with the revised descriptions above.
|