Skip to main content

synapse_core/
mcp_stdio.rs

1use crate::server::semantic_engine::semantic_engine_server::SemanticEngine;
2use crate::server::MySemanticEngine;
3use serde::Deserialize;
4use serde_json::json;
5use std::sync::Arc;
6use tokio::io::{stdin, AsyncBufReadExt, BufReader};
7
8#[derive(Debug, Deserialize)]
9struct JsonRpcRequest {
10    _jsonrpc: String,
11    method: String,
12    params: Option<serde_json::Value>,
13    id: Option<serde_json::Value>,
14}
15
16pub async fn run_mcp_stdio(
17    engine: Arc<MySemanticEngine>,
18) -> Result<(), Box<dyn std::error::Error>> {
19    let mut reader = BufReader::new(stdin()).lines();
20
21    while let Some(line) = reader.next_line().await? {
22        let request: JsonRpcRequest = match serde_json::from_str(&line) {
23            Ok(req) => req,
24            Err(_) => continue,
25        };
26
27        let response = match request.method.as_str() {
28            "initialize" => json!({
29                "jsonrpc": "2.0",
30                "id": request.id,
31                "result": {
32                    "protocolVersion": "2024-11-05",
33                    "capabilities": {
34                        "tools": {}
35                    },
36                    "serverInfo": {
37                        "name": "Synapse-MCP",
38                        "version": "0.1.0"
39                    }
40                }
41            }),
42            "tools/list" => json!({
43                "jsonrpc": "2.0",
44                "id": request.id,
45                "result": {
46                    "tools": [
47                        {
48                            "name": "query_graph",
49                            "description": "Busca triples en el grafo de conocimiento",
50                            "inputSchema": {
51                                "type": "object",
52                                "properties": {
53                                    "namespace": { "type": "string", "default": "robin_os" }
54                                }
55                            }
56                        },
57                        {
58                            "name": "ingest_triple",
59                            "description": "AƱade un nuevo triple (sujeto, predicado, objeto) al grafo",
60                            "inputSchema": {
61                                "type": "object",
62                                "properties": {
63                                    "subject": { "type": "string" },
64                                    "predicate": { "type": "string" },
65                                    "object": { "type": "string" },
66                                    "namespace": { "type": "string", "default": "robin_os" }
67                                },
68                                "required": ["subject", "predicate", "object"]
69                            }
70                        },
71                        {
72                            "name": "query_sparql",
73                            "description": "Ejecuta una consulta SPARQL sobre el grafo",
74                            "inputSchema": {
75                                "type": "object",
76                                "properties": {
77                                    "query": { "type": "string" },
78                                    "namespace": { "type": "string", "default": "robin_os" }
79                                },
80                                "required": ["query"]
81                            }
82                        }
83                    ]
84                }
85            }),
86            "tools/call" => {
87                let params = request.params.unwrap_or(json!({}));
88                let name = params["name"].as_str().unwrap_or("");
89                let args = &params["arguments"];
90
91                match name {
92                    "query_graph" => {
93                        let namespace = args["namespace"].as_str().unwrap_or("robin_os");
94                        let triples = engine
95                            .get_all_triples(tonic::Request::new(
96                                crate::server::semantic_engine::EmptyRequest {
97                                    namespace: namespace.to_string(),
98                                },
99                            ))
100                            .await?;
101
102                        let triples_text = triples
103                            .into_inner()
104                            .triples
105                            .into_iter()
106                            .map(|t| format!("({}, {}, {})", t.subject, t.predicate, t.object))
107                            .collect::<Vec<_>>()
108                            .join("\n");
109
110                        json!({
111                            "jsonrpc": "2.0",
112                            "id": request.id,
113                            "result": {
114                                "content": [{ "type": "text", "text": triples_text }]
115                            }
116                        })
117                    }
118                    "ingest_triple" => {
119                        let sub = args["subject"].as_str().unwrap_or("");
120                        let pred = args["predicate"].as_str().unwrap_or("");
121                        let obj = args["object"].as_str().unwrap_or("");
122                        let namespace = args["namespace"].as_str().unwrap_or("robin_os");
123
124                        let triple = crate::server::semantic_engine::Triple {
125                            subject: sub.to_string(),
126                            predicate: pred.to_string(),
127                            object: obj.to_string(),
128                            provenance: None,
129                        };
130
131                        engine
132                            .ingest_triples(tonic::Request::new(
133                                crate::server::semantic_engine::IngestRequest {
134                                    triples: vec![triple],
135                                    namespace: namespace.to_string(),
136                                },
137                            ))
138                            .await?;
139
140                        json!({
141                            "jsonrpc": "2.0",
142                            "id": request.id,
143                            "result": {
144                                "content": [{ "type": "text", "text": format!("Triple ({}, {}, {}) ingerido correctamente en Synapse", sub, pred, obj) }]
145                            }
146                        })
147                    }
148                    "query_sparql" => {
149                        let query = args["query"].as_str().unwrap_or("");
150                        let namespace = args["namespace"].as_str().unwrap_or("robin_os");
151
152                        let res = engine
153                            .query_sparql(tonic::Request::new(
154                                crate::server::semantic_engine::SparqlRequest {
155                                    query: query.to_string(),
156                                    namespace: namespace.to_string(),
157                                },
158                            ))
159                            .await?;
160
161                        json!({
162                            "jsonrpc": "2.0",
163                            "id": request.id,
164                            "result": {
165                                "content": [{ "type": "text", "text": res.into_inner().results_json }]
166                            }
167                        })
168                    }
169                    _ => json!({
170                        "jsonrpc": "2.0",
171                        "id": request.id,
172                        "error": { "code": -32601, "message": "Tool not found" }
173                    }),
174                }
175            }
176            _ => json!({
177                "jsonrpc": "2.0",
178                "id": request.id,
179                "result": {}
180            }),
181        };
182
183        println!("{}", serde_json::to_string(&response)?);
184    }
185
186    Ok(())
187}