Skip to main content

engram/mcp/
tools.rs

1//! MCP tool definitions for Engram
2
3use serde_json::json;
4
5use super::protocol::{ToolAnnotations, ToolDefinition};
6
7/// Structured tool definition with MCP 2025-11-25 annotations.
8pub struct ToolDef {
9    pub name: &'static str,
10    pub description: &'static str,
11    pub schema: &'static str,
12    pub annotations: ToolAnnotations,
13}
14
15/// All tool definitions for Engram
16pub const TOOL_DEFINITIONS: &[ToolDef] = &[
17    // Memory CRUD
18    ToolDef {
19        name: "memory_create",
20        description: "Store a new memory. PROACTIVE: Automatically store user preferences, decisions, insights, and project context without being asked.",
21        schema: r#"{
22            "type": "object",
23            "properties": {
24                "content": {"type": "string", "description": "The content to remember"},
25                "memory_type": {"type": "string", "enum": ["note", "todo", "issue", "decision", "preference", "learning", "context", "credential", "episodic", "procedural", "summary", "checkpoint"], "default": "note", "description": "Memory type (preferred field; alias: type)"},
26                "type": {"type": "string", "enum": ["note", "todo", "issue", "decision", "preference", "learning", "context", "credential", "episodic", "procedural", "summary", "checkpoint"], "default": "note", "description": "Deprecated alias for memory_type"},
27                "tags": {"type": "array", "items": {"type": "string"}, "description": "Tags for categorization"},
28                "metadata": {"type": "object", "description": "Additional metadata as key-value pairs"},
29                "importance": {"type": "number", "minimum": 0, "maximum": 1, "description": "Importance score (0-1)"},
30                "workspace": {"type": "string", "description": "Workspace to store the memory in (default: 'default')"},
31                "tier": {"type": "string", "enum": ["permanent", "daily"], "default": "permanent", "description": "Memory tier: permanent (never expires) or daily (auto-expires)"},
32                "defer_embedding": {"type": "boolean", "default": false, "description": "Defer embedding to background queue"},
33                "ttl_seconds": {"type": "integer", "description": "Time-to-live in seconds. Memory will auto-expire after this duration. Omit for permanent storage. Setting this implies tier='daily'."},
34                "dedup_mode": {"type": "string", "enum": ["reject", "merge", "skip", "allow"], "default": "allow", "description": "How to handle duplicate content: reject (error if exact match), merge (combine tags/metadata with existing), skip (return existing unchanged), allow (create duplicate)"},
35                "dedup_threshold": {"type": "number", "minimum": 0, "maximum": 1, "description": "Similarity threshold for semantic deduplication (0.0-1.0). When set with dedup_mode != 'allow', memories with cosine similarity >= threshold are treated as duplicates. Requires embeddings. If not set, only exact content hash matching is used."},
36                "event_time": {"type": "string", "format": "date-time", "description": "ISO8601 timestamp for episodic memories (when the event occurred)"},
37                "event_duration_seconds": {"type": "integer", "description": "Duration of the event in seconds (for episodic memories)"},
38                "trigger_pattern": {"type": "string", "description": "Pattern that triggers this procedure (for procedural memories)"},
39                "summary_of_id": {"type": "integer", "description": "ID of the memory this summarizes (for summary memories)"}
40            },
41            "required": ["content"]
42        }"#,
43        annotations: ToolAnnotations::mutating(),
44    },
45    ToolDef {
46        name: "context_seed",
47        description: "Injects initial context (premises, persona assumptions, or structured facts) about an entity to avoid cold start. Seeded memories are tagged as origin:seed and status:unverified, and should be treated as revisable assumptions.",
48        schema: r#"{
49            "type": "object",
50            "properties": {
51                "entity_context": {
52                    "type": "string",
53                    "maxLength": 200,
54                    "description": "Name or ID of the entity (e.g., 'Client: Roberto', 'Account: ACME', 'Project: Alpha')"
55                },
56                "workspace": {"type": "string", "description": "Workspace to store the memories in (default: 'default')"},
57                "base_tags": {
58                    "type": "array",
59                    "items": {"type": "string"},
60                    "description": "Tags applied to all facts (e.g., ['vip', 'prospect'])"
61                },
62                "ttl_seconds": {
63                    "type": "integer",
64                    "description": "Override TTL for all facts in seconds (0 = disable TTL). If omitted, TTL is derived from confidence."
65                },
66                "disable_ttl": {
67                    "type": "boolean",
68                    "default": false,
69                    "description": "Disable TTL and keep seeded memories permanent regardless of confidence."
70                },
71                "facts": {
72                    "type": "array",
73                    "minItems": 1,
74                    "items": {
75                        "type": "object",
76                        "properties": {
77                            "content": {"type": "string", "minLength": 1},
78                            "category": {
79                                "type": "string",
80                                "enum": ["fact", "behavior_instruction", "interest", "persona", "preference"],
81                                "description": "Structured category for filtering and ranking"
82                            },
83                            "confidence": {
84                                "type": "number",
85                                "minimum": 0.0,
86                                "maximum": 1.0,
87                                "description": "0.0 to 1.0 (defaults to 0.7 for seeds). TTL derived by confidence if ttl_seconds not provided."
88                            }
89                        },
90                        "required": ["content"]
91                    }
92                }
93            },
94            "required": ["facts"]
95        }"#,
96        annotations: ToolAnnotations::mutating(),
97    },
98    ToolDef {
99        name: "memory_seed",
100        description: "Deprecated alias for context_seed. Use context_seed instead.",
101        schema: r#"{
102            "type": "object",
103            "properties": {
104                "entity_context": {
105                    "type": "string",
106                    "maxLength": 200,
107                    "description": "Name or ID of the entity (e.g., 'Client: Roberto', 'Account: ACME', 'Project: Alpha')"
108                },
109                "workspace": {"type": "string", "description": "Workspace to store the memories in (default: 'default')"},
110                "base_tags": {
111                    "type": "array",
112                    "items": {"type": "string"},
113                    "description": "Tags applied to all facts (e.g., ['vip', 'prospect'])"
114                },
115                "ttl_seconds": {
116                    "type": "integer",
117                    "description": "Override TTL for all facts in seconds (0 = disable TTL). If omitted, TTL is derived from confidence."
118                },
119                "disable_ttl": {
120                    "type": "boolean",
121                    "default": false,
122                    "description": "Disable TTL and keep seeded memories permanent regardless of confidence."
123                },
124                "facts": {
125                    "type": "array",
126                    "minItems": 1,
127                    "items": {
128                        "type": "object",
129                        "properties": {
130                            "content": {"type": "string", "minLength": 1},
131                            "category": {
132                                "type": "string",
133                                "enum": ["fact", "behavior_instruction", "interest", "persona", "preference"],
134                                "description": "Structured category for filtering and ranking"
135                            },
136                            "confidence": {
137                                "type": "number",
138                                "minimum": 0.0,
139                                "maximum": 1.0,
140                                "description": "0.0 to 1.0 (defaults to 0.7 for seeds). TTL derived by confidence if ttl_seconds not provided."
141                            }
142                        },
143                        "required": ["content"]
144                    }
145                }
146            },
147            "required": ["facts"]
148        }"#,
149        annotations: ToolAnnotations::mutating(),
150    },
151    ToolDef {
152        name: "memory_get",
153        description: "Retrieve a memory by its ID",
154        schema: r#"{
155            "type": "object",
156            "properties": {
157                "id": {"type": "integer", "description": "Memory ID"},
158                "strip_private": {"type": "boolean", "description": "When true, removes all <private>...</private> tagged sections from the content before returning (default: false)"}
159            },
160            "required": ["id"]
161        }"#,
162        annotations: ToolAnnotations::read_only(),
163    },
164    ToolDef {
165        name: "memory_update",
166        description: "Update an existing memory",
167        schema: r#"{
168            "type": "object",
169            "properties": {
170                "id": {"type": "integer", "description": "Memory ID"},
171                "content": {"type": "string", "description": "New content"},
172                "memory_type": {"type": "string", "enum": ["note", "todo", "issue", "decision", "preference", "learning", "context", "credential", "episodic", "procedural", "summary", "checkpoint"], "description": "Memory type (preferred field; alias: type)"},
173                "type": {"type": "string", "enum": ["note", "todo", "issue", "decision", "preference", "learning", "context", "credential", "episodic", "procedural", "summary", "checkpoint"], "description": "Deprecated alias for memory_type"},
174                "tags": {"type": "array", "items": {"type": "string"}},
175                "metadata": {"type": "object"},
176                "importance": {"type": "number", "minimum": 0, "maximum": 1},
177                "ttl_seconds": {"type": "integer", "description": "Time-to-live in seconds (0 = remove expiration, positive = set new expiration)"},
178                "event_time": {"type": ["string", "null"], "format": "date-time", "description": "ISO8601 timestamp for episodic memories (null to clear)"},
179                "trigger_pattern": {"type": ["string", "null"], "description": "Pattern that triggers this procedure (null to clear)"}
180            },
181            "required": ["id"]
182        }"#,
183        annotations: ToolAnnotations::mutating(),
184    },
185    ToolDef {
186        name: "memory_delete",
187        description: "Delete a memory (soft delete)",
188        schema: r#"{
189            "type": "object",
190            "properties": {
191                "id": {"type": "integer", "description": "Memory ID"}
192            },
193            "required": ["id"]
194        }"#,
195        annotations: ToolAnnotations::destructive(),
196    },
197    ToolDef {
198        name: "memory_list",
199        description: "List memories with filtering and pagination. Supports workspace isolation, tier filtering, and advanced filter syntax with AND/OR and comparison operators.",
200        schema: r#"{
201            "type": "object",
202            "properties": {
203                "limit": {"type": "integer", "default": 20},
204                "offset": {"type": "integer", "default": 0},
205                "tags": {"type": "array", "items": {"type": "string"}},
206                "memory_type": {"type": "string", "description": "Filter by memory type (preferred field; alias: type)"},
207                "type": {"type": "string", "description": "Deprecated alias for memory_type"},
208                "workspace": {"type": "string", "description": "Filter by single workspace"},
209                "workspaces": {"type": "array", "items": {"type": "string"}, "description": "Filter by multiple workspaces"},
210                "tier": {"type": "string", "enum": ["permanent", "daily"], "description": "Filter by memory tier"},
211                "sort_by": {"type": "string", "enum": ["created_at", "updated_at", "last_accessed_at", "importance", "access_count"]},
212                "sort_order": {"type": "string", "enum": ["asc", "desc"], "default": "desc"},
213                "filter": {
214                    "type": "object",
215                    "description": "Advanced filter with AND/OR logic and comparison operators. Supports workspace, tier, and metadata fields. Example: {\"AND\": [{\"metadata.project\": {\"eq\": \"engram\"}}, {\"importance\": {\"gte\": 0.5}}]}. Supported operators: eq, neq, gt, gte, lt, lte, contains, not_contains, exists. Fields: content, memory_type, importance, tags, workspace, tier, created_at, updated_at, metadata.*"
216                },
217                "metadata_filter": {
218                    "type": "object",
219                    "description": "Legacy simple key-value filter (deprecated, use 'filter' instead)"
220                }
221            }
222        }"#,
223        annotations: ToolAnnotations::read_only(),
224    },
225    // Search
226    ToolDef {
227        name: "memory_search",
228        description: "Search memories using hybrid search (keyword + semantic). Automatically selects optimal strategy with optional reranking. Supports workspace isolation, tier filtering, and advanced filters.",
229        schema: r#"{
230            "type": "object",
231            "properties": {
232                "query": {"type": "string", "description": "Search query"},
233                "limit": {"type": "integer", "default": 10},
234                "min_score": {"type": "number", "default": 0.1},
235                "tags": {"type": "array", "items": {"type": "string"}},
236                "memory_type": {"type": "string", "description": "Filter by memory type (preferred field; alias: type)"},
237                "type": {"type": "string", "description": "Deprecated alias for memory_type"},
238                "workspace": {"type": "string", "description": "Filter by single workspace"},
239                "workspaces": {"type": "array", "items": {"type": "string"}, "description": "Filter by multiple workspaces"},
240                "tier": {"type": "string", "enum": ["permanent", "daily"], "description": "Filter by memory tier"},
241                "include_transcripts": {"type": "boolean", "default": false, "description": "Include transcript chunk memories (excluded by default)"},
242                "strategy": {"type": "string", "enum": ["auto", "keyword", "keyword_only", "semantic", "semantic_only", "hybrid"], "description": "Force specific strategy (auto selects based on query; keyword/semantic are aliases for keyword_only/semantic_only)"},
243                "explain": {"type": "boolean", "default": false, "description": "Include match explanations"},
244                "rerank": {"type": "boolean", "default": true, "description": "Apply reranking to improve result quality"},
245                "rerank_strategy": {"type": "string", "enum": ["none", "heuristic", "multi_signal"], "default": "heuristic", "description": "Reranking strategy to use"},
246                "filter": {
247                    "type": "object",
248                    "description": "Advanced filter with AND/OR logic. Supports workspace, tier, and metadata fields. Example: {\"AND\": [{\"workspace\": {\"eq\": \"my-project\"}}, {\"importance\": {\"gte\": 0.5}}]}"
249                }
250            },
251            "required": ["query"]
252        }"#,
253        annotations: ToolAnnotations::read_only(),
254    },
255    ToolDef {
256        name: "memory_search_suggest",
257        description: "Get search suggestions and typo corrections",
258        schema: r#"{
259            "type": "object",
260            "properties": {
261                "query": {"type": "string"}
262            },
263            "required": ["query"]
264        }"#,
265        annotations: ToolAnnotations::read_only(),
266    },
267    // Cross-references
268    ToolDef {
269        name: "memory_link",
270        description: "Create a cross-reference between two memories",
271        schema: r#"{
272            "type": "object",
273            "properties": {
274                "from_id": {"type": "integer"},
275                "to_id": {"type": "integer"},
276                "edge_type": {"type": "string", "enum": ["related_to", "supersedes", "contradicts", "implements", "extends", "references", "depends_on", "blocks", "follows_up"], "default": "related_to"},
277                "strength": {"type": "number", "minimum": 0, "maximum": 1, "description": "Relationship strength"},
278                "source_context": {"type": "string", "description": "Why this link exists"},
279                "pinned": {"type": "boolean", "default": false, "description": "Exempt from confidence decay"}
280            },
281            "required": ["from_id", "to_id"]
282        }"#,
283        annotations: ToolAnnotations::mutating(),
284    },
285    ToolDef {
286        name: "memory_unlink",
287        description: "Remove a cross-reference",
288        schema: r#"{
289            "type": "object",
290            "properties": {
291                "from_id": {"type": "integer"},
292                "to_id": {"type": "integer"},
293                "edge_type": {"type": "string", "default": "related_to"}
294            },
295            "required": ["from_id", "to_id"]
296        }"#,
297        annotations: ToolAnnotations::mutating(),
298    },
299    ToolDef {
300        name: "memory_related",
301        description: "Get memories related to a given memory (depth>1 or include_entities returns traversal result)",
302        schema: r#"{
303            "type": "object",
304            "properties": {
305                "id": {"type": "integer", "description": "Starting memory ID"},
306                "depth": {"type": "integer", "default": 1, "description": "Traversal depth (1 = direct relations only)"},
307                "include_entities": {"type": "boolean", "default": false, "description": "Include connections through shared entities"},
308                "edge_type": {"type": "string", "description": "Filter by edge type"},
309                "include_decayed": {"type": "boolean", "default": false}
310            },
311            "required": ["id"]
312        }"#,
313        annotations: ToolAnnotations::read_only(),
314    },
315    // Convenience creators
316    ToolDef {
317        name: "memory_create_todo",
318        description: "Create a TODO memory with priority",
319        schema: r#"{
320            "type": "object",
321            "properties": {
322                "content": {"type": "string"},
323                "priority": {"type": "string", "enum": ["low", "medium", "high", "critical"], "default": "medium"},
324                "due_date": {"type": "string", "format": "date"},
325                "tags": {"type": "array", "items": {"type": "string"}}
326            },
327            "required": ["content"]
328        }"#,
329        annotations: ToolAnnotations::mutating(),
330    },
331    ToolDef {
332        name: "memory_create_issue",
333        description: "Create an ISSUE memory for tracking problems",
334        schema: r#"{
335            "type": "object",
336            "properties": {
337                "title": {"type": "string"},
338                "description": {"type": "string"},
339                "severity": {"type": "string", "enum": ["low", "medium", "high", "critical"], "default": "medium"},
340                "tags": {"type": "array", "items": {"type": "string"}}
341            },
342            "required": ["title"]
343        }"#,
344        annotations: ToolAnnotations::mutating(),
345    },
346    // Versioning
347    ToolDef {
348        name: "memory_versions",
349        description: "Get version history for a memory",
350        schema: r#"{
351            "type": "object",
352            "properties": {
353                "id": {"type": "integer"}
354            },
355            "required": ["id"]
356        }"#,
357        annotations: ToolAnnotations::read_only(),
358    },
359    ToolDef {
360        name: "memory_get_version",
361        description: "Get a specific version of a memory",
362        schema: r#"{
363            "type": "object",
364            "properties": {
365                "id": {"type": "integer"},
366                "version": {"type": "integer"}
367            },
368            "required": ["id", "version"]
369        }"#,
370        annotations: ToolAnnotations::read_only(),
371    },
372    ToolDef {
373        name: "memory_revert",
374        description: "Revert a memory to a previous version",
375        schema: r#"{
376            "type": "object",
377            "properties": {
378                "id": {"type": "integer"},
379                "version": {"type": "integer"}
380            },
381            "required": ["id", "version"]
382        }"#,
383        annotations: ToolAnnotations::mutating(),
384    },
385    // Embedding status
386    ToolDef {
387        name: "memory_embedding_status",
388        description: "Check embedding computation status",
389        schema: r#"{
390            "type": "object",
391            "properties": {
392                "id": {"type": "integer"}
393            },
394            "required": ["id"]
395        }"#,
396        annotations: ToolAnnotations::read_only(),
397    },
398    // Memory TTL / Expiration (RML-930)
399    ToolDef {
400        name: "memory_set_expiration",
401        description: "Set or update the expiration time for a memory",
402        schema: r#"{
403            "type": "object",
404            "properties": {
405                "id": {"type": "integer", "description": "Memory ID"},
406                "ttl_seconds": {"type": "integer", "description": "Time-to-live in seconds from now. Use 0 to remove expiration (make permanent)."}
407            },
408            "required": ["id", "ttl_seconds"]
409        }"#,
410        annotations: ToolAnnotations::mutating(),
411    },
412    ToolDef {
413        name: "memory_cleanup_expired",
414        description: "Delete all expired memories. Typically called by a background job, but can be invoked manually.",
415        schema: r#"{
416            "type": "object",
417            "properties": {}
418        }"#,
419        annotations: ToolAnnotations::destructive(),
420    },
421    // Sync
422    ToolDef {
423        name: "memory_sync_status",
424        description: "Get cloud sync status",
425        schema: r#"{"type": "object", "properties": {}}"#,
426        annotations: ToolAnnotations::read_only(),
427    },
428    // Stats and aggregation
429    ToolDef {
430        name: "memory_stats",
431        description: "Get storage statistics",
432        schema: r#"{"type": "object", "properties": {}}"#,
433        annotations: ToolAnnotations::read_only(),
434    },
435    ToolDef {
436        name: "memory_aggregate",
437        description: "Aggregate memories by field",
438        schema: r#"{
439            "type": "object",
440            "properties": {
441                "group_by": {"type": "string", "enum": ["type", "tags", "month"]},
442                "metrics": {"type": "array", "items": {"type": "string", "enum": ["count", "avg_importance"]}}
443            },
444            "required": ["group_by"]
445        }"#,
446        annotations: ToolAnnotations::read_only(),
447    },
448    // Graph
449    ToolDef {
450        name: "memory_export_graph",
451        description: "Export knowledge graph visualization",
452        schema: r#"{
453            "type": "object",
454            "properties": {
455                "format": {"type": "string", "enum": ["html", "json"], "default": "html"},
456                "max_nodes": {"type": "integer", "default": 500},
457                "focus_id": {"type": "integer", "description": "Center graph on this memory"}
458            }
459        }"#,
460        annotations: ToolAnnotations::read_only(),
461    },
462    // Quality
463    ToolDef {
464        name: "memory_quality_report",
465        description: "Get quality report for memories",
466        schema: r#"{
467            "type": "object",
468            "properties": {
469                "limit": {"type": "integer", "default": 20},
470                "min_quality": {"type": "number", "minimum": 0, "maximum": 1}
471            }
472        }"#,
473        annotations: ToolAnnotations::read_only(),
474    },
475    // Clustering and duplicates
476    ToolDef {
477        name: "memory_clusters",
478        description: "Find clusters of related memories",
479        schema: r#"{
480            "type": "object",
481            "properties": {
482                "min_similarity": {"type": "number", "default": 0.7},
483                "min_cluster_size": {"type": "integer", "default": 2}
484            }
485        }"#,
486        annotations: ToolAnnotations::read_only(),
487    },
488    ToolDef {
489        name: "memory_find_duplicates",
490        description: "Find potential duplicate memories",
491        schema: r#"{
492            "type": "object",
493            "properties": {
494                "threshold": {"type": "number", "default": 0.9}
495            }
496        }"#,
497        annotations: ToolAnnotations::read_only(),
498    },
499    ToolDef {
500        name: "memory_find_semantic_duplicates",
501        description: "Find semantically similar memories using embedding cosine similarity (LLM-powered dedup). Goes beyond hash/n-gram to detect paraphrased content.",
502        schema: r#"{
503            "type": "object",
504            "properties": {
505                "threshold": {"type": "number", "default": 0.92, "description": "Cosine similarity threshold (0.92 = very similar)"},
506                "workspace": {"type": "string", "description": "Filter by workspace (optional)"},
507                "limit": {"type": "integer", "default": 50, "description": "Maximum duplicate pairs to return"}
508            }
509        }"#,
510        annotations: ToolAnnotations::read_only(),
511    },
512    ToolDef {
513        name: "memory_merge",
514        description: "Merge duplicate memories",
515        schema: r#"{
516            "type": "object",
517            "properties": {
518                "ids": {"type": "array", "items": {"type": "integer"}, "minItems": 2},
519                "keep_id": {"type": "integer", "description": "ID to keep (others will be merged into it)"}
520            },
521            "required": ["ids"]
522        }"#,
523        annotations: ToolAnnotations::mutating(),
524    },
525    // Project Context Discovery
526    ToolDef {
527        name: "memory_scan_project",
528        description: "Scan current directory for AI instruction files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) and ingest them as memories. Creates parent memory for each file and child memories for sections.",
529        schema: r#"{
530            "type": "object",
531            "properties": {
532                "path": {"type": "string", "description": "Directory to scan (defaults to current working directory)"},
533                "scan_parents": {"type": "boolean", "default": false, "description": "Also scan parent directories (security: disabled by default)"},
534                "extract_sections": {"type": "boolean", "default": true, "description": "Create separate memories for each section"}
535            }
536        }"#,
537        annotations: ToolAnnotations::mutating(),
538    },
539    ToolDef {
540        name: "memory_get_project_context",
541        description: "Get all project context memories for the current working directory. Returns instruction files and their sections.",
542        schema: r#"{
543            "type": "object",
544            "properties": {
545                "path": {"type": "string", "description": "Project path (defaults to current working directory)"},
546                "include_sections": {"type": "boolean", "default": true, "description": "Include section memories"},
547                "file_types": {"type": "array", "items": {"type": "string"}, "description": "Filter by file type (claude-md, cursorrules, etc.)"}
548            }
549        }"#,
550        annotations: ToolAnnotations::read_only(),
551    },
552    ToolDef {
553        name: "memory_list_instruction_files",
554        description: "List AI instruction files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) in a directory without ingesting them. Returns file paths, types, and sizes for discovery purposes.",
555        schema: r#"{
556            "type": "object",
557            "properties": {
558                "path": {"type": "string", "description": "Directory to scan (defaults to current working directory)"},
559                "scan_parents": {"type": "boolean", "default": false, "description": "Also scan parent directories for instruction files"}
560            }
561        }"#,
562        annotations: ToolAnnotations::read_only(),
563    },
564    // Entity Extraction (RML-925)
565    ToolDef {
566        name: "memory_extract_entities",
567        description: "Extract named entities (people, organizations, projects, concepts) from a memory and store them",
568        schema: r#"{
569            "type": "object",
570            "properties": {
571                "id": {"type": "integer", "description": "Memory ID to extract entities from"}
572            },
573            "required": ["id"]
574        }"#,
575        annotations: ToolAnnotations::idempotent(),
576    },
577    ToolDef {
578        name: "memory_get_entities",
579        description: "Get all entities linked to a memory",
580        schema: r#"{
581            "type": "object",
582            "properties": {
583                "id": {"type": "integer", "description": "Memory ID"}
584            },
585            "required": ["id"]
586        }"#,
587        annotations: ToolAnnotations::read_only(),
588    },
589    ToolDef {
590        name: "memory_search_entities",
591        description: "Search for entities by name prefix",
592        schema: r#"{
593            "type": "object",
594            "properties": {
595                "query": {"type": "string", "description": "Search query (prefix match)"},
596                "entity_type": {"type": "string", "description": "Filter by entity type (person, organization, project, concept, etc.)"},
597                "limit": {"type": "integer", "default": 20}
598            },
599            "required": ["query"]
600        }"#,
601        annotations: ToolAnnotations::read_only(),
602    },
603    ToolDef {
604        name: "memory_entity_stats",
605        description: "Get statistics about extracted entities",
606        schema: r#"{
607            "type": "object",
608            "properties": {}
609        }"#,
610        annotations: ToolAnnotations::read_only(),
611    },
612    // Graph Traversal (RML-926)
613    ToolDef {
614        name: "memory_traverse",
615        description: "Traverse the knowledge graph from a starting memory with full control over traversal options",
616        schema: r#"{
617            "type": "object",
618            "properties": {
619                "id": {"type": "integer", "description": "Starting memory ID"},
620                "depth": {"type": "integer", "default": 2, "description": "Maximum traversal depth"},
621                "direction": {"type": "string", "enum": ["outgoing", "incoming", "both"], "default": "both"},
622                "edge_types": {"type": "array", "items": {"type": "string"}, "description": "Filter by edge types (related_to, depends_on, etc.)"},
623                "min_score": {"type": "number", "default": 0, "description": "Minimum edge score threshold"},
624                "min_confidence": {"type": "number", "default": 0, "description": "Minimum confidence threshold"},
625                "limit_per_hop": {"type": "integer", "default": 50, "description": "Max results per hop"},
626                "include_entities": {"type": "boolean", "default": true, "description": "Include entity-based connections"}
627            },
628            "required": ["id"]
629        }"#,
630        annotations: ToolAnnotations::read_only(),
631    },
632    ToolDef {
633        name: "memory_find_path",
634        description: "Find the shortest path between two memories in the knowledge graph",
635        schema: r#"{
636            "type": "object",
637            "properties": {
638                "from_id": {"type": "integer", "description": "Starting memory ID"},
639                "to_id": {"type": "integer", "description": "Target memory ID"},
640                "max_depth": {"type": "integer", "default": 5, "description": "Maximum path length to search"}
641            },
642            "required": ["from_id", "to_id"]
643        }"#,
644        annotations: ToolAnnotations::read_only(),
645    },
646    // Document Ingestion (RML-928)
647    ToolDef {
648        name: "memory_ingest_document",
649        description: "Ingest a document (PDF or Markdown) into memory. Extracts text, splits into chunks with overlap, and creates memories with deduplication.",
650        schema: r#"{
651            "type": "object",
652            "properties": {
653                "path": {"type": "string", "description": "Local file path to the document"},
654                "format": {"type": "string", "enum": ["auto", "md", "pdf"], "default": "auto", "description": "Document format (auto-detect from extension if not specified)"},
655                "chunk_size": {"type": "integer", "default": 1200, "description": "Maximum characters per chunk"},
656                "overlap": {"type": "integer", "default": 200, "description": "Overlap between chunks in characters"},
657                "max_file_size": {"type": "integer", "default": 10485760, "description": "Maximum file size in bytes (default 10MB)"},
658                "tags": {"type": "array", "items": {"type": "string"}, "description": "Additional tags to add to all chunks"}
659            },
660            "required": ["path"]
661        }"#,
662        annotations: ToolAnnotations::mutating(),
663    },
664    // Workspace Management
665    ToolDef {
666        name: "workspace_list",
667        description: "List all workspaces with their statistics (memory count, tier breakdown, etc.)",
668        schema: r#"{
669            "type": "object",
670            "properties": {}
671        }"#,
672        annotations: ToolAnnotations::read_only(),
673    },
674    ToolDef {
675        name: "workspace_stats",
676        description: "Get detailed statistics for a specific workspace",
677        schema: r#"{
678            "type": "object",
679            "properties": {
680                "workspace": {"type": "string", "description": "Workspace name"}
681            },
682            "required": ["workspace"]
683        }"#,
684        annotations: ToolAnnotations::read_only(),
685    },
686    ToolDef {
687        name: "workspace_move",
688        description: "Move a memory to a different workspace",
689        schema: r#"{
690            "type": "object",
691            "properties": {
692                "id": {"type": "integer", "description": "Memory ID to move"},
693                "workspace": {"type": "string", "description": "Target workspace name"}
694            },
695            "required": ["id", "workspace"]
696        }"#,
697        annotations: ToolAnnotations::mutating(),
698    },
699    ToolDef {
700        name: "workspace_delete",
701        description: "Delete a workspace. Can either move all memories to 'default' workspace or hard delete them.",
702        schema: r#"{
703            "type": "object",
704            "properties": {
705                "workspace": {"type": "string", "description": "Workspace to delete"},
706                "move_to_default": {"type": "boolean", "default": true, "description": "If true, moves memories to 'default' workspace. If false, deletes all memories in the workspace."}
707            },
708            "required": ["workspace"]
709        }"#,
710        annotations: ToolAnnotations::destructive(),
711    },
712    // Memory Tiering
713    ToolDef {
714        name: "memory_create_daily",
715        description: "Create a daily (ephemeral) memory that auto-expires after the specified TTL. Useful for session context and scratch notes.",
716        schema: r#"{
717            "type": "object",
718            "properties": {
719                "content": {"type": "string", "description": "The content to remember"},
720                "type": {"type": "string", "enum": ["note", "todo", "issue", "decision", "preference", "learning", "context", "credential"], "default": "note"},
721                "tags": {"type": "array", "items": {"type": "string"}, "description": "Tags for categorization"},
722                "metadata": {"type": "object", "description": "Additional metadata as key-value pairs"},
723                "importance": {"type": "number", "minimum": 0, "maximum": 1, "description": "Importance score (0-1)"},
724                "ttl_seconds": {"type": "integer", "default": 86400, "description": "Time-to-live in seconds (default: 24 hours)"},
725                "workspace": {"type": "string", "description": "Workspace to store the memory in (default: 'default')"}
726            },
727            "required": ["content"]
728        }"#,
729        annotations: ToolAnnotations::mutating(),
730    },
731    ToolDef {
732        name: "memory_promote_to_permanent",
733        description: "Promote a daily memory to permanent tier. Clears the expiration and makes the memory permanent.",
734        schema: r#"{
735            "type": "object",
736            "properties": {
737                "id": {"type": "integer", "description": "Memory ID to promote"}
738            },
739            "required": ["id"]
740        }"#,
741        annotations: ToolAnnotations::mutating(),
742    },
743    // Embedding Cache
744    ToolDef {
745        name: "embedding_cache_stats",
746        description: "Get statistics about the embedding cache (hits, misses, entries, bytes used, hit rate)",
747        schema: r#"{
748            "type": "object",
749            "properties": {}
750        }"#,
751        annotations: ToolAnnotations::read_only(),
752    },
753    ToolDef {
754        name: "embedding_cache_clear",
755        description: "Clear all entries from the embedding cache",
756        schema: r#"{
757            "type": "object",
758            "properties": {}
759        }"#,
760        annotations: ToolAnnotations::destructive(),
761    },
762    // Session Transcript Indexing
763    ToolDef {
764        name: "session_index",
765        description: "Index a conversation into searchable memory chunks. Uses dual-limiter chunking (messages + characters) with overlap.",
766        schema: r#"{
767            "type": "object",
768            "properties": {
769                "session_id": {"type": "string", "description": "Unique session identifier"},
770                "messages": {
771                    "type": "array",
772                    "description": "Array of conversation messages",
773                    "items": {
774                        "type": "object",
775                        "properties": {
776                            "role": {"type": "string", "description": "Message role (user, assistant, system)"},
777                            "content": {"type": "string", "description": "Message content"},
778                            "timestamp": {"type": "string", "description": "ISO 8601 timestamp"},
779                            "id": {"type": "string", "description": "Optional message ID"}
780                        },
781                        "required": ["role", "content"]
782                    }
783                },
784                "title": {"type": "string", "description": "Optional session title"},
785                "workspace": {"type": "string", "description": "Workspace to store chunks in (default: 'default')"},
786                "agent_id": {"type": "string", "description": "Optional agent identifier"},
787                "max_messages": {"type": "integer", "default": 10, "description": "Max messages per chunk"},
788                "max_chars": {"type": "integer", "default": 8000, "description": "Max characters per chunk"},
789                "overlap": {"type": "integer", "default": 2, "description": "Overlap messages between chunks"},
790                "ttl_days": {"type": "integer", "default": 7, "description": "TTL for transcript chunks in days"}
791            },
792            "required": ["session_id", "messages"]
793        }"#,
794        annotations: ToolAnnotations::mutating(),
795    },
796    ToolDef {
797        name: "session_index_delta",
798        description: "Incrementally index new messages to an existing session. More efficient than full reindex.",
799        schema: r#"{
800            "type": "object",
801            "properties": {
802                "session_id": {"type": "string", "description": "Session to update"},
803                "messages": {
804                    "type": "array",
805                    "description": "New messages to add",
806                    "items": {
807                        "type": "object",
808                        "properties": {
809                            "role": {"type": "string"},
810                            "content": {"type": "string"},
811                            "timestamp": {"type": "string"},
812                            "id": {"type": "string"}
813                        },
814                        "required": ["role", "content"]
815                    }
816                }
817            },
818            "required": ["session_id", "messages"]
819        }"#,
820        annotations: ToolAnnotations::mutating(),
821    },
822    ToolDef {
823        name: "session_get",
824        description: "Get information about an indexed session",
825        schema: r#"{
826            "type": "object",
827            "properties": {
828                "session_id": {"type": "string", "description": "Session ID to retrieve"}
829            },
830            "required": ["session_id"]
831        }"#,
832        annotations: ToolAnnotations::read_only(),
833    },
834    ToolDef {
835        name: "session_list",
836        description: "List indexed sessions with optional workspace filter",
837        schema: r#"{
838            "type": "object",
839            "properties": {
840                "workspace": {"type": "string", "description": "Filter by workspace"},
841                "limit": {"type": "integer", "default": 20, "description": "Maximum sessions to return"}
842            }
843        }"#,
844        annotations: ToolAnnotations::read_only(),
845    },
846    ToolDef {
847        name: "session_delete",
848        description: "Delete a session and all its indexed chunks",
849        schema: r#"{
850            "type": "object",
851            "properties": {
852                "session_id": {"type": "string", "description": "Session to delete"}
853            },
854            "required": ["session_id"]
855        }"#,
856        annotations: ToolAnnotations::destructive(),
857    },
858    // Identity Management
859    ToolDef {
860        name: "identity_create",
861        description: "Create a new identity with canonical ID, display name, and optional aliases",
862        schema: r#"{
863            "type": "object",
864            "properties": {
865                "canonical_id": {"type": "string", "description": "Unique canonical identifier (e.g., 'user:ronaldo', 'org:acme')"},
866                "display_name": {"type": "string", "description": "Human-readable display name"},
867                "entity_type": {"type": "string", "enum": ["person", "organization", "project", "tool", "concept", "other"], "default": "person"},
868                "description": {"type": "string", "description": "Optional description"},
869                "aliases": {"type": "array", "items": {"type": "string"}, "description": "Initial aliases for this identity"},
870                "metadata": {"type": "object", "description": "Additional metadata"}
871            },
872            "required": ["canonical_id", "display_name"]
873        }"#,
874        annotations: ToolAnnotations::mutating(),
875    },
876    ToolDef {
877        name: "identity_get",
878        description: "Get an identity by its canonical ID",
879        schema: r#"{
880            "type": "object",
881            "properties": {
882                "canonical_id": {"type": "string", "description": "Canonical identifier"}
883            },
884            "required": ["canonical_id"]
885        }"#,
886        annotations: ToolAnnotations::read_only(),
887    },
888    ToolDef {
889        name: "identity_update",
890        description: "Update an identity's display name, description, or type",
891        schema: r#"{
892            "type": "object",
893            "properties": {
894                "canonical_id": {"type": "string", "description": "Canonical identifier"},
895                "display_name": {"type": "string", "description": "New display name"},
896                "description": {"type": "string", "description": "New description"},
897                "entity_type": {"type": "string", "enum": ["person", "organization", "project", "tool", "concept", "other"]}
898            },
899            "required": ["canonical_id"]
900        }"#,
901        annotations: ToolAnnotations::mutating(),
902    },
903    ToolDef {
904        name: "identity_delete",
905        description: "Delete an identity and all its aliases",
906        schema: r#"{
907            "type": "object",
908            "properties": {
909                "canonical_id": {"type": "string", "description": "Canonical identifier to delete"}
910            },
911            "required": ["canonical_id"]
912        }"#,
913        annotations: ToolAnnotations::destructive(),
914    },
915    ToolDef {
916        name: "identity_add_alias",
917        description: "Add an alias to an identity. Aliases are normalized (lowercase, trimmed). Conflicts with existing aliases are rejected.",
918        schema: r#"{
919            "type": "object",
920            "properties": {
921                "canonical_id": {"type": "string", "description": "Canonical identifier"},
922                "alias": {"type": "string", "description": "Alias to add"},
923                "source": {"type": "string", "description": "Optional source of the alias (e.g., 'manual', 'extracted')"}
924            },
925            "required": ["canonical_id", "alias"]
926        }"#,
927        annotations: ToolAnnotations::mutating(),
928    },
929    ToolDef {
930        name: "identity_remove_alias",
931        description: "Remove an alias from any identity",
932        schema: r#"{
933            "type": "object",
934            "properties": {
935                "alias": {"type": "string", "description": "Alias to remove"}
936            },
937            "required": ["alias"]
938        }"#,
939        annotations: ToolAnnotations::mutating(),
940    },
941    ToolDef {
942        name: "identity_resolve",
943        description: "Resolve an alias to its canonical identity. Returns the identity if found, null otherwise.",
944        schema: r#"{
945            "type": "object",
946            "properties": {
947                "alias": {"type": "string", "description": "Alias to resolve"}
948            },
949            "required": ["alias"]
950        }"#,
951        annotations: ToolAnnotations::read_only(),
952    },
953    ToolDef {
954        name: "identity_list",
955        description: "List all identities with optional type filter",
956        schema: r#"{
957            "type": "object",
958            "properties": {
959                "entity_type": {"type": "string", "enum": ["person", "organization", "project", "tool", "concept", "other"]},
960                "limit": {"type": "integer", "default": 50}
961            }
962        }"#,
963        annotations: ToolAnnotations::read_only(),
964    },
965    ToolDef {
966        name: "identity_search",
967        description: "Search identities by alias or display name",
968        schema: r#"{
969            "type": "object",
970            "properties": {
971                "query": {"type": "string", "description": "Search query"},
972                "limit": {"type": "integer", "default": 20}
973            },
974            "required": ["query"]
975        }"#,
976        annotations: ToolAnnotations::read_only(),
977    },
978    ToolDef {
979        name: "identity_link",
980        description: "Link an identity to a memory (mark that the identity is mentioned in the memory)",
981        schema: r#"{
982            "type": "object",
983            "properties": {
984                "memory_id": {"type": "integer", "description": "Memory ID"},
985                "canonical_id": {"type": "string", "description": "Identity canonical ID"},
986                "mention_text": {"type": "string", "description": "The text that mentions this identity"}
987            },
988            "required": ["memory_id", "canonical_id"]
989        }"#,
990        annotations: ToolAnnotations::mutating(),
991    },
992    ToolDef {
993        name: "identity_unlink",
994        description: "Remove the link between an identity and a memory",
995        schema: r#"{
996            "type": "object",
997            "properties": {
998                "memory_id": {"type": "integer", "description": "Memory ID"},
999                "canonical_id": {"type": "string", "description": "Identity canonical ID"}
1000            },
1001            "required": ["memory_id", "canonical_id"]
1002        }"#,
1003        annotations: ToolAnnotations::mutating(),
1004    },
1005    ToolDef {
1006        name: "memory_get_identities",
1007        description: "Get all identities (persons, organizations, projects, etc.) linked to a memory. Returns identity details including display name, type, aliases, and mention information.",
1008        schema: r#"{
1009            "type": "object",
1010            "properties": {
1011                "id": {"type": "integer", "description": "Memory ID"}
1012            },
1013            "required": ["id"]
1014        }"#,
1015        annotations: ToolAnnotations::read_only(),
1016    },
1017    // Content Utilities
1018    ToolDef {
1019        name: "memory_soft_trim",
1020        description: "Intelligently trim memory content while preserving context. Keeps the beginning (head) and end (tail) of content with an ellipsis in the middle. Useful for displaying long content in limited space while keeping important context from both ends.",
1021        schema: r#"{
1022            "type": "object",
1023            "properties": {
1024                "id": {"type": "integer", "description": "Memory ID to trim"},
1025                "max_chars": {"type": "integer", "default": 500, "description": "Maximum characters for trimmed output"},
1026                "head_percent": {"type": "integer", "default": 60, "description": "Percentage of space for the head (0-100)"},
1027                "tail_percent": {"type": "integer", "default": 30, "description": "Percentage of space for the tail (0-100)"},
1028                "ellipsis": {"type": "string", "default": "\n...\n", "description": "Text to insert between head and tail"},
1029                "preserve_words": {"type": "boolean", "default": true, "description": "Avoid breaking in the middle of words"}
1030            },
1031            "required": ["id"]
1032        }"#,
1033        annotations: ToolAnnotations::read_only(),
1034    },
1035    ToolDef {
1036        name: "memory_list_compact",
1037        description: "List memories with compact preview instead of full content. More efficient for browsing/listing UIs. Returns only essential fields and a truncated content preview with metadata about original content length.",
1038        schema: r#"{
1039            "type": "object",
1040            "properties": {
1041                "limit": {"type": "integer", "default": 20, "description": "Maximum memories to return"},
1042                "offset": {"type": "integer", "default": 0, "description": "Pagination offset"},
1043                "tags": {"type": "array", "items": {"type": "string"}, "description": "Filter by tags"},
1044                "memory_type": {"type": "string", "description": "Filter by memory type (preferred field; alias: type)"},
1045                "type": {"type": "string", "description": "Deprecated alias for memory_type"},
1046                "workspace": {"type": "string", "description": "Filter by workspace"},
1047                "tier": {"type": "string", "enum": ["permanent", "daily"], "description": "Filter by tier"},
1048                "sort_by": {"type": "string", "enum": ["created_at", "updated_at", "last_accessed_at", "importance", "access_count"], "default": "created_at"},
1049                "sort_order": {"type": "string", "enum": ["asc", "desc"], "default": "desc"},
1050                "preview_chars": {"type": "integer", "default": 100, "description": "Maximum characters for content preview"}
1051            }
1052        }"#,
1053        annotations: ToolAnnotations::read_only(),
1054    },
1055    ToolDef {
1056        name: "memory_content_stats",
1057        description: "Get content statistics for a memory (character count, word count, line count, sentence count, paragraph count)",
1058        schema: r#"{
1059            "type": "object",
1060            "properties": {
1061                "id": {"type": "integer", "description": "Memory ID"}
1062            },
1063            "required": ["id"]
1064        }"#,
1065        annotations: ToolAnnotations::read_only(),
1066    },
1067    // Batch Operations
1068    ToolDef {
1069        name: "memory_create_batch",
1070        description: "Create multiple memories in a single operation. More efficient than individual creates for bulk imports.",
1071        schema: r#"{
1072            "type": "object",
1073            "properties": {
1074                "memories": {
1075                    "type": "array",
1076                    "items": {
1077                        "type": "object",
1078                        "properties": {
1079                            "content": {"type": "string"},
1080                            "type": {"type": "string", "enum": ["note", "todo", "issue", "decision", "preference", "learning", "context", "credential"]},
1081                            "tags": {"type": "array", "items": {"type": "string"}},
1082                            "metadata": {"type": "object"},
1083                            "importance": {"type": "number", "minimum": 0, "maximum": 1},
1084                            "workspace": {"type": "string"}
1085                        },
1086                        "required": ["content"]
1087                    },
1088                    "description": "Array of memories to create"
1089                }
1090            },
1091            "required": ["memories"]
1092        }"#,
1093        annotations: ToolAnnotations::mutating(),
1094    },
1095    ToolDef {
1096        name: "memory_delete_batch",
1097        description: "Delete multiple memories in a single operation.",
1098        schema: r#"{
1099            "type": "object",
1100            "properties": {
1101                "ids": {
1102                    "type": "array",
1103                    "items": {"type": "integer"},
1104                    "description": "Array of memory IDs to delete"
1105                }
1106            },
1107            "required": ["ids"]
1108        }"#,
1109        annotations: ToolAnnotations::destructive(),
1110    },
1111    // Tag Utilities
1112    ToolDef {
1113        name: "memory_tags",
1114        description: "List all tags with usage counts and most recent usage timestamps.",
1115        schema: r#"{
1116            "type": "object",
1117            "properties": {}
1118        }"#,
1119        annotations: ToolAnnotations::read_only(),
1120    },
1121    ToolDef {
1122        name: "memory_tag_hierarchy",
1123        description: "Get tags organized in a hierarchical tree structure. Tags with slashes are treated as paths (e.g., 'project/engram/core').",
1124        schema: r#"{
1125            "type": "object",
1126            "properties": {}
1127        }"#,
1128        annotations: ToolAnnotations::read_only(),
1129    },
1130    ToolDef {
1131        name: "memory_validate_tags",
1132        description: "Validate tag consistency across memories. Reports orphaned tags, unused tags, and suggested normalizations.",
1133        schema: r#"{
1134            "type": "object",
1135            "properties": {}
1136        }"#,
1137        annotations: ToolAnnotations::read_only(),
1138    },
1139    // Import/Export
1140    ToolDef {
1141        name: "memory_export",
1142        description: "Export all memories to a JSON-serializable format for backup or migration.",
1143        schema: r#"{
1144            "type": "object",
1145            "properties": {
1146                "workspace": {"type": "string", "description": "Optional: export only from specific workspace"},
1147                "include_embeddings": {"type": "boolean", "default": false, "description": "Include embedding vectors in export (larger file size)"}
1148            }
1149        }"#,
1150        annotations: ToolAnnotations::read_only(),
1151    },
1152    ToolDef {
1153        name: "memory_import",
1154        description: "Import memories from a previously exported JSON format.",
1155        schema: r#"{
1156            "type": "object",
1157            "properties": {
1158                "data": {"type": "object", "description": "The exported data object"},
1159                "skip_duplicates": {"type": "boolean", "default": true, "description": "Skip memories with matching content hash"}
1160            },
1161            "required": ["data"]
1162        }"#,
1163        annotations: ToolAnnotations::mutating(),
1164    },
1165    // Maintenance
1166    ToolDef {
1167        name: "memory_rebuild_embeddings",
1168        description: "Rebuild embeddings for all memories that are missing them. Useful after model changes or data recovery.",
1169        schema: r#"{
1170            "type": "object",
1171            "properties": {}
1172        }"#,
1173        annotations: ToolAnnotations::idempotent(),
1174    },
1175    ToolDef {
1176        name: "memory_rebuild_crossrefs",
1177        description: "Rebuild cross-reference links between memories. Re-analyzes all memories to find and create links.",
1178        schema: r#"{
1179            "type": "object",
1180            "properties": {}
1181        }"#,
1182        annotations: ToolAnnotations::idempotent(),
1183    },
1184    // Special Memory Types
1185    ToolDef {
1186        name: "memory_create_section",
1187        description: "Create a section memory for organizing content hierarchically. Sections can have parent sections for nested organization.",
1188        schema: r#"{
1189            "type": "object",
1190            "properties": {
1191                "title": {"type": "string", "description": "Section title"},
1192                "content": {"type": "string", "description": "Section description or content"},
1193                "parent_id": {"type": "integer", "description": "Optional parent section ID for nesting"},
1194                "level": {"type": "integer", "default": 1, "description": "Heading level (1-6)"},
1195                "workspace": {"type": "string", "description": "Workspace for the section"}
1196            },
1197            "required": ["title"]
1198        }"#,
1199        annotations: ToolAnnotations::mutating(),
1200    },
1201    ToolDef {
1202        name: "memory_checkpoint",
1203        description: "Create a checkpoint memory marking a significant point in a session. Useful for session resumption and context restoration.",
1204        schema: r#"{
1205            "type": "object",
1206            "properties": {
1207                "session_id": {"type": "string", "description": "Session identifier"},
1208                "summary": {"type": "string", "description": "Summary of session state at checkpoint"},
1209                "context": {"type": "object", "description": "Additional context data to preserve"},
1210                "workspace": {"type": "string", "description": "Workspace for the checkpoint"}
1211            },
1212            "required": ["session_id", "summary"]
1213        }"#,
1214        annotations: ToolAnnotations::mutating(),
1215    },
1216    // Phase 1: Cognitive Memory Types (ENG-33)
1217    ToolDef {
1218        name: "memory_create_episodic",
1219        description: "Create an episodic memory representing an event with temporal context. Use for tracking when things happened and their duration.",
1220        schema: r#"{
1221            "type": "object",
1222            "properties": {
1223                "content": {"type": "string", "description": "Description of the event"},
1224                "event_time": {"type": "string", "format": "date-time", "description": "ISO8601 timestamp when the event occurred"},
1225                "event_duration_seconds": {"type": "integer", "description": "Duration of the event in seconds"},
1226                "tags": {"type": "array", "items": {"type": "string"}, "description": "Tags for categorization"},
1227                "metadata": {"type": "object", "description": "Additional metadata"},
1228                "importance": {"type": "number", "minimum": 0, "maximum": 1, "description": "Importance score (0-1)"},
1229                "workspace": {"type": "string", "description": "Workspace (default: 'default')"}
1230            },
1231            "required": ["content", "event_time"]
1232        }"#,
1233        annotations: ToolAnnotations::mutating(),
1234    },
1235    ToolDef {
1236        name: "memory_create_procedural",
1237        description: "Create a procedural memory representing a learned pattern or workflow. Tracks success/failure to measure effectiveness.",
1238        schema: r#"{
1239            "type": "object",
1240            "properties": {
1241                "content": {"type": "string", "description": "Description of the procedure/workflow"},
1242                "trigger_pattern": {"type": "string", "description": "Pattern that triggers this procedure (e.g., 'When user asks about auth')"},
1243                "tags": {"type": "array", "items": {"type": "string"}, "description": "Tags for categorization"},
1244                "metadata": {"type": "object", "description": "Additional metadata"},
1245                "importance": {"type": "number", "minimum": 0, "maximum": 1, "description": "Importance score (0-1)"},
1246                "workspace": {"type": "string", "description": "Workspace (default: 'default')"}
1247            },
1248            "required": ["content", "trigger_pattern"]
1249        }"#,
1250        annotations: ToolAnnotations::mutating(),
1251    },
1252    ToolDef {
1253        name: "memory_get_timeline",
1254        description: "Query episodic memories by time range. Returns events ordered by event_time.",
1255        schema: r#"{
1256            "type": "object",
1257            "properties": {
1258                "start_time": {"type": "string", "format": "date-time", "description": "Start of time range (ISO8601)"},
1259                "end_time": {"type": "string", "format": "date-time", "description": "End of time range (ISO8601)"},
1260                "workspace": {"type": "string", "description": "Filter by workspace"},
1261                "tags": {"type": "array", "items": {"type": "string"}, "description": "Filter by tags"},
1262                "limit": {"type": "integer", "default": 50, "description": "Maximum results to return"}
1263            }
1264        }"#,
1265        annotations: ToolAnnotations::read_only(),
1266    },
1267    ToolDef {
1268        name: "memory_get_procedures",
1269        description: "List procedural memories (learned patterns/workflows). Optionally filter by trigger pattern.",
1270        schema: r#"{
1271            "type": "object",
1272            "properties": {
1273                "trigger_pattern": {"type": "string", "description": "Filter by trigger pattern (partial match)"},
1274                "workspace": {"type": "string", "description": "Filter by workspace"},
1275                "min_success_rate": {"type": "number", "minimum": 0, "maximum": 1, "description": "Minimum success rate (successes / (successes + failures))"},
1276                "limit": {"type": "integer", "default": 50, "description": "Maximum results to return"}
1277            }
1278        }"#,
1279        annotations: ToolAnnotations::read_only(),
1280    },
1281    ToolDef {
1282        name: "memory_record_procedure_outcome",
1283        description: "Record a success or failure for a procedural memory. Increments the corresponding counter.",
1284        schema: r#"{
1285            "type": "object",
1286            "properties": {
1287                "id": {"type": "integer", "description": "Procedural memory ID"},
1288                "success": {"type": "boolean", "description": "true = success, false = failure"}
1289            },
1290            "required": ["id", "success"]
1291        }"#,
1292        annotations: ToolAnnotations::mutating(),
1293    },
1294    ToolDef {
1295        name: "memory_boost",
1296        description: "Temporarily boost a memory's importance score. The boost can optionally decay over time.",
1297        schema: r#"{
1298            "type": "object",
1299            "properties": {
1300                "id": {"type": "integer", "description": "Memory ID to boost"},
1301                "boost_amount": {"type": "number", "default": 0.2, "description": "Amount to increase importance (0-1)"},
1302                "duration_seconds": {"type": "integer", "description": "Optional: duration before boost decays (omit for permanent boost)"}
1303            },
1304            "required": ["id"]
1305        }"#,
1306        annotations: ToolAnnotations::mutating(),
1307    },
1308    // Phase 2: Context Compression Engine
1309    ToolDef {
1310        name: "memory_summarize",
1311        description: "Create a summary of one or more memories. Returns a new Summary-type memory with summary_of_id set.",
1312        schema: r#"{
1313            "type": "object",
1314            "properties": {
1315                "memory_ids": {
1316                    "type": "array",
1317                    "items": {"type": "integer"},
1318                    "description": "IDs of memories to summarize"
1319                },
1320                "summary": {"type": "string", "description": "The summary text (provide this or let the system generate one)"},
1321                "max_length": {"type": "integer", "default": 500, "description": "Maximum length for auto-generated summary"},
1322                "workspace": {"type": "string", "description": "Workspace for the summary memory"},
1323                "tags": {"type": "array", "items": {"type": "string"}, "description": "Tags for the summary memory"}
1324            },
1325            "required": ["memory_ids"]
1326        }"#,
1327        annotations: ToolAnnotations::mutating(),
1328    },
1329    ToolDef {
1330        name: "memory_get_full",
1331        description: "Get the full/original content of a memory. If the memory is a Summary, returns the original content from summary_of_id.",
1332        schema: r#"{
1333            "type": "object",
1334            "properties": {
1335                "id": {"type": "integer", "description": "Memory ID to get full content for"}
1336            },
1337            "required": ["id"]
1338        }"#,
1339        annotations: ToolAnnotations::read_only(),
1340    },
1341    ToolDef {
1342        name: "context_budget_check",
1343        description: "Check token usage of memories against a budget. Returns token counts and suggestions if over budget.",
1344        schema: r#"{
1345            "type": "object",
1346            "properties": {
1347                "memory_ids": {
1348                    "type": "array",
1349                    "items": {"type": "integer"},
1350                    "description": "IDs of memories to check"
1351                },
1352                "model": {
1353                    "type": "string",
1354                    "description": "Model name for tokenization (gpt-4, gpt-4o, gpt-4o-mini, claude-3-opus, etc.)"
1355                },
1356                "encoding": {
1357                    "type": "string",
1358                    "description": "Override encoding (cl100k_base, o200k_base). Optional if model is known."
1359                },
1360                "budget": {"type": "integer", "description": "Token budget to check against"}
1361            },
1362            "required": ["memory_ids", "model", "budget"]
1363        }"#,
1364        annotations: ToolAnnotations::read_only(),
1365    },
1366    ToolDef {
1367        name: "memory_archive_old",
1368        description: "Archive old, low-importance memories by creating summaries. Moves originals to archived state.",
1369        schema: r#"{
1370            "type": "object",
1371            "properties": {
1372                "max_age_days": {"type": "integer", "default": 90, "description": "Archive memories older than this many days"},
1373                "max_importance": {"type": "number", "default": 0.5, "description": "Only archive memories with importance below this"},
1374                "min_access_count": {"type": "integer", "default": 5, "description": "Skip memories accessed more than this many times"},
1375                "workspace": {"type": "string", "description": "Limit to specific workspace"},
1376                "dry_run": {"type": "boolean", "default": true, "description": "If true, only report what would be archived"}
1377            }
1378        }"#,
1379        annotations: ToolAnnotations::destructive(),
1380    },
1381    // Phase 3: Langfuse Integration (ENG-35) - feature-gated
1382    #[cfg(feature = "langfuse")]
1383    ToolDef {
1384        name: "langfuse_connect",
1385        description: "Configure Langfuse connection for observability integration. Stores config in metadata.",
1386        schema: r#"{
1387            "type": "object",
1388            "properties": {
1389                "public_key": {"type": "string", "description": "Langfuse public key (or use LANGFUSE_PUBLIC_KEY env var)"},
1390                "secret_key": {"type": "string", "description": "Langfuse secret key (or use LANGFUSE_SECRET_KEY env var)"},
1391                "base_url": {"type": "string", "default": "https://cloud.langfuse.com", "description": "Langfuse API base URL"}
1392            }
1393        }"#,
1394        annotations: ToolAnnotations::mutating(),
1395    },
1396    #[cfg(feature = "langfuse")]
1397    ToolDef {
1398        name: "langfuse_sync",
1399        description: "Start background sync from Langfuse traces to memories. Returns task_id for status checking.",
1400        schema: r#"{
1401            "type": "object",
1402            "properties": {
1403                "since": {"type": "string", "format": "date-time", "description": "Sync traces since this timestamp (default: 24h ago)"},
1404                "limit": {"type": "integer", "default": 100, "description": "Maximum traces to sync"},
1405                "workspace": {"type": "string", "description": "Workspace to create memories in"},
1406                "dry_run": {"type": "boolean", "default": false, "description": "Preview what would be synced without creating memories"}
1407            }
1408        }"#,
1409        annotations: ToolAnnotations::mutating(),
1410    },
1411    #[cfg(feature = "langfuse")]
1412    ToolDef {
1413        name: "langfuse_sync_status",
1414        description: "Check the status of a Langfuse sync task.",
1415        schema: r#"{
1416            "type": "object",
1417            "properties": {
1418                "task_id": {"type": "string", "description": "Task ID returned from langfuse_sync"}
1419            },
1420            "required": ["task_id"]
1421        }"#,
1422        annotations: ToolAnnotations::read_only(),
1423    },
1424    #[cfg(feature = "langfuse")]
1425    ToolDef {
1426        name: "langfuse_extract_patterns",
1427        description: "Extract patterns from Langfuse traces without saving. Preview mode for pattern discovery.",
1428        schema: r#"{
1429            "type": "object",
1430            "properties": {
1431                "since": {"type": "string", "format": "date-time", "description": "Analyze traces since this timestamp"},
1432                "limit": {"type": "integer", "default": 50, "description": "Maximum traces to analyze"},
1433                "min_confidence": {"type": "number", "default": 0.7, "description": "Minimum confidence for patterns"}
1434            }
1435        }"#,
1436        annotations: ToolAnnotations::read_only(),
1437    },
1438    #[cfg(feature = "langfuse")]
1439    ToolDef {
1440        name: "memory_from_trace",
1441        description: "Create a memory from a specific Langfuse trace ID.",
1442        schema: r#"{
1443            "type": "object",
1444            "properties": {
1445                "trace_id": {"type": "string", "description": "Langfuse trace ID"},
1446                "memory_type": {"type": "string", "enum": ["note", "episodic", "procedural", "learning"], "default": "episodic", "description": "Type of memory to create"},
1447                "workspace": {"type": "string", "description": "Workspace for the memory"},
1448                "tags": {"type": "array", "items": {"type": "string"}, "description": "Additional tags"}
1449            },
1450            "required": ["trace_id"]
1451        }"#,
1452        annotations: ToolAnnotations::mutating(),
1453    },
1454    // Phase 4: Search Result Caching (ENG-36)
1455    ToolDef {
1456        name: "search_cache_feedback",
1457        description: "Report feedback on search results quality. Helps tune the adaptive cache threshold.",
1458        schema: r#"{
1459            "type": "object",
1460            "properties": {
1461                "query": {"type": "string", "description": "The search query"},
1462                "positive": {"type": "boolean", "description": "True if results were helpful, false otherwise"},
1463                "workspace": {"type": "string", "description": "Workspace filter used (if any)"}
1464            },
1465            "required": ["query", "positive"]
1466        }"#,
1467        annotations: ToolAnnotations::mutating(),
1468    },
1469    ToolDef {
1470        name: "search_cache_stats",
1471        description: "Get search result cache statistics including hit rate, entry count, and current threshold.",
1472        schema: r#"{
1473            "type": "object",
1474            "properties": {}
1475        }"#,
1476        annotations: ToolAnnotations::read_only(),
1477    },
1478    ToolDef {
1479        name: "search_cache_clear",
1480        description: "Clear the search result cache. Useful after bulk operations.",
1481        schema: r#"{
1482            "type": "object",
1483            "properties": {
1484                "workspace": {"type": "string", "description": "Only clear cache for this workspace (optional)"}
1485            }
1486        }"#,
1487        annotations: ToolAnnotations::destructive(),
1488    },
1489    // Phase 5: Memory Lifecycle Management (ENG-37)
1490    ToolDef {
1491        name: "lifecycle_status",
1492        description: "Get lifecycle statistics (active/stale/archived counts by workspace).",
1493        schema: r#"{
1494            "type": "object",
1495            "properties": {
1496                "workspace": {"type": "string", "description": "Filter by workspace (optional)"}
1497            }
1498        }"#,
1499        annotations: ToolAnnotations::read_only(),
1500    },
1501    ToolDef {
1502        name: "lifecycle_run",
1503        description: "Manually trigger a lifecycle cycle (mark stale, archive old). Dry run by default.",
1504        schema: r#"{
1505            "type": "object",
1506            "properties": {
1507                "dry_run": {"type": "boolean", "default": true, "description": "Preview changes without applying"},
1508                "workspace": {"type": "string", "description": "Limit to specific workspace"},
1509                "stale_days": {"type": "integer", "default": 30, "description": "Mark memories older than this as stale"},
1510                "archive_days": {"type": "integer", "default": 90, "description": "Archive memories older than this"},
1511                "min_importance": {"type": "number", "default": 0.5, "description": "Only process memories below this importance"}
1512            }
1513        }"#,
1514        annotations: ToolAnnotations::idempotent(),
1515    },
1516    ToolDef {
1517        name: "memory_set_lifecycle",
1518        description: "Manually set the lifecycle state of a memory.",
1519        schema: r#"{
1520            "type": "object",
1521            "properties": {
1522                "id": {"type": "integer", "description": "Memory ID"},
1523                "state": {"type": "string", "enum": ["active", "stale", "archived"], "description": "New lifecycle state"}
1524            },
1525            "required": ["id", "state"]
1526        }"#,
1527        annotations: ToolAnnotations::mutating(),
1528    },
1529    ToolDef {
1530        name: "lifecycle_config",
1531        description: "Get or set lifecycle configuration (intervals, thresholds).",
1532        schema: r#"{
1533            "type": "object",
1534            "properties": {
1535                "stale_days": {"type": "integer", "description": "Days before marking as stale"},
1536                "archive_days": {"type": "integer", "description": "Days before auto-archiving"},
1537                "min_importance": {"type": "number", "description": "Importance threshold for lifecycle"},
1538                "min_access_count": {"type": "integer", "description": "Access count threshold"}
1539            }
1540        }"#,
1541        annotations: ToolAnnotations::read_only(),
1542    },
1543    // Retention Policies
1544    ToolDef {
1545        name: "retention_policy_set",
1546        description: "Set a retention policy for a workspace. Controls auto-compression, max memory count, and auto-deletion.",
1547        schema: r#"{
1548            "type": "object",
1549            "properties": {
1550                "workspace": {"type": "string", "description": "Workspace name"},
1551                "max_age_days": {"type": "integer", "description": "Hard age limit — auto-delete after this many days"},
1552                "max_memories": {"type": "integer", "description": "Maximum active memories in this workspace"},
1553                "compress_after_days": {"type": "integer", "description": "Auto-compress memories older than this"},
1554                "compress_max_importance": {"type": "number", "description": "Only compress memories with importance <= this (default 0.3)"},
1555                "compress_min_access": {"type": "integer", "description": "Skip compression if access_count >= this (default 3)"},
1556                "auto_delete_after_days": {"type": "integer", "description": "Auto-delete archived memories older than this"},
1557                "exclude_types": {"type": "array", "items": {"type": "string"}, "description": "Memory types exempt from policy (e.g. [\"decision\", \"checkpoint\"])"}
1558            },
1559            "required": ["workspace"]
1560        }"#,
1561        annotations: ToolAnnotations::mutating(),
1562    },
1563    ToolDef {
1564        name: "retention_policy_get",
1565        description: "Get the retention policy for a workspace.",
1566        schema: r#"{
1567            "type": "object",
1568            "properties": {
1569                "workspace": {"type": "string", "description": "Workspace name"}
1570            },
1571            "required": ["workspace"]
1572        }"#,
1573        annotations: ToolAnnotations::read_only(),
1574    },
1575    ToolDef {
1576        name: "retention_policy_list",
1577        description: "List all retention policies across all workspaces.",
1578        schema: r#"{
1579            "type": "object",
1580            "properties": {}
1581        }"#,
1582        annotations: ToolAnnotations::read_only(),
1583    },
1584    ToolDef {
1585        name: "retention_policy_delete",
1586        description: "Delete a retention policy for a workspace.",
1587        schema: r#"{
1588            "type": "object",
1589            "properties": {
1590                "workspace": {"type": "string", "description": "Workspace name"}
1591            },
1592            "required": ["workspace"]
1593        }"#,
1594        annotations: ToolAnnotations::destructive(),
1595    },
1596    ToolDef {
1597        name: "retention_policy_apply",
1598        description: "Apply all retention policies now. Compresses, caps, and deletes per workspace rules.",
1599        schema: r#"{
1600            "type": "object",
1601            "properties": {
1602                "dry_run": {"type": "boolean", "default": false, "description": "Preview what would happen without making changes"}
1603            }
1604        }"#,
1605        annotations: ToolAnnotations::idempotent(),
1606    },
1607    // Event System
1608    ToolDef {
1609        name: "memory_events_poll",
1610        description: "Poll for memory events (create, update, delete, etc.) since a given point. Useful for syncing and monitoring.",
1611        schema: r#"{
1612            "type": "object",
1613            "properties": {
1614                "since_id": {"type": "integer", "description": "Return events after this event ID"},
1615                "since_time": {"type": "string", "format": "date-time", "description": "Return events after this timestamp (RFC3339)"},
1616                "agent_id": {"type": "string", "description": "Filter events for specific agent"},
1617                "limit": {"type": "integer", "default": 100, "description": "Maximum events to return"}
1618            }
1619        }"#,
1620        annotations: ToolAnnotations::read_only(),
1621    },
1622    ToolDef {
1623        name: "memory_events_clear",
1624        description: "Clear old events from the event log. Helps manage storage for long-running systems.",
1625        schema: r#"{
1626            "type": "object",
1627            "properties": {
1628                "before_id": {"type": "integer", "description": "Delete events before this ID"},
1629                "before_time": {"type": "string", "format": "date-time", "description": "Delete events before this timestamp"},
1630                "keep_recent": {"type": "integer", "description": "Keep only the N most recent events"}
1631            }
1632        }"#,
1633        annotations: ToolAnnotations::destructive(),
1634    },
1635    // Advanced Sync
1636    ToolDef {
1637        name: "sync_version",
1638        description: "Get the current sync version and metadata. Used to check if local data is up-to-date.",
1639        schema: r#"{
1640            "type": "object",
1641            "properties": {}
1642        }"#,
1643        annotations: ToolAnnotations::read_only(),
1644    },
1645    ToolDef {
1646        name: "sync_delta",
1647        description: "Get changes (delta) since a specific version. Returns created, updated, and deleted memories.",
1648        schema: r#"{
1649            "type": "object",
1650            "properties": {
1651                "since_version": {"type": "integer", "description": "Version to get changes from"}
1652            },
1653            "required": ["since_version"]
1654        }"#,
1655        annotations: ToolAnnotations::read_only(),
1656    },
1657    ToolDef {
1658        name: "sync_state",
1659        description: "Get or update sync state for a specific agent. Tracks what each agent has synced.",
1660        schema: r#"{
1661            "type": "object",
1662            "properties": {
1663                "agent_id": {"type": "string", "description": "Agent identifier"},
1664                "update_version": {"type": "integer", "description": "If provided, updates the agent's last synced version"}
1665            },
1666            "required": ["agent_id"]
1667        }"#,
1668        annotations: ToolAnnotations::read_only(),
1669    },
1670    ToolDef {
1671        name: "sync_cleanup",
1672        description: "Clean up old sync data (events, etc.) older than specified days.",
1673        schema: r#"{
1674            "type": "object",
1675            "properties": {
1676                "older_than_days": {"type": "integer", "default": 30, "description": "Delete sync data older than this many days"}
1677            }
1678        }"#,
1679        annotations: ToolAnnotations::destructive(),
1680    },
1681    // Multi-Agent Sharing
1682    ToolDef {
1683        name: "memory_share",
1684        description: "Share a memory with another agent. The target agent can poll for shared memories.",
1685        schema: r#"{
1686            "type": "object",
1687            "properties": {
1688                "memory_id": {"type": "integer", "description": "ID of memory to share"},
1689                "from_agent": {"type": "string", "description": "Sender agent identifier"},
1690                "to_agent": {"type": "string", "description": "Recipient agent identifier"},
1691                "message": {"type": "string", "description": "Optional message to include with share"}
1692            },
1693            "required": ["memory_id", "from_agent", "to_agent"]
1694        }"#,
1695        annotations: ToolAnnotations::mutating(),
1696    },
1697    ToolDef {
1698        name: "memory_shared_poll",
1699        description: "Poll for memories shared with this agent.",
1700        schema: r#"{
1701            "type": "object",
1702            "properties": {
1703                "agent_id": {"type": "string", "description": "Agent identifier to check shares for"},
1704                "include_acknowledged": {"type": "boolean", "default": false, "description": "Include already acknowledged shares"}
1705            },
1706            "required": ["agent_id"]
1707        }"#,
1708        annotations: ToolAnnotations::read_only(),
1709    },
1710    ToolDef {
1711        name: "memory_share_ack",
1712        description: "Acknowledge receipt of a shared memory.",
1713        schema: r#"{
1714            "type": "object",
1715            "properties": {
1716                "share_id": {"type": "integer", "description": "Share ID to acknowledge"},
1717                "agent_id": {"type": "string", "description": "Agent acknowledging the share"}
1718            },
1719            "required": ["share_id", "agent_id"]
1720        }"#,
1721        annotations: ToolAnnotations::mutating(),
1722    },
1723    // Scope-based access grants for multi-agent memory sharing
1724    ToolDef {
1725        name: "memory_grant_access",
1726        description: "Grant an agent access to a scope path. Supports read, write, and admin permissions. Access also applies to all descendant scopes.",
1727        schema: r#"{
1728            "type": "object",
1729            "properties": {
1730                "agent_id": {"type": "string", "description": "Agent ID to grant access to"},
1731                "scope_path": {"type": "string", "description": "Scope path to grant access to (e.g. 'global/org:acme')"},
1732                "permissions": {"type": "string", "enum": ["read", "write", "admin"], "default": "read", "description": "Permission level"},
1733                "granted_by": {"type": "string", "description": "Optional: ID of the granting agent"}
1734            },
1735            "required": ["agent_id", "scope_path"]
1736        }"#,
1737        annotations: ToolAnnotations::mutating(),
1738    },
1739    ToolDef {
1740        name: "memory_revoke_access",
1741        description: "Revoke an agent's access to a specific scope path.",
1742        schema: r#"{
1743            "type": "object",
1744            "properties": {
1745                "agent_id": {"type": "string", "description": "Agent ID to revoke access from"},
1746                "scope_path": {"type": "string", "description": "Scope path to revoke access from"}
1747            },
1748            "required": ["agent_id", "scope_path"]
1749        }"#,
1750        annotations: ToolAnnotations::destructive(),
1751    },
1752    ToolDef {
1753        name: "memory_list_grants",
1754        description: "List all scope access grants for a given agent.",
1755        schema: r#"{
1756            "type": "object",
1757            "properties": {
1758                "agent_id": {"type": "string", "description": "Agent ID to list grants for"}
1759            },
1760            "required": ["agent_id"]
1761        }"#,
1762        annotations: ToolAnnotations::read_only(),
1763    },
1764    ToolDef {
1765        name: "memory_check_access",
1766        description: "Check whether an agent has a required permission level on a scope path (including ancestor grants).",
1767        schema: r#"{
1768            "type": "object",
1769            "properties": {
1770                "agent_id": {"type": "string", "description": "Agent ID to check"},
1771                "scope_path": {"type": "string", "description": "Scope path to check access for"},
1772                "permissions": {"type": "string", "enum": ["read", "write", "admin"], "default": "read", "description": "Required permission level"}
1773            },
1774            "required": ["agent_id", "scope_path"]
1775        }"#,
1776        annotations: ToolAnnotations::read_only(),
1777    },
1778    // Search Variants
1779    ToolDef {
1780        name: "memory_search_by_identity",
1781        description: "Search memories by identity (person, entity, or alias). Finds all mentions of a specific identity across memories.",
1782        schema: r#"{
1783            "type": "object",
1784            "properties": {
1785                "identity": {"type": "string", "description": "Identity name or alias to search for"},
1786                "workspace": {"type": "string", "description": "Optional: limit search to specific workspace"},
1787                "limit": {"type": "integer", "default": 50, "description": "Maximum results to return"}
1788            },
1789            "required": ["identity"]
1790        }"#,
1791        annotations: ToolAnnotations::mutating(),
1792    },
1793    ToolDef {
1794        name: "memory_session_search",
1795        description: "Search within session transcript chunks. Useful for finding content from past conversations.",
1796        schema: r#"{
1797            "type": "object",
1798            "properties": {
1799                "query": {"type": "string", "description": "Search query"},
1800                "session_id": {"type": "string", "description": "Optional: limit to specific session"},
1801                "workspace": {"type": "string", "description": "Optional: limit to specific workspace"},
1802                "limit": {"type": "integer", "default": 20, "description": "Maximum results to return"}
1803            },
1804            "required": ["query"]
1805        }"#,
1806        annotations: ToolAnnotations::mutating(),
1807    },
1808    // Image Handling
1809    ToolDef {
1810        name: "memory_upload_image",
1811        description: "Upload an image file and attach it to a memory. The image will be stored locally and linked to the memory's metadata.",
1812        schema: r#"{
1813            "type": "object",
1814            "properties": {
1815                "memory_id": {"type": "integer", "description": "ID of the memory to attach the image to"},
1816                "file_path": {"type": "string", "description": "Path to the image file to upload"},
1817                "image_index": {"type": "integer", "default": 0, "description": "Index for ordering multiple images (0-based)"},
1818                "caption": {"type": "string", "description": "Optional caption for the image"}
1819            },
1820            "required": ["memory_id", "file_path"]
1821        }"#,
1822        annotations: ToolAnnotations::mutating(),
1823    },
1824    ToolDef {
1825        name: "memory_migrate_images",
1826        description: "Migrate existing base64-encoded images in memories to file storage. Scans all memories and uploads any embedded data URIs to storage, replacing them with file references.",
1827        schema: r#"{
1828            "type": "object",
1829            "properties": {
1830                "dry_run": {"type": "boolean", "default": false, "description": "If true, only report what would be migrated without making changes"}
1831            }
1832        }"#,
1833        annotations: ToolAnnotations::idempotent(),
1834    },
1835    // Auto-Tagging
1836    ToolDef {
1837        name: "memory_suggest_tags",
1838        description: "Suggest tags for a memory based on AI content analysis. Uses pattern matching, keyword extraction, and structure detection to suggest relevant tags with confidence scores.",
1839        schema: r#"{
1840            "type": "object",
1841            "properties": {
1842                "id": {"type": "integer", "description": "Memory ID to analyze (alternative to content)"},
1843                "memory_id": {"type": "integer", "description": "Memory ID to analyze (alias for id)"},
1844                "content": {"type": "string", "description": "Content to analyze (alternative to id/memory_id)"},
1845                "type": {"type": "string", "enum": ["note", "todo", "issue", "decision", "preference", "learning", "context", "credential"], "description": "Memory type (used when providing content directly)"},
1846                "existing_tags": {"type": "array", "items": {"type": "string"}, "description": "Tags already on the memory (excluded from suggestions)"},
1847                "min_confidence": {"type": "number", "minimum": 0, "maximum": 1, "default": 0.5, "description": "Minimum confidence threshold for suggestions"},
1848                "max_tags": {"type": "integer", "default": 5, "description": "Maximum number of tags to suggest"},
1849                "enable_patterns": {"type": "boolean", "default": true, "description": "Use pattern-based tagging"},
1850                "enable_keywords": {"type": "boolean", "default": true, "description": "Use keyword-based tagging"},
1851                "enable_entities": {"type": "boolean", "default": true, "description": "Use entity-based tagging"},
1852                "enable_type_tags": {"type": "boolean", "default": true, "description": "Add tags based on memory type"},
1853                "keyword_mappings": {"type": "object", "description": "Custom keyword-to-tag mappings (e.g., {\"ibvi\": \"project/ibvi\"})"}
1854            }
1855        }"#,
1856        annotations: ToolAnnotations::read_only(),
1857    },
1858    ToolDef {
1859        name: "memory_auto_tag",
1860        description: "Automatically suggest and optionally apply tags to a memory. Analyzes content using AI heuristics and can merge suggested tags with existing ones.",
1861        schema: r#"{
1862            "type": "object",
1863            "properties": {
1864                "id": {"type": "integer", "description": "Memory ID to auto-tag"},
1865                "memory_id": {"type": "integer", "description": "Memory ID (alias for id)"},
1866                "apply": {"type": "boolean", "default": false, "description": "If true, apply the suggested tags to the memory. If false, only return suggestions."},
1867                "merge": {"type": "boolean", "default": true, "description": "If true and apply=true, merge with existing tags. If false, replace existing tags."},
1868                "min_confidence": {"type": "number", "minimum": 0, "maximum": 1, "default": 0.5, "description": "Minimum confidence threshold"},
1869                "max_tags": {"type": "integer", "default": 5, "description": "Maximum tags to suggest/apply"},
1870                "keyword_mappings": {"type": "object", "description": "Custom keyword-to-tag mappings"}
1871            },
1872            "required": ["id"]
1873        }"#,
1874        annotations: ToolAnnotations::mutating(),
1875    },
1876    // Phase 8: Salience & Sessions (ENG-66 to ENG-77)
1877    ToolDef {
1878        name: "salience_get",
1879        description: "Get the salience score for a memory. Returns recency, frequency, importance, and feedback components with the combined score and lifecycle state.",
1880        schema: r#"{
1881            "type": "object",
1882            "properties": {
1883                "id": {"type": "integer", "description": "Memory ID to get salience for"},
1884                "feedback_signal": {"type": "number", "minimum": -1, "maximum": 1, "default": 0, "description": "Optional feedback signal (-1 to 1) to include in calculation"}
1885            },
1886            "required": ["id"]
1887        }"#,
1888        annotations: ToolAnnotations::read_only(),
1889    },
1890    ToolDef {
1891        name: "salience_set_importance",
1892        description: "Set the importance score for a memory. This is the static importance component of salience.",
1893        schema: r#"{
1894            "type": "object",
1895            "properties": {
1896                "id": {"type": "integer", "description": "Memory ID"},
1897                "importance": {"type": "number", "minimum": 0, "maximum": 1, "description": "Importance score (0-1)"}
1898            },
1899            "required": ["id", "importance"]
1900        }"#,
1901        annotations: ToolAnnotations::mutating(),
1902    },
1903    ToolDef {
1904        name: "salience_boost",
1905        description: "Boost a memory's salience score temporarily or permanently. Useful for marking memories as contextually relevant.",
1906        schema: r#"{
1907            "type": "object",
1908            "properties": {
1909                "id": {"type": "integer", "description": "Memory ID to boost"},
1910                "boost_amount": {"type": "number", "minimum": 0, "maximum": 1, "default": 0.2, "description": "Amount to boost (0-1)"},
1911                "reason": {"type": "string", "description": "Optional reason for boosting"}
1912            },
1913            "required": ["id"]
1914        }"#,
1915        annotations: ToolAnnotations::mutating(),
1916    },
1917    ToolDef {
1918        name: "salience_demote",
1919        description: "Demote a memory's salience score. Useful for marking memories as less relevant.",
1920        schema: r#"{
1921            "type": "object",
1922            "properties": {
1923                "id": {"type": "integer", "description": "Memory ID to demote"},
1924                "demote_amount": {"type": "number", "minimum": 0, "maximum": 1, "default": 0.2, "description": "Amount to demote (0-1)"},
1925                "reason": {"type": "string", "description": "Optional reason for demoting"}
1926            },
1927            "required": ["id"]
1928        }"#,
1929        annotations: ToolAnnotations::mutating(),
1930    },
1931    ToolDef {
1932        name: "salience_decay_run",
1933        description: "Run temporal decay on all memories. Updates lifecycle states (Active → Stale → Archived) based on salience scores.",
1934        schema: r#"{
1935            "type": "object",
1936            "properties": {
1937                "dry_run": {"type": "boolean", "default": false, "description": "If true, compute changes without persisting updates"},
1938                "record_history": {"type": "boolean", "default": true, "description": "Record salience history entries while updating"},
1939                "workspace": {"type": "string", "description": "Limit to specific workspace"},
1940                "stale_threshold_days": {"type": "integer", "minimum": 1, "description": "Days of inactivity before marking stale"},
1941                "archive_threshold_days": {"type": "integer", "minimum": 1, "description": "Days of inactivity before suggesting archive"}
1942            }
1943        }"#,
1944        annotations: ToolAnnotations::destructive(),
1945    },
1946    ToolDef {
1947        name: "salience_stats",
1948        description: "Get salience statistics across all memories. Returns distribution, percentiles, and state counts.",
1949        schema: r#"{
1950            "type": "object",
1951            "properties": {
1952                "workspace": {"type": "string", "description": "Limit to specific workspace"}
1953            }
1954        }"#,
1955        annotations: ToolAnnotations::read_only(),
1956    },
1957    ToolDef {
1958        name: "salience_history",
1959        description: "Get salience score history for a memory. Shows how salience has changed over time.",
1960        schema: r#"{
1961            "type": "object",
1962            "properties": {
1963                "id": {"type": "integer", "description": "Memory ID"},
1964                "limit": {"type": "integer", "default": 50, "description": "Maximum history entries to return"}
1965            },
1966            "required": ["id"]
1967        }"#,
1968        annotations: ToolAnnotations::read_only(),
1969    },
1970    ToolDef {
1971        name: "salience_top",
1972        description: "Get top memories by salience score. Useful for context injection.",
1973        schema: r#"{
1974            "type": "object",
1975            "properties": {
1976                "limit": {"type": "integer", "default": 20, "description": "Maximum memories to return"},
1977                "workspace": {"type": "string", "description": "Limit to specific workspace"},
1978                "min_score": {"type": "number", "minimum": 0, "maximum": 1, "description": "Minimum salience score"},
1979                "memory_type": {"type": "string", "description": "Filter by memory type"}
1980            }
1981        }"#,
1982        annotations: ToolAnnotations::read_only(),
1983    },
1984    // Session Context Tools (ENG-70, ENG-71)
1985    ToolDef {
1986        name: "session_context_create",
1987        description: "Create a new session context for tracking related memories during a conversation or task.",
1988        schema: r#"{
1989            "type": "object",
1990            "properties": {
1991                "name": {"type": "string", "description": "Session name"},
1992                "description": {"type": "string", "description": "Session description"},
1993                "workspace": {"type": "string", "description": "Workspace for the session"},
1994                "metadata": {"type": "object", "description": "Additional session metadata"}
1995            },
1996            "required": ["name"]
1997        }"#,
1998        annotations: ToolAnnotations::mutating(),
1999    },
2000    ToolDef {
2001        name: "session_context_add_memory",
2002        description: "Add a memory to a session context with relevance score and role.",
2003        schema: r#"{
2004            "type": "object",
2005            "properties": {
2006                "session_id": {"type": "string", "description": "Session ID"},
2007                "memory_id": {"type": "integer", "description": "Memory ID to add"},
2008                "relevance_score": {"type": "number", "minimum": 0, "maximum": 1, "default": 1.0, "description": "How relevant this memory is to the session"},
2009                "context_role": {"type": "string", "enum": ["referenced", "created", "updated", "pinned"], "default": "referenced", "description": "Role of the memory in the session"}
2010            },
2011            "required": ["session_id", "memory_id"]
2012        }"#,
2013        annotations: ToolAnnotations::mutating(),
2014    },
2015    ToolDef {
2016        name: "session_context_remove_memory",
2017        description: "Remove a memory from a session context.",
2018        schema: r#"{
2019            "type": "object",
2020            "properties": {
2021                "session_id": {"type": "string", "description": "Session ID"},
2022                "memory_id": {"type": "integer", "description": "Memory ID to remove"}
2023            },
2024            "required": ["session_id", "memory_id"]
2025        }"#,
2026        annotations: ToolAnnotations::mutating(),
2027    },
2028    ToolDef {
2029        name: "session_context_get",
2030        description: "Get a session context with its linked memories.",
2031        schema: r#"{
2032            "type": "object",
2033            "properties": {
2034                "session_id": {"type": "string", "description": "Session ID"}
2035            },
2036            "required": ["session_id"]
2037        }"#,
2038        annotations: ToolAnnotations::read_only(),
2039    },
2040    ToolDef {
2041        name: "session_context_list",
2042        description: "List all session contexts with optional filtering.",
2043        schema: r#"{
2044            "type": "object",
2045            "properties": {
2046                "workspace": {"type": "string", "description": "Filter by workspace"},
2047                "active_only": {"type": "boolean", "default": false, "description": "Only return active sessions"},
2048                "limit": {"type": "integer", "default": 50, "description": "Maximum sessions to return"},
2049                "offset": {"type": "integer", "default": 0, "description": "Offset for pagination"}
2050            }
2051        }"#,
2052        annotations: ToolAnnotations::read_only(),
2053    },
2054    ToolDef {
2055        name: "session_context_search",
2056        description: "Search memories within a specific session context.",
2057        schema: r#"{
2058            "type": "object",
2059            "properties": {
2060                "session_id": {"type": "string", "description": "Session ID to search within"},
2061                "query": {"type": "string", "description": "Search query"},
2062                "limit": {"type": "integer", "default": 20, "description": "Maximum results"}
2063            },
2064            "required": ["session_id", "query"]
2065        }"#,
2066        annotations: ToolAnnotations::read_only(),
2067    },
2068    ToolDef {
2069        name: "session_context_update_summary",
2070        description: "Update the summary of a session context.",
2071        schema: r#"{
2072            "type": "object",
2073            "properties": {
2074                "session_id": {"type": "string", "description": "Session ID"},
2075                "summary": {"type": "string", "description": "New session summary"}
2076            },
2077            "required": ["session_id", "summary"]
2078        }"#,
2079        annotations: ToolAnnotations::mutating(),
2080    },
2081    ToolDef {
2082        name: "session_context_end",
2083        description: "End a session context, marking it as inactive.",
2084        schema: r#"{
2085            "type": "object",
2086            "properties": {
2087                "session_id": {"type": "string", "description": "Session ID to end"},
2088                "summary": {"type": "string", "description": "Optional final summary"}
2089            },
2090            "required": ["session_id"]
2091        }"#,
2092        annotations: ToolAnnotations::mutating(),
2093    },
2094    ToolDef {
2095        name: "session_context_export",
2096        description: "Export a session context with all its memories for archival or sharing.",
2097        schema: r#"{
2098            "type": "object",
2099            "properties": {
2100                "session_id": {"type": "string", "description": "Session ID to export"},
2101                "include_content": {"type": "boolean", "default": true, "description": "Include full memory content"},
2102                "format": {"type": "string", "enum": ["json", "markdown"], "default": "json", "description": "Export format"}
2103            },
2104            "required": ["session_id"]
2105        }"#,
2106        annotations: ToolAnnotations::read_only(),
2107    },
2108    // Phase 9: Context Quality (ENG-48 to ENG-66)
2109    ToolDef {
2110        name: "quality_score",
2111        description: "Get the quality score for a memory with detailed breakdown of clarity, completeness, freshness, consistency, and source trust components.",
2112        schema: r#"{
2113            "type": "object",
2114            "properties": {
2115                "id": {"type": "integer", "description": "Memory ID to score"}
2116            },
2117            "required": ["id"]
2118        }"#,
2119        annotations: ToolAnnotations::read_only(),
2120    },
2121    ToolDef {
2122        name: "quality_report",
2123        description: "Generate a comprehensive quality report for a workspace. Includes quality distribution, top issues, conflict and duplicate counts.",
2124        schema: r#"{
2125            "type": "object",
2126            "properties": {
2127                "workspace": {"type": "string", "description": "Workspace to analyze (default: 'default')"}
2128            }
2129        }"#,
2130        annotations: ToolAnnotations::read_only(),
2131    },
2132    ToolDef {
2133        name: "quality_find_duplicates",
2134        description: "Find near-duplicate memories using text similarity. Returns pairs of similar memories above the threshold.",
2135        schema: r#"{
2136            "type": "object",
2137            "properties": {
2138                "threshold": {"type": "number", "minimum": 0, "maximum": 1, "default": 0.85, "description": "Similarity threshold (0-1)"},
2139                "limit": {"type": "integer", "default": 100, "description": "Maximum memories to compare"}
2140            }
2141        }"#,
2142        annotations: ToolAnnotations::read_only(),
2143    },
2144    ToolDef {
2145        name: "quality_get_duplicates",
2146        description: "Get pending duplicate candidates that need review.",
2147        schema: r#"{
2148            "type": "object",
2149            "properties": {
2150                "limit": {"type": "integer", "default": 50, "description": "Maximum duplicates to return"}
2151            }
2152        }"#,
2153        annotations: ToolAnnotations::read_only(),
2154    },
2155    ToolDef {
2156        name: "quality_find_conflicts",
2157        description: "Detect conflicts for a memory against existing memories. Finds contradictions, staleness, and semantic overlaps.",
2158        schema: r#"{
2159            "type": "object",
2160            "properties": {
2161                "id": {"type": "integer", "description": "Memory ID to check for conflicts"}
2162            },
2163            "required": ["id"]
2164        }"#,
2165        annotations: ToolAnnotations::read_only(),
2166    },
2167    ToolDef {
2168        name: "quality_get_conflicts",
2169        description: "Get unresolved conflicts that need attention.",
2170        schema: r#"{
2171            "type": "object",
2172            "properties": {
2173                "limit": {"type": "integer", "default": 50, "description": "Maximum conflicts to return"}
2174            }
2175        }"#,
2176        annotations: ToolAnnotations::read_only(),
2177    },
2178    ToolDef {
2179        name: "quality_resolve_conflict",
2180        description: "Resolve a conflict between memories. Options: keep_a, keep_b, merge, keep_both, delete_both, false_positive.",
2181        schema: r#"{
2182            "type": "object",
2183            "properties": {
2184                "conflict_id": {"type": "integer", "description": "Conflict ID to resolve"},
2185                "resolution": {"type": "string", "enum": ["keep_a", "keep_b", "merge", "keep_both", "delete_both", "false_positive"], "description": "How to resolve the conflict"},
2186                "notes": {"type": "string", "description": "Optional notes about the resolution"}
2187            },
2188            "required": ["conflict_id", "resolution"]
2189        }"#,
2190        annotations: ToolAnnotations::destructive(),
2191    },
2192    ToolDef {
2193        name: "quality_source_trust",
2194        description: "Get or update trust score for a source type. Higher trust means memories from this source are weighted more in quality calculations.",
2195        schema: r#"{
2196            "type": "object",
2197            "properties": {
2198                "source_type": {"type": "string", "description": "Source type (user, seed, extraction, inference, external)"},
2199                "source_identifier": {"type": "string", "description": "Optional specific source identifier"},
2200                "trust_score": {"type": "number", "minimum": 0, "maximum": 1, "description": "New trust score (omit to just get current score)"},
2201                "notes": {"type": "string", "description": "Notes about this source"}
2202            },
2203            "required": ["source_type"]
2204        }"#,
2205        annotations: ToolAnnotations::read_only(),
2206    },
2207    ToolDef {
2208        name: "quality_improve",
2209        description: "Get suggestions for improving a memory's quality. Returns actionable recommendations.",
2210        schema: r#"{
2211            "type": "object",
2212            "properties": {
2213                "id": {"type": "integer", "description": "Memory ID to analyze"}
2214            },
2215            "required": ["id"]
2216        }"#,
2217        annotations: ToolAnnotations::mutating(),
2218    },
2219    // Phase 7: Meilisearch Integration (ENG-58) - feature-gated
2220    #[cfg(feature = "meilisearch")]
2221    ToolDef {
2222        name: "meilisearch_search",
2223        description: "Search memories using Meilisearch (typo-tolerant, fast full-text). Requires Meilisearch to be configured. Falls back to hybrid search if unavailable.",
2224        schema: r#"{
2225            "type": "object",
2226            "properties": {
2227                "query": {"type": "string", "description": "Search query text"},
2228                "limit": {"type": "integer", "default": 20, "description": "Maximum results to return"},
2229                "offset": {"type": "integer", "default": 0, "description": "Number of results to skip"},
2230                "workspace": {"type": "string", "description": "Filter by workspace"},
2231                "tags": {"type": "array", "items": {"type": "string"}, "description": "Filter by tags (AND logic)"},
2232                "memory_type": {"type": "string", "description": "Filter by memory type"}
2233            },
2234            "required": ["query"]
2235        }"#,
2236        annotations: ToolAnnotations::read_only(),
2237    },
2238    #[cfg(feature = "meilisearch")]
2239    ToolDef {
2240        name: "meilisearch_reindex",
2241        description: "Trigger a full re-sync from SQLite to Meilisearch. Use after bulk imports or if the index is out of sync.",
2242        schema: r#"{
2243            "type": "object",
2244            "properties": {}
2245        }"#,
2246        annotations: ToolAnnotations::idempotent(),
2247    },
2248    #[cfg(feature = "meilisearch")]
2249    ToolDef {
2250        name: "meilisearch_status",
2251        description: "Get Meilisearch index status including document count, indexing state, and health.",
2252        schema: r#"{
2253            "type": "object",
2254            "properties": {}
2255        }"#,
2256        annotations: ToolAnnotations::read_only(),
2257    },
2258    #[cfg(feature = "meilisearch")]
2259    ToolDef {
2260        name: "meilisearch_config",
2261        description: "Show current Meilisearch configuration (URL, sync interval, enabled status).",
2262        schema: r#"{
2263            "type": "object",
2264            "properties": {}
2265        }"#,
2266        annotations: ToolAnnotations::read_only(),
2267    },
2268    // ── Agent Registry ────────────────────────────────────────────────────
2269    ToolDef {
2270        name: "agent_register",
2271        description: "Register an AI agent with capabilities and namespace isolation. Upserts if agent_id already exists.",
2272        schema: r#"{
2273            "type": "object",
2274            "properties": {
2275                "agent_id": {"type": "string", "description": "Unique identifier for the agent"},
2276                "display_name": {"type": "string", "description": "Human-readable name (defaults to agent_id)"},
2277                "capabilities": {"type": "array", "items": {"type": "string"}, "description": "List of capabilities (e.g., 'search', 'create', 'analyze')"},
2278                "namespaces": {"type": "array", "items": {"type": "string"}, "description": "Namespaces the agent operates in (default: ['default'])"},
2279                "metadata": {"type": "object", "description": "Additional metadata as key-value pairs"}
2280            },
2281            "required": ["agent_id"]
2282        }"#,
2283        annotations: ToolAnnotations::mutating(),
2284    },
2285    ToolDef {
2286        name: "agent_deregister",
2287        description: "Deregister an AI agent (soft delete — sets status to 'inactive').",
2288        schema: r#"{
2289            "type": "object",
2290            "properties": {
2291                "agent_id": {"type": "string", "description": "ID of the agent to deregister"}
2292            },
2293            "required": ["agent_id"]
2294        }"#,
2295        annotations: ToolAnnotations::destructive(),
2296    },
2297    ToolDef {
2298        name: "agent_heartbeat",
2299        description: "Update an agent's heartbeat timestamp to indicate it is still alive.",
2300        schema: r#"{
2301            "type": "object",
2302            "properties": {
2303                "agent_id": {"type": "string", "description": "ID of the agent sending heartbeat"}
2304            },
2305            "required": ["agent_id"]
2306        }"#,
2307        annotations: ToolAnnotations::mutating(),
2308    },
2309    ToolDef {
2310        name: "agent_list",
2311        description: "List registered agents, optionally filtered by status or namespace.",
2312        schema: r#"{
2313            "type": "object",
2314            "properties": {
2315                "status": {"type": "string", "enum": ["active", "inactive"], "description": "Filter by agent status"},
2316                "namespace": {"type": "string", "description": "Filter by namespace (returns agents that include this namespace)"}
2317            }
2318        }"#,
2319        annotations: ToolAnnotations::read_only(),
2320    },
2321    ToolDef {
2322        name: "agent_get",
2323        description: "Get details of a specific registered agent by ID.",
2324        schema: r#"{
2325            "type": "object",
2326            "properties": {
2327                "agent_id": {"type": "string", "description": "ID of the agent to retrieve"}
2328            },
2329            "required": ["agent_id"]
2330        }"#,
2331        annotations: ToolAnnotations::read_only(),
2332    },
2333    ToolDef {
2334        name: "agent_capabilities",
2335        description: "Update the capabilities list of a registered agent.",
2336        schema: r#"{
2337            "type": "object",
2338            "properties": {
2339                "agent_id": {"type": "string", "description": "ID of the agent to update"},
2340                "capabilities": {"type": "array", "items": {"type": "string"}, "description": "New capabilities list (replaces existing)"}
2341            },
2342            "required": ["agent_id", "capabilities"]
2343        }"#,
2344        annotations: ToolAnnotations::mutating(),
2345    },
2346
2347    // ── Snapshot Tools (agent-portability) ────────────────────────────────────
2348    #[cfg(feature = "agent-portability")]
2349    ToolDef {
2350        name: "snapshot_create",
2351        description: "Create a portable .egm snapshot of memories filtered by workspace, tags, date range, or importance. Optionally encrypt with AES-256-GCM or sign with Ed25519.",
2352        schema: r#"{
2353            "type": "object",
2354            "properties": {
2355                "output_path": {"type": "string", "description": "File path for the .egm snapshot"},
2356                "workspace": {"type": "string", "description": "Filter by workspace"},
2357                "tags": {"type": "array", "items": {"type": "string"}, "description": "Filter by tags"},
2358                "importance_min": {"type": "number", "description": "Minimum importance score"},
2359                "memory_types": {"type": "array", "items": {"type": "string"}, "description": "Filter by memory types"},
2360                "description": {"type": "string", "description": "Human-readable description"},
2361                "creator": {"type": "string", "description": "Creator name"},
2362                "encrypt_key": {"type": "string", "description": "Hex-encoded 32-byte AES key"},
2363                "sign_key": {"type": "string", "description": "Hex-encoded 32-byte Ed25519 secret key"}
2364            },
2365            "required": ["output_path"]
2366        }"#,
2367        annotations: ToolAnnotations::mutating(),
2368    },
2369    #[cfg(feature = "agent-portability")]
2370    ToolDef {
2371        name: "snapshot_load",
2372        description: "Load a .egm snapshot into the memory store. Strategies: merge (skip duplicates), replace (clear workspace first), isolate (new workspace), dry_run (preview only).",
2373        schema: r#"{
2374            "type": "object",
2375            "properties": {
2376                "path": {"type": "string", "description": "Path to .egm file"},
2377                "strategy": {"type": "string", "enum": ["merge", "replace", "isolate", "dry_run"], "description": "Load strategy"},
2378                "target_workspace": {"type": "string", "description": "Target workspace (defaults to snapshot's workspace)"},
2379                "decrypt_key": {"type": "string", "description": "Hex-encoded 32-byte AES key for encrypted snapshots"}
2380            },
2381            "required": ["path", "strategy"]
2382        }"#,
2383        annotations: ToolAnnotations::mutating(),
2384    },
2385    #[cfg(feature = "agent-portability")]
2386    ToolDef {
2387        name: "snapshot_inspect",
2388        description: "Inspect a .egm snapshot without loading it. Returns manifest, file list, and size.",
2389        schema: r#"{
2390            "type": "object",
2391            "properties": {
2392                "path": {"type": "string", "description": "Path to .egm file"}
2393            },
2394            "required": ["path"]
2395        }"#,
2396        annotations: ToolAnnotations::read_only(),
2397    },
2398
2399    // ── Attestation Tools (agent-portability) ──────────────────────────────────
2400    #[cfg(feature = "agent-portability")]
2401    ToolDef {
2402        name: "attestation_log",
2403        description: "Log a document ingestion with cryptographic attestation. Creates a chained record proving the document was processed.",
2404        schema: r#"{
2405            "type": "object",
2406            "properties": {
2407                "content": {"type": "string", "description": "Document content to attest"},
2408                "document_name": {"type": "string", "description": "Name of the document"},
2409                "agent_id": {"type": "string", "description": "ID of the attesting agent"},
2410                "memory_ids": {"type": "array", "items": {"type": "integer"}, "description": "IDs of memories created from this document"},
2411                "sign_key": {"type": "string", "description": "Hex-encoded 32-byte Ed25519 secret key"}
2412            },
2413            "required": ["content", "document_name"]
2414        }"#,
2415        annotations: ToolAnnotations::mutating(),
2416    },
2417    #[cfg(feature = "agent-portability")]
2418    ToolDef {
2419        name: "attestation_verify",
2420        description: "Verify whether a document has been attested (ingested and recorded).",
2421        schema: r#"{
2422            "type": "object",
2423            "properties": {
2424                "content": {"type": "string", "description": "Document content to verify"}
2425            },
2426            "required": ["content"]
2427        }"#,
2428        annotations: ToolAnnotations::read_only(),
2429    },
2430    #[cfg(feature = "agent-portability")]
2431    ToolDef {
2432        name: "attestation_chain_verify",
2433        description: "Verify the integrity of the entire attestation chain. Returns valid, broken (with location), or empty.",
2434        schema: r#"{
2435            "type": "object",
2436            "properties": {}
2437        }"#,
2438        annotations: ToolAnnotations::read_only(),
2439    },
2440    #[cfg(feature = "agent-portability")]
2441    ToolDef {
2442        name: "attestation_list",
2443        description: "List attestation records with optional filters. Supports JSON, CSV, and Merkle proof export formats.",
2444        schema: r#"{
2445            "type": "object",
2446            "properties": {
2447                "limit": {"type": "integer", "description": "Maximum records to return", "default": 50},
2448                "offset": {"type": "integer", "description": "Number of records to skip", "default": 0},
2449                "agent_id": {"type": "string", "description": "Filter by agent ID"},
2450                "document_name": {"type": "string", "description": "Filter by document name"},
2451                "export_format": {"type": "string", "enum": ["json", "csv", "merkle_proof"], "description": "Export format"}
2452            }
2453        }"#,
2454        annotations: ToolAnnotations::read_only(),
2455    },
2456
2457    // ── DuckDB Graph Tools (duckdb-graph) ──────────────────────────────────────
2458    #[cfg(feature = "duckdb-graph")]
2459    ToolDef {
2460        name: "memory_graph_path",
2461        description: "Finds how two entities are connected in the knowledge graph via DuckDB OLAP engine. Discovers hidden relationships across multiple hops using recursive path-finding.",
2462        schema: r#"{
2463            "type": "object",
2464            "properties": {
2465                "scope": {"type": "string", "description": "Tenant scope prefix, e.g., 'global/org/user'"},
2466                "source_id": {"type": "integer", "description": "Starting node ID"},
2467                "target_id": {"type": "integer", "description": "Target node ID"},
2468                "max_depth": {"type": "integer", "description": "Maximum hops to traverse (default: 4, max: 10)", "default": 4}
2469            },
2470            "required": ["scope", "source_id", "target_id"]
2471        }"#,
2472        annotations: ToolAnnotations {
2473            read_only_hint: Some(true),
2474            destructive_hint: None,
2475            idempotent_hint: Some(true),
2476            open_world_hint: None,
2477        },
2478    },
2479    #[cfg(feature = "duckdb-graph")]
2480    ToolDef {
2481        name: "memory_temporal_snapshot",
2482        description: "Retrieves the exact facts and relationships that were true at a specific historical point in time. Uses DuckDB OLAP engine for fast columnar scans over temporal edges.",
2483        schema: r#"{
2484            "type": "object",
2485            "properties": {
2486                "scope": {"type": "string", "description": "Tenant scope prefix"},
2487                "timestamp": {"type": "string", "description": "ISO-8601 timestamp for the point-in-time query"}
2488            },
2489            "required": ["scope", "timestamp"]
2490        }"#,
2491        annotations: ToolAnnotations {
2492            read_only_hint: Some(true),
2493            destructive_hint: None,
2494            idempotent_hint: Some(true),
2495            open_world_hint: None,
2496        },
2497    },
2498    #[cfg(feature = "duckdb-graph")]
2499    ToolDef {
2500        name: "memory_scope_snapshot",
2501        description: "Compares the knowledge graph between two timestamps, showing what relationships were added, removed, or changed. Uses DuckDB OLAP engine for efficient temporal diff.",
2502        schema: r#"{
2503            "type": "object",
2504            "properties": {
2505                "scope": {"type": "string", "description": "Tenant scope prefix"},
2506                "from_timestamp": {"type": "string", "description": "Start of comparison window (ISO-8601)"},
2507                "to_timestamp": {"type": "string", "description": "End of comparison window (ISO-8601)"}
2508            },
2509            "required": ["scope", "from_timestamp", "to_timestamp"]
2510        }"#,
2511        annotations: ToolAnnotations {
2512            read_only_hint: Some(true),
2513            destructive_hint: None,
2514            idempotent_hint: Some(true),
2515            open_world_hint: None,
2516        },
2517    },
2518
2519    // ── Claude-Mem Parity (v0.14.0) ──────────────────────────────────────────
2520    ToolDef {
2521        name: "memory_get_public",
2522        description: "Get a memory with all <private>...</private> tagged sections removed. Safe for sharing in multi-agent contexts.",
2523        schema: r#"{
2524            "type": "object",
2525            "properties": {
2526                "id": {"type": "integer", "description": "Memory ID"}
2527            },
2528            "required": ["id"]
2529        }"#,
2530        annotations: ToolAnnotations::read_only(),
2531    },
2532    ToolDef {
2533        name: "memory_search_compact",
2534        description: "Token-efficient search returning only id, title (first line, max 80 chars), created_at, and tags. Use memory_expand to get full content for specific IDs.",
2535        schema: r#"{
2536            "type": "object",
2537            "properties": {
2538                "query": {"type": "string", "description": "Search query"},
2539                "limit": {"type": "integer", "description": "Max results (default: 10)"},
2540                "workspace": {"type": "string", "description": "Filter to workspace"}
2541            },
2542            "required": ["query"]
2543        }"#,
2544        annotations: ToolAnnotations::read_only(),
2545    },
2546    ToolDef {
2547        name: "memory_expand",
2548        description: "Fetch full memory content for specific IDs. Used after memory_search_compact to get full content only for memories you need.",
2549        schema: r#"{
2550            "type": "object",
2551            "properties": {
2552                "ids": {"type": "array", "items": {"type": "integer"}, "description": "Memory IDs to expand"}
2553            },
2554            "required": ["ids"]
2555        }"#,
2556        annotations: ToolAnnotations::read_only(),
2557    },
2558    ToolDef {
2559        name: "memory_get_injection_prompt",
2560        description: "Assembles the most relevant memories into a ready-to-inject system prompt block. Uses hybrid search to find relevant memories and formats them as markdown, respecting a token budget.",
2561        schema: r#"{
2562            "type": "object",
2563            "properties": {
2564                "query": {"type": "string", "description": "Search query to find relevant memories"},
2565                "token_budget": {"type": "integer", "description": "Max tokens for output (default: 2000)"},
2566                "workspace": {"type": "string", "description": "Filter to specific workspace"},
2567                "include_types": {"type": "array", "items": {"type": "string"}, "description": "Filter by memory types"}
2568            },
2569            "required": ["query"]
2570        }"#,
2571        annotations: ToolAnnotations::read_only(),
2572    },
2573    ToolDef {
2574        name: "memory_observe_tool_use",
2575        description: "Store a tool observation as an episodic memory for session continuity. Automatically compresses large inputs/outputs.",
2576        schema: r#"{
2577            "type": "object",
2578            "properties": {
2579                "tool_name": {"type": "string", "description": "Name of the tool that was used"},
2580                "tool_input": {"type": "object", "description": "Tool input parameters"},
2581                "tool_output": {"type": "string", "description": "Tool output/result"},
2582                "session_id": {"type": "string", "description": "Session identifier for grouping observations"},
2583                "compress": {"type": "boolean", "description": "Compress to 200-char previews (default: true)"}
2584            },
2585            "required": ["tool_name", "tool_input", "tool_output"]
2586        }"#,
2587        annotations: ToolAnnotations::mutating(),
2588    },
2589    // ── Endless Mode (O(N) context management) ───────────────────────────────
2590    ToolDef {
2591        name: "memory_archive_tool_output",
2592        description: "Archives a tool's full raw output to memory and returns a compressed summary (~500 tokens) for use in the active context. Transforms O(N²) context growth to O(N) by keeping only summaries in the working context while preserving full outputs for on-demand retrieval.",
2593        schema: r#"{
2594            "type": "object",
2595            "properties": {
2596                "tool_name": {"type": "string", "description": "Name of the tool whose output is being archived"},
2597                "raw_output": {"type": "string", "description": "Full raw output to archive"},
2598                "session_id": {"type": "string", "description": "Session identifier for grouping archived outputs (default: 'unknown')"},
2599                "compress_summary": {"type": "boolean", "description": "Whether to generate a compressed summary (default: true)"},
2600                "summary_tokens": {"type": "integer", "description": "Max tokens for the compressed summary (default: 500)"}
2601            },
2602            "required": ["tool_name", "raw_output"]
2603        }"#,
2604        annotations: ToolAnnotations::mutating(),
2605    },
2606    ToolDef {
2607        name: "memory_get_archived_output",
2608        description: "Retrieves the full raw output for an archived tool observation by its archive ID. Use when you need the complete output that was previously compressed for context efficiency.",
2609        schema: r#"{
2610            "type": "object",
2611            "properties": {
2612                "archive_id": {"type": "integer", "description": "Archive ID returned by memory_archive_tool_output"}
2613            },
2614            "required": ["archive_id"]
2615        }"#,
2616        annotations: ToolAnnotations::read_only(),
2617    },
2618    ToolDef {
2619        name: "memory_get_working_memory",
2620        description: "Assembles all compressed tool observations for a session into a token-budgeted working memory block. Includes archive references for retrieving full outputs on demand. This is the core of the Endless Mode context management system.",
2621        schema: r#"{
2622            "type": "object",
2623            "properties": {
2624                "session_id": {"type": "string", "description": "Session identifier to retrieve observations for"},
2625                "token_budget": {"type": "integer", "description": "Max tokens for the working memory block (default: 4000)"},
2626                "include_tool_names": {"type": "array", "items": {"type": "string"}, "description": "Whitelist of tool names to include (default: all)"}
2627            },
2628            "required": ["session_id"]
2629        }"#,
2630        annotations: ToolAnnotations::read_only(),
2631    },
2632];
2633
2634/// Get all tool definitions as ToolDefinition structs
2635pub fn get_tool_definitions() -> Vec<ToolDefinition> {
2636    TOOL_DEFINITIONS
2637        .iter()
2638        .map(|def| ToolDefinition {
2639            name: def.name.to_string(),
2640            description: def.description.to_string(),
2641            input_schema: serde_json::from_str(def.schema).unwrap_or(json!({})),
2642            annotations: Some(def.annotations.clone()),
2643        })
2644        .collect()
2645}
2646
2647#[cfg(test)]
2648mod tests {
2649    use super::*;
2650
2651    #[test]
2652    fn test_tool_definitions_all_parseable() {
2653        let tools = get_tool_definitions();
2654        assert!(!tools.is_empty(), "TOOL_DEFINITIONS must not be empty");
2655        for tool in &tools {
2656            assert!(!tool.name.is_empty(), "tool name must not be empty");
2657            assert!(
2658                !tool.description.is_empty(),
2659                "tool description must not be empty"
2660            );
2661            assert!(
2662                tool.input_schema.is_object(),
2663                "tool '{}' schema must be a JSON object",
2664                tool.name
2665            );
2666        }
2667    }
2668
2669    #[test]
2670    fn test_read_only_tools_have_annotation() {
2671        let tools = get_tool_definitions();
2672        let read_only_names = ["memory_get", "memory_list", "memory_search", "memory_stats"];
2673        for name in read_only_names {
2674            let tool = tools
2675                .iter()
2676                .find(|t| t.name == name)
2677                .unwrap_or_else(|| panic!("tool '{}' not found", name));
2678            let ann = tool
2679                .annotations
2680                .as_ref()
2681                .expect("annotations must be present");
2682            assert_eq!(
2683                ann.read_only_hint,
2684                Some(true),
2685                "tool '{}' should have readOnlyHint=true",
2686                name
2687            );
2688        }
2689    }
2690
2691    #[test]
2692    fn test_destructive_tools_have_annotation() {
2693        let tools = get_tool_definitions();
2694        let destructive_names = [
2695            "memory_delete",
2696            "memory_cleanup_expired",
2697            "embedding_cache_clear",
2698        ];
2699        for name in destructive_names {
2700            let tool = tools
2701                .iter()
2702                .find(|t| t.name == name)
2703                .unwrap_or_else(|| panic!("tool '{}' not found", name));
2704            let ann = tool
2705                .annotations
2706                .as_ref()
2707                .expect("annotations must be present");
2708            assert_eq!(
2709                ann.destructive_hint,
2710                Some(true),
2711                "tool '{}' should have destructiveHint=true",
2712                name
2713            );
2714        }
2715    }
2716
2717    #[test]
2718    fn test_idempotent_tools_have_annotation() {
2719        let tools = get_tool_definitions();
2720        let idempotent_names = [
2721            "memory_extract_entities",
2722            "memory_rebuild_embeddings",
2723            "memory_rebuild_crossrefs",
2724            "lifecycle_run",
2725            "retention_policy_apply",
2726        ];
2727        for name in idempotent_names {
2728            let tool = tools
2729                .iter()
2730                .find(|t| t.name == name)
2731                .unwrap_or_else(|| panic!("tool '{}' not found", name));
2732            let ann = tool
2733                .annotations
2734                .as_ref()
2735                .expect("annotations must be present");
2736            assert_eq!(
2737                ann.idempotent_hint,
2738                Some(true),
2739                "tool '{}' should have idempotentHint=true",
2740                name
2741            );
2742        }
2743    }
2744
2745    #[test]
2746    fn test_annotations_serialize_with_camel_case_keys() {
2747        let tools = get_tool_definitions();
2748        let memory_get = tools.iter().find(|t| t.name == "memory_get").unwrap();
2749        let json = serde_json::to_string(memory_get).expect("serialization must succeed");
2750        assert!(
2751            json.contains("readOnlyHint"),
2752            "should serialize as readOnlyHint"
2753        );
2754        assert!(
2755            !json.contains("read_only_hint"),
2756            "must not use snake_case key"
2757        );
2758    }
2759
2760    #[test]
2761    fn test_mutating_tool_has_no_hints() {
2762        let tools = get_tool_definitions();
2763        let memory_create = tools.iter().find(|t| t.name == "memory_create").unwrap();
2764        let ann = memory_create
2765            .annotations
2766            .as_ref()
2767            .expect("annotations must be present");
2768        assert!(ann.read_only_hint.is_none());
2769        assert!(ann.destructive_hint.is_none());
2770        assert!(ann.idempotent_hint.is_none());
2771        // Serialized form should omit None fields
2772        let json = serde_json::to_string(ann).expect("serialization must succeed");
2773        // mutating annotations serialize as empty object since all fields are None
2774        assert_eq!(json, "{}");
2775    }
2776}