rust_docs_mcp/deps/
tools.rs

1use 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        // First ensure the crate is cached
49        match cache
50            .ensure_crate_or_member_docs(
51                &params.crate_name,
52                &params.version,
53                params.member.as_deref(),
54            )
55            .await
56        {
57            Ok(_) => {
58                // Load the dependency metadata
59                match cache
60                    .load_dependencies(&params.crate_name, &params.version)
61                    .await
62                {
63                    Ok(metadata) => {
64                        // Process the metadata to extract dependency information
65                        match process_cargo_metadata(
66                            &metadata,
67                            &params.crate_name,
68                            &params.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}