systemprompt-models 0.6.1

Foundation data models for systemprompt.io AI governance infrastructure. Shared DTOs, config, and domain types consumed by every layer of the MCP governance pipeline.
Documentation
use serde::{Deserialize, Serialize};
use serde_json::Value;
use systemprompt_identifiers::McpExecutionId;

use crate::errors::MetadataError;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct McpToolResultMetadata {
    pub mcp_execution_id: McpExecutionId,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub execution_time_ms: Option<i64>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub server_version: Option<String>,
}

impl McpToolResultMetadata {
    pub const fn new(mcp_execution_id: McpExecutionId) -> Self {
        Self {
            mcp_execution_id,
            execution_time_ms: None,
            server_version: None,
        }
    }

    pub const fn with_execution_time(mut self, time_ms: i64) -> Self {
        self.execution_time_ms = Some(time_ms);
        self
    }

    pub fn with_server_version(mut self, version: impl Into<String>) -> Self {
        self.server_version = Some(version.into());
        self
    }

    pub fn validate(&self) -> Result<(), MetadataError> {
        if self.mcp_execution_id.as_str().is_empty() {
            return Err(MetadataError::MissingExecutionId);
        }
        Ok(())
    }

    pub fn to_meta(&self) -> Result<rmcp::model::Meta, MetadataError> {
        self.validate()?;

        let json_value = serde_json::to_value(self)?;
        let json_object = json_value
            .as_object()
            .ok_or(MetadataError::NotJsonObject)?
            .clone();

        Ok(rmcp::model::Meta(json_object))
    }

    pub fn from_meta(meta: &rmcp::model::Meta) -> Result<Self, MetadataError> {
        let json_value = Value::Object(meta.0.clone());
        let metadata: Self = serde_json::from_value(json_value)?;

        metadata.validate()?;
        Ok(metadata)
    }

    pub fn from_call_tool_result(
        result: &rmcp::model::CallToolResult,
    ) -> Result<Self, MetadataError> {
        let meta = result.meta.as_ref().ok_or(MetadataError::MetaMissing)?;

        Self::from_meta(meta)
    }
}