diff --git a/Cargo.lock b/Cargo.lock
index 474bd31..4fc7ca8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -99,30 +99,6 @@ version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
-[[package]]
-name = "aws-lc-rs"
-version = "1.18.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e"
-dependencies = [
- "aws-lc-sys",
- "untrusted 0.7.1",
- "zeroize",
-]
-
-[[package]]
-name = "aws-lc-sys"
-version = "0.45.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27"
-dependencies = [
- "cc",
- "cmake",
- "dunce",
- "fs_extra",
- "pkg-config",
-]
-
[[package]]
name = "axum"
version = "0.8.9"
@@ -175,35 +151,12 @@ dependencies = [
"tracing",
]
-[[package]]
-name = "axum-server"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1df331683d982a0b9492b38127151e6453639cd34926eb9c07d4cd8c6d22bfc"
-dependencies = [
- "bytes",
- "either",
- "fs-err",
- "http",
- "http-body",
- "hyper",
- "hyper-util",
- "tokio",
- "tower-service",
-]
-
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
-[[package]]
-name = "base64"
-version = "0.23.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
-
[[package]]
name = "bitflags"
version = "2.13.1"
@@ -219,15 +172,6 @@ dependencies = [
"crunchy",
]
-[[package]]
-name = "block-buffer"
-version = "0.10.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
-dependencies = [
- "generic-array",
-]
-
[[package]]
name = "bon"
version = "3.10.1"
@@ -306,8 +250,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06"
dependencies = [
"cfg-if",
- "cpufeatures 0.3.1",
- "rand_core 0.10.1",
+ "cpufeatures",
+ "rand_core",
]
[[package]]
@@ -350,59 +294,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
-[[package]]
-name = "cmake"
-version = "0.1.58"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
-dependencies = [
- "cc",
-]
-
[[package]]
name = "colorchoice"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
-[[package]]
-name = "cookie"
-version = "0.18.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87"
-dependencies = [
- "percent-encoding",
- "time",
- "version_check",
-]
-
-[[package]]
-name = "cookie_store"
-version = "0.22.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206"
-dependencies = [
- "cookie",
- "document-features",
- "idna",
- "log",
- "publicsuffix",
- "serde",
- "serde_derive",
- "serde_json",
- "time",
- "url",
-]
-
-[[package]]
-name = "cpufeatures"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
-dependencies = [
- "libc",
-]
-
[[package]]
name = "cpufeatures"
version = "0.3.1"
@@ -461,16 +358,6 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
-[[package]]
-name = "crypto-common"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
-dependencies = [
- "generic-array",
- "typenum",
-]
-
[[package]]
name = "darling"
version = "0.24.1"
@@ -520,17 +407,6 @@ dependencies = [
"serde_core",
]
-[[package]]
-name = "digest"
-version = "0.10.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
-dependencies = [
- "block-buffer",
- "crypto-common",
- "subtle",
-]
-
[[package]]
name = "dirs"
version = "6.0.0"
@@ -563,15 +439,6 @@ dependencies = [
"syn 3.0.5",
]
-[[package]]
-name = "document-features"
-version = "0.2.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
-dependencies = [
- "litrs",
-]
-
[[package]]
name = "downcast-rs"
version = "2.0.2"
@@ -579,10 +446,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc"
[[package]]
-name = "dunce"
-version = "1.0.5"
+name = "dyn-clone"
+version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
[[package]]
name = "either"
@@ -656,16 +523,6 @@ dependencies = [
"percent-encoding",
]
-[[package]]
-name = "fs-err"
-version = "3.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b91aa448ca50d7e79433bdf3ee8d99215430d2ec02ade5aefab2a073a1822e8a"
-dependencies = [
- "autocfg",
- "tokio",
-]
-
[[package]]
name = "fs4"
version = "0.13.1"
@@ -676,27 +533,6 @@ dependencies = [
"windows-sys 0.59.0",
]
-[[package]]
-name = "fs_extra"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
-
-[[package]]
-name = "futures"
-version = "0.3.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-executor",
- "futures-io",
- "futures-sink",
- "futures-task",
- "futures-util",
-]
-
[[package]]
name = "futures-channel"
version = "0.3.34"
@@ -704,7 +540,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4"
dependencies = [
"futures-core",
- "futures-sink",
]
[[package]]
@@ -713,17 +548,6 @@ version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
-[[package]]
-name = "futures-executor"
-version = "0.3.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432"
-dependencies = [
- "futures-core",
- "futures-task",
- "futures-util",
-]
-
[[package]]
name = "futures-io"
version = "0.3.34"
@@ -759,7 +583,6 @@ version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
dependencies = [
- "futures-channel",
"futures-core",
"futures-io",
"futures-macro",
@@ -770,16 +593,6 @@ dependencies = [
"slab",
]
-[[package]]
-name = "generic-array"
-version = "0.14.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
-dependencies = [
- "typenum",
- "version_check",
-]
-
[[package]]
name = "getrandom"
version = "0.2.17"
@@ -803,7 +616,7 @@ dependencies = [
"js-sys",
"libc",
"r-efi",
- "rand_core 0.10.1",
+ "rand_core",
"wasm-bindgen",
]
@@ -813,25 +626,6 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
-[[package]]
-name = "h2"
-version = "0.4.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16"
-dependencies = [
- "atomic-waker",
- "bytes",
- "fnv",
- "futures-core",
- "futures-sink",
- "http",
- "indexmap",
- "slab",
- "tokio",
- "tokio-util",
- "tracing",
-]
-
[[package]]
name = "hashbrown"
version = "0.16.1"
@@ -843,27 +637,12 @@ dependencies = [
"foldhash",
]
-[[package]]
-name = "hashbrown"
-version = "0.17.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
-
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
-[[package]]
-name = "hmac"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
-dependencies = [
- "digest",
-]
-
[[package]]
name = "htmlescape"
version = "0.3.1"
@@ -925,7 +704,6 @@ dependencies = [
"bytes",
"futures-channel",
"futures-core",
- "h2",
"http",
"http-body",
"httparse",
@@ -959,7 +737,7 @@ version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
dependencies = [
- "base64 0.22.1",
+ "base64",
"bytes",
"futures-channel",
"futures-util",
@@ -1086,16 +864,6 @@ dependencies = [
"icu_properties",
]
-[[package]]
-name = "indexmap"
-version = "2.14.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
-dependencies = [
- "equivalent",
- "hashbrown 0.17.1",
-]
-
[[package]]
name = "inventory"
version = "0.3.24"
@@ -1153,24 +921,6 @@ dependencies = [
"wasm-bindgen",
]
-[[package]]
-name = "jsonwebtoken"
-version = "10.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc"
-dependencies = [
- "aws-lc-rs",
- "base64 0.22.1",
- "getrandom 0.2.17",
- "js-sys",
- "pem",
- "serde",
- "serde_json",
- "signature",
- "simple_asn1",
- "zeroize",
-]
-
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -1210,12 +960,6 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae"
-[[package]]
-name = "litrs"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
-
[[package]]
name = "lock_api"
version = "0.4.14"
@@ -1237,7 +981,7 @@ version = "0.16.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39"
dependencies = [
- "hashbrown 0.16.1",
+ "hashbrown",
]
[[package]]
@@ -1269,12 +1013,12 @@ dependencies = [
"futures-util",
"glob",
"reqwest",
- "rust-mcp-axum",
- "rust-mcp-sdk",
+ "schemars",
"serde",
"serde_json",
"tantivy",
"tokio",
+ "tokio-stream",
"tokio-util",
"tracing",
"tracing-subscriber",
@@ -1322,16 +1066,6 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
-[[package]]
-name = "mime_guess"
-version = "2.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
-dependencies = [
- "mime",
- "unicase",
-]
-
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@@ -1374,31 +1108,12 @@ dependencies = [
"windows-sys 0.61.2",
]
-[[package]]
-name = "num-bigint"
-version = "0.4.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
-dependencies = [
- "num-integer",
- "num-traits",
-]
-
[[package]]
name = "num-conv"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
-[[package]]
-name = "num-integer"
-version = "0.1.47"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b"
-dependencies = [
- "num-traits",
-]
-
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -1408,15 +1123,6 @@ dependencies = [
"autocfg",
]
-[[package]]
-name = "num_threads"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
-dependencies = [
- "libc",
-]
-
[[package]]
name = "once_cell"
version = "1.21.4"
@@ -1482,16 +1188,6 @@ dependencies = [
"windows-link",
]
-[[package]]
-name = "pem"
-version = "3.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
-dependencies = [
- "base64 0.22.1",
- "serde_core",
-]
-
[[package]]
name = "percent-encoding"
version = "2.3.2"
@@ -1544,22 +1240,6 @@ dependencies = [
"unicode-ident",
]
-[[package]]
-name = "psl-types"
-version = "2.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
-
-[[package]]
-name = "publicsuffix"
-version = "2.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf"
-dependencies = [
- "idna",
- "psl-types",
-]
-
[[package]]
name = "quinn"
version = "0.11.11"
@@ -1639,16 +1319,7 @@ checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
dependencies = [
"chacha20",
"getrandom 0.4.3",
- "rand_core 0.10.1",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom 0.2.17",
+ "rand_core",
]
[[package]]
@@ -1663,7 +1334,7 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a"
dependencies = [
- "rand_core 0.10.1",
+ "rand_core",
]
[[package]]
@@ -1706,6 +1377,26 @@ dependencies = [
"thiserror",
]
+[[package]]
+name = "ref-cast"
+version = "1.0.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3"
+dependencies = [
+ "ref-cast-impl",
+]
+
+[[package]]
+name = "ref-cast-impl"
+version = "1.0.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.5",
+]
+
[[package]]
name = "regex"
version = "1.13.1"
@@ -1741,10 +1432,8 @@ version = "0.12.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [
- "base64 0.22.1",
+ "base64",
"bytes",
- "cookie",
- "cookie_store",
"futures-core",
"futures-util",
"http",
@@ -1755,7 +1444,6 @@ dependencies = [
"hyper-util",
"js-sys",
"log",
- "mime_guess",
"percent-encoding",
"pin-project-lite",
"quinn",
@@ -1789,100 +1477,10 @@ dependencies = [
"cfg-if",
"getrandom 0.2.17",
"libc",
- "untrusted 0.9.0",
+ "untrusted",
"windows-sys 0.52.0",
]
-[[package]]
-name = "rust-mcp-axum"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8fdd00ac2a7ee6389439a865292faea8ef678b3e01ad66046ea8cf320587dc7"
-dependencies = [
- "async-trait",
- "axum",
- "axum-server",
- "futures",
- "http",
- "rust-mcp-sdk",
- "thiserror",
- "tokio",
- "tracing",
-]
-
-[[package]]
-name = "rust-mcp-macros"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58bd70356e3da5f64bb9cae7b1acc90279378c9220c6b69731d8af945938fef8"
-dependencies = [
- "proc-macro2",
- "quote",
- "serde",
- "serde_json",
- "syn 3.0.5",
-]
-
-[[package]]
-name = "rust-mcp-schema"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a66664d256795cdd61b7d4d6516d8afe041c947bedd1d10aff9e41e15771b43"
-dependencies = [
- "serde",
- "serde_json",
-]
-
-[[package]]
-name = "rust-mcp-sdk"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9849649137179b679d8115c79716fcf03b1bd7d3b579bb61081eed59d9bbabcb"
-dependencies = [
- "async-trait",
- "base64 0.23.1",
- "bytes",
- "futures",
- "hmac",
- "http",
- "http-body",
- "http-body-util",
- "jsonwebtoken",
- "reqwest",
- "rust-mcp-macros",
- "rust-mcp-schema",
- "rust-mcp-transport",
- "serde",
- "serde_json",
- "sha2",
- "thiserror",
- "time",
- "tokio",
- "tokio-stream",
- "tracing",
- "url",
- "uuid",
-]
-
-[[package]]
-name = "rust-mcp-transport"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1421cf8c7524c398b94c84af024a5af59c146bdbc1d4afed076b2eac9b0a99df"
-dependencies = [
- "async-trait",
- "bytes",
- "futures",
- "reqwest",
- "rust-mcp-schema",
- "serde",
- "serde_json",
- "thiserror",
- "tokio",
- "tokio-stream",
- "tracing",
-]
-
[[package]]
name = "rust-stemmers"
version = "1.2.0"
@@ -1944,7 +1542,7 @@ checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2"
dependencies = [
"ring",
"rustls-pki-types",
- "untrusted 0.9.0",
+ "untrusted",
]
[[package]]
@@ -1959,6 +1557,31 @@ version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
+[[package]]
+name = "schemars"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a"
+dependencies = [
+ "dyn-clone",
+ "ref-cast",
+ "schemars_derive",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "schemars_derive"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d98c67716b46af2f0b8cf752abc930f6f9aecfbf671ecfb531db8a31dbe4e2ba"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde_derive_internals",
+ "syn 3.0.5",
+]
+
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -1995,6 +1618,17 @@ dependencies = [
"syn 3.0.5",
]
+[[package]]
+name = "serde_derive_internals"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f852137cce035d6a4df67ccce505ff6b3e9fd3a10e3e52b24dc71e650bb1a9bd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.5",
+]
+
[[package]]
name = "serde_json"
version = "1.0.151"
@@ -2031,17 +1665,6 @@ dependencies = [
"serde",
]
-[[package]]
-name = "sha2"
-version = "0.10.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
-dependencies = [
- "cfg-if",
- "cpufeatures 0.2.17",
- "digest",
-]
-
[[package]]
name = "sharded-slab"
version = "0.1.7"
@@ -2067,27 +1690,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "signature"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
-dependencies = [
- "rand_core 0.6.4",
-]
-
-[[package]]
-name = "simple_asn1"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d"
-dependencies = [
- "num-bigint",
- "num-traits",
- "thiserror",
- "time",
-]
-
[[package]]
name = "sketches-ddsketch"
version = "0.4.1"
@@ -2187,7 +1789,7 @@ checksum = "edde6a10743fff00a4e1a8c9ef020bf5f3cbad301b7d2d39f2b07f123c4eac07"
dependencies = [
"aho-corasick",
"arc-swap",
- "base64 0.22.1",
+ "base64",
"bitpacking",
"bon",
"byteorder",
@@ -2376,9 +1978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134"
dependencies = [
"deranged",
- "libc",
"num-conv",
- "num_threads",
"powerfmt",
"serde_core",
"time-core",
@@ -2484,7 +2084,6 @@ dependencies = [
"bytes",
"futures-core",
"futures-sink",
- "libc",
"pin-project-lite",
"tokio",
]
@@ -2605,12 +2204,6 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
-[[package]]
-name = "typenum"
-version = "1.20.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
-
[[package]]
name = "typetag"
version = "0.2.23"
@@ -2635,24 +2228,12 @@ dependencies = [
"syn 3.0.5",
]
-[[package]]
-name = "unicase"
-version = "2.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
-
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
-[[package]]
-name = "untrusted"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
-
[[package]]
name = "untrusted"
version = "0.9.0"
@@ -2669,7 +2250,6 @@ dependencies = [
"idna",
"percent-encoding",
"serde",
- "serde_derive",
]
[[package]]
@@ -2708,12 +2288,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
-[[package]]
-name = "version_check"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
-
[[package]]
name = "want"
version = "0.3.1"
@@ -3000,20 +2574,6 @@ name = "zeroize"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
-dependencies = [
- "zeroize_derive",
-]
-
-[[package]]
-name = "zeroize_derive"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.119",
-]
[[package]]
name = "zerotrie"
diff --git a/server/Cargo.toml b/server/Cargo.toml
index 2c0e38a..0e94f20 100644
--- a/server/Cargo.toml
+++ b/server/Cargo.toml
@@ -11,12 +11,12 @@ dirs = "6.0.0"
futures-util = "0.3.34"
glob = "0.3.4"
reqwest = { version = "0.12", default-features = false, features = ["stream", "rustls-tls"] }
-rust-mcp-axum = "2.0.0"
-rust-mcp-sdk = "2.0.0"
+schemars = "1.2.2"
serde = { version = "1.0.229", features = ["derive"] }
serde_json = "1.0.151"
tantivy = "0.26.1"
tokio = { version = "1.53.1", features = ["full"] }
+tokio-stream = "0.1.19"
tokio-util = { version = "0.7.19", features = ["io"] }
tracing = "0.1.44"
tracing-subscriber = "0.3.23"
diff --git a/server/src/dashboard.html b/server/src/dashboard.html
deleted file mode 100644
index 54a580d..0000000
--- a/server/src/dashboard.html
+++ /dev/null
@@ -1,73 +0,0 @@
-
-
-
- Brain Monitor
-
-
-
-
-
-
-
-
-
-
diff --git a/server/src/handlers.rs b/server/src/handlers.rs
index 5519c06..2f2da52 100644
--- a/server/src/handlers.rs
+++ b/server/src/handlers.rs
@@ -1,116 +1,57 @@
use crate::models::*;
use crate::state::MemoryState;
use crate::tools::*;
-use async_trait::async_trait;
-use rust_mcp_sdk::{
- McpServer, RequestContext,
- mcp_server::ServerHandler,
- schema::{
- CallToolRequestParams, CallToolResult, ListToolsResult, ListToolsResultCacheScope,
- PaginatedRequestParams, RpcError, ServerResult, schema_utils::CallToolError,
- },
-};
+
+
use serde::{Deserialize, de::DeserializeOwned};
use std::collections::HashSet;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
fn parse_args(
- args: Option>,
-) -> std::result::Result {
- serde_json::from_value(serde_json::Value::Object(args.unwrap_or_default()))
- .map_err(|_| CallToolError::unknown_tool("Invalid args"))
+ args: serde_json::Value,
+) -> Result {
+ serde_json::from_value(args).map_err(|e| format!("Invalid args: {}", e))
}
+
pub struct MemoryHandler {
pub state: Arc,
}
-#[async_trait]
-impl ServerHandler for MemoryHandler {
- async fn handle_list_tools_request(
- &self,
- _r: Option,
- _c: &RequestContext,
- _rt: Arc,
- ) -> std::result::Result {
- Ok(ListToolsResult {
- tools: vec![
- CreateEntitiesTool::tool(),
- CreateRelationsTool::tool(),
- AddObservationsTool::tool(),
- DeleteEntitiesTool::tool(),
- DeleteObservationsTool::tool(),
- DeleteRelationsTool::tool(),
- ReadGraphTool::tool(),
- SearchNodesTool::tool(),
- OpenNodesTool::tool(),
- LogCodeChangeTool::tool(),
- QueryRecentChangesTool::tool(),
- VisualizeGraphTool::tool(),
- AddStickyNoteTool::tool(),
- ReadStickyNotesTool::tool(),
- CondenseEntityTool::tool(),
- AddTaskTool::tool(),
- UpdateTaskStatusTool::tool(),
- ListActiveTasksTool::tool(),
- StoreSnippetTool::tool(),
- SearchSnippetsTool::tool(),
- DeleteSnippetTool::tool(),
- LogDecisionTool::tool(),
- QueryDecisionsTool::tool(),
- MergeEntitiesTool::tool(),
- FindOrphansTool::tool(),
- LearnPreferenceTool::tool(),
- ReadPreferencesTool::tool(),
- LogErrorFixTool::tool(),
- SearchErrorFixesTool::tool(),
- PinFileTool::tool(),
- UnpinFileTool::tool(),
- ListPinnedFilesTool::tool(),
- AddSessionSummaryTool::tool(),
- GetProjectTimelineTool::tool(),
- LeaveHandoffMemoTool::tool(),
- ReadHandoffMemosTool::tool(),
- ClearHandoffMemosTool::tool(),
- UpdateEnvFingerprintTool::tool(),
- ReadEnvFingerprintTool::tool(),
- LogEnvRequirementTool::tool(),
- AddMilestoneTool::tool(),
- UpdateMilestoneTool::tool(),
- ListMilestonesTool::tool(),
- GenerateStandupReportTool::tool(),
- RegisterEnvironmentTool::tool(),
- GetEnvironmentDetailsTool::tool(),
- AddPrChecklistItemTool::tool(),
- GetPrChecklistTool::tool(),
- ClearPrChecklistTool::tool(),
- LogTechDebtTool::tool(),
- ResolveTechDebtTool::tool(),
- ListTechDebtTool::tool(),
- SaveContextWorkspaceTool::tool(),
- LoadContextWorkspaceTool::tool(),
- ListContextWorkspacesTool::tool(),
- OmniSearchTool::tool(),
- GetProjectHealthTool::tool(),
- ],
- cache_scope: ListToolsResultCacheScope::Private,
- result_type: "complete".to_string(),
- ttl_ms: 0,
- meta: None,
- next_cursor: None,
- })
- }
- async fn handle_call_tool_request(
- &self,
- params: CallToolRequestParams,
- _c: &RequestContext,
- _rt: Arc,
- ) -> std::result::Result {
- let args = params.arguments;
- match params.name.as_str() {
+impl MemoryHandler {
+ pub async fn handle_request(&self, req: serde_json::Value) -> Option {
+ let id = req.get("id").cloned().unwrap_or(serde_json::Value::Null);
+ let method = req.get("method").and_then(|m| m.as_str()).unwrap_or("");
+
+ match method {
+ "initialize" => {
+ return Some(crate::mcp::success(id, serde_json::json!({
+ "protocolVersion": "2024-11-05",
+ "capabilities": {},
+ "serverInfo": {
+ "name": "gemini-mcp-memory",
+ "version": "3.0.0"
+ }
+ })));
+ }
+ "notifications/initialized" => {
+ return None;
+ }
+ "tools/list" => {
+ let tools = vec![ crate::mcp::tool_def::("create_entities", ""), crate::mcp::tool_def::("create_relations", ""), crate::mcp::tool_def::("add_observations", ""), crate::mcp::tool_def::("delete_entities", ""), crate::mcp::tool_def::("delete_observations", ""), crate::mcp::tool_def::("delete_relations", ""), crate::mcp::tool_def::("read_graph", ""), crate::mcp::tool_def::("search_nodes", ""), crate::mcp::tool_def::("open_nodes", ""), crate::mcp::tool_def::("log_code_change", ""), crate::mcp::tool_def::("query_recent_changes", ""), crate::mcp::tool_def::("visualize_graph", ""), crate::mcp::tool_def::("add_sticky_note", ""), crate::mcp::tool_def::("read_sticky_notes", ""), crate::mcp::tool_def::("condense_entity", ""), crate::mcp::tool_def::("add_task", ""), crate::mcp::tool_def::("update_task_status", ""), crate::mcp::tool_def::("list_active_tasks", ""), crate::mcp::tool_def::("store_snippet", ""), crate::mcp::tool_def::("search_snippets", ""), crate::mcp::tool_def::("delete_snippet", ""), crate::mcp::tool_def::("log_decision", ""), crate::mcp::tool_def::("query_decisions", ""), crate::mcp::tool_def::("merge_entities", ""), crate::mcp::tool_def::("find_orphans", ""), crate::mcp::tool_def::("learn_preference", ""), crate::mcp::tool_def::("read_preferences", ""), crate::mcp::tool_def::("log_error_fix", ""), crate::mcp::tool_def::("search_error_fixes", ""), crate::mcp::tool_def::("pin_file", ""), crate::mcp::tool_def::("unpin_file", ""), crate::mcp::tool_def::("list_pinned_files", ""), crate::mcp::tool_def::("add_session_summary", ""), crate::mcp::tool_def::("get_project_timeline", ""), crate::mcp::tool_def::("leave_handoff_memo", ""), crate::mcp::tool_def::("read_handoff_memos", ""), crate::mcp::tool_def::("clear_handoff_memos", ""), crate::mcp::tool_def::("update_env_fingerprint", ""), crate::mcp::tool_def::("read_env_fingerprint", ""), crate::mcp::tool_def::("log_env_requirement", ""), crate::mcp::tool_def::("add_milestone", ""), crate::mcp::tool_def::("update_milestone", ""), crate::mcp::tool_def::("list_milestones", ""), crate::mcp::tool_def::("generate_standup_report", ""), crate::mcp::tool_def::("register_environment", ""), crate::mcp::tool_def::("get_environment_details", ""), crate::mcp::tool_def::("add_pr_checklist_item", ""), crate::mcp::tool_def::("get_pr_checklist", ""), crate::mcp::tool_def::("clear_pr_checklist", ""), crate::mcp::tool_def::("log_tech_debt", ""), crate::mcp::tool_def::("resolve_tech_debt", ""), crate::mcp::tool_def::("list_tech_debt", ""), crate::mcp::tool_def::("save_context_workspace", ""), crate::mcp::tool_def::("load_context_workspace", ""), crate::mcp::tool_def::("list_context_workspaces", ""), crate::mcp::tool_def::("omni_search", ""), crate::mcp::tool_def::("get_project_health", ""),
+ ];
+ return Some(crate::mcp::success(id, serde_json::json!({ "tools": tools })));
+ }
+ "tools/call" => {
+ let params = req.get("params").unwrap_or(&serde_json::Value::Null);
+ let name = params.get("name").and_then(|n| n.as_str()).unwrap_or("");
+ let args = params.get("arguments").cloned().unwrap_or(serde_json::Value::Object(Default::default()));
+
+ let result: Result = match name {
+
"create_entities" => {
- let req: CreateEntitiesTool = parse_args(args)?;
+ let req: CreateEntitiesTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.write_to_local_delta(|g| {
for e_val in req.entities {
if let Some(e) = if e_val.is_string() {
@@ -128,12 +69,12 @@ impl ServerHandler for MemoryHandler {
}
}
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Entities created".into(),
- ])))
+ Ok(vec![
+ "Entities created".to_string(),
+ ][0].clone())
}
"create_relations" => {
- let req: CreateRelationsTool = parse_args(args)?;
+ let req: CreateRelationsTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.write_to_local_delta(|g| {
for r_val in req.relations {
if let Some(r) = if r_val.is_string() {
@@ -148,12 +89,12 @@ impl ServerHandler for MemoryHandler {
}
}
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Relations created".into(),
- ])))
+ Ok(vec![
+ "Relations created".to_string(),
+ ][0].clone())
}
"add_observations" => {
- let req: AddObservationsTool = parse_args(args)?;
+ let req: AddObservationsTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
#[derive(Deserialize)]
struct ObsInput {
#[serde(rename = "entityName")]
@@ -185,12 +126,12 @@ impl ServerHandler for MemoryHandler {
}
}
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Observations added".into(),
- ])))
+ Ok(vec![
+ "Observations added".to_string(),
+ ][0].clone())
}
"delete_entities" => {
- let req: DeleteEntitiesTool = parse_args(args)?;
+ let req: DeleteEntitiesTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let to_delete: HashSet<_> = req.entity_names.into_iter().collect();
self.state.apply_sync_write(|master| {
for name in &to_delete {
@@ -200,12 +141,12 @@ impl ServerHandler for MemoryHandler {
.relations
.retain(|r| !to_delete.contains(&r.from) && !to_delete.contains(&r.to));
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Entities deleted".into(),
- ])))
+ Ok(vec![
+ "Entities deleted".to_string(),
+ ][0].clone())
}
"delete_observations" => {
- let req: DeleteObservationsTool = parse_args(args)?;
+ let req: DeleteObservationsTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
#[derive(Deserialize)]
struct ObsDel {
#[serde(rename = "entityName")]
@@ -225,12 +166,12 @@ impl ServerHandler for MemoryHandler {
}
}
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Observations deleted".into(),
- ])))
+ Ok(vec![
+ "Observations deleted".to_string(),
+ ][0].clone())
}
"delete_relations" => {
- let req: DeleteRelationsTool = parse_args(args)?;
+ let req: DeleteRelationsTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.apply_sync_write(|master| {
let mut to_rem = HashSet::new();
for r_val in req.relations {
@@ -252,24 +193,24 @@ impl ServerHandler for MemoryHandler {
))
});
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Relations deleted".into(),
- ])))
+ Ok(vec![
+ "Relations deleted".to_string(),
+ ][0].clone())
}
"read_graph" => {
- let req: ReadGraphTool = parse_args(args)?;
+ let req: ReadGraphTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut full = self.state.get_full_graph();
if let Some(ns) = req.namespace {
full.entities.retain(|_, e| e.namespace == ns);
full.relations.retain(|r| r.namespace == ns);
}
let data = serde_json::to_string(&full).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"search_nodes" => {
- let req: SearchNodesTool = parse_args(args)?;
+ let req: SearchNodesTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let matches = if let Ok(idx) = self.state.search_index.read() {
idx.search(&req.query, req.namespace.as_deref()).unwrap_or_default()
} else {
@@ -286,12 +227,12 @@ impl ServerHandler for MemoryHandler {
}
}
let data = serde_json::to_string(&result).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"open_nodes" => {
- let req: OpenNodesTool = parse_args(args)?;
+ let req: OpenNodesTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let targets: HashSet<_> = req.names.into_iter().collect();
let full = self.state.get_full_graph();
let mut result = KnowledgeGraph::default();
@@ -311,12 +252,12 @@ impl ServerHandler for MemoryHandler {
}
}
let data = serde_json::to_string(&result).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"log_code_change" => {
- let req: LogCodeChangeTool = parse_args(args)?;
+ let req: LogCodeChangeTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.ledger.modify(|ledger| {
ledger.push(CodeChange {
timestamp: SystemTime::now()
@@ -329,19 +270,19 @@ impl ServerHandler for MemoryHandler {
git_branch: req.git_branch,
});
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Code change logged".into(),
- ])))
+ Ok(vec![
+ "Code change logged".to_string(),
+ ][0].clone())
}
"query_recent_changes" => {
let data = serde_json::to_string(&self.state.ledger.read())
.unwrap_or_else(|_| "[]".to_string());
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"visualize_graph" => {
- let req: VisualizeGraphTool = parse_args(args)?;
+ let req: VisualizeGraphTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let query = req.query.unwrap_or_default().to_lowercase();
let full = self.state.get_full_graph();
let mut included = HashSet::new();
@@ -405,12 +346,12 @@ impl ServerHandler for MemoryHandler {
if output == "graph TD;\n" {
output = "No nodes found to visualize.".to_string();
}
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- output.into(),
- ])))
+ Ok(vec![
+ output.to_string(),
+ ][0].clone())
}
"add_sticky_note" => {
- let req: AddStickyNoteTool = parse_args(args)?;
+ let req: AddStickyNoteTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.sticky.modify(|notes| {
notes.push(StickyNote {
timestamp: SystemTime::now()
@@ -420,30 +361,30 @@ impl ServerHandler for MemoryHandler {
content: req.content,
});
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Sticky note added.".into(),
- ])))
+ Ok(vec![
+ "Sticky note added.".to_string(),
+ ][0].clone())
}
"read_sticky_notes" => {
let data = serde_json::to_string(&self.state.sticky.read())
.unwrap_or_else(|_| "[]".to_string());
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"condense_entity" => {
- let req: CondenseEntityTool = parse_args(args)?;
+ let req: CondenseEntityTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.apply_sync_write(|master| {
if let Some(e) = master.entities.get_mut(&req.entity_name) {
e.observations = req.summarized_observations;
}
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Entity condensed".into(),
- ])))
+ Ok(vec![
+ "Entity condensed".to_string(),
+ ][0].clone())
}
"add_task" => {
- let req: AddTaskTool = parse_args(args)?;
+ let req: AddTaskTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
@@ -464,12 +405,12 @@ impl ServerHandler for MemoryHandler {
self.state.tasks.modify(|tasks| {
tasks.push(task);
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- format!("Task added with ID: {}", id).into(),
- ])))
+ Ok(vec![
+ format!("Task added with ID: {}", id).to_string(),
+ ][0].clone())
}
"update_task_status" => {
- let req: UpdateTaskStatusTool = parse_args(args)?;
+ let req: UpdateTaskStatusTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut found = false;
self.state.tasks.modify(|tasks| {
for t in tasks.iter_mut() {
@@ -485,29 +426,29 @@ impl ServerHandler for MemoryHandler {
}
});
if found {
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Task updated.".into(),
- ])))
+ Ok(vec![
+ "Task updated.".to_string(),
+ ][0].clone())
} else {
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Task not found.".into(),
- ])))
+ Ok(vec![
+ "Task not found.".to_string(),
+ ][0].clone())
}
}
"list_active_tasks" => {
- let req: ListActiveTasksTool = parse_args(args)?;
+ let req: ListActiveTasksTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut tasks = self.state.tasks.read();
tasks.retain(|t| t.status != "done");
if let Some(branch) = req.git_branch {
tasks.retain(|t| t.git_branch.is_none() || t.git_branch.as_deref() == Some(branch.as_str()));
}
let data = serde_json::to_string(&tasks).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"store_snippet" => {
- let req: StoreSnippetTool = parse_args(args)?;
+ let req: StoreSnippetTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.snippets.modify(|snippets| {
snippets.retain(|s| s.name != req.name);
snippets.push(Snippet {
@@ -521,12 +462,12 @@ impl ServerHandler for MemoryHandler {
.as_secs(),
});
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- format!("Snippet '{}' stored.", req.name).into(),
- ])))
+ Ok(vec![
+ format!("Snippet '{}' stored.", req.name).to_string(),
+ ][0].clone())
}
"search_snippets" => {
- let req: SearchSnippetsTool = parse_args(args)?;
+ let req: SearchSnippetsTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let query = req.query.to_lowercase();
let snippets = self.state.snippets.read();
let mut results = Vec::new();
@@ -539,12 +480,12 @@ impl ServerHandler for MemoryHandler {
}
}
let data = serde_json::to_string(&results).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"delete_snippet" => {
- let req: DeleteSnippetTool = parse_args(args)?;
+ let req: DeleteSnippetTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut deleted = false;
self.state.snippets.modify(|snippets| {
let orig = snippets.len();
@@ -552,17 +493,17 @@ impl ServerHandler for MemoryHandler {
deleted = snippets.len() < orig;
});
if deleted {
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Snippet deleted.".into(),
- ])))
+ Ok(vec![
+ "Snippet deleted.".to_string(),
+ ][0].clone())
} else {
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Snippet not found.".into(),
- ])))
+ Ok(vec![
+ "Snippet not found.".to_string(),
+ ][0].clone())
}
}
"log_decision" => {
- let req: LogDecisionTool = parse_args(args)?;
+ let req: LogDecisionTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut id = String::new();
self.state.adrs.modify(|adrs| {
id = format!("ADR-{:04}", adrs.len() + 1);
@@ -578,12 +519,12 @@ impl ServerHandler for MemoryHandler {
.as_secs(),
});
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- format!("Decision logged as {}", id).into(),
- ])))
+ Ok(vec![
+ format!("Decision logged as {}", id).to_string(),
+ ][0].clone())
}
"query_decisions" => {
- let req: QueryDecisionsTool = parse_args(args)?;
+ let req: QueryDecisionsTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut adrs = self.state.adrs.read();
if let Some(q) = req.query {
let q = q.to_lowercase();
@@ -594,12 +535,12 @@ impl ServerHandler for MemoryHandler {
});
}
let data = serde_json::to_string(&adrs).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"merge_entities" => {
- let req: MergeEntitiesTool = parse_args(args)?;
+ let req: MergeEntitiesTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.apply_sync_write(|master| {
if let Some(src) = master.entities.remove(&req.source_entity) {
if let Some(tgt) = master.entities.get_mut(&req.target_entity) {
@@ -621,9 +562,9 @@ impl ServerHandler for MemoryHandler {
}
master.relations = MemoryState::unique_items(master.relations.clone());
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Entities merged".into(),
- ])))
+ Ok(vec![
+ "Entities merged".to_string(),
+ ][0].clone())
}
"find_orphans" => {
let full = self.state.get_full_graph();
@@ -639,12 +580,12 @@ impl ServerHandler for MemoryHandler {
.cloned()
.collect();
let data = serde_json::to_string(&orphans).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"learn_preference" => {
- let req: LearnPreferenceTool = parse_args(args)?;
+ let req: LearnPreferenceTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.prefs.modify(|prefs| {
prefs.insert(
req.key.clone(),
@@ -658,19 +599,19 @@ impl ServerHandler for MemoryHandler {
},
);
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Preference learned".into(),
- ])))
+ Ok(vec![
+ "Preference learned".to_string(),
+ ][0].clone())
}
"read_preferences" => {
let prefs = self.state.prefs.read();
let data = serde_json::to_string(&prefs).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"log_error_fix" => {
- let req: LogErrorFixTool = parse_args(args)?;
+ let req: LogErrorFixTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.error_fixes.modify(|fixes| {
fixes.push(crate::models::ErrorFix {
signature: req.signature,
@@ -683,12 +624,12 @@ impl ServerHandler for MemoryHandler {
git_branch: req.git_branch,
})
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Error fix logged".into(),
- ])))
+ Ok(vec![
+ "Error fix logged".to_string(),
+ ][0].clone())
}
"search_error_fixes" => {
- let req: SearchErrorFixesTool = parse_args(args)?;
+ let req: SearchErrorFixesTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let q = req.query.to_lowercase();
let mut fixes = self.state.error_fixes.read();
fixes.retain(|f| {
@@ -696,12 +637,12 @@ impl ServerHandler for MemoryHandler {
|| f.solution.to_lowercase().contains(&q)
});
let data = serde_json::to_string(&fixes).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"pin_file" => {
- let req: PinFileTool = parse_args(args)?;
+ let req: PinFileTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.pinned_files.modify(|pinned| {
pinned.retain(|p| {
!(p.namespace == req.namespace && p.file_path == req.file_path)
@@ -716,22 +657,22 @@ impl ServerHandler for MemoryHandler {
git_branch: req.git_branch,
});
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "File pinned".into(),
- ])))
+ Ok(vec![
+ "File pinned".to_string(),
+ ][0].clone())
}
"unpin_file" => {
- let req: UnpinFileTool = parse_args(args)?;
+ let req: UnpinFileTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.pinned_files.modify(|pinned| {
pinned
.retain(|p| !(p.namespace == req.namespace && p.file_path == req.file_path))
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "File unpinned".into(),
- ])))
+ Ok(vec![
+ "File unpinned".to_string(),
+ ][0].clone())
}
"list_pinned_files" => {
- let req: ListPinnedFilesTool = parse_args(args)?;
+ let req: ListPinnedFilesTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut pinned = self.state.pinned_files.read();
if let Some(ns) = req.namespace {
pinned.retain(|p| p.namespace == ns);
@@ -740,12 +681,12 @@ impl ServerHandler for MemoryHandler {
pinned.retain(|p| p.git_branch.is_none() || p.git_branch.as_deref() == Some(branch.as_str()));
}
let data = serde_json::to_string(&pinned).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"add_session_summary" => {
- let req: AddSessionSummaryTool = parse_args(args)?;
+ let req: AddSessionSummaryTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.session_summaries.modify(|summaries| {
summaries.push(crate::models::SessionSummary {
summary: req.summary,
@@ -756,24 +697,24 @@ impl ServerHandler for MemoryHandler {
.as_secs(),
})
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Session summary added".into(),
- ])))
+ Ok(vec![
+ "Session summary added".to_string(),
+ ][0].clone())
}
"get_project_timeline" => {
- let req: GetProjectTimelineTool = parse_args(args)?;
+ let req: GetProjectTimelineTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut summaries = self.state.session_summaries.read();
if let Some(ns) = req.namespace {
summaries.retain(|s| s.namespace == ns);
}
summaries.sort_by_key(|s| s.timestamp);
let data = serde_json::to_string(&summaries).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"leave_handoff_memo" => {
- let req: LeaveHandoffMemoTool = parse_args(args)?;
+ let req: LeaveHandoffMemoTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.handoff_memos.modify(|memos| {
memos.push(crate::models::HandoffMemo {
id: uuid::Uuid::new_v4().to_string(),
@@ -786,33 +727,33 @@ impl ServerHandler for MemoryHandler {
.as_secs(),
})
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Handoff memo left".into(),
- ])))
+ Ok(vec![
+ "Handoff memo left".to_string(),
+ ][0].clone())
}
"read_handoff_memos" => {
- let req: ReadHandoffMemosTool = parse_args(args)?;
+ let req: ReadHandoffMemosTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut memos = self.state.handoff_memos.read();
if let Some(ns) = req.namespace {
memos.retain(|m| m.namespace == ns);
}
let data = serde_json::to_string(&memos).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"clear_handoff_memos" => {
- let req: ClearHandoffMemosTool = parse_args(args)?;
+ let req: ClearHandoffMemosTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let ids: HashSet<_> = req.ids.into_iter().collect();
self.state
.handoff_memos
.modify(|memos| memos.retain(|m| !ids.contains(&m.id)));
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Handoff memos cleared".into(),
- ])))
+ Ok(vec![
+ "Handoff memos cleared".to_string(),
+ ][0].clone())
}
"update_env_fingerprint" => {
- let req: UpdateEnvFingerprintTool = parse_args(args)?;
+ let req: UpdateEnvFingerprintTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.env_fingerprints.modify(|fps| {
fps.insert(
req.namespace.clone(),
@@ -828,26 +769,26 @@ impl ServerHandler for MemoryHandler {
},
);
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Env fingerprint updated".into(),
- ])))
+ Ok(vec![
+ "Env fingerprint updated".to_string(),
+ ][0].clone())
}
"read_env_fingerprint" => {
- let req: ReadEnvFingerprintTool = parse_args(args)?;
+ let req: ReadEnvFingerprintTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let fps = self.state.env_fingerprints.read();
if let Some(fp) = fps.get(&req.namespace) {
let data = serde_json::to_string(fp).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
} else {
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "{}".into(),
- ])))
+ Ok(vec![
+ "{}".to_string(),
+ ][0].clone())
}
}
"log_env_requirement" => {
- let req: LogEnvRequirementTool = parse_args(args)?;
+ let req: LogEnvRequirementTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.env_requirements.modify(|reqs| {
reqs.retain(|r| !(r.namespace == req.namespace && r.key == req.key));
reqs.push(crate::models::EnvRequirement {
@@ -857,12 +798,12 @@ impl ServerHandler for MemoryHandler {
is_secret: req.is_secret,
});
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Env requirement logged".into(),
- ])))
+ Ok(vec![
+ "Env requirement logged".to_string(),
+ ][0].clone())
}
"add_milestone" => {
- let req: AddMilestoneTool = parse_args(args)?;
+ let req: AddMilestoneTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.milestones.modify(|ms| {
ms.push(crate::models::Milestone {
id: uuid::Uuid::new_v4().to_string(),
@@ -872,12 +813,12 @@ impl ServerHandler for MemoryHandler {
target_date: None,
})
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Milestone added".into(),
- ])))
+ Ok(vec![
+ "Milestone added".to_string(),
+ ][0].clone())
}
"update_milestone" => {
- let req: UpdateMilestoneTool = parse_args(args)?;
+ let req: UpdateMilestoneTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut found = false;
self.state.milestones.modify(|ms| {
for m in ms.iter_mut() {
@@ -889,28 +830,28 @@ impl ServerHandler for MemoryHandler {
}
});
if found {
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Milestone updated".into(),
- ])))
+ Ok(vec![
+ "Milestone updated".to_string(),
+ ][0].clone())
} else {
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Milestone not found".into(),
- ])))
+ Ok(vec![
+ "Milestone not found".to_string(),
+ ][0].clone())
}
}
"list_milestones" => {
- let req: ListMilestonesTool = parse_args(args)?;
+ let req: ListMilestonesTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut ms = self.state.milestones.read();
if let Some(ns) = req.namespace {
ms.retain(|m| m.namespace == ns);
}
let data = serde_json::to_string(&ms).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"generate_standup_report" => {
- let req: GenerateStandupReportTool = parse_args(args)?;
+ let req: GenerateStandupReportTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let cutoff = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
@@ -938,12 +879,12 @@ impl ServerHandler for MemoryHandler {
.filter(|s| s.namespace == req.namespace && s.timestamp >= cutoff)
.collect::>();
let report = serde_json::json!({ "tasks_updated": tasks, "code_changes": changes, "session_summaries": summaries });
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- report.to_string().into(),
- ])))
+ Ok(vec![
+ report.to_string().to_string(),
+ ][0].clone())
}
"register_environment" => {
- let req: RegisterEnvironmentTool = parse_args(args)?;
+ let req: RegisterEnvironmentTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.environments.modify(|envs| {
envs.retain(|e| !(e.namespace == req.namespace && e.name == req.name));
envs.push(crate::models::EnvironmentDetail {
@@ -958,21 +899,21 @@ impl ServerHandler for MemoryHandler {
.as_secs(),
});
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Environment registered".into(),
- ])))
+ Ok(vec![
+ "Environment registered".to_string(),
+ ][0].clone())
}
"get_environment_details" => {
- let req: GetEnvironmentDetailsTool = parse_args(args)?;
+ let req: GetEnvironmentDetailsTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut envs = self.state.environments.read();
envs.retain(|e| e.namespace == req.namespace);
let data = serde_json::to_string(&envs).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"add_pr_checklist_item" => {
- let req: AddPrChecklistItemTool = parse_args(args)?;
+ let req: AddPrChecklistItemTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.pr_checklists.modify(|items| {
items.push(crate::models::PrChecklistItem {
namespace: req.namespace,
@@ -980,30 +921,30 @@ impl ServerHandler for MemoryHandler {
description: req.description,
})
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "PR checklist item added".into(),
- ])))
+ Ok(vec![
+ "PR checklist item added".to_string(),
+ ][0].clone())
}
"get_pr_checklist" => {
- let req: GetPrChecklistTool = parse_args(args)?;
+ let req: GetPrChecklistTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut items = self.state.pr_checklists.read();
items.retain(|i| i.namespace == req.namespace);
let data = serde_json::to_string(&items).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"clear_pr_checklist" => {
- let req: ClearPrChecklistTool = parse_args(args)?;
+ let req: ClearPrChecklistTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state
.pr_checklists
.modify(|items| items.retain(|i| i.namespace != req.namespace));
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "PR checklist cleared".into(),
- ])))
+ Ok(vec![
+ "PR checklist cleared".to_string(),
+ ][0].clone())
}
"log_tech_debt" => {
- let req: LogTechDebtTool = parse_args(args)?;
+ let req: LogTechDebtTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.tech_debts.modify(|debts| {
debts.push(crate::models::TechDebt {
id: uuid::Uuid::new_v4().to_string(),
@@ -1019,12 +960,12 @@ impl ServerHandler for MemoryHandler {
git_branch: req.git_branch,
})
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Tech debt logged".into(),
- ])))
+ Ok(vec![
+ "Tech debt logged".to_string(),
+ ][0].clone())
}
"resolve_tech_debt" => {
- let req: ResolveTechDebtTool = parse_args(args)?;
+ let req: ResolveTechDebtTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut found = false;
self.state.tech_debts.modify(|debts| {
for d in debts.iter_mut() {
@@ -1036,28 +977,28 @@ impl ServerHandler for MemoryHandler {
}
});
if found {
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Tech debt resolved".into(),
- ])))
+ Ok(vec![
+ "Tech debt resolved".to_string(),
+ ][0].clone())
} else {
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Tech debt not found".into(),
- ])))
+ Ok(vec![
+ "Tech debt not found".to_string(),
+ ][0].clone())
}
}
"list_tech_debt" => {
- let req: ListTechDebtTool = parse_args(args)?;
+ let req: ListTechDebtTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut debts = self.state.tech_debts.read();
debts.retain(|d| {
d.namespace == req.namespace && (req.include_resolved || !d.is_resolved)
});
let data = serde_json::to_string(&debts).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"save_context_workspace" => {
- let req: SaveContextWorkspaceTool = parse_args(args)?;
+ let req: SaveContextWorkspaceTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
self.state.context_workspaces.modify(|ws| {
ws.retain(|w| !(w.namespace == req.namespace && w.name == req.name));
ws.push(crate::models::ContextWorkspace {
@@ -1071,30 +1012,30 @@ impl ServerHandler for MemoryHandler {
.as_secs(),
});
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- "Context workspace saved".into(),
- ])))
+ Ok(vec![
+ "Context workspace saved".to_string(),
+ ][0].clone())
}
"load_context_workspace" => {
- let req: LoadContextWorkspaceTool = parse_args(args)?;
+ let req: LoadContextWorkspaceTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut ws = self.state.context_workspaces.read();
ws.retain(|w| w.namespace == req.namespace && w.name == req.name);
let data = serde_json::to_string(&ws.first()).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"list_context_workspaces" => {
- let req: ListContextWorkspacesTool = parse_args(args)?;
+ let req: ListContextWorkspacesTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let mut ws = self.state.context_workspaces.read();
ws.retain(|w| w.namespace == req.namespace);
let data = serde_json::to_string(&ws).unwrap_or_default();
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- data.into(),
- ])))
+ Ok(vec![
+ data.to_string(),
+ ][0].clone())
}
"omni_search" => {
- let req: OmniSearchTool = parse_args(args)?;
+ let req: OmniSearchTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let matches = if let Ok(idx) = self.state.search_index.read() {
idx.search(&req.query, req.namespace.as_deref()).unwrap_or_default()
} else {
@@ -1172,12 +1113,12 @@ impl ServerHandler for MemoryHandler {
"handoff_memos": memos,
"error_fixes": error_fixes
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- report.to_string().into(),
- ])))
+ Ok(vec![
+ report.to_string().to_string(),
+ ][0].clone())
}
"get_project_health" => {
- let req: GetProjectHealthTool = parse_args(args)?;
+ let req: GetProjectHealthTool = match parse_args(args.clone()) { Ok(r) => r, Err(e) => return Some(crate::mcp::success(id.clone(), serde_json::json!({"isError": true, "content": [{"type": "text", "text": e}] }))) };
let active_tasks = self
.state
.tasks
@@ -1221,13 +1162,30 @@ impl ServerHandler for MemoryHandler {
"active_milestones": active_milestones,
"remaining_pr_checklist_items": remaining_checklists
});
- Ok(ServerResult::from(CallToolResult::text_content(vec![
- report.to_string().into(),
- ])))
+ Ok(vec![
+ report.to_string().to_string(),
+ ][0].clone())
+ }
+
+ _ => Err(format!("Unknown tool: {}", name)),
+ };
+
+ match result {
+ Ok(text) => return Some(crate::mcp::success(id, serde_json::json!({
+ "content": [{ "type": "text", "text": text }]
+ }))),
+ Err(e) => return Some(crate::mcp::success(id, serde_json::json!({
+ "isError": true,
+ "content": [{ "type": "text", "text": e }]
+ })))
+ }
+ }
+ _ => {
+ if id != serde_json::Value::Null {
+ return Some(crate::mcp::error(id, -32601, "Method not found"));
+ }
+ return None;
}
- _ => Err(CallToolError::unknown_tool(params.name)),
}
}
}
-
-
diff --git a/server/src/main.rs b/server/src/main.rs
index 3c46816..97db735 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -1,5 +1,6 @@
mod handlers;
mod models;
+mod mcp;
mod state;
mod store;
mod tools;
@@ -9,12 +10,7 @@ use crate::handlers::MemoryHandler;
use crate::models::*;
use crate::state::MemoryState;
use crate::store::Store;
-use rust_mcp_sdk::{
- ServerDetails, ToMcpServerHandler,
- error::SdkResult,
- schema::{Implementation, ServerCapabilities, ServerCapabilitiesTools},
-};
use std::fs;
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
@@ -109,94 +105,95 @@ async fn reconcile_worker(state: Arc) {
}
-use rust_mcp_axum::{create_axum_server, AxumServerOptions};
-fn run_server(state: Arc) -> SdkResult<()> {
+
+
+use axum::{
+ extract::{State, Query},
+ response::sse::{Event, Sse},
+ routing::{get, post},
+ Json, Router,
+};
+use futures_util::stream::Stream;
+use std::convert::Infallible;
+use tokio::sync::mpsc;
+use tokio_stream::wrappers::ReceiverStream;
+use std::sync::atomic::{AtomicUsize, Ordering};
+
+struct AppState {
+ handler: Arc,
+ clients: RwLock>>>,
+ next_id: AtomicUsize,
+}
+
+fn run_server(state: Arc) -> Result<(), Box> {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
- let state_clone = state.clone();
- tokio::spawn(async move {
- reconcile_worker(state_clone).await;
+ tokio::spawn(reconcile_worker(Arc::clone(&state)));
+ let app_state = Arc::new(AppState {
+ handler: Arc::new(MemoryHandler { state }),
+ clients: RwLock::new(HashMap::new()),
+ next_id: AtomicUsize::new(1),
});
- let server_details = ServerDetails {
- server_info: Implementation {
- name: "gemini-mcp-memory".into(),
- version: "3.0.0".into(),
- title: Some("MCP Memory Server".into()),
- description: Some("Persistent memory MCP server".into()),
- icons: vec![],
- website_url: None,
- },
- capabilities: ServerCapabilities {
- tools: Some(ServerCapabilitiesTools {
- list_changed: Some(true),
- }),
- ..Default::default()
- },
- instructions: None,
- meta: None,
- };
-
- let options = AxumServerOptions {
- host: "0.0.0.0".to_string(),
- port: 3000,
- custom_sse_endpoint: Some("/sse".to_string()),
- ..Default::default()
- };
-
- let server = create_axum_server(
- server_details,
- MemoryHandler { state: state.clone() }.to_mcp_server_handler(),
- options,
- );
-
- let server = server.with_route(
- "/gate/verify",
- axum::routing::get(|| async move {
- axum::http::StatusCode::OK
- }),
- ).with_route(
- "/dashboard",
- axum::routing::get(|| async move {
- axum::response::Html(include_str!("dashboard.html"))
- }),
- ).with_route(
- "/api/stats",
- axum::routing::get({
- let state_clone = state.clone();
- move || async move {
- let (entities, relations) = {
- let graph = state_clone.get_full_graph();
- (graph.entities.len(), graph.relations.len())
- };
- let tasks = state_clone.tasks.read().len();
- let snippets = state_clone.snippets.read().len();
- let tech_debts = state_clone.tech_debts.read().len();
- let adrs = state_clone.adrs.read().len();
-
- axum::Json(serde_json::json!({
- "entities": entities,
- "relations": relations,
- "tasks": tasks,
- "snippets": snippets,
- "tech_debts": tech_debts,
- "adrs": adrs
- }))
- }
- }),
- );
+ let app = Router::new()
+ .route("/sse", get(sse_handler))
+ .route("/messages", post(message_handler))
+ .with_state(app_state);
+ let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
eprintln!("MCP Memory Server running on http://0.0.0.0:3000/sse");
- server.start().await.unwrap();
-
+ axum::serve(listener, app).await.unwrap();
Ok(())
})
}
+async fn sse_handler(
+ State(state): State>,
+) -> Sse>> {
+ let session_id = format!("{}", state.next_id.fetch_add(1, Ordering::SeqCst));
+ let (tx, rx) = mpsc::channel::>(100);
+
+ state.clients.write().unwrap().insert(session_id.clone(), tx.clone());
+
+ let _ = tx.send(Ok(Event::default().event("endpoint").data(format!("?sessionId={}", session_id)))).await;
+
+ let stream = ReceiverStream::new(rx);
+ Sse::new(stream).keep_alive(axum::response::sse::KeepAlive::new())
+}
+
+#[derive(serde::Deserialize)]
+struct SessionQuery {
+ #[serde(rename = "sessionId")]
+ session_id: String,
+}
+
+async fn message_handler(
+ State(state): State>,
+ Query(query): Query,
+ Json(payload): Json,
+) -> axum::http::StatusCode {
+ let handler = Arc::clone(&state.handler);
+ let session_id = query.session_id.clone();
+ let clients = Arc::clone(&state);
+
+ tokio::spawn(async move {
+ if let Some(response) = handler.handle_request(payload).await {
+ let tx_opt = clients.clients.read().unwrap().get(&session_id).cloned();
+ if let Some(tx) = tx_opt {
+ let data = serde_json::to_string(&response).unwrap();
+ let _ = tx.send(Ok(Event::default().event("message").data(data))).await;
+ }
+ }
+ });
+
+ axum::http::StatusCode::ACCEPTED
+}
+
mod proxy;
-fn main() -> SdkResult<()> {
+
+fn main() -> Result<(), Box> {
let cli = Cli::parse();
#[cfg(target_os = "windows")]
diff --git a/server/src/mcp.rs b/server/src/mcp.rs
new file mode 100644
index 0000000..75d4d8c
--- /dev/null
+++ b/server/src/mcp.rs
@@ -0,0 +1,32 @@
+use schemars::JsonSchema;
+use serde_json::json;
+
+pub fn success(id: serde_json::Value, result: serde_json::Value) -> serde_json::Value {
+ json!({
+ "jsonrpc": "2.0",
+ "id": id,
+ "result": result
+ })
+}
+
+pub fn error(id: serde_json::Value, code: i32, message: &str) -> serde_json::Value {
+ json!({
+ "jsonrpc": "2.0",
+ "id": id,
+ "error": {
+ "code": code,
+ "message": message
+ }
+ })
+}
+
+pub fn tool_def(name: &str, description: &str) -> serde_json::Value {
+ let schema = schemars::schema_for!(T);
+ let schema_val = serde_json::to_value(&schema).unwrap();
+ // MCP expects standard JSON schema. Schemars returns draft-07.
+ json!({
+ "name": name,
+ "description": description,
+ "inputSchema": schema_val
+ })
+}
diff --git a/server/src/proxy.rs b/server/src/proxy.rs
index ab14551..4ab5e38 100644
--- a/server/src/proxy.rs
+++ b/server/src/proxy.rs
@@ -1,22 +1,32 @@
-use rust_mcp_sdk::error::SdkResult;
use tokio_util::io::StreamReader;
use tokio::io::AsyncBufReadExt;
use futures_util::StreamExt;
use std::sync::Arc;
use tokio::sync::RwLock;
-pub fn run_proxy(target_url: &str) -> SdkResult {
+pub fn run_proxy(target_url: &str) -> Result> {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let client = reqwest::Client::builder().build().unwrap();
let sse_url = format!("{}/sse", target_url);
- let resp = match client.get(&sse_url).send().await {
+ eprintln!("[PROXY] Connecting to Streamable HTTP: {}", sse_url);
+ let resp = match client.get(&sse_url)
+ .header("Accept", "text/event-stream")
+ .send().await {
Ok(r) => r,
- Err(_) => return Ok(true), // Connection failed (Leader is dead)
+ Err(e) => {
+ eprintln!("[PROXY] Connection failed: {}", e);
+ return Ok(true);
+ }
};
- let post_url = Arc::new(RwLock::new(format!("{}/messages", target_url)));
+ if resp.status() == reqwest::StatusCode::GONE {
+ eprintln!("[PROXY] Streamable HTTP endpoint is gone.");
+ return Ok(true);
+ }
+
+ let post_url = Arc::new(RwLock::new(format!("{}/messages", target_url))); // Will be updated by endpoint event
let post_url_clone = Arc::clone(&post_url);
let (tx, mut rx) = tokio::sync::mpsc::channel(1);
@@ -47,7 +57,11 @@ pub fn run_proxy(target_url: &str) -> SdkResult {
if url.is_empty() {
url = url_arc.read().await.clone();
}
- let _ = client.post(&url).header("Content-Type", "application/json").body(body).send().await;
+ let _ = client.post(&url)
+ .header("Accept", "application/json, text/event-stream")
+ .header("Content-Type", "application/json")
+ .body(body)
+ .send().await;
});
}
let _ = tx_clone.blocking_send(false); // Stdin EOF
diff --git a/server/src/tools.rs b/server/src/tools.rs
index 86bb7a6..73a3a36 100644
--- a/server/src/tools.rs
+++ b/server/src/tools.rs
@@ -1,64 +1,45 @@
-use rust_mcp_sdk::macros;
+use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
-#[macros::mcp_tool(name = "create_entities", description = "Create multiple new entities")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct CreateEntitiesTool {
pub entities: Vec,
}
-#[macros::mcp_tool(
- name = "create_relations",
- description = "Create multiple new relations"
-)]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct CreateRelationsTool {
pub relations: Vec,
}
-#[macros::mcp_tool(
- name = "add_observations",
- description = "Add observations to entities"
-)]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct AddObservationsTool {
pub observations: Vec,
}
-#[macros::mcp_tool(
- name = "delete_entities",
- description = "Delete entities and relations"
-)]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct DeleteEntitiesTool {
#[serde(rename = "entityNames")]
pub entity_names: Vec,
}
-#[macros::mcp_tool(name = "delete_observations", description = "Delete observations")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct DeleteObservationsTool {
pub deletions: Vec,
}
-#[macros::mcp_tool(name = "delete_relations", description = "Delete relations")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct DeleteRelationsTool {
pub relations: Vec,
}
-#[macros::mcp_tool(name = "read_graph", description = "Read the entire knowledge graph")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ReadGraphTool {
pub namespace: Option,
}
-#[macros::mcp_tool(name = "search_nodes", description = "Search for nodes")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct SearchNodesTool {
pub query: String,
pub namespace: Option,
}
-#[macros::mcp_tool(name = "open_nodes", description = "Open specific nodes")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct OpenNodesTool {
pub names: Vec,
}
-#[macros::mcp_tool(name = "log_code_change", description = "Log a code change")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct LogCodeChangeTool {
#[serde(rename = "filePath")]
pub file_path: String,
@@ -66,218 +47,165 @@ pub struct LogCodeChangeTool {
pub git_commit: Option,
pub git_branch: Option,
}
-#[macros::mcp_tool(name = "query_recent_changes", description = "Query the audit ledger")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct QueryRecentChangesTool {}
-#[macros::mcp_tool(name = "visualize_graph", description = "Generate Mermaid diagram")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct VisualizeGraphTool {
pub query: Option,
pub namespace: Option,
}
-#[macros::mcp_tool(name = "add_sticky_note", description = "Add a sticky note")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct AddStickyNoteTool {
pub content: String,
}
-#[macros::mcp_tool(name = "read_sticky_notes", description = "Read sticky notes")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ReadStickyNotesTool {}
-#[macros::mcp_tool(name = "condense_entity", description = "Condense observations")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct CondenseEntityTool {
#[serde(rename = "entityName")]
pub entity_name: String,
pub summarized_observations: Vec,
}
-#[macros::mcp_tool(name = "add_task", description = "Add a task")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct AddTaskTool {
pub title: String,
pub description: String,
pub git_branch: Option,
}
-#[macros::mcp_tool(name = "update_task_status", description = "Update task status")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct UpdateTaskStatusTool {
pub id: String,
pub status: String,
}
-#[macros::mcp_tool(name = "list_active_tasks", description = "List active tasks")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ListActiveTasksTool { pub git_branch: Option, }
-#[macros::mcp_tool(name = "store_snippet", description = "Store code snippet")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct StoreSnippetTool {
pub name: String,
pub language: String,
pub code: String,
pub description: String,
}
-#[macros::mcp_tool(name = "search_snippets", description = "Search snippets")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct SearchSnippetsTool {
pub query: String,
}
-#[macros::mcp_tool(name = "delete_snippet", description = "Delete snippet")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct DeleteSnippetTool {
pub name: String,
}
-#[macros::mcp_tool(
- name = "log_decision",
- description = "Log architectural decision record"
-)]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct LogDecisionTool {
pub title: String,
pub context: String,
pub decision: String,
pub consequence: String,
}
-#[macros::mcp_tool(name = "query_decisions", description = "Query decision records")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct QueryDecisionsTool {
pub query: Option,
}
-#[macros::mcp_tool(
- name = "merge_entities",
- description = "Merge source entity into target entity"
-)]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct MergeEntitiesTool {
#[serde(rename = "sourceEntity")]
pub source_entity: String,
#[serde(rename = "targetEntity")]
pub target_entity: String,
}
-#[macros::mcp_tool(name = "find_orphans", description = "Find entities with no relations")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct FindOrphansTool {}
-#[macros::mcp_tool(
- name = "learn_preference",
- description = "Store an AI behavioral preference"
-)]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct LearnPreferenceTool {
pub key: String,
pub value: String,
}
-#[macros::mcp_tool(
- name = "read_preferences",
- description = "Read all learned behavioral preferences"
-)]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ReadPreferencesTool {}
-#[macros::mcp_tool(name = "log_error_fix", description = "Log an error fix")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct LogErrorFixTool {
pub signature: String,
pub solution: String,
pub git_commit: Option,
pub git_branch: Option,
}
-#[macros::mcp_tool(name = "search_error_fixes", description = "Search error fixes")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct SearchErrorFixesTool {
pub query: String,
}
-#[macros::mcp_tool(name = "pin_file", description = "Pin a file to working set")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct PinFileTool {
pub namespace: String,
pub file_path: String,
pub git_branch: Option,
}
-#[macros::mcp_tool(name = "unpin_file", description = "Unpin a file")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct UnpinFileTool {
pub namespace: String,
pub file_path: String,
}
-#[macros::mcp_tool(name = "list_pinned_files", description = "List pinned files")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ListPinnedFilesTool {
pub namespace: Option,
pub git_branch: Option,
}
-#[macros::mcp_tool(name = "add_session_summary", description = "Add a session summary")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct AddSessionSummaryTool {
pub summary: String,
pub namespace: String,
}
-#[macros::mcp_tool(name = "get_project_timeline", description = "Get project timeline")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GetProjectTimelineTool {
pub namespace: Option,
}
-#[macros::mcp_tool(
- name = "leave_handoff_memo",
- description = "Leave memo for next session"
-)]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct LeaveHandoffMemoTool {
pub content: String,
pub namespace: String,
}
-#[macros::mcp_tool(name = "read_handoff_memos", description = "Read pending memos")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ReadHandoffMemosTool {
pub namespace: Option,
}
-#[macros::mcp_tool(name = "clear_handoff_memos", description = "Clear read memos")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ClearHandoffMemosTool {
pub ids: Vec,
}
-#[macros::mcp_tool(name = "update_env_fingerprint", description = "Update env versions")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct UpdateEnvFingerprintTool {
pub namespace: String,
pub tool_versions: std::collections::HashMap,
}
-#[macros::mcp_tool(name = "read_env_fingerprint", description = "Read env footprint")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ReadEnvFingerprintTool {
pub namespace: String,
}
-#[macros::mcp_tool(name = "log_env_requirement", description = "Log env var requirement")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct LogEnvRequirementTool {
pub namespace: String,
pub key: String,
pub description: String,
pub is_secret: bool,
}
-#[macros::mcp_tool(name = "add_milestone", description = "Add project milestone")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct AddMilestoneTool {
pub title: String,
pub namespace: String,
}
-#[macros::mcp_tool(name = "update_milestone", description = "Update milestone status")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct UpdateMilestoneTool {
pub id: String,
pub status: String,
}
-#[macros::mcp_tool(name = "list_milestones", description = "List milestones")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ListMilestonesTool {
pub namespace: Option,
}
-#[macros::mcp_tool(
- name = "generate_standup_report",
- description = "Generate a standup report"
-)]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GenerateStandupReportTool {
pub namespace: String,
pub hours_lookback: u64,
}
-#[macros::mcp_tool(name = "register_environment", description = "Register an environment")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct RegisterEnvironmentTool {
pub namespace: String,
pub name: String,
@@ -285,35 +213,24 @@ pub struct RegisterEnvironmentTool {
pub description: String,
pub requires_vpn: bool,
}
-#[macros::mcp_tool(
- name = "get_environment_details",
- description = "Get environment details"
-)]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GetEnvironmentDetailsTool {
pub namespace: String,
}
-#[macros::mcp_tool(
- name = "add_pr_checklist_item",
- description = "Add a PR checklist item"
-)]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct AddPrChecklistItemTool {
pub namespace: String,
pub description: String,
}
-#[macros::mcp_tool(name = "get_pr_checklist", description = "Get the PR checklist")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GetPrChecklistTool {
pub namespace: String,
}
-#[macros::mcp_tool(name = "clear_pr_checklist", description = "Clear the PR checklist")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ClearPrChecklistTool {
pub namespace: String,
}
-#[macros::mcp_tool(name = "log_tech_debt", description = "Log tech debt")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct LogTechDebtTool {
pub namespace: String,
pub description: String,
@@ -321,47 +238,37 @@ pub struct LogTechDebtTool {
pub git_commit: Option,
pub git_branch: Option,
}
-#[macros::mcp_tool(name = "resolve_tech_debt", description = "Resolve tech debt")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ResolveTechDebtTool {
pub id: String,
}
-#[macros::mcp_tool(name = "list_tech_debt", description = "List tech debt")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ListTechDebtTool {
pub namespace: String,
pub include_resolved: bool,
}
-#[macros::mcp_tool(name = "save_context_workspace", description = "Save current context")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct SaveContextWorkspaceTool {
pub namespace: String,
pub name: String,
pub pinned_files: Vec,
pub active_task_ids: Vec,
}
-#[macros::mcp_tool(name = "load_context_workspace", description = "Load context")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct LoadContextWorkspaceTool {
pub namespace: String,
pub name: String,
}
-#[macros::mcp_tool(name = "list_context_workspaces", description = "List contexts")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct ListContextWorkspacesTool {
pub namespace: String,
}
-#[macros::mcp_tool(name = "omni_search", description = "Search across all memory vaults")]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct OmniSearchTool {
pub query: String,
pub namespace: Option,
}
-#[macros::mcp_tool(
- name = "get_project_health",
- description = "Get project health metrics"
-)]
-#[derive(Debug, Deserialize, Serialize, macros::JsonSchema)]
+#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct GetProjectHealthTool {
pub namespace: String,
}
diff --git a/stub/src/main.rs b/stub/src/main.rs
index 59fd52c..8ca054e 100644
--- a/stub/src/main.rs
+++ b/stub/src/main.rs
@@ -20,16 +20,23 @@ fn run_proxy(target_url: &str) -> Result> {
let client = reqwest::Client::builder().build()?;
let sse_url = format!("{}/sse", target_url);
- eprintln!("[PROXY] Connecting to SSE: {}", sse_url);
- let resp = match client.get(&sse_url).send().await {
+ eprintln!("[PROXY] Connecting to Streamable HTTP: {}", sse_url);
+ let resp = match client.get(&sse_url)
+ .header("Accept", "text/event-stream")
+ .send().await {
Ok(r) => r,
Err(e) => {
- eprintln!("[PROXY] SSE connection failed: {}", e);
+ eprintln!("[PROXY] Connection failed: {}", e);
return Ok(true);
}
};
- let post_url = Arc::new(RwLock::new(format!("{}/messages", target_url)));
+ if resp.status() == reqwest::StatusCode::GONE {
+ eprintln!("[PROXY] Streamable HTTP endpoint is gone.");
+ return Ok(true);
+ }
+
+ let post_url = Arc::new(RwLock::new(format!("{}/messages", target_url))); // Will be updated by endpoint event
let post_url_clone = Arc::clone(&post_url);
let (tx, mut rx) = tokio::sync::mpsc::channel(1);
@@ -62,7 +69,11 @@ fn run_proxy(target_url: &str) -> Result> {
url = url_arc.read().await.clone();
}
eprintln!("[PROXY] POSTing to {}", url);
- let res = client.post(&url).header("Content-Type", "application/json").body(body).send().await;
+ let res = client.post(&url)
+ .header("Accept", "application/json, text/event-stream")
+ .header("Content-Type", "application/json")
+ .body(body)
+ .send().await;
eprintln!("[PROXY] POST response: {:?}", res.map(|r| r.status()));
});
}
@@ -81,7 +92,7 @@ fn run_proxy(target_url: &str) -> Result> {
while let Ok(bytes) = reader.read_line(&mut line).await {
if bytes == 0 { break; }
let trimmed = line.trim();
- eprintln!("[PROXY] SSE read: {}", trimmed);
+ eprintln!("[PROXY] Stream read: {}", trimmed);
if trimmed.starts_with("event: message") {
is_message = true;
is_endpoint = false;
@@ -102,7 +113,7 @@ fn run_proxy(target_url: &str) -> Result> {
}
line.clear();
}
- eprintln!("[PROXY] SSE stream closed");
+ eprintln!("[PROXY] Stream closed");
let _ = tx_clone2.send(true).await;
});