semantic-memory-mcp 0.5.0

MCP server wrapping semantic-memory — local-first knowledge management with evidence-scored retrieval, contradiction detection, and adaptive routing
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
//! Tool parameter structs for MCP tools.
//! Each struct derives schemars::JsonSchema so rmcp can auto-generate
//! the JSON Schema for the tool's inputSchema.

use schemars::JsonSchema;
use serde::Deserialize;

/// Edge type for graph edges. JSON Schema enum helps LLMs pick the
/// right value without guessing.
#[derive(Debug, Deserialize, JsonSchema)]
pub enum EdgeType {
    /// Semantic similarity edge (requires cosine_similarity)
    Semantic,
    /// Temporal ordering edge (requires delta_secs)
    Temporal,
    /// Causal relationship edge (requires confidence)
    Causal,
    /// Named relationship edge (requires relation)
    Entity,
}

// ─── Core search tools ──────────────────────────────────────────────────

/// Parameters for sm_search
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SearchParams {
    /// The search query string
    pub query: String,
    /// Maximum number of results to return (default 5)
    #[serde(default)]
    pub top_k: Option<u32>,
    /// Optional namespace filter (restrict search to these namespaces)
    #[serde(default)]
    pub namespaces: Option<Vec<String>>,
}

/// Parameters for sm_search_explained
#[allow(dead_code)]
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SearchExplainedParams {
    /// The query string
    pub query: String,
    /// Maximum number of results to return (default 5)
    #[serde(default)]
    pub top_k: Option<u32>,
}

/// Parameters for sm_add_fact
#[derive(Debug, Deserialize, JsonSchema)]
pub struct AddFactParams {
    /// The fact content text
    pub content: String,
    /// Namespace to store the fact in (e.g. "general", "research", "coding")
    pub namespace: String,
    /// Optional source attribution
    #[serde(default)]
    pub source: Option<String>,
    /// When true, extract named entities via Ollama and link them as graph edges (opt-in)
    #[serde(default)]
    pub extract_entities: Option<bool>,
    /// Memory kind classification: durable_fact, preference, project_state, instruction_policy,
    /// correction, observation, episode_summary, skill_procedure, ephemeral_inference.
    /// Default: durable_fact. Ephemeral inferences require evidence_refs to promote.
    #[serde(default)]
    pub memory_kind: Option<String>,
    /// Sensitivity class: public, internal, confidential, restricted.
    /// Default: internal. Confidential/restricted facts are blocked from autocapture.
    #[serde(default)]
    pub sensitivity: Option<String>,
    /// Evidence references supporting this fact (URLs, fact IDs, source paths).
    #[serde(default)]
    pub evidence_refs: Option<Vec<String>>,
}

/// Parameters for sm_ingest_document
#[derive(Debug, Deserialize, JsonSchema)]
pub struct IngestDocumentParams {
    /// The document content text
    pub content: String,
    /// Document title
    pub title: String,
    /// Namespace to store the document in
    pub namespace: String,
}

/// Parameters for sm_graph_path
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GraphPathParams {
    /// Starting item ID
    pub from_id: String,
    /// Target item ID
    pub to_id: String,
    /// Maximum BFS depth (default 5)
    #[serde(default)]
    pub max_depth: Option<u32>,
}

// ─── Feature-gated tools (full feature only) ────────────────────────────
// Note: cfg gates removed so rmcp's #[tool_router] macro can see all
// tool parameter types at expansion time. The `full` feature in
// Cargo.toml enables the corresponding semantic-memory sub-features.

/// Parameters for sm_route_query
#[derive(Debug, Deserialize, JsonSchema)]
pub struct RouteQueryParams {
    /// The query string to profile and route
    pub query: String,
}

/// Parameters for sm_detect_contradictions
#[derive(Debug, Deserialize, JsonSchema)]
pub struct DetectContradictionsParams {
    /// The query whose top results are scanned for content contradictions
    pub query: String,
    /// How many top results to scan (default 10)
    #[serde(default)]
    pub top_k: Option<u32>,
}

/// Parameters for sm_search_with_routing
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SearchWithRoutingParams {
    /// The search query string
    pub query: String,
    /// Maximum number of results (default 5)
    #[serde(default)]
    pub top_k: Option<u32>,
    /// Known contradiction pairs (item_a, item_b)
    #[serde(default)]
    pub contradictions: Option<Vec<(String, String)>>,
    /// When true, group search results by knowledge graph community membership
    #[serde(default)]
    pub group_by_community: Option<bool>,
}

