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