restructure into multiple crates

add mcp server collection
This commit is contained in:
milan
2026-04-07 00:06:13 +02:00
parent 2dbac8a91a
commit ee17e74753
21 changed files with 2095 additions and 164 deletions
+2
View File
@@ -1 +1,3 @@
/target
*/target
assist.toml
+6
View File
@@ -2,8 +2,14 @@
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/mcp_server_collection/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/own_mcp/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/common/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/mcp_server_collection/target" />
<excludeFolder url="file://$MODULE_DIR$/own_mcp/target" />
<excludeFolder url="file://$MODULE_DIR$/target" />
<excludeFolder url="file://$MODULE_DIR$/common/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
Generated
+132 -35
View File
@@ -2,23 +2,6 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "OwnAssist"
version = "0.1.0"
dependencies = [
"env_logger",
"log",
"ollama-rs",
"rand 0.10.0",
"reqwest 0.13.2",
"rmcp",
"serde",
"thiserror 2.0.18",
"tokio",
"toml",
"url",
]
[[package]]
name = "aho-corasick"
version = "1.1.4"
@@ -195,9 +178,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "cc"
version = "1.2.58"
version = "1.2.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1"
checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -444,9 +427,9 @@ dependencies = [
[[package]]
name = "fastrand"
version = "2.3.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
checksum = "a043dc74da1e37d6afe657061213aa6f425f855399a11d3463c6ecccc4dfda1f"
[[package]]
name = "find-msvc-tools"
@@ -933,9 +916,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.13.0"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
@@ -1139,9 +1122,9 @@ dependencies = [
[[package]]
name = "nix"
version = "0.30.1"
version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
dependencies = [
"bitflags",
"cfg-if",
@@ -1263,6 +1246,46 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "own_assist"
version = "0.1.0"
dependencies = [
"env_logger",
"log",
"ollama-rs",
"own_assist_common",
"own_mcp",
"rmcp",
"serde",
"tokio",
"url",
]
[[package]]
name = "own_assist_common"
version = "0.1.0"
dependencies = [
"serde",
"thiserror 2.0.18",
"toml",
]
[[package]]
name = "own_mcp"
version = "0.1.0"
dependencies = [
"env_logger",
"log",
"ollama-rs",
"rand 0.10.0",
"reqwest 0.13.2",
"rmcp",
"serde",
"thiserror 2.0.18",
"tokio",
"url",
]
[[package]]
name = "pastey"
version = "0.2.1"
@@ -1341,9 +1364,9 @@ dependencies = [
[[package]]
name = "process-wrap"
version = "9.0.1"
version = "9.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd1395947e69c07400ef4d43db0051d6f773c21f647ad8b97382fc01f0204c60"
checksum = "2e842efad9119158434d193c6682e2ebee4b44d6ad801d7b349623b3f57cdf55"
dependencies = [
"futures",
"indexmap",
@@ -1406,7 +1429,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
"windows-sys 0.52.0",
"windows-sys 0.60.2",
]
[[package]]
@@ -1865,9 +1888,9 @@ dependencies = [
[[package]]
name = "semver"
version = "1.0.27"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
[[package]]
name = "serde"
@@ -2174,9 +2197,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.50.0"
version = "1.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd"
dependencies = [
"bytes",
"libc",
@@ -2190,9 +2213,9 @@ dependencies = [
[[package]]
name = "tokio-macros"
version = "2.6.1"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c"
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
dependencies = [
"proc-macro2",
"quote",
@@ -2738,6 +2761,15 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.5",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
@@ -2771,13 +2803,30 @@ dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm 0.53.1",
"windows_aarch64_msvc 0.53.1",
"windows_i686_gnu 0.53.1",
"windows_i686_gnullvm 0.53.1",
"windows_i686_msvc 0.53.1",
"windows_x86_64_gnu 0.53.1",
"windows_x86_64_gnullvm 0.53.1",
"windows_x86_64_msvc 0.53.1",
]
[[package]]
name = "windows-threading"
version = "0.2.1"
@@ -2799,6 +2848,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
@@ -2811,6 +2866,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
@@ -2823,12 +2884,24 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
@@ -2841,6 +2914,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
@@ -2853,6 +2932,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
@@ -2865,6 +2950,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
@@ -2877,6 +2968,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "winnow"
version = "1.0.1"
+6 -8
View File
@@ -1,17 +1,15 @@
[package]
name = "OwnAssist"
name = "own_assist"
version = "0.1.0"
edition = "2024"
[dependencies]
ollama-rs = {version = "0.3.4", features = ["macros", "headers"]}
reqwest = "0.13.2"
ollama-rs = { version = "0.3.4", features = ["macros", "headers"] }
tokio = { version = "1.50.0", features = ["rt", "rt-multi-thread", "macros"] }
rmcp = {version="1.3.0", features = ["transport-streamable-http-client-reqwest", "reqwest", "client", "auth", "transport-child-process"]}
log = {version = "0.4.29"}
rmcp = { version = "1.3.0", features = ["client"] }
log = { version = "0.4.29" }
env_logger = "0.11.10"
serde = { version = "1.0.228", features = ["derive"] }
thiserror = "2.0.17"
toml = "1.1.2+spec-1.1.0"
url = "2.5.8"
rand = "0.10.0"
own_mcp = { path = "own_mcp" }
own_assist_common = { path = "common" }
+172
View File
@@ -0,0 +1,172 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "indexmap"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "own_assist_common"
version = "0.1.0"
dependencies = [
"serde",
"thiserror",
"toml",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_spanned"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
dependencies = [
"serde_core",
]
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee"
dependencies = [
"indexmap",
"serde_core",
"serde_spanned",
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
]
[[package]]
name = "toml_datetime"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "winnow"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5"
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "own_assist_common"
version = "0.1.0"
edition = "2024"
[dependencies]
thiserror = "2.0.18"
toml = "1.1.2"
serde = "1.0.228"
+16
View File
@@ -0,0 +1,16 @@
use std::path::Path;
use thiserror::Error;
use serde::de::DeserializeOwned;
#[derive(Debug, Error)]
pub enum ConfigLoadingError{
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
ParseError(#[from] toml::de::Error),
}
pub fn config_from_file<T: DeserializeOwned>(filepath: impl AsRef<Path>) -> Result<T, ConfigLoadingError> {
let toml_string: String = std::fs::read_to_string(filepath)?;
Ok(toml::from_str(&toml_string)?)
}
+2
View File
@@ -0,0 +1,2 @@
pub mod config_loader;
pub use config_loader::config_from_file;
+1450
View File
File diff suppressed because it is too large Load Diff
+16
View File
@@ -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 = []
+21
View File
@@ -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")
}
}
+47
View File
@@ -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")))
}
}
+63
View File
@@ -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;
}
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "own_mcp"
version = "0.1.0"
edition = "2024"
[dependencies]
ollama-rs = {version = "0.3.4", features = ["macros", "headers"]}
reqwest = "0.13.2"
tokio = { version = "1.50.0", features = ["rt", "rt-multi-thread", "macros"] }
rmcp = {version="1.3.0", features = ["transport-streamable-http-client-reqwest", "reqwest", "client", "auth", "transport-child-process"]}
log = {version = "0.4.29"}
env_logger = "0.11.10"
serde = { version = "1.0.228", features = ["derive"] }
thiserror = "2.0.17"
url = "2.5.8"
rand = "0.10.0"
+3
View File
@@ -0,0 +1,3 @@
pub mod mcp;
pub use mcp::chat::AgentChat;
+23 -12
View File
@@ -28,6 +28,7 @@ pub struct RestrictedTool {
pub struct MCPServerData {
name: String,
client: MCPClient,
/// tools in the ollama format. mcp resources are also translated into tools
translated_tools: Vec<RestrictedTool>,
}
@@ -42,6 +43,20 @@ impl MCPServerData {
})
}
match crate::mcp::translation::get_server_resource_tool_info(&client, &name).await {
Ok(resources_as_tool_infos) => {
for tool_info in resources_as_tool_infos {
tools.push(RestrictedTool {
permission: Default::default(),
tool_info,
})
}
},
Err(_) => {
log::warn!("Server `{name}` does not support resources");
}
}
Ok(MCPServerData {
name,
client,
@@ -68,6 +83,8 @@ pub enum ChatError {
FunctionParseError(String),
#[error("error parsing provided arguments")]
ArgumentParsingError,
#[error("service could not be found")]
ServiceNotFoundError(String),
}
impl AgentChat {
@@ -75,14 +92,9 @@ impl AgentChat {
ollama_client: Ollama,
model: String,
mcp_clients: HashMap<String, MCPClient>,
system_prompt: String
) -> Result<Self, ServiceError> {
let history = vec![
ChatMessage::system("Du bist ein Assistent, der per Sprache bedient wird. Du erhälst die Transkription. \
Wichtiger als deine Antworten sind deine Aktionen.\
Nutze bitte die tools, falls du sie brauchst um Informationen zu bekommen (z.B: über das aktuelle Datum oder den aktuellen Wochentag). \
Du bist in einem Agent Loop und kannst mehrere Tools hintereinander nutzen.".to_string()
)
];
let history = vec![ChatMessage::system(system_prompt)];
let mut servers = HashMap::new();
@@ -111,7 +123,7 @@ impl AgentChat {
all_tools
}
pub async fn message(&mut self, user_message: String) -> Result<(), ChatError> {
pub async fn message(&mut self, user_message: String) -> Result<ChatMessage, ChatError> {
let all_tools = self.get_all_tools();
log::debug!("all tools: {:#?}", all_tools);
@@ -157,7 +169,7 @@ impl AgentChat {
log::debug!("received message: {:#?}", response.message);
}
Ok(())
Ok(response.message)
}
async fn call_tool(
@@ -194,9 +206,8 @@ impl AgentChat {
Ok(self
.mcp_servers
.get(&mcp_name)
.unwrap()
.client
.call_tool(request_params)
.ok_or(ChatError::ServiceNotFoundError(mcp_name))?
.client.call_tool(request_params)
.await?)
}
}
+1 -3
View File
@@ -1,9 +1,7 @@
pub mod chat;
mod translation;
use ollama_rs::generation::tools::{ToolInfo, ToolType};
use rmcp::model::InitializeRequestParams;
use rmcp::serde_json::{Value};
use rmcp::service::{ClientInitializeError, RunningService};
use rmcp::transport::streamable_http_client::StreamableHttpClientTransportConfig;
use rmcp::{
@@ -15,7 +13,7 @@ use std::sync::Arc;
pub type MCPClient = RunningService<RoleClient, InitializeRequestParams>;
pub(crate) async fn get_client(
pub async fn get_client(
uri: impl Into<Arc<str>>,
authentication: Option<impl Into<String>>,
implementation: Implementation,
+85
View File
@@ -0,0 +1,85 @@
use crate::mcp::MCPClient;
use ollama_rs::generation::tools::{ToolFunctionInfo, ToolInfo, ToolType};
use ollama_rs::re_exports::schemars::_private::serde_json::{Map, Value};
use ollama_rs::re_exports::schemars::Schema;
use rmcp::ServiceError;
use crate::mcp::chat::ChatError::ServiceError as ChatServiceError;
pub(in crate::mcp) async fn get_server_tool_info(
client: &MCPClient,
server_name: &String,
) -> Result<Vec<ToolInfo>, ServiceError> {
let tool_info = client
.list_all_tools()
.await?
.iter()
.map(|mcp_tool| tool_info_from_mcp_tool(&mcp_tool, server_name))
.collect::<Vec<_>>();
Ok(tool_info)
}
fn tool_info_from_mcp_tool(mcp_tool: &rmcp::model::Tool, server_name: &String) -> ToolInfo {
let mut value_map: Map<String, Value> = Map::new();
for key in mcp_tool.input_schema.keys() {
let value = mcp_tool.input_schema.get(key).unwrap().clone();
value_map.insert(key.clone(), value);
}
let schema_value = Value::Object(value_map);
let schema = Schema::try_from(schema_value).unwrap();
ToolInfo {
tool_type: ToolType::Function,
function: ToolFunctionInfo {
name: format!("{}::{}", server_name, mcp_tool.name),
description: mcp_tool
.description
.clone()
.unwrap_or(std::borrow::Cow::from(""))
.to_string(),
parameters: schema,
},
}
}
pub(in crate::mcp) async fn get_server_resource_tool_info(
client: &MCPClient,
server_name: &String,
) -> Result<Vec<ToolInfo>, ServiceError> {
let resource_tool_info = client
.list_all_resources()
.await?
.iter()
.map(|resource| tool_info_from_mcp_resource(resource, server_name))
.collect::<Vec<_>>();
Ok(resource_tool_info)
}
fn tool_info_from_mcp_resource(
mcp_resource: &rmcp::model::Resource,
server_name: &String,
) -> ToolInfo {
let schema = Schema::try_from(Value::Object(Map::new())).unwrap();
log::debug!("resource schema: {:#?}", mcp_resource);
ToolInfo {
tool_type: ToolType::Function,
function: ToolFunctionInfo {
name: format!("{}::get_{}", server_name, mcp_resource.name),
description: format!(
"type: {} - {}",
mcp_resource
.mime_type.clone()
.unwrap_or("No type provided".to_string()),
mcp_resource.clone()
.description.clone()
.unwrap_or("No description".to_string())
),
parameters: schema,
},
}
}
+8 -17
View File
@@ -1,13 +1,13 @@
use own_assist_common::config_loader::ConfigLoadingError;
use std::collections::HashMap;
use log::error;
use ollama_rs::headers::{HeaderMap, HeaderValue};
use ollama_rs::Ollama;
use rmcp::model::Implementation;
use serde::Deserialize;
use thiserror::Error;
use url::Url;
use crate::mcp;
use crate::mcp::{guaranteed_mcp_server_name, MCPClient};
use own_mcp::mcp;
use own_mcp::mcp::{guaranteed_mcp_server_name, MCPClient};
use own_assist_common::config_from_file;
#[derive(Debug, Deserialize)]
pub struct Config {
@@ -16,18 +16,9 @@ pub struct Config {
mcp_servers: Vec<MCPServerConfig>
}
#[derive(Debug, Error)]
pub enum ConfigLoadingError{
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
ParseError(#[from] toml::de::Error),
}
impl Config {
pub async fn from_file() -> Result<Self, ConfigLoadingError> {
let toml_string = tokio::fs::read_to_string("assist.toml").await?;
Ok(toml::from_str(&toml_string)?)
pub fn from_file() -> Result<Self, ConfigLoadingError> {
config_from_file("assist.toml")
}
pub fn ollama_config(&self) -> &OllamaConfig {
@@ -56,7 +47,7 @@ impl Config {
let client = mcp::get_client(
mcp_server.url.as_str(),
mcp_server.authorization.clone(),
Implementation::new("OwnAssist", "0.0.1"),
Implementation::new("own_assist", env!("CARGO_PKG_VERSION")),
).await.unwrap();
let server_name = guaranteed_mcp_server_name(mcp_server.name.clone(), &client);
@@ -88,5 +79,5 @@ pub struct OllamaModelConfig {
pub struct MCPServerConfig {
pub name: Option<String>,
pub url: Url,
pub authorization: Option<String>, // should probably implement oauth some time
pub authorization: Option<String>, // should probably implement oauth some time // actually, fuck oauth
}
+17 -48
View File
@@ -1,23 +1,15 @@
pub mod mcp;
mod config;
mod model;
use std::collections::HashMap;
use ollama_rs::Ollama;
use reqwest::header::{HeaderMap, HeaderValue};
use rmcp::model::{Implementation};
use url::Url;
use crate::config::Config;
use crate::mcp::chat::MCPServerData;
use crate::mcp::guaranteed_mcp_server_name;
use crate::model::create_model_from_config;
#[tokio::main]
async fn main() {
env_logger::init();
let config = Config::from_file().await.inspect_err(|e|{
log::error!("error loading assist.toml {}", e);
let config = Config::from_file().inspect_err(|e|{
log::error!("error loading assist.toml: {}", e);
std::process::exit(1);
}).unwrap();
@@ -28,49 +20,26 @@ async fn main() {
let model_name = &config.ollama_config().model.name;
create_model_from_config(&ollama, &config.ollama_config().model).await.inspect_err(|e|{
log::error!("failed creating ollama model {model_name}: {e}")
log::error!("failed creating ollama model {model_name}: {e}");
std::process::exit(1);
}).unwrap();
let mut agent_chat = mcp::chat::AgentChat::new(ollama, model_name.clone(), mcp_clients).await.inspect_err(
let system_prompt = "Du bist ein Assistent, der per Sprache bedient wird. Du erhälst die Transkription. \
Wichtiger als deine Antworten sind deine Aktionen.\
Nutze bitte die tools, falls du sie brauchst um Informationen zu bekommen (z.B: über das aktuelle Datum oder den aktuellen Wochentag). \
Du bist in einem Agent Loop und kannst mehrere Tools hintereinander nutzen.".to_string();
let mut agent_chat = own_mcp::AgentChat::new(ollama, model_name.clone(), mcp_clients, system_prompt).await.inspect_err(
|e| {
log::error!("error creating agent: {}", e);
std::process::exit(1);
}
);
).unwrap();
log::debug!("chat: {:#?}", agent_chat);
/*
let ollama = Ollama::default();
log::info!("all tools: {:#?}", agent_chat.get_all_tools());
let fetch_client = mcp::get_client(
"https://remote.mcpservers.org/fetch/mcp",
None::<String>,
Implementation::new("OwnAssist", "0.0.1"),
).await.unwrap();
let answer = agent_chat.message("Welche Tools kannst du benutzten?".to_string()).await;
let answer = agent_chat.message("Teste beide Server aus. Nutze bei fetch https://example.com/. Melde mir die Ergebnisse zurück.".to_string()).await;
let mcp_clients = HashMap::from([
("fetch".to_string(), fetch_client),
]);
let mut agent_chat = mcp::chat::AgentChat::new(ollama, "ollama:e2b".to_string(), mcp_clients).await.unwrap();
log::debug!("chat: {:#?}", agent_chat);
agent_chat.message(String::from("Was ist auf der Website https://uno.mboemer.com zu finden?")).await.unwrap();
*/
/*let ollama = Ollama::default();
let mut history = vec![
ChatMessage::system("Du bist ein Assistent, der per Sprache bedient wird. Du erhälst die Transkription. \
Wichtiger als deine Antworten sind deine Aktionen.\
Nutze bitte die tools, falls du sie brauchst um Informationen zu bekommen (z.B: über das aktuelle Datum oder den aktuellen Wochentag).".to_string())
];
let user_message = vec![
ChatMessage::user("Welche Funktionen stehen dir zur Verfügung?".to_string()),
];
let response = ollama.send_chat_messages_with_history(&mut history, ChatMessageRequest::new("gemma4:e2b".to_string(), user_message).tools(vec![])).await.unwrap();
log::info!("response: {response:?}");
log::debug!("history: {history:?}");*/
dbg!(answer);
}
-41
View File
@@ -1,41 +0,0 @@
use ollama_rs::generation::tools::{ToolFunctionInfo, ToolInfo, ToolType};
use ollama_rs::re_exports::schemars::_private::serde_json::{Map, Value};
use ollama_rs::re_exports::schemars::Schema;
use rmcp::ServiceError;
use crate::mcp::MCPClient;
pub(in crate::mcp) async fn get_server_tool_info(
client: &MCPClient, server_name: &String,
) -> Result<Vec<ToolInfo>, ServiceError> {
let tool_info = client
.list_tools(Default::default())
.await?
.tools
.iter()
.map(|mcp_tool| tool_info_from_mcp_tool(&mcp_tool, server_name))
.collect::<Vec<_>>();
Ok(tool_info)
}
fn tool_info_from_mcp_tool(mcp_tool: &rmcp::model::Tool, server_name: &String) -> ToolInfo {
let mut value_map: Map<String, Value> = Map::new();
for key in mcp_tool.input_schema.keys() {
let value = mcp_tool.input_schema.get(key).unwrap().clone();
value_map.insert(key.clone(), value);
}
let schema_value = Value::Object(value_map);
let schema = Schema::try_from(schema_value).unwrap();
ToolInfo {
tool_type: ToolType::Function,
function: ToolFunctionInfo {
name: format!("{}::{}", server_name, mcp_tool.name),
description: mcp_tool.description.clone().unwrap_or(std::borrow::Cow::from("")).to_string(),
parameters: schema,
},
}
}