/// Parameters for sm_set_provenance
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SetProvenanceParams {
    /// The item ID to set provenance for
    pub item_id: String,
    /// Confidence score 0.0-1.0
    pub confidence: f64,
    /// Number of supporting observations
    pub support_count: u32,
}

/// Parameters for sm_decoder_analyze
#[derive(Debug, Deserialize, JsonSchema)]
pub struct DecoderAnalyzeParams {
    /// Search results as (item_id, score) pairs
    pub results: Vec<(String, f64)>,
    /// Known contradiction pairs
    #[serde(default)]
    pub contradictions: Option<Vec<(String, String)>>,
}

/// Parameters for sm_discord_search
///
/// MCP-001: graph_edges field removed — edges are now loaded from the store
/// automatically. Caller supplies only the direct result IDs.
#[derive(Debug, Deserialize, JsonSchema)]
pub struct DiscordSearchParams {
    /// Direct result item IDs (the top-K from search)
    pub direct_result_ids: Vec<String>,
}

/// Parameters for sm_run_lifecycle
#[derive(Debug, Deserialize, JsonSchema)]
pub struct RunLifecycleParams {
    /// Item IDs to process in the lifecycle pass
    pub item_ids: Vec<String>,
}

// ─── Graph edge tools ──────────────────────────────────────────────────

/// Parameters for sm_add_graph_edge
#[derive(Debug, Deserialize, JsonSchema)]
pub struct AddGraphEdgeParams {
    /// Source node ID (prefixed, e.g. "fact:<uuid>", "namespace:<name>")
    pub source: String,
    /// Target node ID (prefixed)
    pub target: String,
    /// Edge type: semantic, temporal, causal, or entity
    pub edge_type: EdgeType,
    /// Edge weight (default 1.0)
    #[serde(default = "default_weight")]
    pub weight: f64,
    /// For semantic edges: cosine similarity (0.0-1.0). Ignored for other types.
    #[serde(default)]
    pub cosine_similarity: Option<f32>,
    /// For temporal edges: time delta in seconds. Ignored for other types.
    #[serde(default)]
    pub delta_secs: Option<u64>,
    /// For causal edges: confidence (0.0-1.0). Ignored for other types.
    #[serde(default)]
    pub confidence: Option<f32>,
    /// For causal edges: evidence IDs. Ignored for other types.
    #[serde(default)]
    pub evidence_ids: Option<Vec<String>>,
    /// For entity edges: relationship name (e.g. "mentions", "modifies"). Ignored for other types.
    #[serde(default)]
    pub relation: Option<String>,
    /// Optional metadata as a JSON object string
    #[serde(default)]
    pub metadata: Option<String>,
}

fn default_weight() -> f64 {
    1.0
}

/// Parameters for sm_list_graph_edges
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ListGraphEdgesParams {
    /// Node ID to list edges for. If omitted, lists all edges.
    #[serde(default)]
    pub node_id: Option<String>,
}

/// Parameters for sm_invalidate_graph_edge
#[derive(Debug, Deserialize, JsonSchema)]
pub struct InvalidateGraphEdgeParams {
    /// Edge ID to invalidate
    pub edge_id: String,
    /// Reason for invalidation
    pub reason: String,
}

// ─── Factor graph tools ─────────────────────────────────────────────────

/// A node input for the factor graph.
#[derive(Debug, Deserialize, JsonSchema)]
pub struct FactorGraphNodeInput {
    /// Node/item ID (e.g. "fact:<uuid>")
    pub item_id: String,
    /// Initial belief score (0.0-1.0, from provenance or search score)
    pub initial_belief: f64,
}

/// Parameters for sm_factor_graph
///
/// MCP-001: edges field removed — edges are now loaded from the store
/// automatically. Caller supplies only node initial beliefs and optional
/// config overrides.
#[derive(Debug, Deserialize, JsonSchema)]
pub struct FactorGraphParams {
    /// Nodes with their initial belief scores
    pub nodes: Vec<FactorGraphNodeInput>,
    /// Weight for semantic factors (default 0.35)
    #[serde(default)]
    pub semantic_weight: Option<f64>,
    /// Weight for temporal factors (default 0.20)
    #[serde(default)]
    pub temporal_weight: Option<f64>,
    /// Weight for causal factors (default 0.30)
    #[serde(default)]
    pub causal_weight: Option<f64>,
    /// Weight for entity factors (default 0.15)
    #[serde(default)]
    pub entity_weight: Option<f64>,
    /// How much the node's own initial belief matters vs neighbor influence (default 0.6)
    #[serde(default)]
    pub self_influence: Option<f64>,
    /// Max iterations for message passing (default 50)
    #[serde(default)]
    pub max_iterations: Option<u32>,
    /// Convergence threshold (default 0.001)
    #[serde(default)]
    pub convergence_threshold: Option<f64>,
}

