//! OSCAL assessment MCP tool. //! //! Emits a standard OSCAL assessment-results document for a repo's findings — //! what breakpilot's scanner MCP client pulls. Mapped findings target their //! compliance controls (via the stamped `control_refs`); unmapped findings are //! reported as-is, so nothing is lost. use mongodb::bson::doc; use rmcp::{model::*, ErrorData as McpError}; use schemars::JsonSchema; use serde::Deserialize; use compliance_core::models::oscal_assessment::assess; use compliance_core::models::Finding; use crate::database::Database; #[derive(Debug, Deserialize, JsonSchema)] pub struct OscalAssessmentParams { /// Repository / target id to assess. pub repo_id: String, } pub async fn oscal_assessment( db: &Database, params: OscalAssessmentParams, ) -> Result { let mut cursor = db .findings() .find(doc! { "repo_id": ¶ms.repo_id }) .await .map_err(|e| McpError::internal_error(format!("DB error: {e}"), None))?; let mut findings: Vec = Vec::new(); while cursor .advance() .await .map_err(|e| McpError::internal_error(format!("cursor error: {e}"), None))? { findings.push( cursor .deserialize_current() .map_err(|e| McpError::internal_error(format!("deserialize error: {e}"), None))?, ); } let document = assess(&findings, chrono::Utc::now()); let json = serde_json::to_string_pretty(&document) .map_err(|e| McpError::internal_error(format!("json error: {e}"), None))?; Ok(CallToolResult::success(vec![Content::text(json)])) }