Skip to main content

max / makenotwork

pom: bump rmcp 0.1 to 2.2.0 and schemars 0.8 to 1.2.1 rmcp 2.x replaced the #[tool(tool_box)] macro model with a ToolRouter: - PomServer gains a tool_router: ToolRouter<Self> field, initialized via the generated Self::tool_router() in new(). - The tool impl block uses #[tool_router]; each tool keeps #[tool(...)] but params move from #[tool(aggr)] to the Parameters<T> extractor wrapper. - ServerHandler uses #[tool_handler(router = self.tool_router)]; get_info is rebuilt via ServerInfo::new(caps).with_instructions(...) since InitializeResult is now #[non_exhaustive] (no struct-literal construction). - The redundant #[instrument] on the thin tool wrappers is dropped; every *_impl delegate is already instrumented, so span coverage is unchanged. - error: add a boxed Mcp(ServerInitializeError) variant (serve() now returns it); boxed to keep PomError small (large_enum_variant / result_large_err). - schemars 0.8 -> 1.2 needed no derive changes. New router_smoke test asserts all 8 tools register with object schemas and that Parameters<T> tools expose their properties (verifies schemars 1.0 schema generation through rmcp 2.2). 310+130+17 tests pass, clippy clean.
Author: Max Johnson <me@maxj.phd> · 2026-07-22 22:36 UTC
Commit: e6e7bd404b33d64a47d58bc357bf0ac2d3722308
Parent: b62b0cf
4 files changed, +214 insertions, -83 deletions
M pom/Cargo.lock +122 -52
@@ -106,7 +106,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c"
106 106 dependencies = [
107 107 "proc-macro2",
108 108 "quote",
109 - "syn",
109 + "syn 2.0.117",
110 110 "synstructure",
111 111 ]
112 112
@@ -118,7 +118,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
118 118 dependencies = [
119 119 "proc-macro2",
120 120 "quote",
121 - "syn",
121 + "syn 2.0.117",
122 122 ]
123 123
124 124 [[package]]
@@ -129,7 +129,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
129 129 dependencies = [
130 130 "proc-macro2",
131 131 "quote",
132 - "syn",
132 + "syn 2.0.117",
133 133 ]
134 134
135 135 [[package]]
@@ -227,12 +227,6 @@ dependencies = [
227 227
228 228 [[package]]
229 229 name = "base64"
230 - version = "0.21.7"
231 - source = "registry+https://github.com/rust-lang/crates.io-index"
232 - checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
233 -
234 - [[package]]
235 - name = "base64"
236 230 version = "0.22.1"
237 231 source = "registry+https://github.com/rust-lang/crates.io-index"
238 232 checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
@@ -362,7 +356,7 @@ dependencies = [
362 356 "heck",
363 357 "proc-macro2",
364 358 "quote",
365 - "syn",
359 + "syn 2.0.117",
366 360 ]
367 361
368 362 [[package]]
@@ -538,6 +532,40 @@ dependencies = [
538 532 ]
539 533
540 534 [[package]]
535 + name = "darling"
536 + version = "0.23.0"
537 + source = "registry+https://github.com/rust-lang/crates.io-index"
538 + checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d"
539 + dependencies = [
540 + "darling_core",
541 + "darling_macro",
542 + ]
543 +
544 + [[package]]
545 + name = "darling_core"
546 + version = "0.23.0"
547 + source = "registry+https://github.com/rust-lang/crates.io-index"
548 + checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0"
549 + dependencies = [
550 + "ident_case",
551 + "proc-macro2",
552 + "quote",
553 + "strsim",
554 + "syn 2.0.117",
555 + ]
556 +
557 + [[package]]
558 + name = "darling_macro"
559 + version = "0.23.0"
560 + source = "registry+https://github.com/rust-lang/crates.io-index"
561 + checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
562 + dependencies = [
563 + "darling_core",
564 + "quote",
565 + "syn 2.0.117",
566 + ]
567 +
568 + [[package]]
541 569 name = "data-encoding"
542 570 version = "2.10.0"
543 571 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -616,7 +644,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
616 644 dependencies = [
617 645 "proc-macro2",
618 646 "quote",
619 - "syn",
647 + "syn 2.0.117",
620 648 ]
621 649
622 650 [[package]]
@@ -794,7 +822,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
794 822 dependencies = [
795 823 "proc-macro2",
796 824 "quote",
797 - "syn",
825 + "syn 2.0.117",
798 826 ]
799 827
800 828 [[package]]
@@ -1115,7 +1143,7 @@ version = "0.1.20"
1115 1143 source = "registry+https://github.com/rust-lang/crates.io-index"
1116 1144 checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
1117 1145 dependencies = [
1118 - "base64 0.22.1",
1146 + "base64",
1119 1147 "bytes",
1120 1148 "futures-channel",
1121 1149 "futures-util",
@@ -1244,6 +1272,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1244 1272 checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
1245 1273
1246 1274 [[package]]
1275 + name = "ident_case"
1276 + version = "1.0.1"
1277 + source = "registry+https://github.com/rust-lang/crates.io-index"
1278 + checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
1279 +
1280 + [[package]]
1247 1281 name = "idna"
1248 1282 version = "1.1.0"
1249 1283 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1346,7 +1380,7 @@ dependencies = [
1346 1380 "quote",
1347 1381 "rustc_version",
1348 1382 "simd_cesu8",
1349 - "syn",
1383 + "syn 2.0.117",
1350 1384 ]
1351 1385
1352 1386 [[package]]
@@ -1365,7 +1399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1365 1399 checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
1366 1400 dependencies = [
1367 1401 "quote",
1368 - "syn",
1402 + "syn 2.0.117",
1369 1403 ]
1370 1404
1371 1405 [[package]]
@@ -1650,10 +1684,10 @@ dependencies = [
1650 1684 ]
1651 1685
1652 1686 [[package]]
1653 - name = "paste"
1654 - version = "1.0.15"
1687 + name = "pastey"
1688 + version = "0.2.3"
1655 1689 source = "registry+https://github.com/rust-lang/crates.io-index"
1656 - checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
1690 + checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4"
1657 1691
1658 1692 [[package]]
1659 1693 name = "pem"
@@ -1661,7 +1695,7 @@ version = "3.0.6"
1661 1695 source = "registry+https://github.com/rust-lang/crates.io-index"
1662 1696 checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
1663 1697 dependencies = [
1664 - "base64 0.22.1",
1698 + "base64",
1665 1699 "serde_core",
1666 1700 ]
1667 1701
@@ -1771,7 +1805,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1771 1805 checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
1772 1806 dependencies = [
1773 1807 "proc-macro2",
1774 - "syn",
1808 + "syn 2.0.117",
1775 1809 ]
1776 1810
1777 1811 [[package]]
@@ -1941,6 +1975,26 @@ dependencies = [
1941 1975 ]
1942 1976
1943 1977 [[package]]
1978 + name = "ref-cast"
1979 + version = "1.0.26"
1980 + source = "registry+https://github.com/rust-lang/crates.io-index"
1981 + checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d"
1982 + dependencies = [
1983 + "ref-cast-impl",
1984 + ]
1985 +
1986 + [[package]]
1987 + name = "ref-cast-impl"
1988 + version = "1.0.26"
1989 + source = "registry+https://github.com/rust-lang/crates.io-index"
1990 + checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c"
1991 + dependencies = [
1992 + "proc-macro2",
1993 + "quote",
1994 + "syn 3.0.3",
1995 + ]
1996 +
1997 + [[package]]
1944 1998 name = "regex-automata"
1945 1999 version = "0.4.14"
1946 2000 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1963,7 +2017,7 @@ version = "0.13.4"
1963 2017 source = "registry+https://github.com/rust-lang/crates.io-index"
1964 2018 checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
1965 2019 dependencies = [
1966 - "base64 0.22.1",
2020 + "base64",
1967 2021 "bytes",
1968 2022 "futures-core",
1969 2023 "http",
@@ -2016,14 +2070,15 @@ dependencies = [
2016 2070
2017 2071 [[package]]
2018 2072 name = "rmcp"
2019 - version = "0.1.5"
2073 + version = "2.2.0"
2020 2074 source = "registry+https://github.com/rust-lang/crates.io-index"
2021 - checksum = "33a0110d28bd076f39e14bfd5b0340216dd18effeb5d02b43215944cc3e5c751"
2075 + checksum = "14db48ee17a9ba61810ab1a9c1beb7d06d8136ae39ac25a1137f10d357af01af"
2022 2076 dependencies = [
2023 - "base64 0.21.7",
2077 + "async-trait",
2078 + "base64",
2024 2079 "chrono",
2025 2080 "futures",
2026 - "paste",
2081 + "pastey",
2027 2082 "pin-project-lite",
2028 2083 "rmcp-macros",
2029 2084 "schemars",
@@ -2037,13 +2092,15 @@ dependencies = [
2037 2092
2038 2093 [[package]]
2039 2094 name = "rmcp-macros"
2040 - version = "0.1.5"
2095 + version = "2.2.0"
2041 2096 source = "registry+https://github.com/rust-lang/crates.io-index"
2042 - checksum = "a6e2b2fd7497540489fa2db285edd43b7ed14c49157157438664278da6e42a7a"
2097 + checksum = "783d787bf21813b285f13019adc49e11af501c658890c1e519f31f937c68b7e3"
2043 2098 dependencies = [
2099 + "darling",
2044 2100 "proc-macro2",
2045 2101 "quote",
2046 - "syn",
2102 + "serde_json",
2103 + "syn 2.0.117",
2047 2104 ]
2048 2105
2049 2106 [[package]]
@@ -2178,11 +2235,13 @@ dependencies = [
2178 2235
2179 2236 [[package]]
2180 2237 name = "schemars"
2181 - version = "0.8.22"
2238 + version = "1.2.1"
2182 2239 source = "registry+https://github.com/rust-lang/crates.io-index"
2183 - checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615"
2240 + checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc"
2184 2241 dependencies = [
2242 + "chrono",
2185 2243 "dyn-clone",
2244 + "ref-cast",
2186 2245 "schemars_derive",
2187 2246 "serde",
2188 2247 "serde_json",
@@ -2190,14 +2249,14 @@ dependencies = [
2190 2249
2191 2250 [[package]]
2192 2251 name = "schemars_derive"
2193 - version = "0.8.22"
2252 + version = "1.2.1"
2194 2253 source = "registry+https://github.com/rust-lang/crates.io-index"
2195 - checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d"
2254 + checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f"
2196 2255 dependencies = [
2197 2256 "proc-macro2",
2198 2257 "quote",
2199 2258 "serde_derive_internals",
2200 - "syn",
2259 + "syn 2.0.117",
2201 2260 ]
2202 2261
2203 2262 [[package]]
@@ -2262,7 +2321,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
2262 2321 dependencies = [
2263 2322 "proc-macro2",
2264 2323 "quote",
2265 - "syn",
2324 + "syn 2.0.117",
2266 2325 ]
2267 2326
2268 2327 [[package]]
@@ -2273,7 +2332,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
2273 2332 dependencies = [
2274 2333 "proc-macro2",
2275 2334 "quote",
2276 - "syn",
2335 + "syn 2.0.117",
2277 2336 ]
2278 2337
2279 2338 [[package]]
@@ -2458,7 +2517,7 @@ version = "0.9.0"
2458 2517 source = "registry+https://github.com/rust-lang/crates.io-index"
2459 2518 checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb"
2460 2519 dependencies = [
2461 - "base64 0.22.1",
2520 + "base64",
2462 2521 "bytes",
2463 2522 "cfg-if",
2464 2523 "crc",
@@ -2496,7 +2555,7 @@ dependencies = [
2496 2555 "quote",
2497 2556 "sqlx-core",
2498 2557 "sqlx-macros-core",
2499 - "syn",
2558 + "syn 2.0.117",
2500 2559 ]
2501 2560
2502 2561 [[package]]
@@ -2519,7 +2578,7 @@ dependencies = [
2519 2578 "sqlx-mysql",
2520 2579 "sqlx-postgres",
2521 2580 "sqlx-sqlite",
2522 - "syn",
2581 + "syn 2.0.117",
2523 2582 "thiserror",
2524 2583 "tokio",
2525 2584 "url",
@@ -2558,7 +2617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2558 2617 checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e"
2559 2618 dependencies = [
2560 2619 "atoi",
2561 - "base64 0.22.1",
2620 + "base64",
2562 2621 "bitflags",
2563 2622 "byteorder",
2564 2623 "crc",
@@ -2651,6 +2710,17 @@ dependencies = [
2651 2710 ]
2652 2711
2653 2712 [[package]]
2713 + name = "syn"
2714 + version = "3.0.3"
2715 + source = "registry+https://github.com/rust-lang/crates.io-index"
2716 + checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
2717 + dependencies = [
2718 + "proc-macro2",
2719 + "quote",
2720 + "unicode-ident",
2721 + ]
2722 +
2723 + [[package]]
2654 2724 name = "sync_wrapper"
2655 2725 version = "1.0.2"
2656 2726 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2667,7 +2737,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
2667 2737 dependencies = [
2668 2738 "proc-macro2",
2669 2739 "quote",
2670 - "syn",
2740 + "syn 2.0.117",
2671 2741 ]
2672 2742
2673 2743 [[package]]
@@ -2714,7 +2784,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
2714 2784 dependencies = [
2715 2785 "proc-macro2",
2716 2786 "quote",
2717 - "syn",
2787 + "syn 2.0.117",
2718 2788 ]
2719 2789
2720 2790 [[package]]
@@ -2806,7 +2876,7 @@ checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c"
2806 2876 dependencies = [
2807 2877 "proc-macro2",
2808 2878 "quote",
2809 - "syn",
2879 + "syn 2.0.117",
2810 2880 ]
2811 2881
2812 2882 [[package]]
@@ -2948,7 +3018,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
2948 3018 dependencies = [
2949 3019 "proc-macro2",
2950 3020 "quote",
2951 - "syn",
3021 + "syn 2.0.117",
2952 3022 ]
2953 3023
2954 3024 [[package]]
@@ -3183,7 +3253,7 @@ dependencies = [
3183 3253 "bumpalo",
3184 3254 "proc-macro2",
3185 3255 "quote",
3186 - "syn",
3256 + "syn 2.0.117",
3187 3257 "wasm-bindgen-shared",
3188 3258 ]
3189 3259
@@ -3310,7 +3380,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
3310 3380 dependencies = [
3311 3381 "proc-macro2",
3312 3382 "quote",
3313 - "syn",
3383 + "syn 2.0.117",
3314 3384 ]
3315 3385
3316 3386 [[package]]
@@ -3321,7 +3391,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
3321 3391 dependencies = [
3322 3392 "proc-macro2",
3323 3393 "quote",
3324 - "syn",
3394 + "syn 2.0.117",
3325 3395 ]
3326 3396
3327 3397 [[package]]
@@ -3542,7 +3612,7 @@ dependencies = [
3542 3612 "heck",
3543 3613 "indexmap",
3544 3614 "prettyplease",
3545 - "syn",
3615 + "syn 2.0.117",
3546 3616 "wasm-metadata",
3547 3617 "wit-bindgen-core",
3548 3618 "wit-component",
@@ -3558,7 +3628,7 @@ dependencies = [
3558 3628 "prettyplease",
3559 3629 "proc-macro2",
3560 3630 "quote",
3561 - "syn",
3631 + "syn 2.0.117",
3562 3632 "wit-bindgen-core",
3563 3633 "wit-bindgen-rust",
3564 3634 ]
@@ -3652,7 +3722,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
3652 3722 dependencies = [
3653 3723 "proc-macro2",
3654 3724 "quote",
3655 - "syn",
3725 + "syn 2.0.117",
3656 3726 "synstructure",
3657 3727 ]
3658 3728
@@ -3673,7 +3743,7 @@ checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f"
3673 3743 dependencies = [
3674 3744 "proc-macro2",
3675 3745 "quote",
3676 - "syn",
3746 + "syn 2.0.117",
3677 3747 ]
3678 3748
3679 3749 [[package]]
@@ -3693,7 +3763,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
3693 3763 dependencies = [
3694 3764 "proc-macro2",
3695 3765 "quote",
3696 - "syn",
3766 + "syn 2.0.117",
3697 3767 "synstructure",
3698 3768 ]
3699 3769
@@ -3733,7 +3803,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
3733 3803 dependencies = [
3734 3804 "proc-macro2",
3735 3805 "quote",
3736 - "syn",
3806 + "syn 2.0.117",
3737 3807 ]
3738 3808
3739 3809 [[package]]
M pom/Cargo.toml +2 -2
@@ -14,7 +14,7 @@ path = "src/main.rs"
14 14
15 15 [dependencies]
16 16 # MCP protocol
17 - rmcp = { version = "0.1", features = ["server", "transport-io"] }
17 + rmcp = { version = "2.2", features = ["server", "transport-io"] }
18 18
19 19 # CLI
20 20 clap = { version = "4", features = ["derive"] }
@@ -34,7 +34,7 @@ sqlx = { version = "0.9", features = ["runtime-tokio", "sqlite"] }
34 34 # Serialization
35 35 serde = { version = "1", features = ["derive"] }
36 36 serde_json = "1"
37 - schemars = "0.8"
37 + schemars = "1.2"
38 38
39 39 # Config
40 40 toml = "1.1"
@@ -32,6 +32,12 @@ pub enum PomError {
32 32 #[error(transparent)]
33 33 Join(#[from] tokio::task::JoinError),
34 34
35 + /// MCP server initialization or transport handshake failure. Boxed because
36 + /// `ServerInitializeError` is large and would otherwise bloat every
37 + /// `Result<_, PomError>` in the crate.
38 + #[error(transparent)]
39 + Mcp(Box<rmcp::service::ServerInitializeError>),
40 +
35 41 /// Application-level config validation errors (missing config file, unknown
36 42 /// target name, invalid instance). Distinct from `TomlParse` which covers
37 43 /// syntax-level TOML parse failures.
@@ -39,4 +45,10 @@ pub enum PomError {
39 45 Config(String),
40 46 }
41 47
48 + impl From<rmcp::service::ServerInitializeError> for PomError {
49 + fn from(e: rmcp::service::ServerInitializeError) -> Self {
50 + PomError::Mcp(Box::new(e))
51 + }
52 + }
53 +
42 54 pub type Result<T> = std::result::Result<T, PomError>;
@@ -3,11 +3,11 @@
3 3 pub mod health;
4 4 pub mod tests;
5 5
6 - use rmcp::ServerHandler;
6 + use rmcp::handler::server::router::tool::ToolRouter;
7 + use rmcp::handler::server::wrapper::Parameters;
7 8 use rmcp::model::{ServerCapabilities, ServerInfo};
8 - use rmcp::tool;
9 + use rmcp::{ServerHandler, tool, tool_handler, tool_router};
9 10 use sqlx::SqlitePool;
10 - use tracing::instrument;
11 11
12 12 use crate::config::Config;
13 13
@@ -15,21 +15,27 @@ use crate::config::Config;
15 15 pub struct PomServer {
16 16 pub pool: SqlitePool,
17 17 pub config: Config,
18 + tool_router: ToolRouter<Self>,
18 19 }
19 20
20 21 impl PomServer {
21 22 pub fn new(pool: SqlitePool, config: Config) -> Self {
22 - Self { pool, config }
23 + Self {
24 + pool,
25 + config,
26 + tool_router: Self::tool_router(),
27 + }
23 28 }
24 29 }
25 30
26 - #[tool(tool_box)]
31 + // Each tool method delegates straight to its `*_impl` counterpart, which carries
32 + // its own `#[instrument(skip_all)]`, so the thin wrappers stay untraced by design.
33 + #[tool_router]
27 34 impl PomServer {
28 35 /// Get overall status dashboard for all configured targets.
29 36 #[tool(
30 37 description = "Get overall status dashboard: all targets' latest health check and test run results. Use this for a quick overview."
31 38 )]
32 - #[instrument(skip_all)]
33 39 pub async fn get_status(&self) -> String {
34 40 match self.get_status_impl().await {
35 41 Ok(result) => result,
@@ -41,8 +47,10 @@ impl PomServer {
41 47 #[tool(
42 48 description = "Run a live health check against a target's health endpoint. Stores the result and returns the snapshot. Omit target to check all."
43 49 )]
44 - #[instrument(skip_all)]
45 - pub async fn check_health(&self, #[tool(aggr)] params: health::CheckHealthParams) -> String {
50 + pub async fn check_health(
51 + &self,
52 + Parameters(params): Parameters<health::CheckHealthParams>,
53 + ) -> String {
46 54 match self.check_health_impl(params).await {
47 55 Ok(result) => result,
48 56 Err(e) => format!("Error checking health: {e}"),
@@ -53,10 +61,9 @@ impl PomServer {
53 61 #[tool(
54 62 description = "Get recent health check history. Optionally filter by target and limit results (default 10)."
55 63 )]
56 - #[instrument(skip_all)]
57 64 pub async fn health_history(
58 65 &self,
59 - #[tool(aggr)] params: health::HealthHistoryParams,
66 + Parameters(params): Parameters<health::HealthHistoryParams>,
60 67 ) -> String {
61 68 match self.health_history_impl(params).await {
62 69 Ok(result) => result,
@@ -68,7 +75,6 @@ impl PomServer {
68 75 #[tool(
69 76 description = "List all configured targets with their labels and capabilities (health check, test suite)."
70 77 )]
71 - #[instrument(skip_all)]
72 78 pub async fn list_targets(&self) -> String {
73 79 match self.list_targets_impl().await {
74 80 Ok(result) => result,
@@ -80,8 +86,7 @@ impl PomServer {
80 86 #[tool(
81 87 description = "Run the test suite on a target via SSH. Returns a summary with pass/fail counts. Optionally provide a filter to run specific tests."
82 88 )]
83 - #[instrument(skip_all)]
84 - pub async fn run_tests(&self, #[tool(aggr)] params: tests::RunTestsParams) -> String {
89 + pub async fn run_tests(&self, Parameters(params): Parameters<tests::RunTestsParams>) -> String {
85 90 match self.run_tests_impl(params).await {
86 91 Ok(result) => result,
87 92 Err(e) => format!("Error running tests: {e}"),
@@ -92,8 +97,10 @@ impl PomServer {
92 97 #[tool(
93 98 description = "Get recent test run history (without raw output). Optionally filter by target and limit results (default 10)."
94 99 )]
95 - #[instrument(skip_all)]
96 - pub async fn test_history(&self, #[tool(aggr)] params: tests::TestHistoryParams) -> String {
100 + pub async fn test_history(
101 + &self,
102 + Parameters(params): Parameters<tests::TestHistoryParams>,
103 + ) -> String {
97 104 match self.test_history_impl(params).await {
98 105 Ok(result) => result,
99 106 Err(e) => format!("Error getting test history: {e}"),
@@ -104,10 +111,9 @@ impl PomServer {
104 111 #[tool(
105 112 description = "Get the full raw stdout/stderr output of the most recent test run for a target. Useful for debugging failures."
106 113 )]
107 - #[instrument(skip_all)]
108 114 pub async fn last_test_output(
109 115 &self,
110 - #[tool(aggr)] params: tests::LastTestOutputParams,
116 + Parameters(params): Parameters<tests::LastTestOutputParams>,
111 117 ) -> String {
112 118 match self.last_test_output_impl(params).await {
113 119 Ok(result) => result,
@@ -119,7 +125,6 @@ impl PomServer {
119 125 #[tool(
120 126 description = "Get the peer mesh status showing all PoM instances, their connectivity, versions, and target health. Requires serve mode to be running."
121 127 )]
122 - #[instrument(skip_all)]
123 128 pub async fn get_mesh_status(&self) -> String {
124 129 match self.get_mesh_status_impl().await {
125 130 Ok(result) => result,
@@ -128,19 +133,63 @@ impl PomServer {
128 133 }
129 134 }
130 135
131 - #[tool(tool_box)]
136 + #[tool_handler(router = self.tool_router)]
132 137 impl ServerHandler for PomServer {
133 138 fn get_info(&self) -> ServerInfo {
134 - ServerInfo {
135 - instructions: Some(
136 - "Peace of Mind (PoM) server for monitoring production health and running tests. \
137 - Tools: get_status (dashboard), check_health (live health check), health_history, \
138 - list_targets, run_tests (SSH test execution), test_history, last_test_output, \
139 - get_mesh_status (peer mesh overview)."
140 - .into(),
141 - ),
142 - capabilities: ServerCapabilities::builder().enable_tools().build(),
143 - ..Default::default()
139 + ServerInfo::new(ServerCapabilities::builder().enable_tools().build()).with_instructions(
140 + "Peace of Mind (PoM) server for monitoring production health and running tests. \
141 + Tools: get_status (dashboard), check_health (live health check), health_history, \
142 + list_targets, run_tests (SSH test execution), test_history, last_test_output, \
143 + get_mesh_status (peer mesh overview).",
144 + )
145 + }
146 + }
147 +
148 + #[cfg(test)]
149 + mod router_smoke {
150 + use super::PomServer;
151 +
152 + #[test]
153 + fn all_tools_register_with_schemas() {
154 + let router = PomServer::tool_router();
155 + let tools = router.list_all();
156 + let names: Vec<&str> = tools.iter().map(|t| t.name.as_ref()).collect();
157 + eprintln!("registered tools: {names:?}");
158 + assert_eq!(tools.len(), 8, "expected 8 tools, got {}", tools.len());
159 + for expected in [
160 + "get_status",
161 + "check_health",
162 + "health_history",
163 + "list_targets",
164 + "run_tests",
165 + "test_history",
166 + "last_test_output",
167 + "get_mesh_status",
168 + ] {
169 + let t = tools
170 + .iter()
171 + .find(|t| t.name == expected)
172 + .unwrap_or_else(|| panic!("missing tool {expected}"));
173 + assert!(
174 + t.description.as_ref().is_some_and(|d| !d.is_empty()),
175 + "{expected} has no description"
176 + );
177 + // input_schema is an Arc<Map>; every tool must have an object schema.
178 + assert_eq!(
179 + t.input_schema.get("type").and_then(|v| v.as_str()),
180 + Some("object"),
181 + "{expected} schema not an object"
182 + );
144 183 }
184 + // Tools that take Parameters<T> must expose their properties in the schema.
185 + let ch = tools.iter().find(|t| t.name == "check_health").unwrap();
186 + assert!(
187 + ch.input_schema
188 + .get("properties")
189 + .and_then(|p| p.get("target"))
190 + .is_some(),
191 + "check_health schema missing 'target' property: {:?}",
192 + ch.input_schema
193 + );
145 194 }
146 195 }