poet 0.7.0

Static site generator with optional MCP server and LLM SEO optimizations
Documentation
use actix_web::HttpRequest;
use actix_web::HttpResponse;
use actix_web::Result;
use actix_web::body::BoxBody;
use actix_web::error::ErrorBadRequest;
use async_trait::async_trait;
use mime::Mime;

use crate::mcp::MCP_HEADER_PROTOCOL_VERSION;
use crate::mcp::mcp_responder_context::McpResponderContext;
use crate::mcp::session::Session;

#[async_trait(?Send)]
pub trait McpResponder: Clone {
    fn accepts() -> Vec<Mime>;

    async fn respond_to(self, context: McpResponderContext) -> Result<HttpResponse<BoxBody>>;

    fn assert_protocol_version_header(
        &self,
        req: &HttpRequest,
        expected_version: &str,
    ) -> Result<()> {
        match req.headers().get(MCP_HEADER_PROTOCOL_VERSION) {
            Some(header_protocol_version) => {
                if header_protocol_version == expected_version {
                    Ok(())
                } else {
                    Err(ErrorBadRequest(format!(
                        "Unsupported protocol version: {header_protocol_version:?}"
                    )))
                }
            }
            None => Err(ErrorBadRequest(format!(
                "You need to use {MCP_HEADER_PROTOCOL_VERSION} header"
            ))),
        }
    }

    fn assert_no_session(&self, session: &Option<Session>) -> Result<()> {
        if session.is_some() {
            Err(ErrorBadRequest(
                "Unexpected session headers. Do not use session with this JSONRPC method.",
            ))
        } else {
            Ok(())
        }
    }

    fn assert_session(&self, session: &Option<Session>) -> Result<Session> {
        match session {
            Some(session) => Ok(session.clone()),
            None => Err(ErrorBadRequest("Expected session headers.")),
        }
    }
}