use crate::repl;
use crate::tool_definition;
use rmcp::schemars;
use rmcp::schemars::JsonSchema;
use rmcp::{
Json, ServerHandler, ServiceExt,
handler::server::{tool::ToolRouter, wrapper::Parameters},
model::{ErrorCode, ErrorData as McpError, ServerCapabilities, ServerInfo},
tool, tool_handler, tool_router,
};
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::sync::Arc;
#[derive(Debug, Deserialize, JsonSchema)]
pub struct LuaReplRequest {
#[schemars(description = "Lua source code to execute in the sandboxed REPL environment")]
pub source_code: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct LuaReplOutput {
pub output: Vec<String>,
pub result: Result<Vec<String>, String>,
}
#[derive(Clone)]
pub struct LuaReplServer {
repl: Arc<repl::Repl>,
tool_router: ToolRouter<LuaReplServer>,
}
#[tool_router]
impl LuaReplServer {
pub fn new(repl: repl::Repl) -> Self {
Self {
repl: Arc::new(repl),
tool_router: Self::tool_router(),
}
}
#[tool(description = "Execute Lua code in a long-lived sandboxed REPL environment")]
async fn lua_repl(
&self,
params: Parameters<LuaReplRequest>,
) -> Result<Json<LuaReplOutput>, McpError> {
let source_code = params.0.source_code;
let repl = Arc::clone(&self.repl);
let eval_outcome = tokio::task::spawn_blocking(move || repl.eval(&source_code))
.await
.map_err(|e| McpError {
code: ErrorCode(-32603),
message: Cow::from(format!("Task join error: {}", e)),
data: None,
})?;
match eval_outcome {
Ok(outcome) => {
Ok(Json(LuaReplOutput {
output: outcome.output,
result: outcome.result.map_err(|e| e.to_string()),
}))
}
Err(err) => {
Ok(Json(LuaReplOutput {
output: Vec::new(),
result: Err(format!("REPL evaluation failed: {}", err)),
}))
}
}
}
}
#[tool_handler]
impl ServerHandler for LuaReplServer {
fn get_info(&self) -> ServerInfo {
ServerInfo {
capabilities: ServerCapabilities::builder().enable_tools().build(),
instructions: Some(format!(
"This server provides a sandboxed Lua REPL tool.\n\n{}",
tool_definition::DESCRIPTION
)),
..Default::default()
}
}
}
pub async fn serve(server: LuaReplServer) -> Result<(), Box<dyn std::error::Error>> {
let service = server.serve(rmcp::transport::stdio()).await?;
service.waiting().await?;
Ok(())
}