systemprompt-models 0.1.21

Shared data models and types for systemprompt.io OS
Documentation
use anyhow::{Result, anyhow};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use systemprompt_identifiers::McpExecutionId;

#[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<()> {
        if self.mcp_execution_id.as_str().is_empty() {
            return Err(anyhow!(
                "McpToolResultMetadata: mcp_execution_id cannot be empty"
            ));
        }
        Ok(())
    }

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

        let json_value = serde_json::to_value(self)?;
        let json_object = json_value
            .as_object()
            .ok_or_else(|| anyhow!("Failed to serialize McpToolResultMetadata as JSON object"))?
            .clone();

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

    pub fn from_meta(meta: &rmcp::model::Meta) -> Result<Self> {
        let json_value = Value::Object(meta.0.clone());
        let metadata: Self = serde_json::from_value(json_value).map_err(|e| {
            anyhow!(
                "Failed to parse McpToolResultMetadata from _meta: {e}. Expected fields: \
                 mcp_execution_id (required), execution_time_ms (optional), server_version \
                 (optional)"
            )
        })?;

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

    pub fn from_call_tool_result(result: &rmcp::model::CallToolResult) -> Result<Self> {
        let meta = result.meta.as_ref().ok_or_else(|| {
            anyhow!("CallToolResult._meta is missing (required for MCP execution tracking)")
        })?;

        Self::from_meta(meta)
    }
}