diff --git a/crates/dtrack-tools/src/lib.rs b/crates/dtrack-tools/src/lib.rs index 450131f..349f381 100644 --- a/crates/dtrack-tools/src/lib.rs +++ b/crates/dtrack-tools/src/lib.rs @@ -40,10 +40,20 @@ pub struct DtrackServer { /// Argument fuer projektbezogene Tools. #[derive(Debug, Deserialize, JsonSchema)] pub struct ProjectArg { - /// UUID des Projekts (aus `list_projects`). + /// UUID des Projekts (aus `list_projects` oder `lookup_project`). pub uuid: String, } +/// Argument fuer die Projekt-Suche per Name. +#[derive(Debug, Deserialize, JsonSchema)] +pub struct LookupArg { + /// Projektname (exakt). + pub name: String, + /// Optional: Projektversion. Weglassen sucht ohne Versionsfilter. + #[serde(default)] + pub version: Option, +} + #[tool_router] impl DtrackServer { pub fn new(client: SharedClient) -> Self { @@ -63,6 +73,32 @@ impl DtrackServer { Ok(CallToolResult::success(vec![Content::text(body)])) } + #[tool(description = "Findet ein Projekt per Name (optional Version) und liefert u.a. dessen UUID.")] + async fn lookup_project( + &self, + Parameters(arg): Parameters, + ) -> Result { + let client = self.client.read().await.clone(); + let body = client + .lookup_project(&arg.name, arg.version.as_deref()) + .await + .map_err(|e| McpError::internal_error(e.to_string(), None))?; + Ok(CallToolResult::success(vec![Content::text(body)])) + } + + #[tool(description = "Komponenten/Abhaengigkeiten eines Projekts anhand seiner UUID.")] + async fn project_components( + &self, + Parameters(arg): Parameters, + ) -> Result { + let client = self.client.read().await.clone(); + let body = client + .project_components(&arg.uuid) + .await + .map_err(|e| McpError::internal_error(e.to_string(), None))?; + Ok(CallToolResult::success(vec![Content::text(body)])) + } + #[tool(description = "Findings (Schwachstellen) eines Projekts anhand seiner UUID.")] async fn project_findings( &self, @@ -88,6 +124,45 @@ impl DtrackServer { .map_err(|e| McpError::internal_error(e.to_string(), None))?; Ok(CallToolResult::success(vec![Content::text(body)])) } + + #[tool(description = "Policy-Verstoesse eines Projekts (UUID). Braucht das DT-Recht VIEW_POLICY_VIOLATION.")] + async fn project_violations( + &self, + Parameters(arg): Parameters, + ) -> Result { + let client = self.client.read().await.clone(); + let body = client + .project_violations(&arg.uuid) + .await + .map_err(|e| McpError::internal_error(e.to_string(), None))?; + Ok(CallToolResult::success(vec![Content::text(body)])) + } + + #[tool(description = "VEX (CycloneDX) eines Projekts (UUID): Exploitability-/Analyse-Status der Schwachstellen.")] + async fn project_vex( + &self, + Parameters(arg): Parameters, + ) -> Result { + let client = self.client.read().await.clone(); + let body = client + .project_vex(&arg.uuid) + .await + .map_err(|e| McpError::internal_error(e.to_string(), None))?; + Ok(CallToolResult::success(vec![Content::text(body)])) + } + + #[tool(description = "SBOM (CycloneDX, JSON) eines Projekts anhand seiner UUID.")] + async fn project_bom( + &self, + Parameters(arg): Parameters, + ) -> Result { + let client = self.client.read().await.clone(); + let body = client + .project_bom(&arg.uuid) + .await + .map_err(|e| McpError::internal_error(e.to_string(), None))?; + Ok(CallToolResult::success(vec![Content::text(body)])) + } } #[tool_handler] @@ -98,7 +173,8 @@ impl ServerHandler for DtrackServer { capabilities: ServerCapabilities::builder().enable_tools().build(), server_info: Implementation::from_build_env(), instructions: Some( - "Read-only Zugriff auf Dependency-Track (Projekte, Findings, Metriken)." + "Read-only Zugriff auf Dependency-Track: Projekte (Liste/Lookup), \ + Komponenten, Findings, Metriken, Policy-Verstoesse, VEX und SBOM (CycloneDX)." .to_string(), ), }