semantic-memory-mcp 0.5.1

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
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
//! 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, Serialize};

/// 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 mandatory witnessed autonomous retrieval.
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SearchWitnessedParams {
    pub query: String,
    #[serde(default)]
    pub top_k: Option<u32>,
    #[serde(default)]
    pub namespaces: Option<Vec<String>>,
    /// Optional caller correlation ID; generated when omitted.
    #[serde(default)]
    pub request_id: Option<String>,
    /// Retrieval stage selection. Defaults to the current hybrid behavior.
    #[serde(default)]
    pub retrieval_mode: Option<RetrievalModeParam>,
    /// Replay input retention. Defaults to no_replay for privacy.
    #[serde(default)]
    pub replay_mode: Option<ReplayModeParam>,
}

#[derive(Debug, Clone, Copy, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum RetrievalModeParam {
    Hybrid,
    FtsOnly,
    VectorOnly,
}

/// Opt-in retention policy for complete search replay.
#[derive(Debug, Clone, Copy, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ReplayModeParam {
    NoReplay,
    StoreInputs,
}

/// Exact namespace/resource scope used by governed authority decisions.
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct GovernedNamespaceScopeParams {
    pub namespace: String,
    #[serde(default)]
    pub domain: Option<String>,
    #[serde(default)]
    pub workspace_id: Option<String>,
    #[serde(default)]
    pub repo_id: Option<String>,
}

/// Purpose values carried by a delegation/elevation lease. The MCP tool fixes
/// the decision purpose independently, so callers cannot substitute recall.
#[derive(Debug, Clone, Copy, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum GovernedAccessPurposeParam {
    Recall,
    Assertion,
    Action,
    Export,
    Replay,
    Admin,
}

/// Existing multi-principal delegation/elevation lease contract in MCP-safe fields.
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct GovernedLeaseParams {
    pub lease_id: String,
    pub delegator: String,
    pub delegatee: String,
    pub purposes: Vec<GovernedAccessPurposeParam>,
    pub scope: GovernedNamespaceScopeParams,
    #[serde(default)]
    pub audiences: Vec<String>,
    pub expires_at: String,
    #[serde(default)]
    pub revoked: bool,
    #[serde(default)]
    pub elevation: bool,
}

/// Multi-principal request for a fixed-purpose assertion or action decision.
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct GovernedDecisionParams {
    pub fact_id: String,
    pub caller: String,
    pub subject: String,
    pub audiences: Vec<String>,
    pub scope: GovernedNamespaceScopeParams,
    #[serde(default)]
    pub delegation_or_elevation: Option<GovernedLeaseParams>,
}

/// 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>>,
    /// Optional caller-provided idempotency key. Retries with the same key and
    /// payload return the original fact; omit it for a distinct append.
    #[serde(default)]
    pub idempotency_key: Option<String>,
}

#[cfg(test)]
mod add_fact_param_tests {
    use super::AddFactParams;

    #[test]
    fn add_fact_idempotency_key_is_optional_and_backward_compatible() {
        let legacy: AddFactParams =
            serde_json::from_str(r#"{"content":"same fact","namespace":"general"}"#).unwrap();
        assert_eq!(legacy.idempotency_key, None);

        let keyed: AddFactParams = serde_json::from_str(
            r#"{"content":"same fact","namespace":"general","idempotency_key":"request-42"}"#,
        )
        .unwrap();
        assert_eq!(keyed.idempotency_key.as_deref(), Some("request-42"));
    }
}

/// 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>,
    /// When true, record each detected contradiction pair to the claim-ledger
    /// as a hash-chained ContradictionCandidate entry (requires the
    /// `claim-integration` feature). Default false.
    #[serde(default)]
    pub record_to_ledger: Option<bool>,
}

/// 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,
    /// Caller-supplied proxy feedback label: "good", "bad", or "neutral".
    /// This is training input, not a verified retrieval outcome.
    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>,
}

/// Parameters for sm_compact_claim_ledger.
#[derive(Debug, Deserialize, JsonSchema)]
pub struct CompactClaimLedgerParams {
    /// Preview the verified compaction without writing. Defaults to true.
    #[serde(default)]
    pub dry_run: Option<bool>,
    /// Compact only when the active tail exceeds this entry count (default 10,000).
    #[serde(default)]
    pub max_entries: Option<usize>,
    /// Compact only when the active tail exceeds this byte count (default 16 MiB).
    #[serde(default)]
    pub max_bytes: Option<u64>,
    /// Minimum number of recent entries retained verbatim (default 256).
    #[serde(default)]
    pub retain_tail_entries: Option<usize>,
    /// Number of prior verified generations/backups to retain (default 3).
    #[serde(default)]
    pub max_backups: Option<usize>,
}

/// Parameters for sm_search_proof_debt
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SearchProofDebtParams {
    /// 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>>,
    /// Proof-debt budget in micros for the gate check (default 500_000, one full proof unit).
    #[serde(default)]
    pub budget_micros: Option<u64>,
}

/// Parameters for sm_benchmark_trust
#[derive(Debug, Deserialize, JsonSchema)]
pub struct BenchmarkTrustParams {
    /// Number of benchmark queries to run (default 10).
    #[serde(default)]
    pub query_count: Option<u32>,
    /// Top-k for each search (default 5).
    #[serde(default)]
    pub top_k: Option<u32>,
    /// Optional namespace filter.
    #[serde(default)]
    pub namespaces: Option<Vec<String>>,
}

/// Parameters for sm_subgraph_prune
#[derive(Debug, Deserialize, JsonSchema)]
pub struct SubgraphPruneParams {
    /// When true, only identify subgraphs and report priority without pruning.
    /// Default: true (dry run).
    #[serde(default)]
    pub dry_run: Option<bool>,
    /// Maximum number of subgraphs to prune (default: 5).
    #[serde(default)]
    pub max_prune: Option<u32>,
}

// ─── 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>>,
}

/// Parameters for sm_replay_search using opt-in stored inputs.
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ReplayStoredSearchParams {
    /// The receipt/request ID whose stored inputs should be replayed.
    pub receipt_id: 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>,
}