docs_mcp/tools/
crate_get.rs1use rmcp::{ErrorData, model::{CallToolResult, Content}};
2use serde::{Deserialize, Serialize};
3use rmcp::schemars::{self, JsonSchema};
4
5use super::AppState;
6
7#[derive(Serialize)]
8struct CrateGetOutput<'a> {
9 name: &'a str,
10 #[serde(skip_serializing_if = "Option::is_none")]
11 description: Option<&'a str>,
12 #[serde(skip_serializing_if = "Option::is_none")]
13 homepage: Option<&'a str>,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 documentation: Option<&'a str>,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 repository: Option<&'a str>,
18 downloads: u64,
19 #[serde(skip_serializing_if = "Option::is_none")]
20 recent_downloads: Option<u64>,
21 updated_at: &'a str,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 max_stable_version: Option<&'a str>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 max_version: Option<&'a str>,
26 features: std::collections::HashMap<String, Vec<String>>,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 keywords: Option<Vec<&'a str>>,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 categories: Option<Vec<&'a str>>,
31}
32
33#[derive(Debug, Deserialize, JsonSchema)]
34pub struct CrateGetParams {
35 pub name: String,
37}
38
39pub async fn execute(state: &AppState, params: CrateGetParams) -> Result<CallToolResult, ErrorData> {
40 let name = ¶ms.name;
41 let client = crate::cratesio::CratesIoClient::new(&state.client, &state.cache);
42
43 let (api_result, index_result) = tokio::join!(
45 client.get_crate(name),
46 state.fetch_index(name)
47 );
48
49 let api = api_result.map_err(|e| ErrorData::internal_error(e.to_string(), None))?;
50 let index_lines = index_result.map_err(|e| ErrorData::internal_error(e.to_string(), None))?;
51
52 let latest_stable = crate::sparse_index::find_latest_stable(&index_lines);
54 let features = latest_stable.map(|l| l.all_features()).unwrap_or_default();
55
56 let krate = &api.krate;
57 let output = CrateGetOutput {
58 name: &krate.name,
59 description: krate.description.as_deref(),
60 homepage: krate.homepage.as_deref(),
61 documentation: krate.documentation.as_deref(),
62 repository: krate.repository.as_deref(),
63 downloads: krate.downloads,
64 recent_downloads: krate.recent_downloads,
65 updated_at: &krate.updated_at,
66 max_stable_version: krate.max_stable_version.as_deref(),
67 max_version: krate.max_version.as_deref(),
68 features,
69 keywords: api.keywords.as_ref().map(|kws| kws.iter().map(|k| k.keyword.as_str()).collect()),
70 categories: api.categories.as_ref().map(|cats| cats.iter().map(|c| c.category.as_str()).collect()),
71 };
72
73 let json = serde_json::to_string_pretty(&output)
74 .map_err(|e| ErrorData::internal_error(e.to_string(), None))?;
75
76 Ok(CallToolResult::success(vec![Content::text(json)]))
77}