diff --git a/CLAUDE.md b/CLAUDE.md index cd52baf..e4720fe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,8 +11,10 @@ stance. Two acquisition modes, both behind one API: -1. Sidecar (default feature): download the platform's .mcpb at first run, - verify sha256 against pins baked into the crate, cache, spawn over stdio +1. Sidecar (default feature): resolve the latest stackql release, download + the platform's .mcpb at first run, verify sha256 against the release's + .sha256 asset (or the pins baked into the crate for + BundleVersion::Pinned), cache, spawn over stdio 2. Vendored (`vendored` feature): caller embeds the binary with `include_bytes!` (we provide the macro/helper + extract-on-first-run) - the single-shippable-binary story for compiled agent apps @@ -27,9 +29,12 @@ justify anything beyond that. Source of truth: stackql/stackql-mcpb-packaging (the packaging repo). -- Per-version sha256 pins from the release .sha256 assets (a consolidated - platforms.json release asset is planned - prefer it once present); pins - are baked at crate build/render time like npm's platforms.json +- Default release is latest: resolved at start-up from + github.com/stackql/stackql/releases/latest and verified against that + release's .sha256 asset (a consolidated platforms.json release asset is + planned - prefer it once present). Per-version sha256 pins are still + baked into the crate for BundleVersion::Pinned and as the offline + fallback; STACKQL_MCP_VERSION=latest|pinned| overrides - Canonical launch args (cwd-independence mandatory): `mcp --mcp.server.type=stdio --approot /.stackql --mcp.config {"server": {"mode": "", "audit": {"disabled": true}}}` @@ -37,7 +42,7 @@ Source of truth: stackql/stackql-mcpb-packaging (the packaging repo). - Shared binary cache: `~/.stackql/mcp-server-bin///` (same as npm/pypi wrappers - check before downloading) - Platform keys: linux-x64, linux-arm64, windows-x64, darwin-universal -- Env overrides honored: STACKQL_MCP_BIN, STACKQL_MCP_BUNDLE +- Env overrides honored: STACKQL_MCP_BIN, STACKQL_MCP_BUNDLE, STACKQL_MCP_VERSION - Conformance: packaging repo's scripts/smoke-test.py --cmd must pass against the crate's example launcher; port the same checks to Rust tests diff --git a/Cargo.lock b/Cargo.lock index 4ed2c5b..93853ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,1963 +1,3319 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anstream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" - -[[package]] -name = "anstyle-parse" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.61.2", -] - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "auditron" -version = "0.1.0" -dependencies = [ - "anyhow", - "chrono", - "clap", - "crossterm", - "csv", - "ratatui", - "rmcp", - "serde", - "serde_json", - "serde_yaml", - "sha2", - "stackql-mcp", - "tokio", - "ureq", - "zip", -] - -[[package]] -name = "autocfg" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bitflags" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" - -[[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 = "bumpalo" -version = "3.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - -[[package]] -name = "castaway" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" -dependencies = [ - "rustversion", -] - -[[package]] -name = "cc" -version = "1.2.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "chrono" -version = "0.4.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" -dependencies = [ - "iana-time-zone", - "num-traits", - "serde", - "windows-link", -] - -[[package]] -name = "clap" -version = "4.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" - -[[package]] -name = "colorchoice" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" - -[[package]] -name = "compact_str" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd622ebbb56a5b2ccb651b32b911cdeb2a9b4b11776b2473bf26a26a286244e" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "rustversion", - "ryu", - "static_assertions", -] - -[[package]] -name = "cookie" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" -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", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "time", - "url", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossterm" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" -dependencies = [ - "bitflags", - "crossterm_winapi", - "mio", - "parking_lot", - "rustix", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - -[[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 = "csv" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde_core", -] - -[[package]] -name = "csv-core" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" -dependencies = [ - "memchr", -] - -[[package]] -name = "darling" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" -dependencies = [ - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" -dependencies = [ - "darling_core", - "quote", - "syn", -] - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "displaydoc" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "document-features" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" -dependencies = [ - "litrs", -] - -[[package]] -name = "either" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "miniz_oxide", - "zlib-rs", -] - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "allocator-api2", - "equivalent", - "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 = "http" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" - -[[package]] -name = "icu_properties" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" - -[[package]] -name = "icu_provider" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.1", -] - -[[package]] -name = "indoc" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" -dependencies = [ - "rustversion", -] - -[[package]] -name = "instability" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" -dependencies = [ - "darling", - "indoc", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "js-sys" -version = "0.3.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" -dependencies = [ - "cfg-if", - "futures-util", - "wasm-bindgen", -] - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - -[[package]] -name = "litemap" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" - -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.5", -] - -[[package]] -name = "memchr" -version = "2.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "num-conv" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "ratatui" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" -dependencies = [ - "bitflags", - "cassowary", - "compact_str", - "crossterm", - "indoc", - "instability", - "itertools", - "lru", - "paste", - "strum", - "unicode-segmentation", - "unicode-truncate", - "unicode-width 0.2.0", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rmcp" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0810a9f717d9828f475fe1f629f4c305c8464b7f496c3a854b58d29e65f4058e" -dependencies = [ - "async-trait", - "chrono", - "futures", - "pin-project-lite", - "serde", - "serde_json", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", -] - -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.23.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" -dependencies = [ - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" -dependencies = [ - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "signal-hook" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - -[[package]] -name = "simd-adler32" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "stackql-mcp" -version = "0.1.0" -dependencies = [ - "rmcp", - "serde", - "serde_json", - "sha2", - "tokio", - "ureq", - "zip", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "time" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tokio" -version = "1.52.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "signal-hook-registry", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-stream" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "typed-path" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" - -[[package]] -name = "typenum" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-segmentation" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" - -[[package]] -name = "unicode-truncate" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" -dependencies = [ - "itertools", - "unicode-segmentation", - "unicode-width 0.1.14", -] - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" - -[[package]] -name = "unsafe-libyaml" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "ureq" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" -dependencies = [ - "base64", - "cookie_store", - "flate2", - "log", - "percent-encoding", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "ureq-proto", - "utf8-zero", - "webpki-roots", -] - -[[package]] -name = "ureq-proto" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" -dependencies = [ - "base64", - "http", - "httparse", - "log", -] - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8-zero" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasm-bindgen" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "webpki-roots" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "writeable" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" - -[[package]] -name = "yoke" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerofrom" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" - -[[package]] -name = "zerotrie" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zip" -version = "8.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" -dependencies = [ - "crc32fast", - "flate2", - "indexmap", - "memchr", - "typed-path", - "zopfli", -] - -[[package]] -name = "zlib-rs" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" - -[[package]] -name = "zopfli" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" -dependencies = [ - "bumpalo", - "crc32fast", - "log", - "simd-adler32", -] +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-any" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0f477b951e452a0b6b4a10b53ccd569042d1d01729b519e02074a9c0958a063" + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "auditron" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "crossterm", + "csv", + "ratatui", + "rmcp", + "serde", + "serde_json", + "serde_yaml", + "sha2", + "stackql-mcp", + "tokio", + "ureq", + "zip", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "aws-lc-rs" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[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 = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.11.1", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +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 = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "compact_str" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd622ebbb56a5b2ccb651b32b911cdeb2a9b4b11776b2473bf26a26a286244e" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +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", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags", + "crossterm_winapi", + "mio", + "parking_lot", + "rustix", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[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 = "csv" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde_core", +] + +[[package]] +name = "csv-core" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" +dependencies = [ + "memchr", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "deluxe" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed332aaf752b459088acf3dd4eca323e3ef4b83c70a84ca48fb0ec5305f1488" +dependencies = [ + "deluxe-core", + "deluxe-macros", + "once_cell", + "proc-macro2", + "syn", +] + +[[package]] +name = "deluxe-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddada51c8576df9d6a8450c351ff63042b092c9458b8ac7d20f89cbd0ffd313" +dependencies = [ + "arrayvec", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn", +] + +[[package]] +name = "deluxe-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87546d9c837f0b7557e47b8bd6eae52c3c223141b76aa233c345c9ab41d9117" +dependencies = [ + "deluxe-core", + "heck 0.4.1", + "if_chain", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "eventsource-stream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" +dependencies = [ + "futures-core", + "nom", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[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.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-timer" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "h2" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "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.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "http" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "if_chain" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd62e6b5e86ea8eeeb8db1de02880a6abc01a397b2ebb64b5d74ac255318f5cb" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "instability" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "mime" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nanoid" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" +dependencies = [ + "rand 0.8.6", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "ordered-float" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" +dependencies = [ + "num-traits", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pastey" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.12+spec-1.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.4", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[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", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "ratatui" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" +dependencies = [ + "bitflags", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "instability", + "itertools", + "lru", + "paste", + "strum", + "unicode-segmentation", + "unicode-truncate", + "unicode-width 0.2.0", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "rig-core" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "557f11c26c2c2ea61d9cb843ce5adff138cc5785f58ff4b87d779de8acaa32ae" +dependencies = [ + "as-any", + "async-stream", + "base64", + "bytes", + "eventsource-stream", + "fastrand", + "futures", + "futures-timer", + "glob", + "http", + "mime", + "mime_guess", + "nanoid", + "ordered-float", + "pin-project-lite", + "reqwest", + "rig-derive", + "rmcp", + "schemars", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-tungstenite", + "tracing", + "tracing-futures", + "url", +] + +[[package]] +name = "rig-derive" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643eb495acbd4bd5976164cd068f186d534f741def8d5d052f860266b98d07f8" +dependencies = [ + "convert_case", + "deluxe", + "indoc", + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "serde_json", + "syn", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rmcp" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0810a9f717d9828f475fe1f629f4c305c8464b7f496c3a854b58d29e65f4058e" +dependencies = [ + "async-trait", + "base64", + "chrono", + "futures", + "pastey", + "pin-project-lite", + "rmcp-macros", + "schemars", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "rmcp-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aefac48c364756e97f04c0401ba3231e8607882c7c1d92da0437dc16307904d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "serde_json", + "syn", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "chrono", + "dyn-clone", + "ref-cast", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stackql-agent" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "futures", + "rig-core", + "rmcp", + "serde_json", + "stackql-mcp", + "tokio", +] + +[[package]] +name = "stackql-mcp" +version = "0.2.0" +dependencies = [ + "rmcp", + "serde", + "serde_json", + "sha2", + "tokio", + "ureq", + "zip", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots 0.26.11", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.3", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.3", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "futures", + "futures-task", + "pin-project", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.4", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror", + "utf-8", +] + +[[package]] +name = "typed-path" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[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 = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools", + "unicode-segmentation", + "unicode-width 0.1.14", +] + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" +dependencies = [ + "base64", + "cookie_store", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "ureq-proto", + "utf8-zero", + "webpki-roots 1.0.7", +] + +[[package]] +name = "ureq-proto" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" +dependencies = [ + "base64", + "http", + "httparse", + "log", +] + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8-zero" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.7", +] + +[[package]] +name = "webpki-roots" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "8.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" +dependencies = [ + "crc32fast", + "flate2", + "indexmap", + "memchr", + "typed-path", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml index a4ddd94..0e56e8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,52 +1,53 @@ -[workspace] -members = ["auditron"] -resolver = "3" - -[package] -name = "stackql-mcp" -version = "0.1.0" -edition = "2021" -# Effective MSRV is set by rmcp 1.x (let-chains): 1.88. -rust-version = "1.88" -description = "Embedded StackQL MCP server for Rust agentic apps - cloud queries and provisioning over SQL, served over MCP" -license = "MIT" -repository = "https://github.com/stackql/stackql-mcp-rs" -documentation = "https://docs.rs/stackql-mcp" -readme = "README.md" -keywords = ["mcp", "stackql", "sql", "cloud", "agents"] -categories = ["api-bindings", "development-tools"] -exclude = [".github/", "CLAUDE.md", "auditron/", "controls/", "pr.md"] - -[package.metadata.docs.rs] -all-features = true - -[features] -default = ["sidecar"] -# Download the platform .mcpb bundle at first run, verify it against the -# sha256 pins baked into the crate, and cache it under ~/.stackql. -sidecar = ["dep:ureq"] -# Caller embeds the .mcpb with include_bytes! and the crate extracts it on -# first run - no network at runtime. -vendored = [] - -[dependencies] -rmcp = { version = "1.7", default-features = false, features = ["client", "transport-async-rw"] } -serde = { version = "1", features = ["derive"] } -serde_json = "1" -sha2 = "0.10" -tokio = { version = "1", features = ["process", "io-util", "rt"] } -ureq = { version = "3", optional = true } -zip = { version = "8", default-features = false, features = ["deflate"] } - -[dev-dependencies] -tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] } - -[[example]] -name = "minimal" - -[[example]] -name = "launcher" - -[[example]] -name = "fetch_bundle" -required-features = ["sidecar"] +[workspace] +members = ["auditron", "stackql-agent"] +resolver = "3" + +[package] +name = "stackql-mcp" +version = "0.2.0" +edition = "2021" +# Effective MSRV is set by rmcp 1.x (let-chains): 1.88. +rust-version = "1.88" +description = "Embedded StackQL MCP server for Rust agentic apps - cloud queries and provisioning over SQL, served over MCP" +license = "MIT" +repository = "https://github.com/stackql/stackql-mcp-rs" +documentation = "https://docs.rs/stackql-mcp" +readme = "README.md" +keywords = ["mcp", "stackql", "sql", "cloud", "agents"] +categories = ["api-bindings", "development-tools"] +exclude = [".github/", "CLAUDE.md", "auditron/", "controls/", "pr.md"] + +[package.metadata.docs.rs] +all-features = true + +[features] +default = ["sidecar"] +# Resolve the latest stackql release, download the platform .mcpb bundle at +# first run, verify its sha256 (release .sha256 asset, or the pins baked into +# the crate for BundleVersion::Pinned), and cache it under ~/.stackql. +sidecar = ["dep:ureq"] +# Caller embeds the .mcpb with include_bytes! and the crate extracts it on +# first run - no network at runtime. +vendored = [] + +[dependencies] +rmcp = { version = "1.7", default-features = false, features = ["client", "transport-async-rw"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +sha2 = "0.10" +tokio = { version = "1", features = ["process", "io-util", "rt"] } +ureq = { version = "3", optional = true } +zip = { version = "8", default-features = false, features = ["deflate"] } + +[dev-dependencies] +tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] } + +[[example]] +name = "minimal" + +[[example]] +name = "launcher" + +[[example]] +name = "fetch_bundle" +required-features = ["sidecar"] diff --git a/README.md b/README.md index 99938fa..67ac5dc 100644 --- a/README.md +++ b/README.md @@ -1,107 +1,120 @@ -# stackql-mcp - -[![crates.io](https://img.shields.io/crates/v/stackql-mcp.svg)](https://crates.io/crates/stackql-mcp) -[![docs.rs](https://docs.rs/stackql-mcp/badge.svg)](https://docs.rs/stackql-mcp) -[![ci](https://github.com/stackql/stackql-mcp-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/stackql/stackql-mcp-rs/actions/workflows/ci.yml) - -Embedded [StackQL](https://stackql.io) MCP server for Rust agentic apps. StackQL exposes cloud providers (AWS, GitHub, Google, Azure, and more) as SQL tables; this crate acquires the `stackql` binary, launches it as an MCP server over stdio, and hands you a connected [rmcp](https://crates.io/crates/rmcp) client. - -## Quickstart - -```rust -use stackql_mcp::{Mode, StackqlMcp}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - let server = StackqlMcp::builder() - .mode(Mode::ReadOnly) - .auth(serde_json::json!({"github": {"type": "null_auth"}})) - .start() - .await?; - let tools = server.list_all_tools().await?; - println!("{} tools available", tools.len()); - server.shutdown().await?; - Ok(()) -} -``` - -Run it: `cargo run --example minimal`. The github provider in `null_auth` mode needs no cloud credentials. - -## Acquisition modes - -Two ways to get the server binary, both behind the same API: - -- sidecar (default feature): downloads the platform's `.mcpb` bundle at first run, verifies its sha256 against pins baked into the crate, and caches it. Subsequent starts are offline. -- vendored (`vendored` feature): embed the bundle in your binary and extract it on first run - no network at runtime, a single shippable binary: - -```rust -let server = StackqlMcp::builder() - .bundle_bytes(include_bytes!("../stackql-mcp-linux-x64.mcpb")) - .start() - .await?; -``` - -Bundles are published per release at [stackql/stackql](https://github.com/stackql/stackql/releases) by [stackql/stackql-mcpb-packaging](https://github.com/stackql/stackql-mcpb-packaging). Platforms: linux-x64, linux-arm64, windows-x64, darwin-universal. - -## Safety modes - -The server enforces a safety contract per session; the crate defaults to the most restrictive. - -| Mode | Allows | -|---|---| -| `Mode::ReadOnly` (default) | SELECT and metadata tools only | -| `Mode::Safe` | reads plus non-destructive mutations | -| `Mode::DeleteSafe` | safe plus deletes | -| `Mode::FullAccess` | everything, including lifecycle provisioning | - -Escalation is an explicit caller opt-in via `.mode(...)`. - -## Cache and overrides - -The binary cache is shared with the StackQL npm and PyPI wrappers: `~/.stackql/mcp-server-bin///`. Existing cache entries are used before any download. - -Env overrides: - -- `STACKQL_MCP_BIN`: path to a stackql binary to run directly (skips acquisition) -- `STACKQL_MCP_BUNDLE`: path to a local `.mcpb` to extract instead of downloading - -Builder equivalents: `.binary(path)`, `.bundle_path(path)`, plus `.approot(path)` to relocate StackQL's application root (default `~/.stackql`). - -If you bring your own MCP stack, `Builder::command()` returns a `std::process::Command` preloaded with the canonical launch arguments instead of starting anything. - -## Demo app: auditron - -The repo ships `auditron`, a terminal compliance copilot built on this crate: point-in-time control checks with auditor-ready evidence packs. Control packs are YAML data under [controls/](controls/); the github pack runs unauthenticated, so it works with zero cloud credentials. - -```sh -cargo run -p auditron -- scan # live TUI, github-core pack -cargo run -p auditron -- scan --no-tui # line output for CI/pipes -cargo run -p auditron -- scan --var org=your-org # point it at your org -cargo run -p auditron -- evidence --out evidence-2026-06.zip -``` - -The TUI streams pass/fail/error per control and always shows the SQL that produced a finding. Select a finding and press `e` to have Claude explain it and draft remediation steps (needs `ANTHROPIC_API_KEY`). The evidence zip contains the run manifest, the exact pack and SQL, and per-control CSVs - re-runnable by an auditor. - -auditron is also the single-binary pitch. Build it with the server embedded: - -```sh -BUNDLE=$(cargo run -p stackql-mcp --example fetch_bundle) -STACKQL_MCP_BUNDLE_FILE=$BUNDLE cargo build -p auditron --features vendored --release -``` - -The resulting binary (~80 MB) carries the StackQL server inside and runs on a clean machine with no downloads. - -## Development - -```sh -cargo test --workspace # unit tests -cargo test -p stackql-mcp --features vendored # vendored path -cargo test -p stackql-mcp --test conformance -- --include-ignored # downloads the pinned bundle -cargo fmt --check && cargo clippy --workspace --all-targets -- -D warnings -``` - -MSRV: 1.88 (set by rmcp 1.x). - -## License - -MIT +# stackql-mcp + +[![crates.io](https://img.shields.io/crates/v/stackql-mcp.svg)](https://crates.io/crates/stackql-mcp) +[![docs.rs](https://docs.rs/stackql-mcp/badge.svg)](https://docs.rs/stackql-mcp) +[![ci](https://github.com/stackql/stackql-mcp-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/stackql/stackql-mcp-rs/actions/workflows/ci.yml) + +Embedded [StackQL](https://stackql.io) MCP server for Rust agentic apps. StackQL exposes cloud providers (AWS, GitHub, Google, Azure, and more) as SQL tables; this crate acquires the `stackql` binary, launches it as an MCP server over stdio, and hands you a connected [rmcp](https://crates.io/crates/rmcp) client. + +## Quickstart + +```rust +use stackql_mcp::{Mode, StackqlMcp}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let server = StackqlMcp::builder() + .mode(Mode::ReadOnly) + .auth(serde_json::json!({"github": {"type": "null_auth"}})) + .start() + .await?; + let tools = server.list_all_tools().await?; + println!("{} tools available", tools.len()); + server.shutdown().await?; + Ok(()) +} +``` + +Run it: `cargo run --example minimal`. The github provider in `null_auth` mode needs no cloud credentials. + +## Acquisition modes + +Two ways to get the server binary, both behind the same API: + +- sidecar (default feature): resolves the latest stackql release at start-up, downloads the platform's `.mcpb` bundle on first use of that release, verifies its sha256 against the release's published `.sha256` asset, and caches it. Pin a release with `.version(BundleVersion::Pinned)` (the release whose pins are baked into the crate, verified offline) or `.version(BundleVersion::Exact("0.10.601".into()))`; `STACKQL_MCP_VERSION=latest|pinned|` overrides. If the latest release cannot be resolved (offline), the newest cached release is used, then the pinned one. +- vendored (`vendored` feature): embed the bundle in your binary and extract it on first run - no network at runtime, a single shippable binary: + +```rust +let server = StackqlMcp::builder() + .bundle_bytes(include_bytes!("../stackql-mcp-linux-x64.mcpb")) + .start() + .await?; +``` + +Bundles are published per release at [stackql/stackql](https://github.com/stackql/stackql/releases) by [stackql/stackql-mcpb-packaging](https://github.com/stackql/stackql-mcpb-packaging). Platforms: linux-x64, linux-arm64, windows-x64, darwin-universal. + +## Safety modes + +The server enforces a safety contract per session; the crate defaults to the most restrictive. + +| Mode | Allows | +|---|---| +| `Mode::ReadOnly` (default) | SELECT and metadata tools only | +| `Mode::Safe` | reads plus non-destructive mutations | +| `Mode::DeleteSafe` | safe plus deletes | +| `Mode::FullAccess` | everything, including lifecycle provisioning | + +Escalation is an explicit caller opt-in via `.mode(...)`. + +## Cache and overrides + +The binary cache is shared with the StackQL npm and PyPI wrappers: `~/.stackql/mcp-server-bin///`. Existing cache entries are used before any download. + +Env overrides: + +- `STACKQL_MCP_BIN`: path to a stackql binary to run directly (skips acquisition) +- `STACKQL_MCP_BUNDLE`: path to a local `.mcpb` to extract instead of downloading +- `STACKQL_MCP_VERSION`: `latest` (default), `pinned`, or a release version such as `0.10.601` + +Builder equivalents: `.binary(path)`, `.bundle_path(path)`, `.version(BundleVersion)`, plus `.approot(path)` to relocate StackQL's application root (default `~/.stackql`). + +If you bring your own MCP stack, `Builder::command()` returns a `std::process::Command` preloaded with the canonical launch arguments instead of starting anything. + +## Demo app: auditron + +The repo ships `auditron`, a terminal compliance copilot built on this crate: point-in-time control checks with auditor-ready evidence packs. Control packs are YAML data under [controls/](controls/); the github pack runs unauthenticated, so it works with zero cloud credentials. + +```sh +cargo run -p auditron -- scan # live TUI, github-core pack +cargo run -p auditron -- scan --no-tui # line output for CI/pipes +cargo run -p auditron -- scan --var org=your-org # point it at your org +cargo run -p auditron -- evidence --out evidence-2026-06.zip +``` + +The TUI streams pass/fail/error per control and always shows the SQL that produced a finding. Select a finding and press `e` to have Claude explain it and draft remediation steps (needs `ANTHROPIC_API_KEY`). The evidence zip contains the run manifest, the exact pack and SQL, and per-control CSVs - re-runnable by an auditor. + +auditron is also the single-binary pitch. Build it with the server embedded: + +```sh +BUNDLE=$(cargo run -p stackql-mcp --example fetch_bundle) +STACKQL_MCP_BUNDLE_FILE=$BUNDLE cargo build -p auditron --features vendored --release +``` + +The resulting binary (~80 MB) carries the StackQL server inside and runs on a clean machine with no downloads. + +## Demo app: stackql-agent + +[stackql-agent](stackql-agent) is the agentic companion: it embeds this crate and wires the StackQL MCP tools into a [rig](https://docs.rig.rs) agent. One binary becomes a platform-engineering, SRE, or audit agent by swapping a system prompt - the backend and the read-only contract are identical across all three. + +```sh +export ANTHROPIC_API_KEY=sk-ant-... +cargo run -p stackql-agent -- --persona platform # also: sre, audit +cargo run -p stackql-agent -- --check # pre-flight, no model calls +``` + +It runs against public GitHub data with zero cloud credentials; point `--auth` at a credentialed provider for IGA, CSPM, FinOps, and AWS/Google/Azure. The integration is about ten lines: `server.list_all_tools()` plus `server.peer()` feed straight into rig's `rmcp_tools()`. + +## Development + +```sh +cargo test --workspace # unit tests +cargo test -p stackql-mcp --features vendored # vendored path +cargo test -p stackql-mcp --test conformance -- --include-ignored # downloads the pinned bundle +cargo fmt --check && cargo clippy --workspace --all-targets -- -D warnings +``` + +MSRV: 1.88 (set by rmcp 1.x). + +## License + +MIT diff --git a/examples/fetch_bundle.rs b/examples/fetch_bundle.rs index 8eb2824..93b2086 100644 --- a/examples/fetch_bundle.rs +++ b/examples/fetch_bundle.rs @@ -1,4 +1,4 @@ -//! Fetch the pinned platform .mcpb into the shared cache and print its path. +//! Fetch the platform .mcpb (latest release by default) into the shared cache and print its path. //! The producer step for vendored builds: //! //! ```text diff --git a/pr.md b/pr.md deleted file mode 100644 index 871bb5e..0000000 --- a/pr.md +++ /dev/null @@ -1,261 +0,0 @@ -# stackql-mcp launch kit - -Working drafts for the crates.io launch of `stackql-mcp` and the auditron demo. Blog publishes first; the Reddit and This Week in Rust posts point back to it. - -Placeholders to fill before posting: - -- `BLOG_URL` - final URL of the blog post on stackql.io -- `ASCIINEMA_URL` - recording of the auditron TUI scan - -## a) Blog post (Docusaurus) - -Suggested path: `blog/2026-06-embedded-mcp-rust.md` - -~~~markdown ---- -title: An embedded StackQL MCP server for Rust agents -description: Query and provision cloud infrastructure over SQL from inside your Rust agent process, with no external server to deploy. Plus auditron, a compliance copilot in a single binary. -authors: [jeffreyaven] -tags: [mcp, rust, agents, compliance] ---- - -StackQL exposes cloud and SaaS providers (AWS, GitHub, Google, Azure, and -more) as SQL tables: `SELECT` to query your estate, `INSERT`/`DELETE` to -provision and tear down. The StackQL MCP server makes that capability -available to AI agents over the Model Context Protocol. - -Today we are releasing [stackql-mcp](https://crates.io/crates/stackql-mcp), -a Rust crate that embeds that server inside your agent process. No -deployment, no sidecar to operate, no network dependency at runtime if you -choose so. It joins the npm and PyPI wrappers in the embedded MCP family, -sharing the same binary cache and the same launch contract. - - - -## Why embedded - -MCP servers are usually deployed as standalone processes that agents -connect to. That is the right shape for shared, multi-tenant capability, -but it is a poor fit for distributable agent software: your users now have -two things to install, version, and secure. - -The embedded model inverts this. Your Rust binary owns the server -lifecycle: it acquires the `stackql` binary, launches it as a child over -stdio, completes the MCP handshake, and hands you a connected client from -[rmcp](https://crates.io/crates/rmcp), the official Rust MCP SDK. The -server is an implementation detail of your app. - -```rust -use stackql_mcp::{Mode, StackqlMcp}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - let server = StackqlMcp::builder() - .mode(Mode::ReadOnly) - .auth(serde_json::json!({"github": {"type": "null_auth"}})) - .start() - .await?; - let tools = server.list_all_tools().await?; - println!("{} tools available", tools.len()); - server.shutdown().await?; - Ok(()) -} -``` - -Two acquisition modes sit behind that one API: - -- **Sidecar** (the default): the platform's `.mcpb` bundle is downloaded at - first run, verified against sha256 pins baked into the crate at release - time, and cached under `~/.stackql/mcp-server-bin/` - the same cache the - npm and PyPI wrappers use. Subsequent starts are offline. -- **Vendored**: the bundle is embedded in your binary with - `include_bytes!` and extracted on first run. No network at runtime, one - artifact to ship. - -```rust -let server = StackqlMcp::builder() - .bundle_bytes(stackql_mcp::include_bundle!()) - .start() - .await?; -``` - -Safety is a first-class part of the contract. The server enforces a mode -per session (`read_only`, `safe`, `delete_safe`, `full_access`) and the -crate defaults to the most restrictive; escalation is an explicit caller -opt-in. - -## auditron: an auditor in a single binary - -To show what the vendored mode is for, the repo ships -[auditron](https://github.com/stackql/stackql-mcp-rs/tree/main/auditron), -a terminal compliance copilot. The business problem: compliance engineers -run point-in-time control checks and need auditor-ready evidence, and the -usual answer involves cloud consoles and screenshots. - -auditron runs a YAML control pack (id, description, SQL, pass criteria per -control) through the embedded server in read-only mode and streams -pass/fail/error into a ratatui table as results arrive. The SQL that -produced each finding is always on screen. Select a finding and press `e` -and Claude explains it and drafts remediation steps. - -[ASCIINEMA_URL] - -Then there is the part the auditor actually wants: - -```sh -auditron evidence --out evidence-2026-06.zip -``` - -The zip contains the run manifest (pack checksum, collector identity, -server version, timings), the exact pack source and per-control SQL, and -per-control CSVs. Another auditor can re-run the same pack against the -same estate and diff the results. - -The demo pack checks GitHub org security posture (branch protection, -default branches, licenses, staleness) using the github provider in -`null_auth` mode, so it runs against public org data with zero -credentials. Point it at your own org: - -```sh -auditron scan --var org=your-org -``` - -Control packs are data, not code. They live in the repo as -`controls/*.yaml` and are community extensible. - -## Lessons from building on live cloud APIs - -Two things we hit while building the demo are worth knowing if you build -on this stack: - -First, MCP tool results from the server carry the typed payload in -`structuredContent` and a markdown rendering in the text content. Read the -structured payload; parsing the markdown is a mistake we made so you do -not have to. - -Second, provider API errors (rate limits especially) can surface as empty -result sets rather than tool errors. For a compliance tool, where "no rows" -means "compliant", that distinction is everything. The github pack includes -an inventory control that must return rows, doubling as a canary for the -whole run; we have also raised the underlying behavior upstream. - -## Getting started - -```sh -cargo add stackql-mcp -``` - -The crate is on [crates.io](https://crates.io/crates/stackql-mcp) with API -docs on [docs.rs](https://docs.rs/stackql-mcp). The source, the auditron -demo, and the control packs are at -[stackql/stackql-mcp-rs](https://github.com/stackql/stackql-mcp-rs), and -the engine itself lives at -[stackql/stackql](https://github.com/stackql/stackql). Issues and control -pack contributions welcome. -~~~ - -## b) Community posts - -### Reddit (r/rust) - -Title: - -~~~text -stackql-mcp: embed a cloud-querying MCP server in your Rust agent (and auditron, a compliance TUI in a single 80MB binary) -~~~ - -Body: - -~~~markdown -We just published [stackql-mcp](https://crates.io/crates/stackql-mcp), a -crate that embeds the StackQL MCP server (cloud infrastructure as SQL: -AWS, GitHub, Google, Azure...) inside a Rust process. Builder API in, -connected [rmcp](https://crates.io/crates/rmcp) client out: - - let server = StackqlMcp::builder() - .mode(Mode::ReadOnly) - .auth(json!({"github": {"type": "null_auth"}})) - .start() - .await?; - -Things r/rust might find interesting: - -- Two acquisition modes behind one API: sidecar (download at first run, - sha256-verified against pins baked into the crate, shared cache) and - vendored (`include_bytes!` the server bundle, extract on first run). - The vendored mode is the fun one: your agent plus a full SQL engine for - cloud APIs in one shippable binary, no network needed at runtime. -- The demo app, auditron, is a ratatui compliance copilot: it streams - control checks (defined as YAML with SQL and pass criteria) into a live - table, always shows the SQL behind a finding, has Claude draft - remediation steps on demand, and exports an auditor-ready evidence zip - (manifest, exact SQL, per-control CSVs - re-runnable by an auditor). - The github demo pack runs unauthenticated against public org data, so - `cargo run -p auditron -- scan` works with zero credentials. -- Dependency surface is deliberately small: rmcp, ureq, zip, sha2, - serde. Server modes (read_only by default) are enforced server-side, - not by the client promising to behave. - -Blog with the design rationale: BLOG_URL -Repo (crate + demo + control packs): https://github.com/stackql/stackql-mcp-rs -The engine: https://github.com/stackql/stackql - -Feedback welcome, especially on the builder API surface and the vendored -build flow (compile-time env var + `include_bundle!` macro - we went back -and forth on build.rs vs explicit pipeline and chose explicit). -~~~ - -### This Week in Rust - -For the "Project/Tooling Updates" section (PR adding a line to the next -draft in [rust-lang/this-week-in-rust](https://github.com/rust-lang/this-week-in-rust)): - -~~~markdown -* [stackql-mcp](https://github.com/stackql/stackql-mcp-rs) - a new crate - embedding the StackQL MCP server (query AWS, GitHub, Google and more - over SQL) in Rust agentic apps, with sha256-pinned sidecar download or - a fully vendored single-binary mode; ships with - [auditron](BLOG_URL), a ratatui compliance copilot demo that produces - auditor-ready evidence packs -~~~ - -## c) Meetup talk synopsis - -Title: - -~~~text -An auditor in a single binary: embedded MCP backends for native Rust agents -~~~ - -Abstract (~150 words): - -~~~text -AI agents are mostly wired together from hosted services: a model API -here, an MCP server deployed there. This talk makes the case for the -opposite shape: a native Rust agent that ships as one binary and owns its -entire backend. - -We build up in three layers. First, the anatomy of a native Rust agent: -tokio, the official rmcp MCP SDK, and a child process speaking MCP over -stdio. Second, the embedded backend: stackql-mcp, a crate that embeds -StackQL (cloud infrastructure as SQL) with two acquisition modes - -sha256-pinned sidecar download, or a server bundle vendored into the -binary with include_bytes!, including what it takes to make extraction, -caching, and process lifecycle boring and reliable. Third, a real agent -on top: auditron, a terminal compliance copilot that runs SQL control -packs against live cloud APIs, explains findings with an LLM, and emits -re-runnable, auditor-ready evidence zips. - -Live demo: zero credentials, one binary, real findings. -~~~ - -Talk notes (not for publication): - -- Demo flow: `auditron scan` TUI against a public GitHub org (null_auth, - no setup on conference wifi beyond GitHub API reachability; mind the - 60 req/h unauthenticated rate limit - warm the cache or bring a token) -- War stories section maps to the upstream issues: markdown vs - structuredContent, boolean typing across resources, errors surfacing as - empty result sets and the canary-control mitigation -- Closing slide: cargo add stackql-mcp, repo links, control packs as the - community contribution surface diff --git a/src/acquire.rs b/src/acquire.rs index 7b9de33..9c45f00 100644 --- a/src/acquire.rs +++ b/src/acquire.rs @@ -1,129 +1,200 @@ -//! Binary acquisition: resolve a runnable stackql binary from (in order) -//! env overrides, builder overrides, vendored bytes, or sidecar download. - -use std::fs; -use std::path::{Path, PathBuf}; - -use crate::bundle; -use crate::cache; -use crate::error::{Error, Result}; -#[cfg(feature = "sidecar")] -use crate::platform::Platform; - -/// Acquisition inputs collected from the builder. Env overrides are read at -/// resolve time and take precedence over everything here. -#[derive(Default)] -pub struct Acquisition { - /// Run this binary directly, skipping bundles entirely. - pub binary: Option, - /// Extract this local .mcpb instead of downloading. - pub bundle_path: Option, - /// Embedded .mcpb bytes (vendored feature). - #[cfg(feature = "vendored")] - pub bundle_bytes: Option<&'static [u8]>, -} - -/// Resolve the server binary, acquiring it if needed. Blocking; call from a -/// blocking context (`start()` wraps this in `spawn_blocking`). -pub fn resolve_binary(acq: &Acquisition) -> Result { - // 1. Env binary override: run it as-is. - if let Some(bin) = std::env::var_os(cache::ENV_BIN).filter(|v| !v.is_empty()) { - return existing(PathBuf::from(bin), cache::ENV_BIN); - } - // 2. Builder binary override. - if let Some(bin) = &acq.binary { - return existing(bin.clone(), "Builder::binary"); - } - // 3. Env bundle override: extract a local .mcpb. No pin check - the - // override is explicit operator intent and may be a custom build. - if let Some(bundle_path) = std::env::var_os(cache::ENV_BUNDLE).filter(|v| !v.is_empty()) { - return extract_local_bundle(&PathBuf::from(bundle_path), cache::ENV_BUNDLE); - } - // 4. Builder bundle override. - if let Some(bundle_path) = &acq.bundle_path { - return extract_local_bundle(bundle_path, "Builder::bundle_path"); - } - // 5. Vendored bytes embedded by the caller. - #[cfg(feature = "vendored")] - if let Some(bytes) = acq.bundle_bytes { - return extract_vendored(bytes); - } - // 6. Sidecar: shared cache, then verified download. - #[cfg(feature = "sidecar")] - { - return sidecar(); - } - #[allow(unreachable_code)] - Err(Error::Bundle( - "no binary source: enable the sidecar feature, embed a bundle with the vendored \ - feature, or set STACKQL_MCP_BIN / STACKQL_MCP_BUNDLE" - .into(), - )) -} - -fn existing(path: PathBuf, what: &'static str) -> Result { - if path.is_file() { - Ok(path) - } else { - Err(Error::OverrideNotFound { what, path }) - } -} - -/// Extract a caller-supplied .mcpb into a cache slot keyed by its content -/// hash, so different bundles never collide. -fn extract_local_bundle(bundle_path: &Path, what: &'static str) -> Result { - if !bundle_path.is_file() { - return Err(Error::OverrideNotFound { - what, - path: bundle_path.to_path_buf(), - }); - } - let digest = crate::download::sha256_file(bundle_path)?; - let dest = cache::bin_cache_root()?.join("custom").join(&digest[..16]); - if let Some(binary) = bundle::cached_binary(&dest) { - return Ok(binary); - } - let file = fs::File::open(bundle_path)?; - bundle::extract_bundle(file, &dest) -} - -#[cfg(feature = "vendored")] -fn extract_vendored(bytes: &'static [u8]) -> Result { - use sha2::{Digest, Sha256}; - let digest = Sha256::digest(bytes); - let mut key = String::with_capacity(16); - for b in &digest[..8] { - key.push_str(&format!("{b:02x}")); - } - let dest = cache::bin_cache_root()?.join("vendored").join(key); - if let Some(binary) = bundle::cached_binary(&dest) { - return Ok(binary); - } - bundle::extract_bundle(std::io::Cursor::new(bytes), &dest) -} - -#[cfg(feature = "sidecar")] -fn sidecar() -> Result { - let platform = Platform::detect()?; - let pin = crate::pins::pin_for(platform)?; - let dest = cache::bundle_cache_dir(crate::pins::STACKQL_VERSION, platform.key())?; - if let Some(binary) = bundle::cached_binary(&dest) { - return Ok(binary); - } - - let url = crate::pins::bundle_url(pin); - let mcpb = cache::bin_cache_root()? - .join(crate::pins::STACKQL_VERSION) - .join(pin.bundle_name); - eprintln!( - "stackql-mcp: downloading {} (first run, cached at {})", - url, - dest.display() - ); - crate::download::download_verified(&url, pin.sha256, &mcpb)?; - let file = fs::File::open(&mcpb)?; - let binary = bundle::extract_bundle(file, &dest)?; - // The extracted dir is the cache; drop the archive to halve disk use. - let _ = fs::remove_file(&mcpb); - Ok(binary) -} +//! Binary acquisition: resolve a runnable stackql binary from (in order) +//! env overrides, builder overrides, vendored bytes, or sidecar download. + +use std::fs; +use std::path::{Path, PathBuf}; + +use crate::bundle; +use crate::cache; +use crate::error::{Error, Result}; +#[cfg(feature = "sidecar")] +use crate::platform::Platform; +use crate::release::BundleVersion; + +/// Acquisition inputs collected from the builder. Env overrides are read at +/// resolve time and take precedence over everything here. +#[derive(Default)] +pub struct Acquisition { + /// Run this binary directly, skipping bundles entirely. + pub binary: Option, + /// Extract this local .mcpb instead of downloading. + pub bundle_path: Option, + /// Embedded .mcpb bytes (vendored feature). + #[cfg(feature = "vendored")] + pub bundle_bytes: Option<&'static [u8]>, + /// Which release the sidecar runs (`STACKQL_MCP_VERSION` overrides). + pub version: BundleVersion, +} + +/// Resolve the server binary, acquiring it if needed. Blocking; call from a +/// blocking context (`start()` wraps this in `spawn_blocking`). +pub fn resolve_binary(acq: &Acquisition) -> Result { + // 1. Env binary override: run it as-is. + if let Some(bin) = std::env::var_os(cache::ENV_BIN).filter(|v| !v.is_empty()) { + return existing(PathBuf::from(bin), cache::ENV_BIN); + } + // 2. Builder binary override. + if let Some(bin) = &acq.binary { + return existing(bin.clone(), "Builder::binary"); + } + // 3. Env bundle override: extract a local .mcpb. No pin check - the + // override is explicit operator intent and may be a custom build. + if let Some(bundle_path) = std::env::var_os(cache::ENV_BUNDLE).filter(|v| !v.is_empty()) { + return extract_local_bundle(&PathBuf::from(bundle_path), cache::ENV_BUNDLE); + } + // 4. Builder bundle override. + if let Some(bundle_path) = &acq.bundle_path { + return extract_local_bundle(bundle_path, "Builder::bundle_path"); + } + // 5. Vendored bytes embedded by the caller. + #[cfg(feature = "vendored")] + if let Some(bytes) = acq.bundle_bytes { + return extract_vendored(bytes); + } + // 6. Sidecar: shared cache, then verified download. + #[cfg(feature = "sidecar")] + { + return sidecar(&acq.version.resolved_from_env()); + } + #[cfg(not(feature = "sidecar"))] + let _ = &acq.version; + #[allow(unreachable_code)] + Err(Error::Bundle( + "no binary source: enable the sidecar feature, embed a bundle with the vendored \ + feature, or set STACKQL_MCP_BIN / STACKQL_MCP_BUNDLE" + .into(), + )) +} + +fn existing(path: PathBuf, what: &'static str) -> Result { + if path.is_file() { + Ok(path) + } else { + Err(Error::OverrideNotFound { what, path }) + } +} + +/// Extract a caller-supplied .mcpb into a cache slot keyed by its content +/// hash, so different bundles never collide. +fn extract_local_bundle(bundle_path: &Path, what: &'static str) -> Result { + if !bundle_path.is_file() { + return Err(Error::OverrideNotFound { + what, + path: bundle_path.to_path_buf(), + }); + } + let digest = crate::download::sha256_file(bundle_path)?; + let dest = cache::bin_cache_root()?.join("custom").join(&digest[..16]); + if let Some(binary) = bundle::cached_binary(&dest) { + return Ok(binary); + } + let file = fs::File::open(bundle_path)?; + bundle::extract_bundle(file, &dest) +} + +#[cfg(feature = "vendored")] +fn extract_vendored(bytes: &'static [u8]) -> Result { + use sha2::{Digest, Sha256}; + let digest = Sha256::digest(bytes); + let mut key = String::with_capacity(16); + for b in &digest[..8] { + key.push_str(&format!("{b:02x}")); + } + let dest = cache::bin_cache_root()?.join("vendored").join(key); + if let Some(binary) = bundle::cached_binary(&dest) { + return Ok(binary); + } + bundle::extract_bundle(std::io::Cursor::new(bytes), &dest) +} + +#[cfg(feature = "sidecar")] +fn sidecar(version: &BundleVersion) -> Result { + use crate::release::{self, ResolvedBundle}; + + let platform = Platform::detect()?; + let bundle: ResolvedBundle = match release::resolve(version, platform) { + Ok(b) => b, + // Latest could not be resolved (offline, GitHub unreachable): run + // what is already on disk rather than failing the start. + Err(e) if *version == BundleVersion::Latest => { + if let Some((cached_version, binary)) = newest_cached(platform)? { + eprintln!( + "stackql-mcp: could not resolve the latest release ({e}); using cached {cached_version}" + ); + return Ok(binary); + } + eprintln!( + "stackql-mcp: could not resolve the latest release ({e}); using pinned {}", + crate::pins::STACKQL_VERSION + ); + release::pinned(platform)? + } + Err(e) => return Err(e), + }; + + let dest = cache::bundle_cache_dir(&bundle.version, platform.key())?; + if let Some(binary) = bundle::cached_binary(&dest) { + return Ok(binary); + } + + let url = bundle.url(); + let mcpb = cache::bin_cache_root()? + .join(&bundle.version) + .join(bundle.bundle_name); + eprintln!( + "stackql-mcp: downloading {} (first run, cached at {})", + url, + dest.display() + ); + crate::download::download_verified(&url, &bundle.sha256, &mcpb)?; + let file = fs::File::open(&mcpb)?; + let binary = bundle::extract_bundle(file, &dest)?; + // The extracted dir is the cache; drop the archive to halve disk use. + let _ = fs::remove_file(&mcpb); + Ok(binary) +} + +/// The highest-versioned release already extracted for `platform` in the +/// shared cache, if any: `(version, binary path)`. +#[cfg(feature = "sidecar")] +fn newest_cached(platform: Platform) -> Result> { + let root = cache::bin_cache_root()?; + let Ok(entries) = fs::read_dir(&root) else { + return Ok(None); + }; + let mut found: Vec<(Vec, String, PathBuf)> = entries + .filter_map(|e| e.ok()) + .filter_map(|e| { + let name = e.file_name().into_string().ok()?; + let key = version_key(&name)?; + let binary = bundle::cached_binary(&e.path().join(platform.key()))?; + Some((key, name, binary)) + }) + .collect(); + found.sort(); + Ok(found.pop().map(|(_, v, b)| (v, b))) +} + +/// `"0.10.601"` -> `[0, 10, 601]`; `None` for non-version dir names such as +/// `custom` or `vendored`. +#[cfg(feature = "sidecar")] +fn version_key(name: &str) -> Option> { + let parts: Vec = name + .split('.') + .map(|p| p.parse().ok()) + .collect::>()?; + (!parts.is_empty()).then_some(parts) +} + +#[cfg(all(test, feature = "sidecar"))] +mod tests { + use super::*; + + #[test] + fn version_key_orders_numerically_and_skips_non_versions() { + assert!(version_key("0.10.601") > version_key("0.10.99")); + assert_eq!(version_key("custom"), None); + assert_eq!(version_key("vendored"), None); + assert_eq!(version_key(""), None); + } +} diff --git a/src/bundle.rs b/src/bundle.rs index 6245648..c2a00a7 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -57,10 +57,23 @@ pub fn extract_bundle(reader: R, dest: &Path) -> Result } let result = extract_into(reader, &tmp).and_then(|entry| { + // We only reach here when `dest` was not a valid cache at entry. If + // something already occupies `dest`, it is either a valid cache a + // concurrent extractor just produced (use it) or a stale/partial dir + // from an interrupted run (replace it). Without this, `rename` fails + // with ENOTEMPTY on a non-empty `dest` and the binary is bricked until + // the cache is cleared by hand. + if dest.exists() { + if let Some(binary) = cached_binary(dest) { + let _ = fs::remove_dir_all(&tmp); + return Ok(binary); + } + fs::remove_dir_all(dest)?; + } match fs::rename(&tmp, dest) { Ok(()) => {} Err(_) if cached_binary(dest).is_some() => { - // Another process won the race; its copy is valid. + // A concurrent extractor won the race; its copy is valid. let _ = fs::remove_dir_all(&tmp); } Err(e) => return Err(Error::Io(e)), @@ -158,6 +171,27 @@ mod tests { fs::remove_dir_all(dest.parent().unwrap()).unwrap(); } + #[test] + fn replaces_a_stale_invalid_cache_dir() { + // A non-empty `dest` that is not a valid cached bundle (e.g. left by an + // interrupted extraction) must be replaced, not cause ENOTEMPTY. + let dest = temp_dest("stale"); + fs::create_dir_all(dest.join("server")).unwrap(); + fs::write(dest.join("partial.tmp"), b"junk").unwrap(); + assert!( + cached_binary(&dest).is_none(), + "precondition: dest is invalid" + ); + + let binary = extract_bundle(Cursor::new(fake_bundle("server/stackql")), &dest).unwrap(); + assert!(binary.is_file()); + assert!( + !dest.join("partial.tmp").exists(), + "stale contents should be gone" + ); + fs::remove_dir_all(dest.parent().unwrap()).unwrap(); + } + #[test] fn rejects_traversal_in_entry_point() { let dest = temp_dest("traversal"); diff --git a/src/cache.rs b/src/cache.rs index d6dfb23..18fda96 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -38,6 +38,7 @@ pub fn bin_cache_root() -> Result { /// Cache directory for one extracted bundle: /// `/.stackql/mcp-server-bin///`. +#[cfg_attr(not(feature = "sidecar"), allow(dead_code))] pub fn bundle_cache_dir(version: &str, platform_key: &str) -> Result { Ok(bin_cache_root()?.join(version).join(platform_key)) } diff --git a/src/error.rs b/src/error.rs index ba0441e..7342774 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,86 +1,87 @@ -use std::fmt; -use std::path::PathBuf; - -/// Errors produced while acquiring, verifying, or running the embedded server. -#[derive(Debug)] -#[non_exhaustive] -pub enum Error { - /// No .mcpb bundle is published for the host OS/arch. - UnsupportedPlatform { - os: &'static str, - arch: &'static str, - }, - /// The home directory could not be resolved (HOME / USERPROFILE unset). - NoHomeDir, - /// Downloading the bundle failed. - Http { url: String, message: String }, - /// The downloaded bundle did not match the sha256 pin baked into the crate. - ChecksumMismatch { - bundle: String, - expected: String, - actual: String, - }, - /// The .mcpb bundle is malformed (bad zip, missing or invalid manifest). - Bundle(String), - /// Filesystem error. - Io(std::io::Error), - /// The server process could not be spawned. - Spawn(std::io::Error), - /// MCP handshake or client error. - Mcp(String), - /// A path given via an env override or builder option does not exist. - OverrideNotFound { what: &'static str, path: PathBuf }, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::UnsupportedPlatform { os, arch } => { - write!(f, "no stackql mcp bundle is published for {os}/{arch}") - } - Error::NoHomeDir => { - write!( - f, - "could not resolve the home directory (HOME / USERPROFILE unset)" - ) - } - Error::Http { url, message } => write!(f, "download of {url} failed: {message}"), - Error::ChecksumMismatch { - bundle, - expected, - actual, - } => write!( - f, - "sha256 mismatch for {bundle}: expected {expected}, got {actual}" - ), - Error::Bundle(msg) => write!(f, "invalid .mcpb bundle: {msg}"), - Error::Io(e) => write!(f, "io error: {e}"), - Error::Spawn(e) => write!(f, "failed to spawn the stackql mcp server: {e}"), - Error::Mcp(msg) => write!(f, "mcp client error: {msg}"), - Error::OverrideNotFound { what, path } => { - write!( - f, - "{what} points to {} which does not exist", - path.display() - ) - } - } - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::Io(e) | Error::Spawn(e) => Some(e), - _ => None, - } - } -} - -impl From for Error { - fn from(e: std::io::Error) -> Self { - Error::Io(e) - } -} - -pub type Result = std::result::Result; +use std::fmt; +use std::path::PathBuf; + +/// Errors produced while acquiring, verifying, or running the embedded server. +#[derive(Debug)] +#[non_exhaustive] +pub enum Error { + /// No .mcpb bundle is published for the host OS/arch. + UnsupportedPlatform { + os: &'static str, + arch: &'static str, + }, + /// The home directory could not be resolved (HOME / USERPROFILE unset). + NoHomeDir, + /// Downloading the bundle failed. + Http { url: String, message: String }, + /// The downloaded bundle did not match its expected sha256 (the pin baked + /// into the crate, or the release's published .sha256 asset). + ChecksumMismatch { + bundle: String, + expected: String, + actual: String, + }, + /// The .mcpb bundle is malformed (bad zip, missing or invalid manifest). + Bundle(String), + /// Filesystem error. + Io(std::io::Error), + /// The server process could not be spawned. + Spawn(std::io::Error), + /// MCP handshake or client error. + Mcp(String), + /// A path given via an env override or builder option does not exist. + OverrideNotFound { what: &'static str, path: PathBuf }, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::UnsupportedPlatform { os, arch } => { + write!(f, "no stackql mcp bundle is published for {os}/{arch}") + } + Error::NoHomeDir => { + write!( + f, + "could not resolve the home directory (HOME / USERPROFILE unset)" + ) + } + Error::Http { url, message } => write!(f, "download of {url} failed: {message}"), + Error::ChecksumMismatch { + bundle, + expected, + actual, + } => write!( + f, + "sha256 mismatch for {bundle}: expected {expected}, got {actual}" + ), + Error::Bundle(msg) => write!(f, "invalid .mcpb bundle: {msg}"), + Error::Io(e) => write!(f, "io error: {e}"), + Error::Spawn(e) => write!(f, "failed to spawn the stackql mcp server: {e}"), + Error::Mcp(msg) => write!(f, "mcp client error: {msg}"), + Error::OverrideNotFound { what, path } => { + write!( + f, + "{what} points to {} which does not exist", + path.display() + ) + } + } + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Error::Io(e) | Error::Spawn(e) => Some(e), + _ => None, + } + } +} + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Error::Io(e) + } +} + +pub type Result = std::result::Result; diff --git a/src/lib.rs b/src/lib.rs index d2fb26e..d5a7064 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,290 +1,314 @@ -//! Embedded StackQL MCP server for Rust agentic apps. -//! -//! StackQL exposes cloud providers (AWS, GitHub, Google, Azure, ...) as SQL -//! tables, served over the Model Context Protocol. This crate acquires the -//! `stackql` binary, launches it as an MCP server over stdio, and hands you a -//! connected [`rmcp`] client. -//! -//! Two acquisition modes behind one API: -//! -//! - sidecar (default feature): download the platform's .mcpb bundle at first -//! run, verify its sha256 against pins baked into the crate, and cache it -//! under `~/.stackql/mcp-server-bin/` (shared with the npm and PyPI -//! wrappers) -//! - vendored (`vendored` feature): embed the .mcpb with `include_bytes!` and -//! extract on first run - no network at runtime, single shippable binary -//! -//! ```no_run -//! use stackql_mcp::{Mode, StackqlMcp}; -//! -//! # async fn run() -> Result<(), Box> { -//! let server = StackqlMcp::builder() -//! .mode(Mode::ReadOnly) -//! .auth(serde_json::json!({"github": {"type": "null_auth"}})) -//! .start() -//! .await?; -//! let tools = server.list_all_tools().await?; -//! println!("{} tools available", tools.len()); -//! server.shutdown().await?; -//! # Ok(()) -//! # } -//! ``` - -mod acquire; -mod bundle; -mod cache; -mod download; -mod error; -mod launch; -mod pins; -mod platform; - -use std::ops::Deref; -use std::path::PathBuf; -use std::process::Stdio; - -use rmcp::service::RunningService; -use rmcp::{RoleClient, ServiceExt}; - -pub use cache::{ENV_BIN, ENV_BUNDLE}; -pub use error::{Error, Result}; -pub use pins::{Pin, PINS, STACKQL_VERSION}; -pub use platform::Platform; - -/// Safety contract for query / mutation / lifecycle tools, enforced -/// server-side. Maps to `server.mode` in the server's `--mcp.config`. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum Mode { - /// SELECT and metadata tools only. The default: escalation is an - /// explicit caller opt-in. - #[default] - ReadOnly, - /// Reads plus non-destructive mutations (the server's own default). - Safe, - /// Safe plus deletes. - DeleteSafe, - /// All operations, including lifecycle provisioning. - FullAccess, -} - -impl Mode { - /// The wire value for `server.mode`. - pub fn as_str(self) -> &'static str { - match self { - Mode::ReadOnly => "read_only", - Mode::Safe => "safe", - Mode::DeleteSafe => "delete_safe", - Mode::FullAccess => "full_access", - } - } -} - -/// Download the pinned .mcpb bundle for the host platform into the shared -/// cache (verified against the baked sha256 pin) and return its path. Skips -/// the download when a verified copy is already present. -/// -/// This is the producer side of vendored builds: fetch the bundle once on the -/// build machine, then embed it with [`include_bundle!`]. -#[cfg(feature = "sidecar")] -pub fn fetch_bundle() -> Result { - let platform = Platform::detect()?; - let pin = pins::pin_for(platform)?; - let dest = cache::bin_cache_root()? - .join(pins::STACKQL_VERSION) - .join(pin.bundle_name); - if dest.is_file() && download::sha256_file(&dest)? == pin.sha256 { - return Ok(dest); - } - download::download_verified(&pins::bundle_url(pin), pin.sha256, &dest)?; - Ok(dest) -} - -/// Embed the .mcpb bundle named by the compile-time env var -/// `STACKQL_MCP_BUNDLE_FILE`, for use with `Builder::bundle_bytes` (vendored -/// feature): -/// -/// ```ignore -/// let server = StackqlMcp::builder() -/// .bundle_bytes(stackql_mcp::include_bundle!()) -/// .start() -/// .await?; -/// ``` -/// -/// Build with `STACKQL_MCP_BUNDLE_FILE=/abs/path/to/bundle.mcpb cargo build`. -/// Pair with [`fetch_bundle`] to produce the bundle. -#[macro_export] -macro_rules! include_bundle { - () => { - include_bytes!(env!( - "STACKQL_MCP_BUNDLE_FILE", - "set STACKQL_MCP_BUNDLE_FILE to the absolute path of the platform .mcpb bundle \ - (see stackql_mcp::fetch_bundle)" - )) - }; -} - -/// Entry point. See the crate docs for the full example. -pub struct StackqlMcp; - -impl StackqlMcp { - pub fn builder() -> Builder { - Builder::default() - } -} - -/// Configures and starts the embedded server. -#[derive(Default)] -pub struct Builder { - mode: Mode, - auth: Option, - approot: Option, - acquisition: acquire::Acquisition, -} - -impl Builder { - /// Safety mode for the server. Defaults to [`Mode::ReadOnly`]. - pub fn mode(mut self, mode: Mode) -> Self { - self.mode = mode; - self - } - - /// Provider auth document, passed to the server as `--auth=`. - /// Example: `json!({"github": {"type": "null_auth"}})`. - pub fn auth(mut self, auth: serde_json::Value) -> Self { - self.auth = Some(auth); - self - } - - /// Override the server's application root. Defaults to `/.stackql`. - pub fn approot(mut self, approot: impl Into) -> Self { - self.approot = Some(approot.into()); - self - } - - /// Run an existing stackql binary instead of acquiring one. The - /// `STACKQL_MCP_BIN` env var takes precedence over this. - pub fn binary(mut self, path: impl Into) -> Self { - self.acquisition.binary = Some(path.into()); - self - } - - /// Extract a local .mcpb bundle instead of downloading. The - /// `STACKQL_MCP_BUNDLE` env var takes precedence over this. - pub fn bundle_path(mut self, path: impl Into) -> Self { - self.acquisition.bundle_path = Some(path.into()); - self - } - - /// Embed the .mcpb bundle in your binary and extract it on first run: - /// `builder.bundle_bytes(include_bytes!("../stackql-mcp-linux-x64.mcpb"))`. - #[cfg(feature = "vendored")] - pub fn bundle_bytes(mut self, bytes: &'static [u8]) -> Self { - self.acquisition.bundle_bytes = Some(bytes); - self - } - - /// Resolve the binary (acquiring it if needed) and return a - /// [`std::process::Command`] preloaded with the canonical launch args. - /// Blocking. The escape hatch for callers bringing their own MCP stack - /// or process supervision; stdio configuration is left to the caller. - pub fn command(&self) -> Result { - let binary = acquire::resolve_binary(&self.acquisition)?; - let approot = self.resolved_approot()?; - let mut cmd = std::process::Command::new(binary); - cmd.args(launch::launch_args(self.mode, &approot, self.auth.as_ref())); - Ok(cmd) - } - - /// Acquire the binary if needed, spawn the server, and complete the MCP - /// handshake. Must be called from within a tokio runtime. - pub async fn start(self) -> Result { - let approot = self.resolved_approot()?; - let acquisition = self.acquisition; - let binary = tokio::task::spawn_blocking(move || acquire::resolve_binary(&acquisition)) - .await - .map_err(|e| Error::Mcp(format!("acquisition task failed: {e}")))??; - - let mut child = tokio::process::Command::new(&binary) - .args(launch::launch_args(self.mode, &approot, self.auth.as_ref())) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - // Diagnostics belong on stderr; let them flow through. - .stderr(Stdio::inherit()) - .kill_on_drop(true) - .spawn() - .map_err(Error::Spawn)?; - - let stdout = child - .stdout - .take() - .ok_or_else(|| Error::Mcp("child stdout not captured".into()))?; - let stdin = child - .stdin - .take() - .ok_or_else(|| Error::Mcp("child stdin not captured".into()))?; - - let client = () - .serve((stdout, stdin)) - .await - .map_err(|e| Error::Mcp(format!("initialize failed: {e}")))?; - - Ok(RunningServer { - child, - client, - binary, - }) - } - - fn resolved_approot(&self) -> Result { - match &self.approot { - Some(p) => Ok(p.clone()), - None => cache::default_approot(), - } - } -} - -/// A running embedded server: the child process handle plus a connected -/// rmcp client. Derefs to the client, so rmcp peer methods -/// (`list_all_tools`, `call_tool`, ...) are available directly. -pub struct RunningServer { - child: tokio::process::Child, - client: RunningService, - binary: PathBuf, -} - -impl RunningServer { - /// The connected rmcp client. - pub fn client(&self) -> &RunningService { - &self.client - } - - /// OS process id of the server, if it is still running. - pub fn pid(&self) -> Option { - self.child.id() - } - - /// Path of the stackql binary that was launched. - pub fn binary_path(&self) -> &std::path::Path { - &self.binary - } - - /// Close the MCP session and stop the server process. - pub async fn shutdown(self) -> Result<()> { - let RunningServer { - mut child, client, .. - } = self; - // Cancelling drops the transport; the server sees EOF on stdin and - // exits. The kill is a backstop for a wedged process. - let _ = client.cancel().await; - let _ = child.kill().await; - Ok(()) - } -} - -impl Deref for RunningServer { - type Target = RunningService; - - fn deref(&self) -> &Self::Target { - &self.client - } -} +//! Embedded StackQL MCP server for Rust agentic apps. +//! +//! StackQL exposes cloud providers (AWS, GitHub, Google, Azure, ...) as SQL +//! tables, served over the Model Context Protocol. This crate acquires the +//! `stackql` binary, launches it as an MCP server over stdio, and hands you a +//! connected [`rmcp`] client. +//! +//! Two acquisition modes behind one API: +//! +//! - sidecar (default feature): resolve the latest stackql release at +//! start-up, download the platform's .mcpb bundle on first use of that +//! release, verify its sha256 against the release's published `.sha256` +//! asset, and cache it under `~/.stackql/mcp-server-bin/` (shared with the +//! npm and PyPI wrappers). [`Builder::version`] / `STACKQL_MCP_VERSION` +//! select a pinned or exact release instead; when the latest release cannot +//! be resolved (offline) a cached release, then the crate's pinned release, +//! is used +//! - vendored (`vendored` feature): embed the .mcpb with `include_bytes!` and +//! extract on first run - no network at runtime, single shippable binary +//! +//! ```no_run +//! use stackql_mcp::{Mode, StackqlMcp}; +//! +//! # async fn run() -> Result<(), Box> { +//! let server = StackqlMcp::builder() +//! .mode(Mode::ReadOnly) +//! .auth(serde_json::json!({"github": {"type": "null_auth"}})) +//! .start() +//! .await?; +//! let tools = server.list_all_tools().await?; +//! println!("{} tools available", tools.len()); +//! server.shutdown().await?; +//! # Ok(()) +//! # } +//! ``` + +mod acquire; +mod bundle; +mod cache; +mod download; +mod error; +mod launch; +mod pins; +mod platform; +mod release; + +use std::ops::Deref; +use std::path::PathBuf; +use std::process::Stdio; + +use rmcp::service::RunningService; +use rmcp::{RoleClient, ServiceExt}; + +pub use cache::{ENV_BIN, ENV_BUNDLE}; +pub use error::{Error, Result}; +pub use pins::{Pin, PINS, STACKQL_VERSION}; +pub use platform::Platform; +pub use release::{BundleVersion, ENV_VERSION}; + +/// Safety contract for query / mutation / lifecycle tools, enforced +/// server-side. Maps to `server.mode` in the server's `--mcp.config`. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum Mode { + /// SELECT and metadata tools only. The default: escalation is an + /// explicit caller opt-in. + #[default] + ReadOnly, + /// Reads plus non-destructive mutations (the server's own default). + Safe, + /// Safe plus deletes. + DeleteSafe, + /// All operations, including lifecycle provisioning. + FullAccess, +} + +impl Mode { + /// The wire value for `server.mode`. + pub fn as_str(self) -> &'static str { + match self { + Mode::ReadOnly => "read_only", + Mode::Safe => "safe", + Mode::DeleteSafe => "delete_safe", + Mode::FullAccess => "full_access", + } + } +} + +/// Download the .mcpb bundle for the host platform into the shared cache +/// and return its path. Uses the default release selection (latest, or +/// `STACKQL_MCP_VERSION`); see [`fetch_bundle_with`] to choose explicitly. +/// Skips the download when a verified copy is already present. +/// +/// This is the producer side of vendored builds: fetch the bundle once on the +/// build machine, then embed it with [`include_bundle!`]. +#[cfg(feature = "sidecar")] +pub fn fetch_bundle() -> Result { + fetch_bundle_with(&BundleVersion::default().resolved_from_env()) +} + +/// [`fetch_bundle`] for a specific release selection. `Latest` and `Exact` +/// verify against the release's published `.sha256` asset; `Pinned` against +/// the pins baked into this crate. +#[cfg(feature = "sidecar")] +pub fn fetch_bundle_with(version: &BundleVersion) -> Result { + let platform = Platform::detect()?; + let bundle = release::resolve(version, platform)?; + let dest = cache::bin_cache_root()? + .join(&bundle.version) + .join(bundle.bundle_name); + if dest.is_file() && download::sha256_file(&dest)? == bundle.sha256 { + return Ok(dest); + } + download::download_verified(&bundle.url(), &bundle.sha256, &dest)?; + Ok(dest) +} + +/// Embed the .mcpb bundle named by the compile-time env var +/// `STACKQL_MCP_BUNDLE_FILE`, for use with `Builder::bundle_bytes` (vendored +/// feature): +/// +/// ```ignore +/// let server = StackqlMcp::builder() +/// .bundle_bytes(stackql_mcp::include_bundle!()) +/// .start() +/// .await?; +/// ``` +/// +/// Build with `STACKQL_MCP_BUNDLE_FILE=/abs/path/to/bundle.mcpb cargo build`. +/// Pair with [`fetch_bundle`] to produce the bundle. +#[macro_export] +macro_rules! include_bundle { + () => { + include_bytes!(env!( + "STACKQL_MCP_BUNDLE_FILE", + "set STACKQL_MCP_BUNDLE_FILE to the absolute path of the platform .mcpb bundle \ + (see stackql_mcp::fetch_bundle)" + )) + }; +} + +/// Entry point. See the crate docs for the full example. +pub struct StackqlMcp; + +impl StackqlMcp { + pub fn builder() -> Builder { + Builder::default() + } +} + +/// Configures and starts the embedded server. +#[derive(Default)] +pub struct Builder { + mode: Mode, + auth: Option, + approot: Option, + acquisition: acquire::Acquisition, +} + +impl Builder { + /// Safety mode for the server. Defaults to [`Mode::ReadOnly`]. + pub fn mode(mut self, mode: Mode) -> Self { + self.mode = mode; + self + } + + /// Provider auth document, passed to the server as `--auth=`. + /// Example: `json!({"github": {"type": "null_auth"}})`. + pub fn auth(mut self, auth: serde_json::Value) -> Self { + self.auth = Some(auth); + self + } + + /// Override the server's application root. Defaults to `/.stackql`. + pub fn approot(mut self, approot: impl Into) -> Self { + self.approot = Some(approot.into()); + self + } + + /// Run an existing stackql binary instead of acquiring one. The + /// `STACKQL_MCP_BIN` env var takes precedence over this. + pub fn binary(mut self, path: impl Into) -> Self { + self.acquisition.binary = Some(path.into()); + self + } + + /// Extract a local .mcpb bundle instead of downloading. The + /// `STACKQL_MCP_BUNDLE` env var takes precedence over this. + pub fn bundle_path(mut self, path: impl Into) -> Self { + self.acquisition.bundle_path = Some(path.into()); + self + } + + /// Which stackql release the sidecar runs. Defaults to + /// [`BundleVersion::Latest`]. The `STACKQL_MCP_VERSION` env var + /// (`latest`, `pinned`, or a version) takes precedence over this. Ignored + /// when a binary or bundle override is in effect. + pub fn version(mut self, version: BundleVersion) -> Self { + self.acquisition.version = version; + self + } + + /// Embed the .mcpb bundle in your binary and extract it on first run: + /// `builder.bundle_bytes(include_bytes!("../stackql-mcp-linux-x64.mcpb"))`. + #[cfg(feature = "vendored")] + pub fn bundle_bytes(mut self, bytes: &'static [u8]) -> Self { + self.acquisition.bundle_bytes = Some(bytes); + self + } + + /// Resolve the binary (acquiring it if needed) and return a + /// [`std::process::Command`] preloaded with the canonical launch args. + /// Blocking. The escape hatch for callers bringing their own MCP stack + /// or process supervision; stdio configuration is left to the caller. + pub fn command(&self) -> Result { + let binary = acquire::resolve_binary(&self.acquisition)?; + let approot = self.resolved_approot()?; + let mut cmd = std::process::Command::new(binary); + cmd.args(launch::launch_args(self.mode, &approot, self.auth.as_ref())); + Ok(cmd) + } + + /// Acquire the binary if needed, spawn the server, and complete the MCP + /// handshake. Must be called from within a tokio runtime. + pub async fn start(self) -> Result { + let approot = self.resolved_approot()?; + let acquisition = self.acquisition; + let binary = tokio::task::spawn_blocking(move || acquire::resolve_binary(&acquisition)) + .await + .map_err(|e| Error::Mcp(format!("acquisition task failed: {e}")))??; + + let mut child = tokio::process::Command::new(&binary) + .args(launch::launch_args(self.mode, &approot, self.auth.as_ref())) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + // Diagnostics belong on stderr; let them flow through. + .stderr(Stdio::inherit()) + .kill_on_drop(true) + .spawn() + .map_err(Error::Spawn)?; + + let stdout = child + .stdout + .take() + .ok_or_else(|| Error::Mcp("child stdout not captured".into()))?; + let stdin = child + .stdin + .take() + .ok_or_else(|| Error::Mcp("child stdin not captured".into()))?; + + let client = () + .serve((stdout, stdin)) + .await + .map_err(|e| Error::Mcp(format!("initialize failed: {e}")))?; + + Ok(RunningServer { + child, + client, + binary, + }) + } + + fn resolved_approot(&self) -> Result { + match &self.approot { + Some(p) => Ok(p.clone()), + None => cache::default_approot(), + } + } +} + +/// A running embedded server: the child process handle plus a connected +/// rmcp client. Derefs to the client, so rmcp peer methods +/// (`list_all_tools`, `call_tool`, ...) are available directly. +pub struct RunningServer { + child: tokio::process::Child, + client: RunningService, + binary: PathBuf, +} + +impl RunningServer { + /// The connected rmcp client. + pub fn client(&self) -> &RunningService { + &self.client + } + + /// OS process id of the server, if it is still running. + pub fn pid(&self) -> Option { + self.child.id() + } + + /// Path of the stackql binary that was launched. + pub fn binary_path(&self) -> &std::path::Path { + &self.binary + } + + /// Close the MCP session and stop the server process. + pub async fn shutdown(self) -> Result<()> { + let RunningServer { + mut child, client, .. + } = self; + // Cancelling drops the transport; the server sees EOF on stdin and + // exits. The kill is a backstop for a wedged process. + let _ = client.cancel().await; + let _ = child.kill().await; + Ok(()) + } +} + +impl Deref for RunningServer { + type Target = RunningService; + + fn deref(&self) -> &Self::Target { + &self.client + } +} diff --git a/src/pins.rs b/src/pins.rs index cde8157..bbd3ece 100644 --- a/src/pins.rs +++ b/src/pins.rs @@ -4,13 +4,17 @@ //! packaging repo (stackql/stackql-mcpb-packaging) targets. Update this table //! when bumping STACKQL_VERSION. Once the packaging repo publishes a //! consolidated platforms.json release asset, prefer rendering from that. +//! +//! These pins back `BundleVersion::Pinned` and the offline fallback; the +//! default (`BundleVersion::Latest`) verifies against the release's own +//! .sha256 asset instead - see `release.rs`. use crate::error::{Error, Result}; use crate::platform::Platform; /// The stackql release this crate version pins (release.yaml in the /// packaging repo, leading v stripped). -pub const STACKQL_VERSION: &str = "0.10.500"; +pub const STACKQL_VERSION: &str = "0.10.601"; /// A pinned bundle: name and sha256 as published on the GitHub release. #[derive(Clone, Copy, Debug)] @@ -24,27 +28,28 @@ pub const PINS: &[Pin] = &[ Pin { platform_key: "linux-x64", bundle_name: "stackql-mcp-linux-x64.mcpb", - sha256: "6615737747156b1a8413a976afb23af2e7eec29ebc98a6f0a0f65d1b153c44be", + sha256: "4a1cad1345fba1aae1f31269fd96aebed7a7825b38f6509466c1c995ce114e52", }, Pin { platform_key: "linux-arm64", bundle_name: "stackql-mcp-linux-arm64.mcpb", - sha256: "594bedbabc3096dc3563c907724e845ce0b61a67de4b3fed4158b40c0363786c", + sha256: "518b2be638fa2b31438e39ed7adafc1621d7397f6700016e9f2d516e500d3cd1", }, Pin { platform_key: "windows-x64", bundle_name: "stackql-mcp-windows-x64.mcpb", - sha256: "d2ce895e88f9c6b557df07073158629808f56d75598f3a701164d65506b791b0", + sha256: "35ed2c66ff6aa5551b9fd7976cc6c49e370cb2b619eb2b5047c3bdbd33076eab", }, Pin { platform_key: "darwin-universal", bundle_name: "stackql-mcp-darwin-universal.mcpb", - sha256: "4eed70af5cfa67295ae0b42fa3a6dca71ac9acabd0d67914fd96ad1247a9b4cc", + sha256: "1cb60eba9dbc849ad9d505eb4992d0a348e4088ab51c8856b5206b7cc389995d", }, ]; /// Look up the pin for a platform. Every `Platform` variant has a pin; a miss /// here is a crate bug, so it surfaces as `UnsupportedPlatform`. +#[cfg_attr(not(feature = "sidecar"), allow(dead_code))] pub fn pin_for(platform: Platform) -> Result<&'static Pin> { PINS.iter() .find(|p| p.platform_key == platform.key()) @@ -54,15 +59,6 @@ pub fn pin_for(platform: Platform) -> Result<&'static Pin> { }) } -/// Download URL for a pinned bundle. Bundles are attached to the matching -/// stackql/stackql release. -pub fn bundle_url(pin: &Pin) -> String { - format!( - "https://github.com/stackql/stackql/releases/download/v{STACKQL_VERSION}/{}", - pin.bundle_name - ) -} - #[cfg(test)] mod tests { use super::*; @@ -96,13 +92,4 @@ mod tests { assert_eq!(pin.sha256, pin.sha256.to_lowercase()); } } - - #[test] - fn bundle_url_points_at_the_pinned_release() { - let pin = pin_for(Platform::LinuxX64).unwrap(); - assert_eq!( - bundle_url(pin), - "https://github.com/stackql/stackql/releases/download/v0.10.500/stackql-mcp-linux-x64.mcpb" - ); - } } diff --git a/src/release.rs b/src/release.rs new file mode 100644 index 0000000..69f6bd2 --- /dev/null +++ b/src/release.rs @@ -0,0 +1,234 @@ +//! Which stackql release to run in sidecar mode, and how its bundle is +//! resolved and verified. +//! +//! - [`BundleVersion::Latest`] (the default): resolve the newest +//! stackql/stackql release at start-up and verify the download against the +//! `.mcpb.sha256` asset published alongside it +//! - [`BundleVersion::Pinned`]: the release whose sha256 pins are baked into +//! this crate ([`crate::STACKQL_VERSION`]) - fully offline verification, +//! and the automatic fallback when the latest release cannot be resolved +//! - [`BundleVersion::Exact`]: a specific release, verified like `Latest` + +#[cfg(feature = "sidecar")] +use crate::error::Result; +#[cfg(feature = "sidecar")] +use crate::platform::Platform; + +/// Env var selecting the release: `latest`, `pinned`, or a version such as +/// `0.10.601` (leading `v` accepted). Overrides [`crate::Builder::version`]. +pub const ENV_VERSION: &str = "STACKQL_MCP_VERSION"; + +#[cfg(feature = "sidecar")] +const RELEASES: &str = "https://github.com/stackql/stackql/releases"; + +/// Which stackql release the sidecar should run. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[non_exhaustive] +pub enum BundleVersion { + /// The newest published release, resolved at start-up (default). Falls + /// back to a cached release, then to [`BundleVersion::Pinned`], when the + /// release cannot be resolved (offline). + #[default] + Latest, + /// The release pinned in this crate ([`crate::STACKQL_VERSION`]), + /// verified against the baked sha256 pins - no resolution step. + Pinned, + /// A specific release, e.g. `0.10.601`. + Exact(String), +} + +impl BundleVersion { + /// Parse the env-var / string form: `latest`, `pinned`, or a version. + pub fn parse(s: &str) -> Self { + let s = s.trim(); + match s.to_ascii_lowercase().as_str() { + "" | "latest" => BundleVersion::Latest, + "pinned" => BundleVersion::Pinned, + _ => BundleVersion::Exact(s.trim_start_matches('v').to_string()), + } + } + + /// The effective selection: `STACKQL_MCP_VERSION` if set, else `self`. + pub fn resolved_from_env(&self) -> Self { + match std::env::var(ENV_VERSION) { + Ok(v) if !v.trim().is_empty() => BundleVersion::parse(&v), + _ => self.clone(), + } + } +} + +#[cfg(feature = "sidecar")] +/// A concrete bundle to download: release version, asset name, expected +/// sha256 and URL. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ResolvedBundle { + pub version: String, + pub bundle_name: &'static str, + pub sha256: String, +} + +#[cfg(feature = "sidecar")] +impl ResolvedBundle { + pub fn url(&self) -> String { + format!("{RELEASES}/download/v{}/{}", self.version, self.bundle_name) + } +} + +#[cfg(feature = "sidecar")] +/// The pinned bundle for `platform`, without any network access. +pub fn pinned(platform: Platform) -> Result { + let pin = crate::pins::pin_for(platform)?; + Ok(ResolvedBundle { + version: crate::pins::STACKQL_VERSION.to_string(), + bundle_name: pin.bundle_name, + sha256: pin.sha256.to_string(), + }) +} + +/// Resolve `version` for `platform`. `Latest` and `Exact` need the network +/// (two small requests: the release tag, then the `.sha256` asset). +#[cfg(feature = "sidecar")] +pub fn resolve(version: &BundleVersion, platform: Platform) -> Result { + let bundle_name = crate::pins::pin_for(platform)?.bundle_name; + let version = match version { + BundleVersion::Pinned => return pinned(platform), + BundleVersion::Latest => latest_version()?, + BundleVersion::Exact(v) => v.clone(), + }; + let sha256 = published_sha256(&version, bundle_name)?; + Ok(ResolvedBundle { + version, + bundle_name, + sha256, + }) +} + +/// The newest release version: `releases/latest` redirects to +/// `releases/tag/v`; read the redirect instead of following it. +#[cfg(feature = "sidecar")] +fn latest_version() -> Result { + use crate::error::Error; + let url = format!("{RELEASES}/latest"); + let http = |message: String| Error::Http { + url: url.clone(), + message, + }; + let response = ureq::get(&url) + .config() + .max_redirects(0) + .max_redirects_will_error(false) + .build() + .call() + .map_err(|e| http(e.to_string()))?; + let location = response + .headers() + .get("location") + .and_then(|v| v.to_str().ok()) + .ok_or_else(|| { + http(format!( + "expected a redirect, got HTTP {}", + response.status() + )) + })?; + parse_tag_location(location) + .map(str::to_string) + .ok_or_else(|| http(format!("unexpected redirect target {location}"))) +} + +/// Fetch and parse the `.mcpb.sha256` asset for one release/bundle. +#[cfg(feature = "sidecar")] +fn published_sha256(version: &str, bundle_name: &str) -> Result { + use crate::error::Error; + let url = format!("{RELEASES}/download/v{version}/{bundle_name}.sha256"); + let http = |message: String| Error::Http { + url: url.clone(), + message, + }; + let body = ureq::get(&url) + .call() + .map_err(|e| http(e.to_string()))? + .body_mut() + .read_to_string() + .map_err(|e| http(e.to_string()))?; + parse_sha256_asset(&body) + .map(str::to_string) + .ok_or_else(|| http(format!("malformed sha256 asset: {body:?}"))) +} + +#[cfg(feature = "sidecar")] +/// `.../releases/tag/v0.10.601` -> `0.10.601`. +fn parse_tag_location(location: &str) -> Option<&str> { + let (_, tag) = location.trim_end_matches('/').rsplit_once("/tag/")?; + let version = tag.trim_start_matches('v'); + (!version.is_empty() + && version + .chars() + .all(|c| c.is_ascii_alphanumeric() || c == '.' || c == '-')) + .then_some(version) +} + +#[cfg(feature = "sidecar")] +/// First token of a `sha256sum`-style line, validated as 64 hex chars. +fn parse_sha256_asset(body: &str) -> Option<&str> { + let token = body.split_whitespace().next()?; + (token.len() == 64 && token.chars().all(|c| c.is_ascii_hexdigit())).then_some(token) +} + +#[cfg(test)] +mod tests { + #[allow(unused_imports)] + use super::*; + + #[test] + fn parses_version_selection() { + assert_eq!(BundleVersion::parse("latest"), BundleVersion::Latest); + assert_eq!(BundleVersion::parse(""), BundleVersion::Latest); + assert_eq!(BundleVersion::parse("Pinned"), BundleVersion::Pinned); + assert_eq!( + BundleVersion::parse("v0.10.601"), + BundleVersion::Exact("0.10.601".into()) + ); + } + + #[cfg(feature = "sidecar")] + #[test] + fn parses_release_tag_redirect() { + assert_eq!( + parse_tag_location("https://github.com/stackql/stackql/releases/tag/v0.10.601"), + Some("0.10.601") + ); + assert_eq!( + parse_tag_location("https://github.com/stackql/stackql/releases"), + None + ); + assert_eq!( + parse_tag_location("https://github.com/x/releases/tag/"), + None + ); + } + + #[cfg(feature = "sidecar")] + #[test] + fn parses_sha256_asset() { + let hex = "4a1cad1345fba1aae1f31269fd96aebed7a7825b38f6509466c1c995ce114e52"; + assert_eq!( + parse_sha256_asset(&format!("{hex} stackql-mcp-linux-x64.mcpb\n")), + Some(hex) + ); + assert_eq!(parse_sha256_asset("not a hash"), None); + assert_eq!(parse_sha256_asset(""), None); + } + + #[cfg(feature = "sidecar")] + #[test] + fn pinned_bundle_url_points_at_the_pinned_release() { + let b = pinned(Platform::LinuxX64).unwrap(); + assert_eq!( + b.url(), + format!( + "https://github.com/stackql/stackql/releases/download/v{}/stackql-mcp-linux-x64.mcpb", + crate::pins::STACKQL_VERSION + ) + ); + } +} diff --git a/stackql-agent/Cargo.toml b/stackql-agent/Cargo.toml new file mode 100644 index 0000000..88cbd5f --- /dev/null +++ b/stackql-agent/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "stackql-agent" +version = "0.1.0" +edition = "2021" +rust-version = "1.88" +publish = false +description = "Rust-native agent over your cloud estate: an embedded StackQL MCP server wired into a rig agent. One backend, three personas (platform engineering, SRE, audit)." +license = "MIT" + +[features] +default = [] +# Single-binary build: embed the .mcpb at compile time via +# STACKQL_MCP_BUNDLE_FILE (see stackql_mcp::fetch_bundle). +vendored = ["stackql-mcp/vendored"] + +[dependencies] +anyhow = "1" +clap = { version = "4", features = ["derive"] } +futures = "0.3" +# rig-core's lib is `rig_core`; rename to `rig` to match rig's own docs/examples. +rig = { package = "rig-core", version = "0.38", features = ["rmcp"] } +rmcp = { version = "1.7", default-features = false, features = ["client"] } +serde_json = "1" +stackql-mcp = { path = ".." } +tokio = { version = "1", features = ["macros", "rt-multi-thread", "io-std", "io-util"] } diff --git a/stackql-agent/README.md b/stackql-agent/README.md new file mode 100644 index 0000000..5946b65 --- /dev/null +++ b/stackql-agent/README.md @@ -0,0 +1,80 @@ +# stackql-agent + +A Rust-native agent over your cloud and SaaS estate. It embeds the StackQL MCP server (via [stackql-mcp](https://crates.io/crates/stackql-mcp)) and wires it into a [rig](https://docs.rig.rs) agent. The agent's tools are the StackQL MCP tools; the persona is just a system prompt. + +One binary becomes a platform-engineering, SRE, or audit agent by swapping `--persona`. The backend, the tools, and the read-only safety contract are identical across all three. This is the demo companion to [auditron](../auditron) (the deterministic, evidence-pack sibling) and a worked example of embedding and vendoring an MCP server into a Rust agent. + +## Quickstart + +Needs `ANTHROPIC_API_KEY`. The default GitHub provider runs in `null_auth` mode, so it queries public data with zero cloud credentials. + +```sh +export ANTHROPIC_API_KEY=sk-ant-... + +# Interactive REPL, platform-engineering persona +cargo run -p stackql-agent -- --persona platform + +# One-shot +cargo run -p stackql-agent -- --persona sre \ + -p "Show the most recent failed workflow runs for stackql/stackql" + +# Pre-flight: start the server, list the MCP tools, exit. No model calls. +cargo run -p stackql-agent -- --check +``` + +## Personas + +The only difference between them is the system prompt in [src/persona.rs](src/persona.rs). + +| Persona | Focus | +|---|---| +| `platform` | Platform engineering: repo and CI configuration, branch protection, golden-path conformance, drift | +| `sre` | Site reliability: workflow runs, failures and blast radius, what changed, triage | +| `audit` | Compliance: IGA and entitlements, security posture (CSPM), and cost (FinOps), with verifiable findings | + +## Options + +```text +--persona Which system prompt to wear (default: platform) +--provider Provider to pull and query (default: github) +--auth Provider auth document (default: github null_auth) +--prompt, -p One-shot: answer and exit +--max-turns Max agent tool-call rounds per question (default: 20) +--check Pre-flight: start server, list tools, exit (no model calls) +``` + +To point at a credentialed provider, pass its auth document, for example: + +```sh +cargo run -p stackql-agent -- --persona audit --provider aws \ + --auth '{"aws":{"type":"aws_signing_v4"}}' \ + -p "Which security groups allow 0.0.0.0/0 on port 22?" +``` + +## Single-binary build + +Embed the StackQL server in the binary so it runs with no runtime downloads: + +```sh +BUNDLE=$(cargo run -q -p stackql-mcp --example fetch_bundle) +STACKQL_MCP_BUNDLE_FILE=$BUNDLE cargo build -p stackql-agent --features vendored --release +``` + +The result is a self-contained binary that carries the StackQL engine inside it. + +## How the wiring works + +```rust +let server = StackqlMcp::builder().mode(Mode::ReadOnly).auth(auth).start().await?; +let tools = server.list_all_tools().await?; // the StackQL MCP tools +let sink = server.peer().to_owned(); // the rmcp client peer + +let agent = anthropic::Client::from_env()? + .agent(anthropic::completion::CLAUDE_OPUS_4_8) + .preamble(&persona.preamble) + .rmcp_tools(tools, sink) // rig consumes rmcp directly + .default_max_turns(20) + .build(); +``` + +`stackql-mcp` hands back a connected rmcp client; rig 0.38's `rmcp_tools()` consumes it as-is (both are on rmcp 1.7). See [src/main.rs](src/main.rs) for the streaming REPL that prints each tool call as the agent makes it. diff --git a/stackql-agent/src/main.rs b/stackql-agent/src/main.rs new file mode 100644 index 0000000..bb19845 --- /dev/null +++ b/stackql-agent/src/main.rs @@ -0,0 +1,231 @@ +//! stackql-agent - a Rust-native agent over your cloud estate. +//! +//! An embedded StackQL MCP server (vendored straight into this binary, or +//! downloaded as a verified sidecar) wired into a `rig` agent. The agent's +//! tools ARE the StackQL MCP tools; the persona is just a system prompt. Swap +//! the persona and the same backend becomes a platform-engineering, SRE, or +//! audit agent. +//! +//! Needs `ANTHROPIC_API_KEY`. Diagnostics and tool-call traces go to stderr; +//! the agent's answer goes to stdout. + +mod persona; + +use std::io::Write as _; + +use anyhow::{Context, Result}; +use clap::Parser; +use futures::StreamExt; +use rig::agent::{Agent, MultiTurnStreamItem}; +use rig::client::{CompletionClient, ProviderClient}; +use rig::providers::anthropic; +use rig::providers::anthropic::completion::CompletionModel; +use rig::streaming::{StreamedAssistantContent, StreamingPrompt}; +use rmcp::model::CallToolRequestParams; +use stackql_mcp::{Mode, StackqlMcp}; +use tokio::io::AsyncBufReadExt as _; + +#[derive(Parser)] +#[command( + name = "stackql-agent", + version, + about = "Rust-native agent over your cloud estate, on an embedded StackQL MCP server" +)] +struct Cli { + /// Which persona to wear: platform | sre | audit. Only the system prompt + /// changes between them. + #[arg(long, default_value = "platform")] + persona: String, + /// Provider to pull and query (github runs with zero credentials). + #[arg(long, default_value = "github")] + provider: String, + /// Provider auth document. Defaults to github null_auth (no credentials). + /// Pass a JSON document to use a credentialed provider, e.g. + /// --auth '{"aws":{"type":"aws_signing_v4"}}'. + #[arg(long)] + auth: Option, + /// One-shot: answer this prompt and exit, instead of the REPL. + #[arg(long, short)] + prompt: Option, + /// Max agent turns (tool-call rounds) per question. + #[arg(long, default_value_t = 20)] + max_turns: usize, + /// Pre-flight: start the server, pull the provider, list the MCP tools the + /// agent will get, and exit. Useful to verify a machine before a demo. No + /// model calls, so no ANTHROPIC_API_KEY required. + #[arg(long)] + check: bool, +} + +#[tokio::main] +async fn main() -> Result<()> { + let cli = Cli::parse(); + + let persona = persona::resolve(&cli.persona).with_context(|| { + format!( + "unknown persona {:?} (choose one of: {})", + cli.persona, + persona::KEYS.join(", ") + ) + })?; + + let auth: serde_json::Value = match &cli.auth { + Some(raw) => serde_json::from_str(raw).context("parsing --auth as JSON")?, + None => serde_json::json!({ &cli.provider: { "type": "null_auth" } }), + }; + + // Start the embedded StackQL MCP server. Vendored when built with the + // feature, otherwise downloaded and sha256-verified on first run. + eprintln!("stackql-agent: starting embedded StackQL MCP server..."); + let builder = StackqlMcp::builder().mode(Mode::ReadOnly).auth(auth); + #[cfg(feature = "vendored")] + let builder = builder.bundle_bytes(stackql_mcp::include_bundle!()); + let server = builder + .start() + .await + .context("starting the embedded stackql mcp server")?; + + // Pull the provider once so the first user query is instant. + pull_provider(&server, &cli.provider).await?; + + // Hand the embedded server's MCP tools to a rig agent. This is the whole + // integration: list_all_tools() + peer() come straight off our connected + // rmcp client, and rig 0.38's rmcp_tools() consumes them directly. + let tools = server.list_all_tools().await.context("listing MCP tools")?; + eprintln!( + "stackql-agent: {} | provider '{}' | {} tools | read_only", + persona.title, + cli.provider, + tools.len() + ); + // Pre-flight: prove the embedded server + MCP wiring works on this machine + // without making any model calls. + if cli.check { + println!("MCP tools available to the agent:"); + for tool in &tools { + println!(" - {}", tool.name); + } + match anthropic::Client::from_env() { + Ok(client) => { + let _agent = client + .agent(anthropic::completion::CLAUDE_OPUS_4_8) + .preamble(&persona.preamble) + .rmcp_tools(tools, server.peer().to_owned()) + .default_max_turns(cli.max_turns) + .build(); + eprintln!("stackql-agent: check OK - agent built (ANTHROPIC_API_KEY present)"); + } + Err(_) => { + eprintln!( + "stackql-agent: check OK - server + {} MCP tools ready (set ANTHROPIC_API_KEY to run the agent)", + tools.len() + ); + } + } + server.shutdown().await.ok(); + return Ok(()); + } + + let sink = server.peer().to_owned(); + + let agent = anthropic::Client::from_env() + .context("ANTHROPIC_API_KEY not set")? + .agent(anthropic::completion::CLAUDE_OPUS_4_8) + .preamble(&persona.preamble) + .rmcp_tools(tools, sink) + .default_max_turns(cli.max_turns) + .build(); + + if let Some(prompt) = cli.prompt { + answer(&agent, &prompt, cli.max_turns).await?; + } else { + repl(&agent, &persona, cli.max_turns).await?; + } + + server.shutdown().await.ok(); + Ok(()) +} + +/// Pull a provider into the server's local cache via the MCP tool. Read-only +/// safe (local cache only) and idempotent. +async fn pull_provider(server: &stackql_mcp::RunningServer, provider: &str) -> Result<()> { + let mut params = CallToolRequestParams::new("pull_provider"); + params.arguments = serde_json::json!({ "provider": provider }) + .as_object() + .cloned(); + server + .call_tool(params) + .await + .with_context(|| format!("pulling provider {provider}"))?; + Ok(()) +} + +async fn repl( + agent: &Agent, + persona: &persona::Persona, + max_turns: usize, +) -> Result<()> { + println!("\nstackql-agent - {}", persona.title); + println!("Ask about your estate in plain English. Try:"); + for example in persona.examples { + println!(" - {example}"); + } + println!("Type 'quit' or Ctrl-D to exit.\n"); + + let mut reader = tokio::io::BufReader::new(tokio::io::stdin()).lines(); + loop { + print!("you> "); + std::io::stdout().flush().ok(); + let Some(line) = reader.next_line().await? else { + break; + }; + let line = line.trim(); + if line.is_empty() { + continue; + } + if matches!(line, "quit" | "exit" | ":q") { + break; + } + if let Err(e) = answer(agent, line, max_turns).await { + eprintln!("\nstackql-agent: {e:#}"); + } + } + Ok(()) +} + +/// Stream one answer. Text deltas go to stdout as they arrive; each tool the +/// agent invokes is announced on stderr so the audience sees it querying the +/// live estate. +async fn answer(agent: &Agent, prompt: &str, max_turns: usize) -> Result<()> { + let mut stream = agent.stream_prompt(prompt).multi_turn(max_turns).await; + while let Some(item) = stream.next().await { + match item? { + MultiTurnStreamItem::StreamAssistantItem(StreamedAssistantContent::Text(text)) => { + print!("{}", text.text); + std::io::stdout().flush().ok(); + } + MultiTurnStreamItem::StreamAssistantItem(StreamedAssistantContent::ToolCall { + tool_call, + .. + }) => { + eprintln!( + "\n \u{2192} {}({})", + tool_call.function.name, + truncate(&tool_call.function.arguments.to_string(), 200) + ); + } + _ => {} + } + } + println!(); + Ok(()) +} + +fn truncate(s: &str, max: usize) -> String { + if s.chars().count() <= max { + return s.to_string(); + } + let mut out: String = s.chars().take(max).collect(); + out.push_str(" ..."); + out +} diff --git a/stackql-agent/src/persona.rs b/stackql-agent/src/persona.rs new file mode 100644 index 0000000..793efe5 --- /dev/null +++ b/stackql-agent/src/persona.rs @@ -0,0 +1,117 @@ +//! The three agent personas. The only thing that changes between a platform +//! engineering agent and an SRE agent is this system prompt - the embedded +//! StackQL MCP backend and the wiring are identical. That is the whole point. + +/// Shared StackQL grounding spliced into every persona. Encodes the provider +/// quirks that otherwise trip an agent up on live data, so queries land on the +/// first try during a demo. +const STACKQL_GUIDANCE: &str = r#" +You query real cloud and SaaS estates through StackQL, which exposes providers +as SQL. You have MCP tools for this: +- run_select_query(sql): run a read-only SELECT and get rows back +- list_providers / list_services / list_resources / describe_resource: discover + the schema before querying something unfamiliar + +How to write StackQL well: +- Tables are provider.service.resource, e.g. github.repos.repos, + github.repos.branches, github.actions.workflow_runs, github.orgs.members. +- Many resources require key columns in the WHERE clause that map to API path + parameters. Examples: github.repos.repos needs `org = '...'`; + github.repos.branches and github.actions.workflow_runs need + `owner = '...' AND repo = '...'`; github.orgs.members needs `org = '...'`. + If a query errors, describe_resource to find the required columns. +- All result values come back as strings. Booleans render as 'true'/'false' + but their storage varies per resource: compare with `= 0` / `= 1`, never the + strings 'true'/'false' (e.g. `archived = 0`, `protected = 0`). +- Avoid OR and NOT in WHERE clauses; prefer coalesce(col,'') and `= 0`. +- SQLite scalar/date functions work: coalesce(), datetime('now','-12 months'). +- Everything is read-only. Never attempt mutations. + +Be concrete: when you make a claim, show the SQL you ran and the key rows +behind it. Lead with the answer, then the evidence. If a result set is empty, +say so plainly rather than inventing findings - and remember an empty set can +also mean the query hit a provider error, so sanity-check with a broader query +if a "clean" result looks surprising. +"#; + +pub struct Persona { + pub title: &'static str, + pub preamble: String, + pub examples: &'static [&'static str], +} + +pub fn resolve(key: &str) -> Option { + match key { + "platform" => Some(platform()), + "sre" => Some(sre()), + "audit" => Some(audit()), + _ => None, + } +} + +pub const KEYS: &[&str] = &["platform", "sre", "audit"]; + +fn platform() -> Persona { + Persona { + title: "Agentic Platform Engineering", + preamble: format!( + "You are a platform engineering agent. You help platform teams keep \ + their estate consistent and well-governed: repository configuration, \ + branch protection and rulesets, CI/CD setup, required reviews, and \ + standardisation across many repositories. You answer questions about \ + how the estate is configured and where it drifts from good practice, \ + and you propose concrete remediation (the exact setting, the gh/CLI \ + command, or the IaC change) - but you never make changes yourself.\n{STACKQL_GUIDANCE}" + ), + examples: &[ + "Which repos in the stackql org are missing branch protection on their default branch?", + "List the stackql org's repos that have no description or no license, grouped by what's missing.", + "Across the stackql org, which default branches are not called 'main'?", + "Which repos were last updated over a year ago and should probably be archived?", + ], + } +} + +fn sre() -> Persona { + Persona { + title: "Agentic SRE", + preamble: format!( + "You are a site reliability engineering agent. You help SRE and on-call \ + teams understand operational health from live signals: CI/CD workflow \ + runs, failures and their blast radius, what changed recently, and where \ + reliability is trending the wrong way. You triage: surface the failing \ + thing, say which branch and workflow it is, and suggest the next \ + diagnostic step. You investigate and report; you do not take \ + remediating action yourself.\n{STACKQL_GUIDANCE}" + ), + examples: &[ + "Show the most recent failed GitHub Actions workflow runs for stackql/stackql.", + "For stackql/stackql, what's the success vs failure breakdown of workflow runs on the main branch?", + "Which workflows in stackql/stackql have failed most recently, and on which branches?", + "Are there any in-progress or queued workflow runs for stackql/stackql right now?", + ], + } +} + +fn audit() -> Persona { + Persona { + title: "Agentic Audit (IGA, entitlements, CSPM, FinOps)", + preamble: format!( + "You are a compliance and audit agent covering identity governance (IGA), \ + entitlements, cloud security posture (CSPM), and cost (FinOps). You run \ + point-in-time checks over the live estate and report findings an auditor \ + could act on: who has access to what, where security posture is weak, and \ + where configuration violates policy. State each finding with the SQL and \ + the rows behind it so it is verifiable and re-runnable. You assess and \ + evidence; you never change configuration. When a check needs credentials \ + the current session does not have (private org membership, AWS posture, \ + billing/cost), say so and describe what the credentialed query would be.\n{STACKQL_GUIDANCE}" + ), + examples: &[ + "Who are the public members of the stackql org, and are any of them org owners?", + "Audit branch protection across the stackql org: which default branches are unprotected?", + "Which public repos in the stackql org have no license declared (a compliance risk)?", + "Give me a point-in-time inventory of the stackql org's repos with visibility and last-updated date.", + ], + } +}