1use std::future::Future;
4use std::sync::Arc;
5
6use futu_net::client::FutuClient;
7use rmcp::{RoleServer, handler::server::wrapper::Parameters, service::RequestContext};
8use serde::de::DeserializeOwned;
9
10use crate::handlers;
11use crate::tool_args::ProtoJsonReq;
12
13use super::FutuServer;
14
15async fn run<C2S, F, Fut>(
16 server: &FutuServer,
17 tool_name: &'static str,
18 label: &'static str,
19 req: ProtoJsonReq,
20 req_ctx: RequestContext<RoleServer>,
21 handler: F,
22) -> std::result::Result<String, String>
23where
24 C2S: DeserializeOwned,
25 F: FnOnce(Arc<FutuClient>, C2S) -> Fut,
26 Fut: Future<Output = anyhow::Result<String>>,
27{
28 let c2s = match handlers::proto_json::parse_c2s_json::<C2S>(label, &req.c2s_json) {
29 Ok(c2s) => c2s,
30 Err(error) => return FutuServer::tool_err(error),
31 };
32 let client = server
33 .read_client_or_err(tool_name, &req_ctx, req.api_key.as_deref(), None)
34 .await?;
35 FutuServer::wrap_result(handler(client, c2s).await)
36}
37
38macro_rules! tool_impl {
39 ($fn_name:ident,$module:ident,$tool:literal,$label:literal,$handler:ident) => {
40 async fn $fn_name(
41 &self,
42 Parameters(req): Parameters<ProtoJsonReq>,
43 req_ctx: RequestContext<RoleServer>,
44 ) -> std::result::Result<String, String> {
45 run::<futu_proto::$module::C2s, _, _>(
46 self,
47 $tool,
48 $label,
49 req,
50 req_ctx,
51 |client, c2s| async move { handlers::proto_json::$handler(&client, c2s).await },
52 )
53 .await
54 }
55 };
56}
57
58impl FutuServer {
59 tool_impl!(
60 futu_get_hot_news_impl,
61 qot_get_hot_news,
62 "futu_get_hot_news",
63 "hot-news",
64 hot_news
65 );
66 tool_impl!(
67 futu_get_latest_news_impl,
68 qot_get_latest_news,
69 "futu_get_latest_news",
70 "latest-news",
71 latest_news
72 );
73 tool_impl!(
74 futu_get_watchlist_news_impl,
75 qot_get_watchlist_news,
76 "futu_get_watchlist_news",
77 "watchlist-news",
78 watchlist_news
79 );
80 tool_impl!(
81 futu_get_watchlist_announcement_impl,
82 qot_get_watchlist_announcement,
83 "futu_get_watchlist_announcement",
84 "watchlist-announcement",
85 watchlist_announcement
86 );
87 tool_impl!(
88 futu_get_watchlist_rating_impl,
89 qot_get_watchlist_rating,
90 "futu_get_watchlist_rating",
91 "watchlist-rating",
92 watchlist_rating
93 );
94 tool_impl!(
95 futu_get_stock_news_impl,
96 qot_get_stock_news,
97 "futu_get_stock_news",
98 "stock-news",
99 stock_news
100 );
101}
102
103include!(concat!(env!("OUT_DIR"), "/generated_mcp_routes_news.rs"));