systemprompt-api 0.1.18

HTTP API server and gateway for systemprompt.io OS
Documentation
use axum::routing::get;
use axum::{Json, Router};
use serde_json::json;
use systemprompt_models::api::SingleResponse;
use systemprompt_models::modules::ApiPaths;
use systemprompt_runtime::AppContext;

use super::health::handle_health;

pub async fn handle_root_discovery(
    axum::extract::State(ctx): axum::extract::State<AppContext>,
) -> impl axum::response::IntoResponse {
    let base = &ctx.config().api_external_url;
    let data = json!({
        "name": format!("{} API", ctx.config().sitename),
        "version": "1.0.0",
        "description": "systemprompt.io OS API Gateway",
        "endpoints": {
            "health": format!("{}{}", base, ApiPaths::HEALTH),
            "oauth": {
                "href": format!("{}{}", base, ApiPaths::OAUTH_BASE),
                "description": "OAuth2/OIDC authentication and WebAuthn",
                "endpoints": {
                    "authorize": format!("{}{}", base, ApiPaths::OAUTH_AUTHORIZE),
                    "token": format!("{}{}", base, ApiPaths::OAUTH_TOKEN),
                    "userinfo": format!("{}{}/userinfo", base, ApiPaths::OAUTH_BASE),
                    "introspect": format!("{}{}/introspect", base, ApiPaths::OAUTH_BASE),
                    "revoke": format!("{}{}/revoke", base, ApiPaths::OAUTH_BASE),
                    "webauthn": format!("{}{}/webauthn", base, ApiPaths::OAUTH_BASE)
                }
            },
            "core": {
                "href": format!("{}{}", base, ApiPaths::CORE_BASE),
                "description": "Core conversation, task, and artifact management",
                "endpoints": {
                    "contexts": format!("{}{}", base, ApiPaths::CORE_CONTEXTS),
                    "tasks": format!("{}{}", base, ApiPaths::CORE_TASKS),
                    "artifacts": format!("{}{}", base, ApiPaths::CORE_ARTIFACTS)
                }
            },
            "agents": {
                "href": format!("{}{}", base, ApiPaths::AGENTS_REGISTRY),
                "description": "A2A protocol agent registry and proxy",
                "endpoints": {
                    "registry": format!("{}{}", base, ApiPaths::AGENTS_REGISTRY),
                    "proxy": format!("{}{}{{agent_id}}", base, ApiPaths::AGENTS_BASE)
                }
            },
            "mcp": {
                "href": format!("{}{}", base, ApiPaths::MCP_REGISTRY),
                "description": "MCP server registry and lifecycle management",
                "endpoints": {
                    "registry": format!("{}{}", base, ApiPaths::MCP_REGISTRY),
                    "proxy": format!("{}{}{{server_name}}", base, ApiPaths::MCP_BASE)
                }
            },
            "stream": {
                "href": format!("{}{}", base, ApiPaths::STREAM_BASE),
                "description": "Server-Sent Events (SSE) for real-time updates",
                "endpoints": {
                    "contexts": format!("{}{}", base, ApiPaths::STREAM_CONTEXTS)
                }
            }
        },
        "wellknown": {
            "oauth": format!("{}{}", base, ApiPaths::WELLKNOWN_OAUTH_SERVER),
            "agent": format!("{}{}", base, ApiPaths::WELLKNOWN_AGENT_CARD)
        }
    });

    Json(SingleResponse::new(data))
}

pub async fn handle_core_discovery(
    axum::extract::State(ctx): axum::extract::State<AppContext>,
) -> impl axum::response::IntoResponse {
    let base = &ctx.config().api_external_url;
    let data = json!({
        "name": "Core Services",
        "description": "Core conversation, task, and artifact management APIs",
        "endpoints": {
            "contexts": {
                "href": format!("{}{}", base, ApiPaths::CORE_CONTEXTS),
                "description": "Conversation context management",
                "methods": ["GET", "POST", "DELETE"]
            },
            "tasks": {
                "href": format!("{}{}", base, ApiPaths::CORE_TASKS),
                "description": "Task management for agent operations",
                "methods": ["GET", "POST", "PUT", "DELETE"]
            },
            "artifacts": {
                "href": format!("{}{}", base, ApiPaths::CORE_ARTIFACTS),
                "description": "Artifact storage and retrieval",
                "methods": ["GET", "POST", "DELETE"]
            },
            "oauth": {
                "href": format!("{}{}", base, ApiPaths::OAUTH_BASE),
                "description": "OAuth2/OIDC authentication endpoints"
            }
        }
    });
    Json(SingleResponse::new(data))
}

pub async fn handle_agents_discovery(
    axum::extract::State(ctx): axum::extract::State<AppContext>,
) -> impl axum::response::IntoResponse {
    let base = &ctx.config().api_external_url;
    let data = json!({
        "name": "Agent Services",
        "description": "A2A protocol agent registry and proxy",
        "endpoints": {
            "registry": {
                "href": format!("{}{}", base, ApiPaths::AGENTS_REGISTRY),
                "description": "List and discover available agents",
                "methods": ["GET"]
            },
            "proxy": {
                "href": format!("{}{}/<agent_id>/", base, ApiPaths::AGENTS_BASE),
                "description": "Proxy requests to specific agents",
                "methods": ["GET", "POST"]
            }
        }
    });
    Json(SingleResponse::new(data))
}

pub async fn handle_mcp_discovery(
    axum::extract::State(ctx): axum::extract::State<AppContext>,
) -> impl axum::response::IntoResponse {
    let base = &ctx.config().api_external_url;
    let data = json!({
        "name": "MCP Services",
        "description": "Model Context Protocol server registry and proxy",
        "endpoints": {
            "registry": {
                "href": format!("{}{}", base, ApiPaths::MCP_REGISTRY),
                "description": "List and discover available MCP servers",
                "methods": ["GET"]
            },
            "proxy": {
                "href": format!("{}{}/<server_name>/mcp", base, ApiPaths::MCP_BASE),
                "description": "Proxy requests to specific MCP servers",
                "methods": ["GET", "POST"]
            }
        }
    });
    Json(SingleResponse::new(data))
}

pub fn discovery_router(ctx: &AppContext) -> Router {
    Router::new()
        .route(ApiPaths::DISCOVERY, get(handle_root_discovery))
        .route(ApiPaths::HEALTH, get(handle_health))
        .route("/health", get(handle_health))
        .route(ApiPaths::CORE_BASE, get(handle_core_discovery))
        .route(ApiPaths::AGENTS_BASE, get(handle_agents_discovery))
        .route(ApiPaths::MCP_BASE, get(handle_mcp_discovery))
        .with_state(ctx.clone())
}