use std::sync::Arc;
use axum::routing::{get, post};
use axum::Router;
use crate::tenancy::jwt_lifecycle::JwtLifecycle;
use super::auth::{agent_token, default_jwt, post_authed};
use super::transport::{post_handler, sse_handler};
#[derive(Clone)]
pub(crate) struct McpState {
#[allow(dead_code)]
pub(crate) pool: Option<crate::sql::Pool>,
pub(crate) jwt: Option<Arc<JwtLifecycle>>,
pub(crate) page_size: Option<usize>,
}
impl McpState {
fn new(pool: Option<crate::sql::Pool>) -> Self {
Self {
pool,
jwt: None,
page_size: None,
}
}
}
fn routes(state: McpState) -> Router {
Router::new()
.route("/", post(post_handler).get(sse_handler))
.with_state(state)
}
#[must_use]
pub fn router(pool: crate::sql::Pool) -> Router {
routes(McpState::new(Some(pool)))
}
#[must_use]
pub fn tenant_router() -> Router {
routes(McpState::new(None))
}
#[must_use]
pub fn secure_tenant_router() -> Router {
tenant_router_authed(default_jwt())
}
#[cfg(feature = "config")]
const MCP_MAX_BODY_BYTES: usize = 1024 * 1024;
#[cfg(feature = "config")]
#[must_use]
pub fn secure_tenant_router_from_settings(settings: &crate::config::McpSettings) -> Router {
let jwt = Arc::new(
JwtLifecycle::new(super::auth::jwt_secret()).with_access_ttl(settings.token_ttl_secs()),
);
let state = McpState {
jwt: Some(jwt),
page_size: settings.max_tools_listed,
..McpState::new(None)
};
let mut router = authed_routes(state, settings.sse_enabled());
if !settings.allowed_origins.is_empty() {
use crate::cors::{CorsLayer, CorsRouterExt};
router = router.cors(CorsLayer::new().allow_origins(settings.allowed_origins.clone()));
}
if let Some(rpm) = settings.rate_limit_per_minute {
use crate::rate_limit::{RateLimitLayer, RateLimitRouterExt};
router = router.rate_limit(RateLimitLayer::per_ip(
rpm,
std::time::Duration::from_secs(60),
));
}
{
use crate::body_limit::{BodyLimitLayer, BodyLimitRouterExt};
router = router.body_limit(BodyLimitLayer::new(MCP_MAX_BODY_BYTES));
}
router
}
#[must_use]
pub fn tenant_router_authed(jwt: Arc<JwtLifecycle>) -> Router {
let state = McpState {
jwt: Some(jwt),
..McpState::new(None)
};
authed_routes(state, true)
}
fn authed_routes(state: McpState, enable_sse: bool) -> Router {
use super::oauth;
let root = if enable_sse {
post(post_authed).get(sse_handler)
} else {
post(post_authed)
};
Router::new()
.route("/", root)
.route("/token", post(agent_token))
.route("/oauth/token", post(oauth::oauth_token))
.route(
"/.well-known/oauth-protected-resource",
get(oauth::well_known_protected_resource),
)
.route(
"/.well-known/oauth-authorization-server",
get(oauth::well_known_authorization_server),
)
.with_state(state)
}