From 9be73422bbbf03ecc797dadb6b20440f39029b86 Mon Sep 17 00:00:00 2001 From: Sebas Date: Thu, 25 Jun 2026 06:44:38 +0000 Subject: [PATCH] feat(tools): VEX, SBOM, Komponenten, Policy-Verstoesse + Projekt-Lookup als MCP-Tools --- crates/dtrack-tools/src/lib.rs | 80 +++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) 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(), ), }