ruvector_data_framework/
mcp_server.rs

1//! MCP (Model Context Protocol) Server for RuVector Data Discovery
2//!
3//! Implements the Anthropic MCP specification (2024-11-05) with support for:
4//! - JSON-RPC 2.0 message handling
5//! - STDIO and SSE transports
6//! - 22+ discovery tools across research, medical, economic, and knowledge domains
7//! - Resources for discovered data access
8//! - Pre-built discovery prompts
9
10use std::collections::HashMap;
11use std::io::{self, BufRead, Write};
12use std::sync::Arc;
13use tokio::sync::RwLock;
14
15use serde::{Deserialize, Serialize};
16use serde_json::{json, Value};
17
18use crate::{
19    ArxivClient, BiorxivClient, ClinicalTrialsClient, CrossRefClient,
20    FdaClient, FredClient, MedrxivClient, NativeDiscoveryEngine,
21    NoaaClient, OpenAlexClient, PubMedClient, SemanticScholarClient,
22    SimpleEmbedder, WikidataClient, WikipediaClient, WorldBankClient,
23    NativeEngineConfig, Result, FrameworkError,
24};
25
26// ============================================================================
27// JSON-RPC 2.0 Message Types
28// ============================================================================
29
30/// JSON-RPC 2.0 Request
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct JsonRpcRequest {
33    pub jsonrpc: String,
34    pub id: Option<Value>,
35    pub method: String,
36    pub params: Option<Value>,
37}
38
39/// JSON-RPC 2.0 Response
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct JsonRpcResponse {
42    pub jsonrpc: String,
43    pub id: Option<Value>,
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub result: Option<Value>,
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub error: Option<JsonRpcError>,
48}
49
50/// JSON-RPC 2.0 Error
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct JsonRpcError {
53    pub code: i32,
54    pub message: String,
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub data: Option<Value>,
57}
58
59/// JSON-RPC Error Codes
60#[allow(dead_code)]
61impl JsonRpcError {
62    pub const PARSE_ERROR: i32 = -32700;
63    pub const INVALID_REQUEST: i32 = -32600;
64    pub const METHOD_NOT_FOUND: i32 = -32601;
65    pub const INVALID_PARAMS: i32 = -32602;
66    pub const INTERNAL_ERROR: i32 = -32603;
67
68    pub fn parse_error(msg: &str) -> Self {
69        Self { code: Self::PARSE_ERROR, message: msg.to_string(), data: None }
70    }
71
72    pub fn invalid_request(msg: &str) -> Self {
73        Self { code: Self::INVALID_REQUEST, message: msg.to_string(), data: None }
74    }
75
76    pub fn method_not_found(method: &str) -> Self {
77        Self {
78            code: Self::METHOD_NOT_FOUND,
79            message: format!("Method not found: {}", method),
80            data: None
81        }
82    }
83
84    pub fn invalid_params(msg: &str) -> Self {
85        Self { code: Self::INVALID_PARAMS, message: msg.to_string(), data: None }
86    }
87
88    pub fn internal_error(msg: &str) -> Self {
89        Self { code: Self::INTERNAL_ERROR, message: msg.to_string(), data: None }
90    }
91}
92
93// ============================================================================
94// MCP Protocol Types
95// ============================================================================
96
97/// MCP Server Capabilities
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct ServerCapabilities {
100    pub tools: ToolsCapability,
101    pub resources: ResourcesCapability,
102    pub prompts: PromptsCapability,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct ToolsCapability {
107    pub list_changed: bool,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct ResourcesCapability {
112    pub list_changed: bool,
113    pub subscribe: bool,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct PromptsCapability {
118    pub list_changed: bool,
119}
120
121/// MCP Tool Definition
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct ToolDefinition {
124    pub name: String,
125    pub description: String,
126    pub input_schema: Value,
127}
128
129/// MCP Resource Definition
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct ResourceDefinition {
132    pub uri: String,
133    pub name: String,
134    pub description: String,
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub mime_type: Option<String>,
137}
138
139/// MCP Prompt Definition
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct PromptDefinition {
142    pub name: String,
143    pub description: String,
144    #[serde(skip_serializing_if = "Option::is_none")]
145    pub arguments: Option<Vec<PromptArgument>>,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct PromptArgument {
150    pub name: String,
151    pub description: String,
152    pub required: bool,
153}
154
155// ============================================================================
156// Transport Layer
157// ============================================================================
158
159/// MCP Transport mechanism
160pub enum McpTransport {
161    /// Standard I/O transport (stdin/stdout)
162    Stdio,
163    /// Server-Sent Events (HTTP streaming)
164    Sse { endpoint: String, port: u16 },
165}
166
167// ============================================================================
168// Data Source Clients
169// ============================================================================
170
171/// Container for all data source API clients
172pub struct DataSourceClients {
173    pub openalex: Arc<OpenAlexClient>,
174    pub arxiv: Arc<ArxivClient>,
175    pub semantic_scholar: Arc<SemanticScholarClient>,
176    pub crossref: Arc<CrossRefClient>,
177    pub biorxiv: Arc<BiorxivClient>,
178    pub medrxiv: Arc<MedrxivClient>,
179    pub pubmed: Arc<PubMedClient>,
180    pub clinical_trials: Arc<ClinicalTrialsClient>,
181    pub fda: Arc<FdaClient>,
182    pub fred: Arc<FredClient>,
183    pub worldbank: Arc<WorldBankClient>,
184    pub noaa: Arc<NoaaClient>,
185    pub wikipedia: Arc<WikipediaClient>,
186    pub wikidata: Arc<WikidataClient>,
187    pub embedder: Arc<SimpleEmbedder>,
188}
189
190impl DataSourceClients {
191    /// Create new data source clients
192    pub fn new() -> Self {
193        Self {
194            openalex: Arc::new(OpenAlexClient::new(None).expect("Failed to create OpenAlex client")),
195            arxiv: Arc::new(ArxivClient::new()),
196            semantic_scholar: Arc::new(SemanticScholarClient::new(None)),
197            crossref: Arc::new(CrossRefClient::new(None)),
198            biorxiv: Arc::new(BiorxivClient::new()),
199            medrxiv: Arc::new(MedrxivClient::new()),
200            pubmed: Arc::new(PubMedClient::new(None).expect("Failed to create PubMed client")),
201            clinical_trials: Arc::new(ClinicalTrialsClient::new().expect("Failed to create ClinicalTrials client")),
202            fda: Arc::new(FdaClient::new().expect("Failed to create FDA client")),
203            fred: Arc::new(FredClient::new(None).expect("Failed to create FRED client")),
204            worldbank: Arc::new(WorldBankClient::new().expect("Failed to create WorldBank client")),
205            noaa: Arc::new(NoaaClient::new(None).expect("Failed to create NOAA client")),
206            wikipedia: Arc::new(WikipediaClient::new("en".to_string()).expect("Failed to create Wikipedia client")),
207            wikidata: Arc::new(WikidataClient::new().expect("Failed to create Wikidata client")),
208            embedder: Arc::new(SimpleEmbedder::new(384)),
209        }
210    }
211}
212
213impl Default for DataSourceClients {
214    fn default() -> Self {
215        Self::new()
216    }
217}
218
219// ============================================================================
220// MCP Server Configuration
221// ============================================================================
222
223/// MCP Server Configuration
224#[derive(Debug, Clone)]
225pub struct McpServerConfig {
226    pub name: String,
227    pub version: String,
228    pub max_request_size: usize,
229    pub rate_limit_per_minute: u32,
230}
231
232impl Default for McpServerConfig {
233    fn default() -> Self {
234        Self {
235            name: "ruvector-discovery-mcp".to_string(),
236            version: "1.0.0".to_string(),
237            max_request_size: 10_485_760, // 10MB
238            rate_limit_per_minute: 100,
239        }
240    }
241}
242
243// ============================================================================
244// MCP Discovery Server
245// ============================================================================
246
247/// MCP Server for RuVector Data Discovery
248pub struct McpDiscoveryServer {
249    transport: McpTransport,
250    engine: Arc<RwLock<NativeDiscoveryEngine>>,
251    clients: DataSourceClients,
252    config: McpServerConfig,
253    initialized: bool,
254    request_count: Arc<RwLock<HashMap<String, u32>>>,
255}
256
257impl McpDiscoveryServer {
258    /// Create a new MCP discovery server
259    pub fn new(transport: McpTransport, engine_config: NativeEngineConfig) -> Self {
260        Self {
261            transport,
262            engine: Arc::new(RwLock::new(NativeDiscoveryEngine::new(engine_config))),
263            clients: DataSourceClients::new(),
264            config: McpServerConfig::default(),
265            initialized: false,
266            request_count: Arc::new(RwLock::new(HashMap::new())),
267        }
268    }
269
270    /// Run the MCP server
271    pub async fn run(&mut self) -> Result<()> {
272        match &self.transport {
273            McpTransport::Stdio => self.run_stdio().await,
274            McpTransport::Sse { endpoint, port } => {
275                self.run_sse(endpoint.clone(), *port).await
276            }
277        }
278    }
279
280    /// Run STDIO transport
281    async fn run_stdio(&mut self) -> Result<()> {
282        let stdin = io::stdin();
283        let mut stdout = io::stdout();
284
285        eprintln!("RuVector MCP Server started (STDIO mode)");
286
287        for line in stdin.lock().lines() {
288            let line = line.map_err(|e| FrameworkError::Config(e.to_string()))?;
289
290            // Parse JSON-RPC request
291            let request: JsonRpcRequest = match serde_json::from_str(&line) {
292                Ok(req) => req,
293                Err(e) => {
294                    let error_response = JsonRpcResponse {
295                        jsonrpc: "2.0".to_string(),
296                        id: None,
297                        result: None,
298                        error: Some(JsonRpcError::parse_error(&e.to_string())),
299                    };
300                    let response_json = serde_json::to_string(&error_response)
301                        .map_err(|e| FrameworkError::Serialization(e))?;
302                    writeln!(stdout, "{}", response_json)
303                        .map_err(|e| FrameworkError::Config(e.to_string()))?;
304                    continue;
305                }
306            };
307
308            // Handle request
309            let response = self.handle_request(request).await;
310
311            // Send response
312            let response_json = serde_json::to_string(&response)
313                .map_err(|e| FrameworkError::Serialization(e))?;
314            writeln!(stdout, "{}", response_json)
315                .map_err(|e| FrameworkError::Config(e.to_string()))?;
316            stdout.flush()
317                .map_err(|e| FrameworkError::Config(e.to_string()))?;
318        }
319
320        Ok(())
321    }
322
323    /// Run SSE transport
324    async fn run_sse(&mut self, endpoint: String, port: u16) -> Result<()> {
325        #[cfg(feature = "sse")]
326        {
327            use warp::Filter;
328
329            eprintln!("RuVector MCP Server starting on {}:{} (SSE mode)", endpoint, port);
330
331            let server = Arc::new(RwLock::new(self));
332
333            // SSE endpoint
334            let sse_route = warp::path("sse")
335                .and(warp::get())
336                .map(|| {
337                    warp::sse::reply(warp::sse::keep_alive().stream(
338                        futures::stream::iter(vec![
339                            Ok::<_, warp::Error>(warp::sse::Event::default().data("connected"))
340                        ])
341                    ))
342                });
343
344            // Message endpoint
345            let server_clone = server.clone();
346            let message_route = warp::path("message")
347                .and(warp::post())
348                .and(warp::body::json())
349                .and_then(move |request: JsonRpcRequest| {
350                    let server = server_clone.clone();
351                    async move {
352                        let mut server = server.write().await;
353                        let response = server.handle_request(request).await;
354                        Ok::<_, warp::Rejection>(warp::reply::json(&response))
355                    }
356                });
357
358            let routes = sse_route.or(message_route);
359
360            warp::serve(routes)
361                .run(([127, 0, 0, 1], port))
362                .await;
363
364            Ok(())
365        }
366
367        #[cfg(not(feature = "sse"))]
368        {
369            let _ = (endpoint, port);
370            Err(FrameworkError::Config(
371                "SSE transport requires the 'sse' feature. Compile with --features sse".to_string()
372            ))
373        }
374    }
375
376    /// Handle a JSON-RPC request
377    async fn handle_request(&mut self, request: JsonRpcRequest) -> JsonRpcResponse {
378        // Validate JSON-RPC version
379        if request.jsonrpc != "2.0" {
380            return JsonRpcResponse {
381                jsonrpc: "2.0".to_string(),
382                id: request.id,
383                result: None,
384                error: Some(JsonRpcError::invalid_request("JSON-RPC version must be 2.0")),
385            };
386        }
387
388        // Handle method
389        let result = match request.method.as_str() {
390            "initialize" => self.handle_initialize(request.params).await,
391            "initialized" => Ok(json!({})),
392            "tools/list" => self.handle_tools_list().await,
393            "tools/call" => self.handle_tool_call(request.params).await,
394            "resources/list" => self.handle_resources_list().await,
395            "resources/read" => self.handle_resource_read(request.params).await,
396            "prompts/list" => self.handle_prompts_list().await,
397            "prompts/get" => self.handle_prompt_get(request.params).await,
398            _ => Err(JsonRpcError::method_not_found(&request.method)),
399        };
400
401        match result {
402            Ok(result) => JsonRpcResponse {
403                jsonrpc: "2.0".to_string(),
404                id: request.id,
405                result: Some(result),
406                error: None,
407            },
408            Err(error) => JsonRpcResponse {
409                jsonrpc: "2.0".to_string(),
410                id: request.id,
411                result: None,
412                error: Some(error),
413            },
414        }
415    }
416
417    /// Handle initialize request
418    async fn handle_initialize(&mut self, _params: Option<Value>) -> std::result::Result<Value, JsonRpcError> {
419        self.initialized = true;
420
421        Ok(json!({
422            "protocolVersion": "2024-11-05",
423            "serverInfo": {
424                "name": self.config.name,
425                "version": self.config.version,
426            },
427            "capabilities": ServerCapabilities {
428                tools: ToolsCapability { list_changed: false },
429                resources: ResourcesCapability { list_changed: false, subscribe: false },
430                prompts: PromptsCapability { list_changed: false },
431            }
432        }))
433    }
434
435    /// Handle tools/list request
436    async fn handle_tools_list(&self) -> std::result::Result<Value, JsonRpcError> {
437        let tools = vec![
438            // Research tools
439            self.tool_search_openalex(),
440            self.tool_search_arxiv(),
441            self.tool_search_semantic_scholar(),
442            self.tool_get_citations(),
443            self.tool_search_crossref(),
444            self.tool_search_biorxiv(),
445            self.tool_search_medrxiv(),
446
447            // Medical tools
448            self.tool_search_pubmed(),
449            self.tool_search_clinical_trials(),
450            self.tool_search_fda_events(),
451
452            // Economic tools
453            self.tool_get_fred_series(),
454            self.tool_get_worldbank_indicator(),
455
456            // Climate tools
457            self.tool_get_noaa_data(),
458
459            // Knowledge tools
460            self.tool_search_wikipedia(),
461            self.tool_query_wikidata(),
462
463            // Discovery tools
464            self.tool_run_discovery(),
465            self.tool_analyze_coherence(),
466            self.tool_detect_patterns(),
467            self.tool_export_graph(),
468        ];
469
470        Ok(json!({ "tools": tools }))
471    }
472
473    /// Handle tools/call request
474    async fn handle_tool_call(&mut self, params: Option<Value>) -> std::result::Result<Value, JsonRpcError> {
475        let params = params.ok_or_else(|| JsonRpcError::invalid_params("Missing params"))?;
476
477        let name = params.get("name")
478            .and_then(|v| v.as_str())
479            .ok_or_else(|| JsonRpcError::invalid_params("Missing tool name"))?;
480
481        let arguments = params.get("arguments")
482            .ok_or_else(|| JsonRpcError::invalid_params("Missing arguments"))?;
483
484        // Check rate limiting
485        self.check_rate_limit(name).await?;
486
487        // Execute tool
488        let result = match name {
489            "search_openalex" => self.execute_search_openalex(arguments).await,
490            "search_arxiv" => self.execute_search_arxiv(arguments).await,
491            "search_semantic_scholar" => self.execute_search_semantic_scholar(arguments).await,
492            "get_citations" => self.execute_get_citations(arguments).await,
493            "search_crossref" => self.execute_search_crossref(arguments).await,
494            "search_biorxiv" => self.execute_search_biorxiv(arguments).await,
495            "search_medrxiv" => self.execute_search_medrxiv(arguments).await,
496            "search_pubmed" => self.execute_search_pubmed(arguments).await,
497            "search_clinical_trials" => self.execute_search_clinical_trials(arguments).await,
498            "search_fda_events" => self.execute_search_fda_events(arguments).await,
499            "get_fred_series" => self.execute_get_fred_series(arguments).await,
500            "get_worldbank_indicator" => self.execute_get_worldbank_indicator(arguments).await,
501            "get_noaa_data" => self.execute_get_noaa_data(arguments).await,
502            "search_wikipedia" => self.execute_search_wikipedia(arguments).await,
503            "query_wikidata" => self.execute_query_wikidata(arguments).await,
504            "run_discovery" => self.execute_run_discovery(arguments).await,
505            "analyze_coherence" => self.execute_analyze_coherence(arguments).await,
506            "detect_patterns" => self.execute_detect_patterns(arguments).await,
507            "export_graph" => self.execute_export_graph(arguments).await,
508            _ => Err(JsonRpcError::method_not_found(name)),
509        };
510
511        result
512    }
513
514    /// Handle resources/list request
515    async fn handle_resources_list(&self) -> std::result::Result<Value, JsonRpcError> {
516        let resources = vec![
517            ResourceDefinition {
518                uri: "discovery://patterns".to_string(),
519                name: "Discovered Patterns".to_string(),
520                description: "Current discovered patterns from analysis".to_string(),
521                mime_type: Some("application/json".to_string()),
522            },
523            ResourceDefinition {
524                uri: "discovery://graph".to_string(),
525                name: "Coherence Graph".to_string(),
526                description: "Current coherence graph structure".to_string(),
527                mime_type: Some("application/json".to_string()),
528            },
529            ResourceDefinition {
530                uri: "discovery://history".to_string(),
531                name: "Coherence History".to_string(),
532                description: "Historical coherence signal data".to_string(),
533                mime_type: Some("application/json".to_string()),
534            },
535        ];
536
537        Ok(json!({ "resources": resources }))
538    }
539
540    /// Handle resources/read request
541    async fn handle_resource_read(&self, params: Option<Value>) -> std::result::Result<Value, JsonRpcError> {
542        let params = params.ok_or_else(|| JsonRpcError::invalid_params("Missing params"))?;
543
544        let uri = params.get("uri")
545            .and_then(|v| v.as_str())
546            .ok_or_else(|| JsonRpcError::invalid_params("Missing URI"))?;
547
548        let engine = self.engine.read().await;
549
550        let content = match uri {
551            "discovery://patterns" => {
552                let patterns = engine.get_patterns();
553                json!({
554                    "uri": uri,
555                    "mimeType": "application/json",
556                    "text": serde_json::to_string_pretty(&patterns)
557                        .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
558                })
559            },
560            "discovery://graph" => {
561                let graph = engine.export_graph();
562                json!({
563                    "uri": uri,
564                    "mimeType": "application/json",
565                    "text": serde_json::to_string_pretty(&graph)
566                        .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
567                })
568            },
569            "discovery://history" => {
570                let history = engine.get_coherence_history();
571                json!({
572                    "uri": uri,
573                    "mimeType": "application/json",
574                    "text": serde_json::to_string_pretty(&history)
575                        .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
576                })
577            },
578            _ => return Err(JsonRpcError::invalid_params(&format!("Unknown resource URI: {}", uri))),
579        };
580
581        Ok(json!({ "contents": [content] }))
582    }
583
584    /// Handle prompts/list request
585    async fn handle_prompts_list(&self) -> std::result::Result<Value, JsonRpcError> {
586        let prompts = vec![
587            PromptDefinition {
588                name: "cross_domain_discovery".to_string(),
589                description: "Discover patterns across multiple research domains".to_string(),
590                arguments: Some(vec![
591                    PromptArgument {
592                        name: "domains".to_string(),
593                        description: "Comma-separated list of domains (research,medical,climate,economic)".to_string(),
594                        required: true,
595                    },
596                    PromptArgument {
597                        name: "query".to_string(),
598                        description: "Search query to apply across domains".to_string(),
599                        required: true,
600                    },
601                ]),
602            },
603            PromptDefinition {
604                name: "citation_analysis".to_string(),
605                description: "Build and analyze citation networks".to_string(),
606                arguments: Some(vec![
607                    PromptArgument {
608                        name: "seed_paper_id".to_string(),
609                        description: "Starting paper ID (OpenAlex, Semantic Scholar, or DOI)".to_string(),
610                        required: true,
611                    },
612                    PromptArgument {
613                        name: "depth".to_string(),
614                        description: "Citation depth to traverse (default: 2)".to_string(),
615                        required: false,
616                    },
617                ]),
618            },
619            PromptDefinition {
620                name: "trend_detection".to_string(),
621                description: "Detect temporal patterns and trends".to_string(),
622                arguments: Some(vec![
623                    PromptArgument {
624                        name: "source".to_string(),
625                        description: "Data source (arxiv, pubmed, biorxiv, etc.)".to_string(),
626                        required: true,
627                    },
628                    PromptArgument {
629                        name: "query".to_string(),
630                        description: "Search query".to_string(),
631                        required: true,
632                    },
633                    PromptArgument {
634                        name: "days".to_string(),
635                        description: "Number of days to analyze (default: 30)".to_string(),
636                        required: false,
637                    },
638                ]),
639            },
640        ];
641
642        Ok(json!({ "prompts": prompts }))
643    }
644
645    /// Handle prompts/get request
646    async fn handle_prompt_get(&self, params: Option<Value>) -> std::result::Result<Value, JsonRpcError> {
647        let params = params.ok_or_else(|| JsonRpcError::invalid_params("Missing params"))?;
648
649        let name = params.get("name")
650            .and_then(|v| v.as_str())
651            .ok_or_else(|| JsonRpcError::invalid_params("Missing prompt name"))?;
652
653        let arguments = params.get("arguments")
654            .and_then(|v| v.as_object());
655
656        let messages = match name {
657            "cross_domain_discovery" => {
658                let domains = arguments
659                    .and_then(|a| a.get("domains"))
660                    .and_then(|v| v.as_str())
661                    .ok_or_else(|| JsonRpcError::invalid_params("Missing 'domains' argument"))?;
662                let query = arguments
663                    .and_then(|a| a.get("query"))
664                    .and_then(|v| v.as_str())
665                    .ok_or_else(|| JsonRpcError::invalid_params("Missing 'query' argument"))?;
666
667                vec![json!({
668                    "role": "user",
669                    "content": {
670                        "type": "text",
671                        "text": format!(
672                            "Perform cross-domain discovery across: {}\nQuery: {}\n\n\
673                            Steps:\n\
674                            1. Search each domain using run_discovery\n\
675                            2. Analyze coherence across domains\n\
676                            3. Detect emerging patterns\n\
677                            4. Export visualization graph",
678                            domains, query
679                        )
680                    }
681                })]
682            },
683            "citation_analysis" => {
684                let paper_id = arguments
685                    .and_then(|a| a.get("seed_paper_id"))
686                    .and_then(|v| v.as_str())
687                    .ok_or_else(|| JsonRpcError::invalid_params("Missing 'seed_paper_id' argument"))?;
688                let depth = arguments
689                    .and_then(|a| a.get("depth"))
690                    .and_then(|v| v.as_u64())
691                    .unwrap_or(2);
692
693                vec![json!({
694                    "role": "user",
695                    "content": {
696                        "type": "text",
697                        "text": format!(
698                            "Build citation network starting from paper: {}\nDepth: {}\n\n\
699                            Steps:\n\
700                            1. Get initial paper with get_citations\n\
701                            2. Recursively fetch citations up to depth {}\n\
702                            3. Build coherence graph\n\
703                            4. Analyze citation patterns\n\
704                            5. Export as GraphML for visualization",
705                            paper_id, depth, depth
706                        )
707                    }
708                })]
709            },
710            "trend_detection" => {
711                let source = arguments
712                    .and_then(|a| a.get("source"))
713                    .and_then(|v| v.as_str())
714                    .ok_or_else(|| JsonRpcError::invalid_params("Missing 'source' argument"))?;
715                let query = arguments
716                    .and_then(|a| a.get("query"))
717                    .and_then(|v| v.as_str())
718                    .ok_or_else(|| JsonRpcError::invalid_params("Missing 'query' argument"))?;
719                let days = arguments
720                    .and_then(|a| a.get("days"))
721                    .and_then(|v| v.as_u64())
722                    .unwrap_or(30);
723
724                vec![json!({
725                    "role": "user",
726                    "content": {
727                        "type": "text",
728                        "text": format!(
729                            "Detect trends in {} for query: {}\nTime window: {} days\n\n\
730                            Steps:\n\
731                            1. Fetch recent data from {}\n\
732                            2. Compute temporal coherence signals\n\
733                            3. Detect emerging/declining patterns\n\
734                            4. Generate trend forecast",
735                            source, query, days, source
736                        )
737                    }
738                })]
739            },
740            _ => return Err(JsonRpcError::method_not_found(name)),
741        };
742
743        Ok(json!({
744            "description": format!("Prompt: {}", name),
745            "messages": messages
746        }))
747    }
748
749    /// Check rate limiting
750    async fn check_rate_limit(&self, tool_name: &str) -> std::result::Result<(), JsonRpcError> {
751        let mut counts = self.request_count.write().await;
752        let count = counts.entry(tool_name.to_string()).or_insert(0);
753        *count += 1;
754
755        if *count > self.config.rate_limit_per_minute {
756            return Err(JsonRpcError::internal_error("Rate limit exceeded"));
757        }
758
759        Ok(())
760    }
761
762    // ========================================================================
763    // Tool Definitions
764    // ========================================================================
765
766    fn tool_search_openalex(&self) -> ToolDefinition {
767        ToolDefinition {
768            name: "search_openalex".to_string(),
769            description: "Search OpenAlex for research papers and scholarly works".to_string(),
770            input_schema: json!({
771                "type": "object",
772                "properties": {
773                    "query": { "type": "string", "description": "Search query" },
774                    "limit": { "type": "integer", "description": "Maximum results (default: 10)", "default": 10 }
775                },
776                "required": ["query"]
777            }),
778        }
779    }
780
781    fn tool_search_arxiv(&self) -> ToolDefinition {
782        ToolDefinition {
783            name: "search_arxiv".to_string(),
784            description: "Search arXiv preprint repository".to_string(),
785            input_schema: json!({
786                "type": "object",
787                "properties": {
788                    "query": { "type": "string", "description": "Search query" },
789                    "category": { "type": "string", "description": "arXiv category (e.g., cs.AI, physics.gen-ph)" },
790                    "limit": { "type": "integer", "description": "Maximum results", "default": 10 }
791                },
792                "required": ["query"]
793            }),
794        }
795    }
796
797    fn tool_search_semantic_scholar(&self) -> ToolDefinition {
798        ToolDefinition {
799            name: "search_semantic_scholar".to_string(),
800            description: "Search Semantic Scholar academic database".to_string(),
801            input_schema: json!({
802                "type": "object",
803                "properties": {
804                    "query": { "type": "string", "description": "Search query" },
805                    "limit": { "type": "integer", "description": "Maximum results", "default": 10 }
806                },
807                "required": ["query"]
808            }),
809        }
810    }
811
812    fn tool_get_citations(&self) -> ToolDefinition {
813        ToolDefinition {
814            name: "get_citations".to_string(),
815            description: "Get citations for a paper (Semantic Scholar or OpenAlex)".to_string(),
816            input_schema: json!({
817                "type": "object",
818                "properties": {
819                    "paper_id": { "type": "string", "description": "Paper ID or DOI" }
820                },
821                "required": ["paper_id"]
822            }),
823        }
824    }
825
826    fn tool_search_crossref(&self) -> ToolDefinition {
827        ToolDefinition {
828            name: "search_crossref".to_string(),
829            description: "Search CrossRef DOI database".to_string(),
830            input_schema: json!({
831                "type": "object",
832                "properties": {
833                    "query": { "type": "string", "description": "Search query" },
834                    "limit": { "type": "integer", "description": "Maximum results", "default": 10 }
835                },
836                "required": ["query"]
837            }),
838        }
839    }
840
841    fn tool_search_biorxiv(&self) -> ToolDefinition {
842        ToolDefinition {
843            name: "search_biorxiv".to_string(),
844            description: "Search bioRxiv preprints".to_string(),
845            input_schema: json!({
846                "type": "object",
847                "properties": {
848                    "category": { "type": "string", "description": "Category (e.g., neuroscience, bioinformatics)" },
849                    "days": { "type": "integer", "description": "Days back to search", "default": 7 }
850                },
851                "required": ["category"]
852            }),
853        }
854    }
855
856    fn tool_search_medrxiv(&self) -> ToolDefinition {
857        ToolDefinition {
858            name: "search_medrxiv".to_string(),
859            description: "Search medRxiv medical preprints".to_string(),
860            input_schema: json!({
861                "type": "object",
862                "properties": {
863                    "query": { "type": "string", "description": "Search query" },
864                    "days": { "type": "integer", "description": "Days back to search", "default": 7 }
865                },
866                "required": ["query"]
867            }),
868        }
869    }
870
871    fn tool_search_pubmed(&self) -> ToolDefinition {
872        ToolDefinition {
873            name: "search_pubmed".to_string(),
874            description: "Search PubMed medical literature database".to_string(),
875            input_schema: json!({
876                "type": "object",
877                "properties": {
878                    "query": { "type": "string", "description": "Search query" },
879                    "limit": { "type": "integer", "description": "Maximum results", "default": 10 }
880                },
881                "required": ["query"]
882            }),
883        }
884    }
885
886    fn tool_search_clinical_trials(&self) -> ToolDefinition {
887        ToolDefinition {
888            name: "search_clinical_trials".to_string(),
889            description: "Search ClinicalTrials.gov".to_string(),
890            input_schema: json!({
891                "type": "object",
892                "properties": {
893                    "condition": { "type": "string", "description": "Medical condition" },
894                    "status": { "type": "string", "description": "Trial status (e.g., recruiting, completed)" }
895                },
896                "required": ["condition"]
897            }),
898        }
899    }
900
901    fn tool_search_fda_events(&self) -> ToolDefinition {
902        ToolDefinition {
903            name: "search_fda_events".to_string(),
904            description: "Search FDA adverse event reports".to_string(),
905            input_schema: json!({
906                "type": "object",
907                "properties": {
908                    "drug_name": { "type": "string", "description": "Drug name to search" }
909                },
910                "required": ["drug_name"]
911            }),
912        }
913    }
914
915    fn tool_get_fred_series(&self) -> ToolDefinition {
916        ToolDefinition {
917            name: "get_fred_series".to_string(),
918            description: "Get Federal Reserve Economic Data (FRED) time series".to_string(),
919            input_schema: json!({
920                "type": "object",
921                "properties": {
922                    "series_id": { "type": "string", "description": "FRED series ID (e.g., GDP, UNRATE)" }
923                },
924                "required": ["series_id"]
925            }),
926        }
927    }
928
929    fn tool_get_worldbank_indicator(&self) -> ToolDefinition {
930        ToolDefinition {
931            name: "get_worldbank_indicator".to_string(),
932            description: "Get World Bank development indicators".to_string(),
933            input_schema: json!({
934                "type": "object",
935                "properties": {
936                    "country": { "type": "string", "description": "Country code (e.g., USA, CHN)" },
937                    "indicator": { "type": "string", "description": "Indicator code (e.g., NY.GDP.MKTP.CD)" }
938                },
939                "required": ["country", "indicator"]
940            }),
941        }
942    }
943
944    fn tool_get_noaa_data(&self) -> ToolDefinition {
945        ToolDefinition {
946            name: "get_noaa_data".to_string(),
947            description: "Get NOAA climate data".to_string(),
948            input_schema: json!({
949                "type": "object",
950                "properties": {
951                    "station": { "type": "string", "description": "Weather station ID" },
952                    "start_date": { "type": "string", "description": "Start date (YYYY-MM-DD)" },
953                    "end_date": { "type": "string", "description": "End date (YYYY-MM-DD)" }
954                },
955                "required": ["station", "start_date", "end_date"]
956            }),
957        }
958    }
959
960    fn tool_search_wikipedia(&self) -> ToolDefinition {
961        ToolDefinition {
962            name: "search_wikipedia".to_string(),
963            description: "Search Wikipedia articles".to_string(),
964            input_schema: json!({
965                "type": "object",
966                "properties": {
967                    "query": { "type": "string", "description": "Search query" },
968                    "language": { "type": "string", "description": "Language code (default: en)", "default": "en" }
969                },
970                "required": ["query"]
971            }),
972        }
973    }
974
975    fn tool_query_wikidata(&self) -> ToolDefinition {
976        ToolDefinition {
977            name: "query_wikidata".to_string(),
978            description: "Query Wikidata knowledge graph using SPARQL".to_string(),
979            input_schema: json!({
980                "type": "object",
981                "properties": {
982                    "sparql_query": { "type": "string", "description": "SPARQL query string" }
983                },
984                "required": ["sparql_query"]
985            }),
986        }
987    }
988
989    fn tool_run_discovery(&self) -> ToolDefinition {
990        ToolDefinition {
991            name: "run_discovery".to_string(),
992            description: "Run discovery engine across multiple data sources".to_string(),
993            input_schema: json!({
994                "type": "object",
995                "properties": {
996                    "sources": {
997                        "type": "array",
998                        "items": { "type": "string" },
999                        "description": "Data sources to query"
1000                    },
1001                    "query": { "type": "string", "description": "Discovery query" }
1002                },
1003                "required": ["sources", "query"]
1004            }),
1005        }
1006    }
1007
1008    fn tool_analyze_coherence(&self) -> ToolDefinition {
1009        ToolDefinition {
1010            name: "analyze_coherence".to_string(),
1011            description: "Analyze coherence of vector embeddings".to_string(),
1012            input_schema: json!({
1013                "type": "object",
1014                "properties": {
1015                    "vectors": {
1016                        "type": "array",
1017                        "items": {
1018                            "type": "object",
1019                            "properties": {
1020                                "id": { "type": "string" },
1021                                "embedding": { "type": "array", "items": { "type": "number" } }
1022                            }
1023                        },
1024                        "description": "Vector embeddings to analyze"
1025                    }
1026                },
1027                "required": ["vectors"]
1028            }),
1029        }
1030    }
1031
1032    fn tool_detect_patterns(&self) -> ToolDefinition {
1033        ToolDefinition {
1034            name: "detect_patterns".to_string(),
1035            description: "Detect patterns in coherence signals".to_string(),
1036            input_schema: json!({
1037                "type": "object",
1038                "properties": {
1039                    "signals": {
1040                        "type": "array",
1041                        "items": {
1042                            "type": "object",
1043                            "properties": {
1044                                "window_id": { "type": "integer" },
1045                                "score": { "type": "number" }
1046                            }
1047                        },
1048                        "description": "Coherence signals to analyze"
1049                    }
1050                },
1051                "required": ["signals"]
1052            }),
1053        }
1054    }
1055
1056    fn tool_export_graph(&self) -> ToolDefinition {
1057        ToolDefinition {
1058            name: "export_graph".to_string(),
1059            description: "Export coherence graph in various formats".to_string(),
1060            input_schema: json!({
1061                "type": "object",
1062                "properties": {
1063                    "format": {
1064                        "type": "string",
1065                        "enum": ["graphml", "dot", "csv"],
1066                        "description": "Export format"
1067                    }
1068                },
1069                "required": ["format"]
1070            }),
1071        }
1072    }
1073
1074    // ========================================================================
1075    // Tool Executions
1076    // ========================================================================
1077
1078    async fn execute_search_openalex(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1079        let query = args.get("query")
1080            .and_then(|v| v.as_str())
1081            .ok_or_else(|| JsonRpcError::invalid_params("Missing query"))?;
1082        let limit = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(10) as usize;
1083
1084        let results = self.clients.openalex.fetch_works(query, limit).await
1085            .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?;
1086
1087        Ok(json!({
1088            "content": [{
1089                "type": "text",
1090                "text": serde_json::to_string_pretty(&results)
1091                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1092            }]
1093        }))
1094    }
1095
1096    async fn execute_search_arxiv(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1097        let query = args.get("query")
1098            .and_then(|v| v.as_str())
1099            .ok_or_else(|| JsonRpcError::invalid_params("Missing query"))?;
1100        let limit = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(10) as usize;
1101
1102        let results = self.clients.arxiv.search(query, limit).await
1103            .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?;
1104
1105        Ok(json!({
1106            "content": [{
1107                "type": "text",
1108                "text": serde_json::to_string_pretty(&results)
1109                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1110            }]
1111        }))
1112    }
1113
1114    async fn execute_search_semantic_scholar(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1115        let query = args.get("query")
1116            .and_then(|v| v.as_str())
1117            .ok_or_else(|| JsonRpcError::invalid_params("Missing query"))?;
1118        let limit = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(10) as usize;
1119
1120        let results = self.clients.semantic_scholar.search_papers(query, limit).await
1121            .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?;
1122
1123        Ok(json!({
1124            "content": [{
1125                "type": "text",
1126                "text": serde_json::to_string_pretty(&results)
1127                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1128            }]
1129        }))
1130    }
1131
1132    async fn execute_get_citations(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1133        let paper_id = args.get("paper_id")
1134            .and_then(|v| v.as_str())
1135            .ok_or_else(|| JsonRpcError::invalid_params("Missing paper_id"))?;
1136
1137        let citations = self.clients.semantic_scholar.get_citations(paper_id, 100).await
1138            .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?;
1139
1140        Ok(json!({
1141            "content": [{
1142                "type": "text",
1143                "text": serde_json::to_string_pretty(&citations)
1144                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1145            }]
1146        }))
1147    }
1148
1149    async fn execute_search_crossref(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1150        let query = args.get("query")
1151            .and_then(|v| v.as_str())
1152            .ok_or_else(|| JsonRpcError::invalid_params("Missing query"))?;
1153        let limit = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(10) as usize;
1154
1155        let results = self.clients.crossref.search_works(query, limit).await
1156            .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?;
1157
1158        Ok(json!({
1159            "content": [{
1160                "type": "text",
1161                "text": serde_json::to_string_pretty(&results)
1162                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1163            }]
1164        }))
1165    }
1166
1167    async fn execute_search_biorxiv(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1168        let _category = args.get("category")
1169            .and_then(|v| v.as_str())
1170            .ok_or_else(|| JsonRpcError::invalid_params("Missing category"))?;
1171        let days = args.get("days").and_then(|v| v.as_u64()).unwrap_or(7);
1172        let limit = 10; // Default limit
1173
1174        let results = self.clients.biorxiv.search_recent(days, limit).await
1175            .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?;
1176
1177        Ok(json!({
1178            "content": [{
1179                "type": "text",
1180                "text": serde_json::to_string_pretty(&results)
1181                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1182            }]
1183        }))
1184    }
1185
1186    async fn execute_search_medrxiv(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1187        let _query = args.get("query")
1188            .and_then(|v| v.as_str())
1189            .ok_or_else(|| JsonRpcError::invalid_params("Missing query"))?;
1190        let days = args.get("days").and_then(|v| v.as_u64()).unwrap_or(7);
1191        let limit = 10; // Default limit
1192
1193        let results = self.clients.medrxiv.search_recent(days, limit).await
1194            .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?;
1195
1196        Ok(json!({
1197            "content": [{
1198                "type": "text",
1199                "text": serde_json::to_string_pretty(&results)
1200                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1201            }]
1202        }))
1203    }
1204
1205    async fn execute_search_pubmed(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1206        let query = args.get("query")
1207            .and_then(|v| v.as_str())
1208            .ok_or_else(|| JsonRpcError::invalid_params("Missing query"))?;
1209        let limit = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(10) as usize;
1210
1211        let results = self.clients.pubmed.search_articles(query, limit).await
1212            .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?;
1213
1214        Ok(json!({
1215            "content": [{
1216                "type": "text",
1217                "text": serde_json::to_string_pretty(&results)
1218                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1219            }]
1220        }))
1221    }
1222
1223    async fn execute_search_clinical_trials(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1224        let condition = args.get("condition")
1225            .and_then(|v| v.as_str())
1226            .ok_or_else(|| JsonRpcError::invalid_params("Missing condition"))?;
1227        let status = args.get("status").and_then(|v| v.as_str());
1228
1229        let results = self.clients.clinical_trials.search_trials(condition, status).await
1230            .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?;
1231
1232        Ok(json!({
1233            "content": [{
1234                "type": "text",
1235                "text": serde_json::to_string_pretty(&results)
1236                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1237            }]
1238        }))
1239    }
1240
1241    async fn execute_search_fda_events(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1242        let drug_name = args.get("drug_name")
1243            .and_then(|v| v.as_str())
1244            .ok_or_else(|| JsonRpcError::invalid_params("Missing drug_name"))?;
1245
1246        let results = self.clients.fda.search_drug_events(drug_name).await
1247            .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?;
1248
1249        Ok(json!({
1250            "content": [{
1251                "type": "text",
1252                "text": serde_json::to_string_pretty(&results)
1253                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1254            }]
1255        }))
1256    }
1257
1258    async fn execute_get_fred_series(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1259        let series_id = args.get("series_id")
1260            .and_then(|v| v.as_str())
1261            .ok_or_else(|| JsonRpcError::invalid_params("Missing series_id"))?;
1262        let limit = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(100) as usize;
1263
1264        let results = self.clients.fred.get_series(series_id, Some(limit)).await
1265            .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?;
1266
1267        Ok(json!({
1268            "content": [{
1269                "type": "text",
1270                "text": serde_json::to_string_pretty(&results)
1271                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1272            }]
1273        }))
1274    }
1275
1276    async fn execute_get_worldbank_indicator(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1277        let country = args.get("country")
1278            .and_then(|v| v.as_str())
1279            .ok_or_else(|| JsonRpcError::invalid_params("Missing country"))?;
1280        let indicator = args.get("indicator")
1281            .and_then(|v| v.as_str())
1282            .ok_or_else(|| JsonRpcError::invalid_params("Missing indicator"))?;
1283
1284        let results = self.clients.worldbank.get_indicator(country, indicator).await
1285            .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?;
1286
1287        Ok(json!({
1288            "content": [{
1289                "type": "text",
1290                "text": serde_json::to_string_pretty(&results)
1291                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1292            }]
1293        }))
1294    }
1295
1296    async fn execute_get_noaa_data(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1297        let station_id = args.get("station_id")
1298            .and_then(|v| v.as_str())
1299            .ok_or_else(|| JsonRpcError::invalid_params("Missing station_id"))?;
1300        let start_date = args.get("start_date")
1301            .and_then(|v| v.as_str())
1302            .ok_or_else(|| JsonRpcError::invalid_params("Missing start_date"))?;
1303        let end_date = args.get("end_date")
1304            .and_then(|v| v.as_str())
1305            .ok_or_else(|| JsonRpcError::invalid_params("Missing end_date"))?;
1306
1307        let results = self.clients.noaa.fetch_climate_data(station_id, start_date, end_date).await
1308            .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?;
1309
1310        Ok(json!({
1311            "content": [{
1312                "type": "text",
1313                "text": serde_json::to_string_pretty(&results)
1314                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1315            }]
1316        }))
1317    }
1318
1319    async fn execute_search_wikipedia(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1320        let query = args.get("query")
1321            .and_then(|v| v.as_str())
1322            .ok_or_else(|| JsonRpcError::invalid_params("Missing query"))?;
1323        let limit = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(10) as usize;
1324
1325        let results = self.clients.wikipedia.search(query, limit).await
1326            .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?;
1327
1328        Ok(json!({
1329            "content": [{
1330                "type": "text",
1331                "text": serde_json::to_string_pretty(&results)
1332                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1333            }]
1334        }))
1335    }
1336
1337    async fn execute_query_wikidata(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1338        let sparql_query = args.get("sparql_query")
1339            .and_then(|v| v.as_str())
1340            .ok_or_else(|| JsonRpcError::invalid_params("Missing sparql_query"))?;
1341
1342        let results = self.clients.wikidata.sparql_query(sparql_query).await
1343            .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?;
1344
1345        Ok(json!({
1346            "content": [{
1347                "type": "text",
1348                "text": serde_json::to_string_pretty(&results)
1349                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1350            }]
1351        }))
1352    }
1353
1354    async fn execute_run_discovery(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1355        let _sources = args.get("sources")
1356            .and_then(|v| v.as_array())
1357            .ok_or_else(|| JsonRpcError::invalid_params("Missing sources array"))?;
1358        let query = args.get("query")
1359            .and_then(|v| v.as_str())
1360            .ok_or_else(|| JsonRpcError::invalid_params("Missing query"))?;
1361
1362        // TODO: Implement multi-source discovery
1363        Ok(json!({
1364            "content": [{
1365                "type": "text",
1366                "text": format!("Discovery query '{}' across sources", query)
1367            }]
1368        }))
1369    }
1370
1371    async fn execute_analyze_coherence(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1372        let _vectors = args.get("vectors")
1373            .and_then(|v| v.as_array())
1374            .ok_or_else(|| JsonRpcError::invalid_params("Missing vectors array"))?;
1375
1376        // TODO: Implement coherence analysis
1377        Ok(json!({
1378            "content": [{
1379                "type": "text",
1380                "text": "Coherence analysis complete"
1381            }]
1382        }))
1383    }
1384
1385    async fn execute_detect_patterns(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1386        let _signals = args.get("signals")
1387            .and_then(|v| v.as_array())
1388            .ok_or_else(|| JsonRpcError::invalid_params("Missing signals array"))?;
1389
1390        let engine = self.engine.read().await;
1391        let patterns = engine.get_patterns();
1392
1393        Ok(json!({
1394            "content": [{
1395                "type": "text",
1396                "text": serde_json::to_string_pretty(&patterns)
1397                    .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?
1398            }]
1399        }))
1400    }
1401
1402    async fn execute_export_graph(&self, args: &Value) -> std::result::Result<Value, JsonRpcError> {
1403        let format = args.get("format")
1404            .and_then(|v| v.as_str())
1405            .ok_or_else(|| JsonRpcError::invalid_params("Missing format"))?;
1406
1407        let engine = self.engine.read().await;
1408        let graph = engine.export_graph();
1409
1410        let exported = match format {
1411            "graphml" => serde_json::to_string_pretty(&json!({ "format": "graphml", "graph": graph }))
1412                .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?,
1413            "dot" => serde_json::to_string_pretty(&json!({ "format": "dot", "graph": graph }))
1414                .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?,
1415            "csv" => serde_json::to_string_pretty(&json!({ "format": "csv", "graph": graph }))
1416                .map_err(|e| JsonRpcError::internal_error(&e.to_string()))?,
1417            _ => return Err(JsonRpcError::invalid_params("Invalid format")),
1418        };
1419
1420        Ok(json!({
1421            "content": [{
1422                "type": "text",
1423                "text": exported
1424            }]
1425        }))
1426    }
1427}
1428
1429#[cfg(test)]
1430mod tests {
1431    use super::*;
1432
1433    #[test]
1434    fn test_jsonrpc_error_codes() {
1435        let err = JsonRpcError::parse_error("test");
1436        assert_eq!(err.code, JsonRpcError::PARSE_ERROR);
1437
1438        let err = JsonRpcError::method_not_found("test_method");
1439        assert_eq!(err.code, JsonRpcError::METHOD_NOT_FOUND);
1440        assert!(err.message.contains("test_method"));
1441    }
1442
1443    #[test]
1444    fn test_server_capabilities() {
1445        let caps = ServerCapabilities {
1446            tools: ToolsCapability { list_changed: false },
1447            resources: ResourcesCapability { list_changed: false, subscribe: false },
1448            prompts: PromptsCapability { list_changed: false },
1449        };
1450
1451        let json = serde_json::to_value(&caps).unwrap();
1452        assert!(json.get("tools").is_some());
1453        assert!(json.get("resources").is_some());
1454        assert!(json.get("prompts").is_some());
1455    }
1456}