107 lines
3.3 KiB
Rust
107 lines
3.3 KiB
Rust
use anyhow::Result;
|
|
use dtrack_core::DtrackClient;
|
|
use rmcp::{
|
|
handler::server::router::tool::ToolRouter,
|
|
handler::server::wrapper::Parameters,
|
|
model::{
|
|
CallToolResult, Content, Implementation, ProtocolVersion, ServerCapabilities, ServerInfo,
|
|
},
|
|
tool, tool_handler, tool_router,
|
|
transport::stdio,
|
|
ErrorData as McpError, ServerHandler, ServiceExt,
|
|
};
|
|
use schemars::JsonSchema;
|
|
use serde::Deserialize;
|
|
|
|
/// MCP-Server fuer Dependency-Track. Haelt den DT-Client + den vom Macro
|
|
/// generierten ToolRouter.
|
|
#[derive(Clone)]
|
|
pub struct DtrackServer {
|
|
client: DtrackClient,
|
|
tool_router: ToolRouter<Self>,
|
|
}
|
|
|
|
/// Argument fuer projektbezogene Tools.
|
|
#[derive(Debug, Deserialize, JsonSchema)]
|
|
pub struct ProjectArg {
|
|
/// UUID des Projekts (aus `list_projects`).
|
|
pub uuid: String,
|
|
}
|
|
|
|
#[tool_router]
|
|
impl DtrackServer {
|
|
pub fn new(client: DtrackClient) -> Self {
|
|
Self {
|
|
client,
|
|
tool_router: Self::tool_router(),
|
|
}
|
|
}
|
|
|
|
#[tool(description = "Listet aktive Dependency-Track-Projekte auf (Name, Version, UUID).")]
|
|
async fn list_projects(&self) -> Result<CallToolResult, McpError> {
|
|
let body = self
|
|
.client
|
|
.list_projects()
|
|
.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,
|
|
Parameters(arg): Parameters<ProjectArg>,
|
|
) -> Result<CallToolResult, McpError> {
|
|
let body = self
|
|
.client
|
|
.project_findings(&arg.uuid)
|
|
.await
|
|
.map_err(|e| McpError::internal_error(e.to_string(), None))?;
|
|
Ok(CallToolResult::success(vec![Content::text(body)]))
|
|
}
|
|
|
|
#[tool(description = "Aktuelle Sicherheits-Metriken eines Projekts anhand seiner UUID.")]
|
|
async fn project_metrics(
|
|
&self,
|
|
Parameters(arg): Parameters<ProjectArg>,
|
|
) -> Result<CallToolResult, McpError> {
|
|
let body = self
|
|
.client
|
|
.project_metrics(&arg.uuid)
|
|
.await
|
|
.map_err(|e| McpError::internal_error(e.to_string(), None))?;
|
|
Ok(CallToolResult::success(vec![Content::text(body)]))
|
|
}
|
|
}
|
|
|
|
#[tool_handler]
|
|
impl ServerHandler for DtrackServer {
|
|
fn get_info(&self) -> ServerInfo {
|
|
ServerInfo {
|
|
protocol_version: ProtocolVersion::V_2025_06_18,
|
|
capabilities: ServerCapabilities::builder().enable_tools().build(),
|
|
server_info: Implementation::from_build_env(),
|
|
instructions: Some(
|
|
"Read-only Zugriff auf Dependency-Track (Projekte, Findings, Metriken)."
|
|
.to_string(),
|
|
),
|
|
..Default::default()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<()> {
|
|
// WICHTIG: Logs NUR auf stderr -- stdout ist der JSON-RPC-Kanal des
|
|
// stdio-Transports. println!() wuerde den Stream korrumpieren.
|
|
tracing_subscriber::fmt()
|
|
.with_writer(std::io::stderr)
|
|
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
|
|
.init();
|
|
|
|
let client = DtrackClient::from_env()?;
|
|
let service = DtrackServer::new(client).serve(stdio()).await?;
|
|
service.waiting().await?;
|
|
Ok(())
|
|
}
|