rust_docs_mcp/deps/
tools.rs1use std::sync::Arc;
2use tokio::sync::RwLock;
3
4use rmcp::schemars;
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7
8use crate::cache::CrateCache;
9use crate::deps::{
10 outputs::{CrateIdentifier, Dependency, DepsErrorOutput, GetDependenciesOutput},
11 process_cargo_metadata,
12};
13
14#[derive(Debug, Serialize, Deserialize, JsonSchema)]
15pub struct GetDependenciesParams {
16 #[schemars(description = "The name of the crate")]
17 pub crate_name: String,
18 #[schemars(description = "The version of the crate")]
19 pub version: String,
20 #[schemars(
21 description = "Include the full dependency tree (default: false, only shows direct dependencies)"
22 )]
23 pub include_tree: Option<bool>,
24 #[schemars(description = "Filter dependencies by name (partial match)")]
25 pub filter: Option<String>,
26 #[schemars(
27 description = "For workspace crates, specify the member path (e.g., 'crates/rmcp')"
28 )]
29 pub member: Option<String>,
30}
31
32#[derive(Debug, Clone)]
33pub struct DepsTools {
34 cache: Arc<RwLock<CrateCache>>,
35}
36
37impl DepsTools {
38 pub fn new(cache: Arc<RwLock<CrateCache>>) -> Self {
39 Self { cache }
40 }
41
42 pub async fn get_dependencies(
43 &self,
44 params: GetDependenciesParams,
45 ) -> Result<GetDependenciesOutput, DepsErrorOutput> {
46 let cache = self.cache.write().await;
47
48 match cache
50 .ensure_crate_or_member_docs(
51 ¶ms.crate_name,
52 ¶ms.version,
53 params.member.as_deref(),
54 )
55 .await
56 {
57 Ok(_) => {
58 match cache
60 .load_dependencies(¶ms.crate_name, ¶ms.version)
61 .await
62 {
63 Ok(metadata) => {
64 match process_cargo_metadata(
66 &metadata,
67 ¶ms.crate_name,
68 ¶ms.version,
69 params.include_tree.unwrap_or(false),
70 params.filter.as_deref(),
71 ) {
72 Ok(dep_info) => Ok(GetDependenciesOutput {
73 crate_info: CrateIdentifier {
74 name: dep_info.crate_info.name,
75 version: dep_info.crate_info.version,
76 },
77 direct_dependencies: dep_info
78 .direct_dependencies
79 .into_iter()
80 .map(|d| Dependency {
81 name: d.name,
82 version_req: d.version_req,
83 resolved_version: d.resolved_version,
84 kind: d.kind,
85 optional: d.optional,
86 features: d.features,
87 target: d.target,
88 })
89 .collect(),
90 dependency_tree: dep_info.dependency_tree,
91 total_dependencies: dep_info.total_dependencies,
92 }),
93 Err(e) => Err(DepsErrorOutput::new(format!(
94 "Failed to process dependency metadata: {e}"
95 ))),
96 }
97 }
98 Err(e) => Err(DepsErrorOutput::new(format!(
99 "Dependencies not available for {}-{}. Error: {}",
100 params.crate_name, params.version, e
101 ))),
102 }
103 }
104 Err(e) => Err(DepsErrorOutput::new(format!("Failed to cache crate: {e}"))),
105 }
106 }
107}