// ─── Topology tools ─────────────────────────────────────────────────────

/// Parameters for sm_topology
///
/// MCP-001: edges field removed — edges are now loaded from the store
/// automatically. The params struct is kept for schema compatibility but
/// the edges field is no longer used.
#[derive(Debug, Deserialize, JsonSchema)]
pub struct TopologyParams {
    // No caller-supplied edges — loaded from store internally.
}

// ─── Community tools ────────────────────────────────────────────────────

/// Parameters for sm_community
///
/// MCP-001: edges field removed — edges are now loaded from the store
/// automatically. Configuration params (resolution, seed, contradictions,
/// importance_scores) remain caller-supplied.
#[derive(Debug, Deserialize, JsonSchema)]
pub struct CommunityParams {
    /// Resolution parameter for community detection (default 1.0). Higher values favor smaller communities.
    #[serde(default)]
    pub resolution: Option<f64>,
    /// Random seed for deterministic results (default 42)
    #[serde(default)]
    pub seed: Option<u64>,
    /// Optional contradiction pairs to scan within communities
    #[serde(default)]
    pub contradictions: Option<Vec<(String, String)>>,
    /// Optional importance scores per item for community-aware compression recommendations
    #[serde(default)]
    pub importance_scores: Option<Vec<(String, f64)>>,
    /// When true, generate an LLM summary for each community via Ollama (opt-in)
    #[serde(default)]
    pub summarize: Option<bool>,
}

// ─── Direct read and supersession tools (v0.3.1) ─────────────────────────

/// Parameters for sm_get_fact
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetFactParams {
    /// The fact id. Accepts a bare UUID or a prefixed id like "fact:<uuid>".
    pub fact_id: String,
}

/// Parameters for sm_list_facts
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ListFactsParams {
    /// Namespace to enumerate facts from.
    pub namespace: String,
    /// Maximum facts to return (default 50).
    #[serde(default)]
    pub limit: Option<u32>,
    /// Offset for pagination (default 0).
    #[serde(default)]
    pub offset: Option<u32>,
}

/// Parameters for sm_get_fact_neighbors
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetFactNeighborsParams {
    /// The node id whose neighbors to fetch (bare UUID or prefixed "fact:<uuid>").
    pub item_id: String,
}

/// Parameters for sm_supersede_fact
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SupersedeFactParams {
    /// Existing stale fact id. Accepts a bare UUID or prefixed "fact:<uuid>".
    pub old_fact_id: String,
    /// Replacement fact content.
    pub content: String,
    /// Optional namespace for the replacement fact. Defaults to the old fact's namespace.
    #[serde(default)]
    pub namespace: Option<String>,
    /// Optional source attribution for the replacement fact.
    #[serde(default)]
    pub source: Option<String>,
    /// Optional reason stored on the supersedes graph edge.
    #[serde(default)]
    pub reason: Option<String>,
}

// ─── Conversation / session tools (v0.3.0) ──────────────────────────────

/// Parameters for sm_create_session
#[allow(dead_code)]
#[derive(Debug, Deserialize, JsonSchema)]
pub struct CreateSessionParams {
    /// Channel/label for the session (e.g. "claude-code", "project-x").
    pub channel: String,
    /// Optional metadata as a JSON object string.
    #[serde(default)]
    pub metadata: Option<String>,
}

/// Parameters for sm_add_message
#[allow(dead_code)]
#[derive(Debug, Deserialize, JsonSchema)]
pub struct AddMessageParams {
    /// Session id to append to.
    pub session_id: String,
    /// Role: "user", "assistant", "system", or "tool".
    pub role: String,
    /// Message content (embedded + FTS-indexed for conversation search).
    pub content: String,
}

/// Parameters for sm_list_sessions
#[allow(dead_code)]
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ListSessionsParams {
    /// Maximum sessions to return (default 20).
    #[serde(default)]
    pub limit: Option<u32>,
    /// Offset for pagination (default 0).
    #[serde(default)]
    pub offset: Option<u32>,
}

