1use rmcp::{
4 RoleServer, handler::server::wrapper::Parameters, service::RequestContext, tool, tool_router,
5};
6
7use crate::handlers;
8use crate::tool_args::NoArgs;
9
10use super::FutuServer;
11
12pub(crate) fn json_tool_output<T: serde::Serialize>(
13 tool: &str,
14 value: &T,
15) -> std::result::Result<String, String> {
16 serde_json::to_string_pretty(value).map_err(|e| format!("serialize {tool} output: {e}"))
17}
18
19#[tool_router(router = system_tool_router, vis = "pub(crate)")]
20impl FutuServer {
21 #[tool(description = "Ping the Futu gateway. Returns RTT and connection status.")]
22 async fn futu_ping(
23 &self,
24 Parameters(_req): Parameters<NoArgs>,
25 req_ctx: RequestContext<RoleServer>,
26 ) -> std::result::Result<String, String> {
27 let _ = self.require_acc_read_with_acc_id("futu_ping", &req_ctx, None, None)?;
30 let gateway = self.state.inner.lock().await.gateway.clone();
31 let client = match self.state.client().await {
32 Ok(c) => c,
33 Err(e) => {
34 tracing::info!(tool = "futu_ping", ok = false, "connect failed: {e}");
35 return json_tool_output(
36 "futu_ping",
37 &handlers::core::PingOut {
38 gateway,
39 ok: false,
40 rtt_ms: 0.0,
41 message: format!("connect failed: {e}"),
42 },
43 );
44 }
45 };
46 let out = handlers::core::ping(&client, &gateway).await;
47 tracing::info!(tool = "futu_ping", ok = out.ok, rtt_ms = out.rtt_ms);
48 json_tool_output("futu_ping", &out)
49 }
50}