Skip to main content
Glama
lib.rs5.02 kB
use std::sync::Arc; use clap::Parser; use rmcp::transport::streamable_http_server::{ StreamableHttpService, session::local::LocalSessionManager, }; use axum::routing::get_service; use tower_http::cors::{Any, CorsLayer}; use tower_http::services::fs::ServeDir; use utoipa::OpenApi; use utoipa_axum::router::OpenApiRouter; use utoipa_axum::routes; use utoipa_swagger_ui::SwaggerUi; mod chat; pub mod config; pub mod db; pub mod error; pub mod index; pub mod mcp; pub mod sparql_client; mod utils; mod validate; pub mod void_schema; use crate::db::SearchDB; use crate::error::AppResult; use crate::mcp::SparqlTools; use crate::void_schema::SchemasMap; /// Command line arguments for the `sparql-mcp` server #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] pub struct Args { /// Optional path to a JSON file containing SPARQL endpoints config #[arg(value_name = "CONFIG_JSON", required = false)] pub config_json: Option<String>, /// Use STDIO transport instead of HTTP #[arg(short = 's', long = "stdio", env = "STDIO", default_value_t = false)] pub stdio: bool, /// Address where to deploy this server #[arg( short = 'b', long = "bind", env = "BIND_ADDRESS", default_value = "0.0.0.0:8000" )] pub bind_address: String, /// Path where LanceDB will store the search index #[arg( short = 'd', long = "db-path", env = "DB_PATH", default_value = "./data/lancedb" )] pub db_path: String, /// Only deploy the MCP endpoint without the search endpoint #[arg(short = 'm', long = "mcp-only", default_value_t = false)] pub mcp_only: bool, /// Force reindexing of the SPARQL endpoints at startup #[arg(short = 'f', long = "force-index", default_value_t = false)] pub force_index: bool, /// Automatically index any unknown endpoint when asked about it #[arg( short = 'a', long = "auto-index", env = "AUTO_INDEX", default_value_t = false )] pub auto_index: bool, } /// OpenAPI documentation for the API #[derive(OpenApi)] #[openapi(info( title = "SPARQL MCP server", version = "1.0.0", description = r#"MCP server to help generating SPARQL queries for questions in natural language, developed for the [SIB Expasy project](https://www.expasy.org). If asked about an endpoint it does not know it will automatically try to index it. [Source code](https://github.com/sib-swiss/sparql-mcp)"# ))] struct ApiDoc; #[derive(Clone)] pub struct AppState { pub bind_address: String, pub schemas_map: SchemasMap, pub db: Arc<SearchDB>, } /// Build the Axum router with the configured endpoints and services pub async fn build_router( args: &Args, db: &Arc<SearchDB>, schemas_map: &SchemasMap, ) -> AppResult<axum::Router> { // // Load endpoints config // let endpoints_config = if let Some(ref json_path) = args.config_json { // EndpointsConfig::from_json_file(json_path)? // } else { // EndpointsConfig::default() // }; // // Init search DB // let (db, schemas_map) = // index::init_db(&endpoints_config, &args.db_path, args.force_index).await?; // Init web app state let app_state = AppState { bind_address: args.bind_address.clone(), schemas_map: schemas_map.clone(), db: db.clone(), }; let cors = CorsLayer::new().allow_origin(Any).allow_headers([ axum::http::header::AUTHORIZATION, axum::http::header::CONTENT_TYPE, ]); // Init MCP service let auto_index = args.auto_index; let db = db.clone(); let schemas_map = schemas_map.clone(); let mcp_service = StreamableHttpService::new( move || { Ok(SparqlTools::new( db.clone(), schemas_map.clone(), auto_index, )) }, LocalSessionManager::default().into(), Default::default(), ); // Build router let router = if args.mcp_only { axum::Router::new() .nest_service("/mcp", mcp_service) .layer(cors) } else { let webapp_service = get_service(ServeDir::new("src/webapp")).handle_error(|error| async move { ( axum::http::StatusCode::INTERNAL_SERVER_ERROR, format!("Internal error: {error}"), ) }); let (router, api) = OpenApiRouter::with_openapi(ApiDoc::openapi()) .routes(routes!(chat::chat_handler)) // TODO: add route to get list of endpoints currently supported // .routes(routes!(chat::list_endpoints)) .with_state(app_state) .split_for_parts(); router .fallback_service(webapp_service) .nest_service("/mcp", mcp_service) .merge(SwaggerUi::new("/docs").url("/openapi.json", api)) .layer(cors) }; Ok(router) }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sib-swiss/sparql-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server