use crate::mcp_server::cache::{CacheConfig, CacheKeyBuilder, McpCache};
use crate::mcp_server::handlers;
use crate::mcp_server::state_manager::StateManager;
use crate::models::mcp::{McpRequest, McpResponse};
use serde_json::json;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use std::time::Duration;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::sync::Mutex;
use tracing::{debug, error, info};
pub struct McpServer {
state_manager: Arc<Mutex<StateManager>>,
cache: Arc<McpCache>,
}
impl McpServer {
#[must_use]
pub fn new() -> Self {
let cache_config = CacheConfig {
max_entries: 5000,
default_ttl: Duration::from_secs(600), enable_metrics: true,
};
Self {
state_manager: Arc::new(Mutex::new(StateManager::new())),
cache: Arc::new(McpCache::with_config(cache_config)),
}
}
pub async fn run(&self) -> Result<(), Box<dyn std::error::Error>> {
info!("Starting MCP server on stdin/stdout");
let stdin = tokio::io::stdin();
let mut stdout = tokio::io::stdout();
let reader = BufReader::new(stdin);
let mut lines = reader.lines();
let init_response = json!({
"jsonrpc": "2.0",
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"refactor": {
"start": true,
"nextIteration": true,
"getState": true,
"stop": true
}
},
"serverInfo": {
"name": "pmat-mcp-server",
"version": env!("CARGO_PKG_VERSION")
}
}
});
stdout
.write_all(format!("{init_response}\n").as_bytes())
.await?;
stdout.flush().await?;
while let Some(line) = lines.next_line().await? {
if line.trim().is_empty() {
continue;
}
debug!("Received MCP request: {}", line);
let response = match self.handle_request(&line).await {
Ok(resp) => resp,
Err(e) => {
error!("Error handling request: {}", e);
McpResponse::error(json!(null), -32603, e.to_string())
}
};
let response_json = serde_json::to_string(&response)?;
debug!("Sending MCP response: {}", response_json);
stdout.write_all(response_json.as_bytes()).await?;
stdout.write_all(b"\n").await?;
stdout.flush().await?;
}
info!("MCP server shutting down");
Ok(())
}
async fn handle_request(&self, line: &str) -> Result<McpResponse, Box<dyn std::error::Error>> {
let request: McpRequest = serde_json::from_str(line)?;
if request.jsonrpc != "2.0" {
return Ok(McpResponse::error(
request.id,
-32600,
"Invalid JSON-RPC version".to_string(),
));
}
let cache_key = if matches!(request.method.as_str(), "refactor.getState" | "initialize") {
let mut hasher = DefaultHasher::new();
request.method.hash(&mut hasher);
request.params.hash(&mut hasher);
Some(CacheKeyBuilder::method_result_key(
&request.method,
hasher.finish(),
))
} else {
None
};
if let Some(key) = &cache_key {
if let Some(cached_value) = self.cache.get(key).await {
debug!("Cache hit for method: {}", request.method);
return Ok(McpResponse::success(request.id, cached_value));
}
}
let result = match request.method.as_str() {
"initialize" => {
json!({
"protocolVersion": "2024-11-05",
"capabilities": {
"refactor": {
"start": true,
"nextIteration": true,
"getState": true,
"stop": true
}
},
"serverInfo": {
"name": "pmat-mcp-server",
"version": env!("CARGO_PKG_VERSION")
}
})
}
"refactor.start" => {
handlers::handle_refactor_start(
&self.state_manager,
request.params.unwrap_or(json!({})),
)
.await?
}
"refactor.nextIteration" => {
handlers::handle_refactor_next_iteration(&self.state_manager).await?
}
"refactor.getState" => handlers::handle_refactor_get_state(&self.state_manager).await?,
"refactor.stop" => handlers::handle_refactor_stop(&self.state_manager).await?,
_ => {
return Ok(McpResponse::error(
request.id,
-32601,
format!("Method not found: {}", request.method),
));
}
};
if let Some(key) = cache_key {
self.cache.set(key, result.clone()).await;
debug!("Cached result for method: {}", request.method);
}
Ok(McpResponse::success(request.id, result))
}
pub async fn cache_metrics(&self) -> String {
let metrics = self.cache.metrics().await;
format!(
"Cache Metrics - Hits: {}, Misses: {}, Hit Ratio: {:.2}%, Size: {}",
metrics.hits,
metrics.misses,
metrics.hit_ratio() * 100.0,
self.cache.size().await
)
}
}
impl Default for McpServer {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}