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 - - - -
-

🧠 Antigravity Brain Monitor

-
System Online
-
- -
-
-
Entities
-
-
-
-
-
Relations
-
-
-
-
-
Active Tasks
-
-
-
-
-
Snippets
-
-
-
-
-
Tech Debt
-
-
-
-
-
ADRs
-
-
-
-
- - - - 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; });