refactor: Extract shared MCP Stdio NDJSON parsing loop into mcp-stdio library
This commit is contained in:
1 parent
f8c0d58415
commit
9009f127a9
8 files changed
+77
-80
No files matched your search
+14
-45
@@ -20,47 +20,7 @@ pub struct JsonRpcResponse {
|
||||
pub error: Option<Value>,
|
||||
}
|
||||
|
||||
pub async fn read_message<R: tokio::io::AsyncRead + Unpin>(
|
||||
stdin: &mut BufReader<R>,
|
||||
) -> Option<JsonRpcRequest> {
|
||||
let mut length = 0;
|
||||
loop {
|
||||
let mut line = String::new();
|
||||
if stdin.read_line(&mut line).await.unwrap_or(0) == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
if line.starts_with('{') {
|
||||
return match serde_json::from_str::<JsonRpcRequest>(line.trim_end()) {
|
||||
Ok(req) => Some(req),
|
||||
Err(e) => {
|
||||
tracing::error!(
|
||||
"Failed to parse JSON-RPC request from JSONL: {}. Payload: {}",
|
||||
e,
|
||||
line
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let line = line.trim_end();
|
||||
if line.is_empty() {
|
||||
break;
|
||||
}
|
||||
let lower_line = line.to_lowercase();
|
||||
if let Some(len_str) = lower_line.strip_prefix("content-length:") {
|
||||
length = len_str.trim().parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
if length == 0 {
|
||||
return None;
|
||||
}
|
||||
let mut buffer = vec![0; length];
|
||||
stdin.read_exact(&mut buffer).await.unwrap_or(0);
|
||||
|
||||
serde_json::from_slice(&buffer).ok()
|
||||
}
|
||||
|
||||
pub async fn send_response(response: JsonRpcResponse) {
|
||||
let msg = serde_json::to_string(&response).unwrap();
|
||||
@@ -508,17 +468,25 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
|
||||
tracing::info!("{} MCP server started", app_name);
|
||||
let mut stdin = tokio::io::BufReader::new(tokio::io::stdin());
|
||||
loop {
|
||||
let msg = match read_message(&mut stdin).await {
|
||||
Some(m) => {
|
||||
tracing::info!("Received message method: {}", m.method);
|
||||
m
|
||||
}
|
||||
let raw_msg = match mcp_stdio::read_mcp_message(&mut stdin).await {
|
||||
Some(m) => m,
|
||||
None => {
|
||||
tracing::info!("Stdin closed, exiting loop");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
let msg = match serde_json::from_str::<JsonRpcRequest>(&raw_msg) {
|
||||
Ok(m) => {
|
||||
tracing::info!("Received message method: {}", m.method);
|
||||
m
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to parse JSON-RPC request from JSONL: {}. Payload: {}", e, raw_msg);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let app_name = app_name.to_string();
|
||||
let app_version = app_version.to_string();
|
||||
|
||||
@@ -915,3 +883,4 @@ mod tests {
|
||||
assert!(req.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user