cratedocs-mcp/examples/client.rs
2025-03-12 14:52:36 -07:00

144 lines
No EOL
4.5 KiB
Rust

use anyhow::Result;
use futures::StreamExt;
use reqwest::Client;
use serde_json::{json, Value};
use tokio::io::{self, AsyncBufReadExt, AsyncWriteExt, BufReader};
// Simple example client for interacting with the doc server via stdin/stdout
async fn stdio_client() -> Result<()> {
// Start the stdio-server in a separate process
let mut child = tokio::process::Command::new("cargo")
.args(["run", "--bin", "stdio-server"])
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.spawn()?;
let stdin = child.stdin.take().expect("Failed to open stdin");
let stdout = child.stdout.take().expect("Failed to open stdout");
let mut stdin = io::BufWriter::new(stdin);
let mut stdout = BufReader::new(stdout);
// Send a request to lookup tokio crate
let request = json!({
"jsonrpc": "2.0",
"method": "call_tool",
"params": {
"name": "lookup_crate",
"arguments": {
"crate_name": "tokio"
}
},
"id": 1
});
println!("Sending request to look up tokio crate...");
stdin.write_all(request.to_string().as_bytes()).await?;
stdin.write_all(b"\n").await?;
stdin.flush().await?;
// Read the response
let mut response = String::new();
stdout.read_line(&mut response).await?;
let parsed: Value = serde_json::from_str(&response)?;
println!("Received response: {}", serde_json::to_string_pretty(&parsed)?);
// Terminate the child process
child.kill().await?;
Ok(())
}
// Simple example client for interacting with the doc server via HTTP/SSE
async fn http_sse_client() -> Result<()> {
println!("Connecting to HTTP/SSE server...");
// Create HTTP client
let client = Client::new();
// Create a separate task to run the server
let _server = tokio::spawn(async {
tokio::process::Command::new("cargo")
.args(["run", "--bin", "axum-docs"])
.output()
.await
.expect("Failed to start server");
});
// Give the server some time to start
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
// Connect to the SSE endpoint to get a session ID
let mut session_id = String::new();
let sse_url = "http://127.0.0.1:8080/sse";
println!("Getting session ID from SSE endpoint...");
// For a real implementation, you would use an SSE client library
// This is a simplified example that just gets the session ID from the first message
let response = client.get(sse_url).send().await?;
if !response.status().is_success() {
println!("Error connecting to SSE endpoint: {}", response.status());
return Ok(());
}
// Parse the first message to get the session ID
// In a real implementation, you would properly handle the SSE stream
if let Some(event_data) = response.headers().get("x-accel-buffering") {
// This is just a placeholder - in a real SSE client you would parse the actual event
session_id = "example_session_id".to_string();
} else {
println!("Could not get session ID from SSE endpoint");
return Ok(());
}
// Send a request to search for crates
let request_url = format!("{}?sessionId={}", sse_url, session_id);
let request_body = json!({
"jsonrpc": "2.0",
"method": "call_tool",
"params": {
"name": "search_crates",
"arguments": {
"query": "async runtime",
"limit": 5
}
},
"id": 1
});
println!("Sending request to search for crates...");
let response = client.post(&request_url)
.json(&request_body)
.send()
.await?;
if response.status().is_success() {
println!("Request sent successfully");
} else {
println!("Error sending request: {}", response.status());
}
// In a real implementation, you would read the responses from the SSE stream
println!("In a real implementation, responses would be read from the SSE stream");
Ok(())
}
#[tokio::main]
async fn main() -> Result<()> {
println!("Rust Documentation Server Client Example");
println!("---------------------------------------");
println!("\n1. Testing STDIN/STDOUT client:");
if let Err(e) = stdio_client().await {
println!("Error in STDIN/STDOUT client: {}", e);
}
println!("\n2. Testing HTTP/SSE client:");
if let Err(e) = http_sse_client().await {
println!("Error in HTTP/SSE client: {}", e);
}
Ok(())
}