codesynapse_core/ts_extract/
mcp_config.rs1use 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}