/// Parameters for sm_get_messages
#[allow(dead_code)]
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetMessagesParams {
    /// Session id to read messages from.
    pub session_id: String,
    /// Return the most recent messages fitting within this token budget (default 4000).
    #[serde(default)]
    pub max_tokens: Option<u32>,
}

/// Parameters for sm_search_conversations
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SearchConversationsParams {
    /// The search query string.
    pub query: String,
    /// Maximum number of results (default 5).
    #[serde(default)]
    pub top_k: Option<u32>,
}

// ─── Delete / forget tools (admin-ops) ──────────────────────────────────

/// Parameters for sm_delete_fact
#[derive(Debug, Deserialize, JsonSchema)]
pub struct DeleteFactParams {
    /// The fact id to permanently delete (bare UUID or prefixed "fact:<uuid>").
    pub fact_id: String,
}

/// Parameters for sm_delete_namespace
#[derive(Debug, Deserialize, JsonSchema)]
pub struct DeleteNamespaceParams {
    /// The namespace to permanently delete (all its facts, documents, chunks, and namespaced sessions).
    pub namespace: String,
}

/// Parameters for sm_update_fact
#[derive(Debug, Deserialize, JsonSchema)]
pub struct UpdateFactParams {
    /// The fact ID to update (with or without "fact:" prefix).
    pub fact_id: String,
    /// The new content for the fact.
    pub content: String,
}

/// Parameters for sm_consolidate_facts
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ConsolidateFactsParams {
    /// First fact ID (will be kept and updated with merged content).
    pub keep_id: String,
    /// Second fact ID (will be superseded by the kept fact).
    pub supersede_id: String,
    /// Optional merged content. If not provided, content from both facts will be concatenated.
    pub merged_content: Option<String>,
}

// ─── RL routing feedback tool ──────────────────────────────────────────

/// Parameters for sm_record_outcome
#[derive(Debug, Deserialize, JsonSchema)]
pub struct RecordOutcomeParams {
    /// The query string that was routed.
    pub query: String,
    /// The outcome of the routing decision: "good", "bad", or "neutral".
    pub outcome: String,
}

// ─── Claim-ledger integration ──────────────────────────────────────────

/// Parameters for sm_create_claim
#[derive(Debug, Deserialize, JsonSchema)]
pub struct CreateClaimParams {
    /// The fact ID to create a claim from (with or without "fact:" prefix).
    pub fact_id: String,
    /// Optional source span description (e.g., "line 42-58").
    pub source_span: Option<String>,
}

/// Parameters for sm_add_evidence
#[derive(Debug, Deserialize, JsonSchema)]
pub struct AddEvidenceParams {
    /// The claim ID to add evidence to.
    pub claim_id: String,
    /// The evidence text supporting the claim.
    pub evidence_text: String,
    /// Optional source type (e.g., "document", "web", "experiment").
    pub source_type: Option<String>,
}

/// Parameters for sm_judge_support
#[derive(Debug, Deserialize, JsonSchema)]
pub struct JudgeSupportParams {
    /// The claim ID to judge.
    pub claim_id: String,
    /// The support judgment: "supported", "unsupported", "contested", or "heuristic_only".
    pub judgment: String,
    /// Optional rationale for the judgment.
    pub rationale: Option<String>,
}

// ─── Bitemporal search ─────────────────────────────────────────────────

/// Parameters for sm_search_as_of
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SearchAsOfParams {
    /// The search query.
    pub query: String,
    /// The date to search as of (ISO 8601, e.g., "2026-01-15T00:00:00Z").
    pub as_of_date: String,
    /// Maximum number of results (default: 5).
    #[serde(default)]
    pub top_k: Option<usize>,
    /// Optional namespace filter.
    pub namespace: Option<String>,
}

// ─── Verification gate ─────────────────────────────────────────────────

/// Parameters for sm_verify_claim
#[derive(Debug, Deserialize, JsonSchema)]
pub struct VerifyClaimParams {
    /// The claim text to verify.
    pub claim: String,
    /// Risk class: low, medium, high, critical.
    pub risk_class: String,
    /// Optional evidence references supporting the claim.
    #[serde(default)]
    pub evidence_refs: Option<Vec<String>>,
    /// Whether refutation was attempted (if false, high/critical claims cannot be promoted).
    #[serde(default)]
    pub refutation_attempted: Option<bool>,
}

// ─── Search receipt tools ──────────────────────────────────────────────

/// Parameters for sm_get_search_receipt
#[derive(Debug, Deserialize, JsonSchema)]
pub struct GetSearchReceiptParams {
    /// The receipt/request ID to look up.
    pub receipt_id: String,
}

