systemprompt-mcp 0.14.4

Native Model Context Protocol (MCP) implementation for systemprompt.io. Orchestration, per-server OAuth2, RBAC middleware, and tool-call governance — the core of the AI governance pipeline.
Documentation
use systemprompt_models::domain_error;

domain_error! {
    pub enum McpDomainError {
        common: [repository, io, json, validation],

        #[error("MCP server not found: {0}")]
        ServerNotFound(String),

        #[error("Connection failed to {server}: {message}")]
        ConnectionFailed { server: String, message: String },

        #[error("Tool execution failed: {0}")]
        ToolExecutionFailed(String),

        #[error("Schema validation failed: {0}")]
        SchemaValidation(String),

        #[error("Registry validation failed: {0}")]
        RegistryValidation(String),

        #[error("Process spawn failed for {server}: {message}")]
        ProcessSpawn { server: String, message: String },

        #[error("Port unavailable: {port} - {message}")]
        PortUnavailable { port: u16, message: String },

        #[error("Configuration error: {0}")]
        Configuration(String),

        #[error("Authentication required for {0}")]
        AuthRequired(String),

        #[error("Manifest error: {0}")]
        Manifest(String),

        #[error("Transport error: {0}")]
        Transport(String),

        #[error("MCP server {server} timed out after {after_ms}ms")]
        Timeout { server: String, after_ms: u64 },

        #[error("Circuit breaker open for MCP server {server}; failing fast")]
        CircuitOpen { server: String },

        #[error("MCP server {server} unavailable: concurrency limit reached")]
        DependencyUnavailable { server: String },

        #[error("{0}")]
        Internal(String),

        #[error("Configuration: {0}")]
        Config(#[from] systemprompt_models::errors::ConfigError),

        #[error("services config: {0}")]
        ServicesConfig(#[from] systemprompt_loader::ConfigLoadError),

        #[error("extension load: {0}")]
        ExtensionLoad(#[from] systemprompt_loader::ExtensionLoadError),

        #[error("MCP client initialize: {0}")]
        ClientInitialize(String),

        #[error("MCP service error: {0}")]
        ServiceError(String),

        #[error("Task join error: {0}")]
        TaskJoin(#[from] tokio::task::JoinError),

        #[error("Path error: {0}")]
        Path(String),

        #[error("Config validation: {0}")]
        ConfigValidation(String),
    }
}

impl From<sqlx::Error> for McpDomainError {
    fn from(err: sqlx::Error) -> Self {
        Self::Repository(systemprompt_database::RepositoryError::from(err))
    }
}

impl From<rmcp::service::ClientInitializeError> for McpDomainError {
    fn from(e: rmcp::service::ClientInitializeError) -> Self {
        Self::ClientInitialize(e.to_string())
    }
}

impl From<rmcp::ServiceError> for McpDomainError {
    fn from(e: rmcp::ServiceError) -> Self {
        Self::ServiceError(e.to_string())
    }
}

impl From<systemprompt_models::errors::ConfigValidationError> for McpDomainError {
    fn from(e: systemprompt_models::errors::ConfigValidationError) -> Self {
        Self::ConfigValidation(e.to_string())
    }
}

impl From<systemprompt_models::paths::PathError> for McpDomainError {
    fn from(e: systemprompt_models::paths::PathError) -> Self {
        Self::Path(e.to_string())
    }
}

impl McpDomainError {
    /// Classify this error for the resilience layer — which failures are worth
    /// retrying (transient) and which cannot be helped by a retry (permanent).
    #[must_use]
    pub const fn classify(&self) -> systemprompt_database::resilience::Outcome {
        use systemprompt_database::resilience::Outcome;
        match self {
            Self::ConnectionFailed { .. }
            | Self::Transport(_)
            | Self::Timeout { .. }
            | Self::ServiceError(_) => Outcome::Transient { retry_after: None },
            _ => Outcome::Permanent,
        }
    }
}

pub type McpDomainResult<T> = Result<T, McpDomainError>;