use anyhow::{Context, Result};
use clap::Parser;
use log::{error, info};
use rmcp::ServiceExt;
use tokio::io::{stdin, stdout};
use run_code_rmcp::mcp::CodeRunnerService;
#[derive(Parser)]
#[command(name = "script_runner")]
#[command(author = "MCP Script Runner")]
#[command(version = "0.1.0")]
#[command(about = "通过MCP协议执行JavaScript、TypeScript和Python代码", long_about = None)]
struct Cli {
#[arg(short, long)]
verbose: bool,
}
async fn start_mcp_server(verbose: bool) -> Result<()> {
if verbose {
info!("初始化 MCP 服务...");
}
let service = CodeRunnerService;
let transport = (stdin(), stdout());
if verbose {
info!("MCP 服务已启动,等待连接...");
}
let server = service.serve(transport).await.context("启动MCP服务失败")?;
let result = server.waiting().await;
if verbose {
match &result {
Ok(reason) => error!("MCP 服务已停止: {:?}", reason),
Err(err) => error!("MCP 服务出错: {}", err),
}
}
result.map(|_| ()).map_err(Into::into)
}
#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();
start_mcp_server(cli.verbose).await
}
#[cfg(test)]
mod tests {
use super::*;
use rmcp::{ServiceExt, model::CallToolRequestParams};
use std::time::Duration;
use tokio::sync::oneshot;
use tokio::time::timeout;
#[tokio::test]
#[ignore = "MCP client test requires proper service implementation - TODO: fix later"]
async fn test_start_mcp_server() -> Result<()> {
let (client_stream, server_stream) = tokio::io::duplex(8192);
let (server_read, server_write) = tokio::io::split(server_stream);
let (client_read, client_write) = tokio::io::split(client_stream);
let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>();
let server_task = tokio::spawn(async move {
let service = CodeRunnerService;
let service_fut = service.serve((server_read, server_write));
tokio::pin!(service_fut);
tokio::select! {
res = &mut service_fut => {
match res {
Ok(server) => {
match server.waiting().await {
Ok(reason) => println!("服务正常结束: {:?}", reason),
Err(e) => println!("服务错误: {}", e),
}
}
Err(e) => println!("启动服务失败: {}", e),
}
}
_ = shutdown_rx => {
println!("收到关闭信号");
}
}
});
tokio::time::sleep(Duration::from_millis(200)).await;
let client = ().serve((client_read, client_write)).await?;
let server_info = client.peer_info();
println!("服务器信息: {:?}", server_info);
assert!(
server_info.unwrap().instructions.is_some(),
"服务器应该提供说明"
);
let js_code =
"function handler(input) { return {success: true, message: 'JavaScript测试成功'}; }";
let result = client
.call_tool(CallToolRequestParams {
meta: None,
name: "run_javascript".into(),
arguments: serde_json::json!({
"code": js_code,
})
.as_object()
.cloned(),
task: None,
})
.await?;
println!("JavaScript执行结果: {:?}", result);
let py_code =
"def handler(input):\n return {'success': True, 'message': 'Python测试成功'}";
let result = client
.call_tool(CallToolRequestParams {
meta: None,
name: "run_python".into(),
arguments: serde_json::json!({
"code": py_code,
})
.as_object()
.cloned(),
task: None,
})
.await?;
println!("Python执行结果: {:?}", result);
client.cancel().await?;
let _ = shutdown_tx.send(());
let _ = timeout(Duration::from_secs(5), server_task).await;
Ok(())
}
}