/// Parameters for sm_replay_search_receipt
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ReplaySearchReceiptParams {
    /// The receipt/request ID to replay.
    pub receipt_id: String,
    /// The query text to use for replay (receipts don't store query text).
    pub query: String,
    /// Optional top_k for replay (defaults to original receipt's result count).
    #[serde(default)]
    pub top_k: Option<u32>,
    /// Optional namespace filter for replay.
    #[serde(default)]
    pub namespaces: Option<Vec<String>>,
}

// ─── Reconcile tool ───────────────────────────────────────────────────

/// Parameters for sm_reconcile
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ReconcileParams {
    /// Action to take: "report_only", "rebuild_fts", or "re_embed".
    pub action: String,
}

// ─── Maintenance tools ────────────────────────────────────────────────

/// Parameters for sm_embeddings_are_dirty (no params needed, but struct for consistency)
#[derive(Debug, Deserialize, JsonSchema)]
pub struct EmbeddingsAreDirtyParams {}

/// Parameters for sm_list_imports
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ListImportsParams {
    /// Optional namespace filter.
    #[serde(default)]
    pub namespace: Option<String>,
    /// Maximum number of import records to return (default 20).
    #[serde(default)]
    pub limit: Option<u32>,
}

/// Parameters for sm_import_status
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ImportStatusParams {
    /// The envelope ID to check import status for.
    pub envelope_id: String,
}

/// Parameters for sm_import_envelope
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ImportEnvelopeParams {
    /// The import envelope as a JSON string.
    pub envelope_json: String,
}

// ─── LLM output parser tools ──────────────────────────────────────────

/// Parameters for sm_parse_json
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ParseJsonParams {
    /// The raw LLM output text that should contain JSON (may include think blocks, markdown fences, trailing text).
    pub raw_output: String,
}

/// Parameters for sm_parse_json_value
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ParseJsonValueParams {
    /// The raw LLM output text that should contain JSON.
    pub raw_output: String,
}

/// Parameters for sm_strip_think_tags
#[derive(Debug, Deserialize, JsonSchema)]
pub struct StripThinkTagsParams {
    /// The text that may contain <think>...</think> blocks.
    pub text: String,
}

/// Parameters for sm_repair_json
#[derive(Debug, Deserialize, JsonSchema)]
pub struct RepairJsonParams {
    /// The malformed JSON string to attempt repair on.
    pub json_string: String,
}

/// Parameters for sm_parse_string_list
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ParseStringListParams {
    /// The raw LLM output text that should contain a list of items.
    pub raw_output: String,
}

/// Parameters for sm_parse_choice
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ParseChoiceParams {
    /// The raw LLM output text that should contain a choice.
    pub raw_output: String,
    /// Valid options to choose from.
    pub options: Vec<String>,
}

/// Parameters for sm_parse_number
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ParseNumberParams {
    /// The raw LLM output text that should contain a number.
    pub raw_output: String,
}

// ─── Projection query tools ───────────────────────────────────────────

/// Parameters for projection query tools (sm_query_claim_versions, etc.)
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ProjectionQueryParams {
    /// Namespace to query within.
    pub namespace: String,
    /// Optional domain scope filter.
    #[serde(default)]
    pub domain: Option<String>,
    /// Optional workspace ID scope filter.
    #[serde(default)]
    pub workspace_id: Option<String>,
    /// Optional repo ID scope filter.
    #[serde(default)]
    pub repo_id: Option<String>,
    /// Optional free-text query.
    #[serde(default)]
    pub text_query: Option<String>,
    /// Optional valid-time as-of filter (ISO 8601).
    #[serde(default)]
    pub valid_at: Option<String>,
    /// Optional transaction-time cutoff (ISO 8601).
    #[serde(default)]
    pub recorded_at_or_before: Option<String>,
    /// Optional subject entity ID filter.
    #[serde(default)]
    pub subject_entity_id: Option<String>,
    /// Optional canonical entity ID filter.
    #[serde(default)]
    pub canonical_entity_id: Option<String>,
    /// Optional claim state filter.
    #[serde(default)]
    pub claim_state: Option<String>,
    /// Optional claim ID filter.
    #[serde(default)]
    pub claim_id: Option<String>,
    /// Optional claim version ID filter.
    #[serde(default)]
    pub claim_version_id: Option<String>,
    /// Maximum results to return (default 10).
    #[serde(default)]
    pub limit: Option<u32>,
}