167 lines
5.2 KiB
Rust
167 lines
5.2 KiB
Rust
use anyhow::{Context, Result};
|
||
use clap::Parser;
|
||
use log::{error, info};
|
||
use rmcp::ServiceExt;
|
||
use tokio::io::{stdin, stdout};
|
||
|
||
mod app_error;
|
||
mod cache;
|
||
mod deno_runner;
|
||
mod mcp;
|
||
mod model;
|
||
mod python_runner;
|
||
|
||
use mcp::CodeRunnerService;
|
||
|
||
/// MCP脚本运行器 - 通过MCP协议执行JavaScript、TypeScript和Python代码
|
||
#[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,
|
||
}
|
||
|
||
/// 启动MCP服务器
|
||
async fn start_mcp_server(verbose: bool) -> Result<()> {
|
||
if verbose {
|
||
info!("初始化 MCP 服务...");
|
||
}
|
||
|
||
// 创建服务实例
|
||
let service = CodeRunnerService::default();
|
||
|
||
// 使用标准输入输出作为传输方式
|
||
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();
|
||
|
||
// 启动MCP服务
|
||
start_mcp_server(cli.verbose).await
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use rmcp::{model::CallToolRequestParam, ServiceExt};
|
||
use tokio::sync::oneshot;
|
||
use tokio::time::timeout;
|
||
use std::time::Duration;
|
||
|
||
#[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::default();
|
||
|
||
// 创建服务Future并pin
|
||
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;
|
||
|
||
// 使用RMCP客户端SDK连接到服务器
|
||
let client = ().serve((client_read, client_write)).await?;
|
||
|
||
// 获取服务器信息
|
||
let server_info = client.peer_info();
|
||
println!("服务器信息: {:?}", server_info);
|
||
|
||
// 验证服务器信息
|
||
assert!(!server_info.unwrap().instructions.is_none(), "服务器应该提供说明");
|
||
|
||
// 测试执行JavaScript代码
|
||
let js_code = "function handler(input) { return {success: true, message: 'JavaScript测试成功'}; }";
|
||
let result = client.call_tool(CallToolRequestParam {
|
||
name: "run_javascript".into(),
|
||
arguments: serde_json::json!({
|
||
"code": js_code,
|
||
}).as_object().cloned(),
|
||
}).await?;
|
||
|
||
// 验证JavaScript结果,这里只简单验证调用成功
|
||
println!("JavaScript执行结果: {:?}", result);
|
||
|
||
// 测试执行Python代码
|
||
let py_code = "def handler(input):\n return {'success': True, 'message': 'Python测试成功'}";
|
||
let result = client.call_tool(CallToolRequestParam {
|
||
name: "run_python".into(),
|
||
arguments: serde_json::json!({
|
||
"code": py_code,
|
||
}).as_object().cloned(),
|
||
}).await?;
|
||
|
||
// 验证Python结果,这里只简单验证调用成功
|
||
println!("Python执行结果: {:?}", result);
|
||
|
||
// 关闭客户端
|
||
client.cancel().await?;
|
||
|
||
// 发送服务器关闭信号
|
||
let _ = shutdown_tx.send(());
|
||
|
||
// 等待服务器任务结束
|
||
let _ = timeout(Duration::from_secs(5), server_task).await;
|
||
|
||
Ok(())
|
||
}
|
||
}
|