restructure into multiple crates
add mcp server collection
This commit is contained in:
Generated
+1450
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "mcp_server_collection"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
rmcp = { version = "1.3.0", features = ["server", "transport-streamable-http-server-session", "transport-streamable-http-server"] }
|
||||
chrono = "0.4.44"
|
||||
axum = "0.8.8"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
tokio = { version = "1.51.0", features = ["full"] }
|
||||
own_assist_common = {path="../common"}
|
||||
tokio-util = "0.7.18"
|
||||
|
||||
[features]
|
||||
datetime = []
|
||||
@@ -0,0 +1,21 @@
|
||||
use serde::Deserialize;
|
||||
use own_assist_common::config_from_file;
|
||||
use own_assist_common::config_loader::ConfigLoadingError;
|
||||
use crate::McpServiceType;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(crate) struct Config {
|
||||
pub(crate) servers: Vec<ServerConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(crate) struct ServerConfig {
|
||||
pub(crate) path: String,
|
||||
pub(crate) r#type: McpServiceType
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn from_file() -> Result<Self, ConfigLoadingError> {
|
||||
config_from_file("servers.toml")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
use chrono::{Datelike, Local};
|
||||
use rmcp::model::{Implementation, ServerCapabilities, ServerInfo};
|
||||
use rmcp::{tool, tool_handler, tool_router, ServerHandler};
|
||||
use rmcp::handler::server::tool::ToolRouter;
|
||||
use crate::McpServerHandler;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DateTimeHandler {
|
||||
tool_router: ToolRouter<Self>
|
||||
}
|
||||
|
||||
impl McpServerHandler for DateTimeHandler {
|
||||
fn new() -> Self {
|
||||
DateTimeHandler { tool_router: Self::tool_router() }
|
||||
}
|
||||
}
|
||||
|
||||
#[tool_router]
|
||||
impl DateTimeHandler {
|
||||
#[tool(description = "returns the local datetime in ISO 8601", annotations(read_only_hint = true))]
|
||||
fn get_local_datetime() -> String {
|
||||
Local::now().format("%+").to_string()
|
||||
}
|
||||
|
||||
#[tool(description = "returns the utc datetime in ISO 8601", annotations(read_only_hint = true))]
|
||||
fn get_utc_datetime() -> String {
|
||||
Local::now().format("%+").to_string()
|
||||
}
|
||||
|
||||
#[tool(description = "return current week number in year", annotations(read_only_hint = true))]
|
||||
fn get_week() -> String {
|
||||
Local::now().iso_week().week().to_string()
|
||||
}
|
||||
|
||||
#[tool(description = "return current weekday as 3-character-string (e.g. Mon, Tue, ...)", annotations(read_only_hint = true))]
|
||||
fn get_weekday() -> String {
|
||||
Local::now().weekday().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[tool_handler]
|
||||
impl ServerHandler for DateTimeHandler {
|
||||
fn get_info(&self) -> ServerInfo {
|
||||
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
|
||||
.with_instructions("A simple clock/datetime provider. It is read only and thus safe to use.".to_string()).with_server_info(Implementation::new("datetime", env!("CARGO_PKG_VERSION")))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#[cfg(feature = "datetime")]
|
||||
pub mod datetime;
|
||||
mod config;
|
||||
|
||||
use axum::Router;
|
||||
use rmcp::transport::{
|
||||
StreamableHttpServerConfig,
|
||||
streamable_http_server::{session::local::LocalSessionManager, tower::StreamableHttpService},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use tokio::main;
|
||||
use crate::config::{Config, ServerConfig};
|
||||
#[cfg(feature = "datetime")]
|
||||
use crate::datetime::DateTimeHandler;
|
||||
|
||||
pub trait McpServerHandler: rmcp::ServerHandler {
|
||||
fn new() -> Self;
|
||||
|
||||
fn mcp_service() -> StreamableHttpService<Self, LocalSessionManager> {
|
||||
StreamableHttpService::new(|| Ok(Self::new()), LocalSessionManager::default().into(), StreamableHttpServerConfig::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub enum McpServiceType {
|
||||
DateTime
|
||||
}
|
||||
|
||||
pub(crate) fn mcp_router(server_configs: Vec<ServerConfig>) -> Router {
|
||||
let mut router = Router::new();
|
||||
|
||||
for config in &server_configs {
|
||||
router = match config.r#type {
|
||||
McpServiceType::DateTime => {
|
||||
router.route_service(config.path.as_str(), DateTimeHandler::mcp_service())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
router
|
||||
}
|
||||
|
||||
#[main]
|
||||
async fn main() {
|
||||
let config = Config::from_file().inspect_err(|e|{
|
||||
eprintln!("Error loading config: {}", e); // FIXME: use actual logger instead
|
||||
std::process::exit(1);
|
||||
}).unwrap();
|
||||
|
||||
let router = mcp_router(config.servers);
|
||||
|
||||
let tcp_listener = tokio::net::TcpListener::bind("127.0.0.1:8000").await.unwrap(); // use toml config
|
||||
|
||||
let ct = tokio_util::sync::CancellationToken::new();
|
||||
|
||||
let _ = axum::serve(tcp_listener, router)
|
||||
.with_graceful_shutdown(async move {
|
||||
tokio::signal::ctrl_c().await.unwrap();
|
||||
ct.cancel();
|
||||
})
|
||||
.await;
|
||||
}
|
||||
Reference in New Issue
Block a user