Skip to main content

codesynapse_core/ts_extract/
mcp_config.rs

1use super::{add_contains_edge, add_node_if_missing, make_file_node};
2use crate::error::Result;
3use crate::extract::{make_id, ImportNode, LanguageExtractor};
4use crate::types::{Edge, ExtractionFragment, Node};
5use std::collections::HashMap;
6use std::path::Path;
7
8pub struct McpConfigExtractor;
9
10impl McpConfigExtractor {
11    pub fn extract(source: &[u8], path: &Path) -> Result<ExtractionFragment> {
12        let (file_id, _, file_node) = make_file_node(path);
13        let mut fragment = ExtractionFragment {
14            nodes: vec![file_node],
15            edges: vec![],
16        };
17
18        let content = std::str::from_utf8(source).unwrap_or("");
19
20        if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(content) {
21            if let Some(servers) = parsed.get("servers").and_then(|s| s.as_array()) {
22                for server in servers {
23                    if let Some(name) = server.get("name").and_then(|n| n.as_str()) {
24                        let server_id = make_id(&[&file_id, name]);
25                        fragment.nodes.push(Node {
26                            id: server_id.clone(),
27                            label: name.to_string(),
28                            file_type: "config".to_string(),
29                            source_file: path.to_string_lossy().to_string(),
30                            source_location: None,
31                            community: None,
32                            rationale: None,
33                            docstring: None,
34                            metadata: HashMap::new(),
35                        });
36                        add_contains_edge(&mut fragment, &file_id, server_id.clone(), path);
37
38                        if let Some(env) = server.get("env").and_then(|e| e.as_object()) {
39                            for (env_key, _env_val) in env {
40                                let env_id = make_id(&[&server_id, env_key]);
41                                let env_node = Node {
42                                    id: env_id.clone(),
43                                    label: format!("env:{}", env_key),
44                                    file_type: "config".to_string(),
45                                    source_file: path.to_string_lossy().to_string(),
46                                    source_location: None,
47                                    community: None,
48                                    rationale: None,
49                                    docstring: None,
50                                    metadata: {
51                                        let mut m = HashMap::new();
52                                        m.insert("kind".to_string(), "env_var".to_string());
53                                        m
54                                    },
55                                };
56                                add_node_if_missing(&mut fragment, env_node);
57                                fragment.edges.push(Edge {
58                                    source: server_id.clone(),
59                                    target: env_id,
60                                    relation: "requires".to_string(),
61                                    confidence: "EXTRACTED".to_string(),
62                                    source_file: Some(path.to_string_lossy().to_string()),
63                                    weight: 1.0,
64                                    context: None,
65                                });
66                            }
67                        }
68                    }
69                }
70            }
71        }
72
73        Ok(fragment)
74    }
75}
76
77impl LanguageExtractor for McpConfigExtractor {
78    fn file_extensions(&self) -> Vec<&'static str> {
79        vec!["json"]
80    }
81    fn extract(&self, source: &[u8], path: &Path) -> Result<ExtractionFragment> {
82        Self::extract(source, path)
83    }
84    fn resolve_imports(&self, _imports: &[ImportNode]) -> Vec<Edge> {
85        vec![]
86    }
87    fn collect_type_refs(&self, _fragment: &mut ExtractionFragment) {}
88}