poet 0.7.0

Static site generator with optional MCP server and LLM SEO optimizations
Documentation
mod handler;

use std::sync::Arc;

use actix_web::FromRequest as _;
use actix_web::HttpResponse;
use actix_web::Result;
use actix_web::body::BoxBody;
use async_trait::async_trait;
use log::error;
use mime::Mime;

use crate::mcp::MCP_PROTOCOL_VERSION;
use crate::mcp::jsonrpc::client_to_server_message::ClientToServerMessage;
use crate::mcp::jsonrpc::implementation::Implementation;
use crate::mcp::jsonrpc::response::error::Error;
use crate::mcp::mcp_http_service::respond_to_post::handler::Handler as _;
use crate::mcp::mcp_http_service::respond_to_post::handler::initialize_handler::InitializeHandler;
use crate::mcp::mcp_http_service::respond_to_post::handler::initialized_handler::InitializedHandler;
use crate::mcp::mcp_http_service::respond_to_post::handler::logging_set_level_handler::LoggingSetLevelHandler;
use crate::mcp::mcp_http_service::respond_to_post::handler::ping_handler::PingHandler;
use crate::mcp::mcp_http_service::respond_to_post::handler::prompts_get_handler::PromptsGetHandler;
use crate::mcp::mcp_http_service::respond_to_post::handler::prompts_list_handler::PromptsListHandler;
use crate::mcp::mcp_http_service::respond_to_post::handler::resources_list_handler::ResourcesListHandler;
use crate::mcp::mcp_http_service::respond_to_post::handler::resources_read_handler::ResourcesReadHandler;
use crate::mcp::mcp_http_service::respond_to_post::handler::resources_subscribe_handler::ResourcesSubscribeHandler;
use crate::mcp::mcp_http_service::respond_to_post::handler::resources_templates_list_handler::ResourcesTemplatesListHandler;
use crate::mcp::mcp_http_service::respond_to_post::handler::resources_unsubscribe_handler::ResourcesUnsubscribeHandler;
use crate::mcp::mcp_http_service::respond_to_post::handler::tools_call_handler::ToolsCallHandler;
use crate::mcp::mcp_http_service::respond_to_post::handler::tools_list_handler::ToolsListHandler;
use crate::mcp::mcp_responder::McpResponder;
use crate::mcp::mcp_responder_context::McpResponderContext;
use crate::mcp::resource_list_aggregate::ResourceListAggregate;
use crate::mcp::session_manager::SessionManager;
use crate::mcp::tool_registry::ToolRegistry;
use crate::prompt_controller_collection_holder::PromptControllerCollectionHolder;

#[derive(Clone)]
pub struct RespondToPost {
    pub prompt_controller_collection_holder: PromptControllerCollectionHolder,
    pub resource_list_aggregate: Arc<ResourceListAggregate>,
    pub server_info: Implementation,
    pub session_manager: SessionManager,
    pub tool_registry: Arc<ToolRegistry>,
}

#[async_trait(?Send)]
impl McpResponder for RespondToPost {
    fn accepts() -> Vec<Mime> {
        vec![mime::APPLICATION_JSON, mime::TEXT_EVENT_STREAM]
    }

    async fn respond_to(
        self,
        McpResponderContext {
            req,
            mut payload,
            session,
            session_manager,
            ..
        }: McpResponderContext,
    ) -> Result<HttpResponse<BoxBody>> {
        let client_to_server_message: ClientToServerMessage =
            match String::from_request(&req, &mut payload).await {
                Ok(string_payload) => match serde_json::from_str(&string_payload) {
                    Ok(client_to_server_message) => client_to_server_message,
                    Err(err) => {
                        let message = format!("Parse error: {err:#}\nPayload: {string_payload}");

                        error!("{message}");

                        return Ok(HttpResponse::BadRequest().json(Error::parse(message)));
                    }
                },
                Err(err) => {
                    return Ok(
                        HttpResponse::BadRequest().json(Error::invalid_request(format!(
                            "No deserializable string payload: {err:#}"
                        ))),
                    );
                }
            };

        match client_to_server_message {
            ClientToServerMessage::Initialize(_) => {}
            _ => {
                self.assert_protocol_version_header(&req, MCP_PROTOCOL_VERSION)?;
            }
        }

        match client_to_server_message {
            ClientToServerMessage::Initialize(request) => {
                self.assert_no_session(&session)?;

                InitializeHandler {
                    server_info: self.server_info,
                    session_manager: self.session_manager,
                }
                .handle(request, ())
                .await
            }
            ClientToServerMessage::Initialized(request) => {
                let session = self.assert_session(&session)?;

                InitializedHandler {}.handle(request, session).await
            }
            ClientToServerMessage::LoggingSetLevel(request) => {
                let session = self.assert_session(&session)?;

                LoggingSetLevelHandler { session_manager }
                    .handle(request, session)
                    .await
            }
            ClientToServerMessage::Ping(request) => PingHandler {}.handle(request, ()).await,
            ClientToServerMessage::PromptsGet(request) => {
                let session = self.assert_session(&session)?;

                PromptsGetHandler {
                    prompt_controller_collection_holder: self.prompt_controller_collection_holder,
                }
                .handle(request, session)
                .await
            }
            ClientToServerMessage::PromptsList(request) => {
                let session = self.assert_session(&session)?;

                PromptsListHandler {
                    prompt_controller_collection_holder: self.prompt_controller_collection_holder,
                }
                .handle(request, session)
                .await
            }
            ClientToServerMessage::ResourcesList(request) => {
                let session = self.assert_session(&session)?;

                ResourcesListHandler {
                    resource_list_aggregate: self.resource_list_aggregate,
                }
                .handle(request, session)
                .await
            }
            ClientToServerMessage::ResourcesRead(request) => {
                let session = self.assert_session(&session)?;

                ResourcesReadHandler {
                    resource_list_aggregate: self.resource_list_aggregate,
                }
                .handle(request, session)
                .await
            }
            ClientToServerMessage::ResourcesSubscribe(request) => {
                let session = self.assert_session(&session)?;

                ResourcesSubscribeHandler {
                    resource_list_aggregate: self.resource_list_aggregate,
                }
                .handle(request, session)
                .await
            }
            ClientToServerMessage::ResourcesTemplatesList(request) => {
                let session = self.assert_session(&session)?;

                ResourcesTemplatesListHandler {
                    resource_list_aggregate: self.resource_list_aggregate,
                }
                .handle(request, session)
                .await
            }
            ClientToServerMessage::ResourcesUnsubscribe(request) => {
                let session = self.assert_session(&session)?;

                ResourcesUnsubscribeHandler {}
                    .handle(request, session)
                    .await
            }
            ClientToServerMessage::ToolsCall(request) => {
                let session = self.assert_session(&session)?;

                ToolsCallHandler {
                    tool_registry: self.tool_registry,
                }
                .handle(request, session)
                .await
            }
            ClientToServerMessage::ToolsList(request) => {
                let session = self.assert_session(&session)?;

                ToolsListHandler {
                    tool_registry: self.tool_registry,
                }
                .handle(request, session)
                .await
            }
        }
    }
}