From e710c5a2c206fe406f4ecf18ee818db7d1d71fbd Mon Sep 17 00:00:00 2001 From: Igor Novgorodov Date: Mon, 3 Aug 2026 11:11:14 +0000 Subject: [PATCH 1/4] Add MCP, ACME additional names --- Cargo.lock | 573 +++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 2 + src/cli.rs | 26 +++ src/core.rs | 17 ++ src/lib.rs | 2 + src/main.rs | 2 + src/mcp.rs | 45 ++++ src/tls/mod.rs | 4 +- 8 files changed, 656 insertions(+), 15 deletions(-) create mode 100644 src/mcp.rs diff --git a/Cargo.lock b/Cargo.lock index f06ed13..aac62ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -142,6 +142,12 @@ dependencies = [ "object", ] +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + [[package]] name = "arc-swap" version = "1.9.2" @@ -169,13 +175,22 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term 0.7.0", +] + [[package]] name = "ascii-canvas" version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891" dependencies = [ - "term", + "term 1.2.1", ] [[package]] @@ -549,6 +564,12 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + [[package]] name = "binrw" version = "0.15.2" @@ -572,6 +593,15 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec 0.6.3", +] + [[package]] name = "bit-set" version = "0.8.0" @@ -581,6 +611,12 @@ dependencies = [ "bit-vec 0.8.0", ] +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bit-vec" version = "0.8.0" @@ -862,6 +898,28 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "candid_parser" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "470a4cf0c110e217e54ab3d21c4a9c290736c4a70ab6263d65155522d4b93fb6" +dependencies = [ + "anyhow", + "candid", + "codespan-reporting", + "convert_case", + "handlebars", + "hex", + "lalrpop 0.20.2", + "lalrpop-util 0.20.2", + "logos", + "num-bigint", + "pretty", + "serde", + "thiserror 1.0.69", + "toml", +] + [[package]] name = "cast" version = "0.3.0" @@ -1028,6 +1086,16 @@ dependencies = [ "cc", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width 0.1.14", +] + [[package]] name = "colorchoice" version = "1.0.5" @@ -1125,6 +1193,15 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -1543,6 +1620,37 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.119", +] + [[package]] name = "digest" version = "0.9.0" @@ -1584,6 +1692,16 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + [[package]] name = "dirs-sys" version = "0.5.0" @@ -1592,10 +1710,21 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.5.2", "windows-sys 0.61.2", ] +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users 0.4.6", + "winapi", +] + [[package]] name = "dispatch2" version = "0.3.1" @@ -1804,7 +1933,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8" dependencies = [ - "bit-set", + "bit-set 0.8.0", "regex-automata", "regex-syntax", ] @@ -1847,6 +1976,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "fixedbitset" version = "0.5.7" @@ -2202,6 +2337,22 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "handlebars" +version = "6.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4633d16a2350341713c379d6d06a4b9e1845329386026a49ce4fd09c2f3b16f6" +dependencies = [ + "derive_builder", + "log", + "num-order", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror 2.0.19", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -2965,6 +3116,7 @@ dependencies = [ "ic-http-certification 3.2.0", "ic-http-gateway-protocol", "ic-transport-types 0.49.2", + "imcp2", "isbot", "itertools 0.15.0", "lazy_static", @@ -3241,6 +3393,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2732829022822ec69021c336d23b32a053e07abdd08553c71407d6e2d1675d" dependencies = [ + "arbitrary", "crc32fast", "data-encoding", "rangemap", @@ -3358,6 +3511,36 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "imcp2" +version = "0.1.0" +source = "git+https://github.com/dfinity/imcp2?rev=7f0293a991d8af5a8d18ae765c2ac476ee2783ba#7f0293a991d8af5a8d18ae765c2ac476ee2783ba" +dependencies = [ + "anyhow", + "axum", + "base64 0.22.1", + "candid", + "candid_parser", + "crc32fast", + "getrandom 0.3.4", + "hex", + "ic-agent", + "regex", + "reqwest 0.13.4", + "rmcp", + "serde", + "serde_json", + "sha2 0.11.0", + "tokio", + "tokio-util", + "tower-http 0.7.0", + "tracing", + "tracing-subscriber", + "url", + "urlencoding", + "uuid", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -3503,6 +3686,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -3657,27 +3849,58 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "lalrpop" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" +dependencies = [ + "ascii-canvas 3.0.0", + "bit-set 0.5.3", + "ena", + "itertools 0.11.0", + "lalrpop-util 0.20.2", + "petgraph 0.6.5", + "pico-args", + "regex", + "regex-syntax", + "string_cache", + "term 0.7.0", + "tiny-keccak", + "unicode-xid", + "walkdir", +] + [[package]] name = "lalrpop" version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba4ebbd48ce411c1d10fb35185f5a51a7bfa3d8b24b4e330d30c9e3a34129501" dependencies = [ - "ascii-canvas", - "bit-set", + "ascii-canvas 4.0.0", + "bit-set 0.8.0", "ena", "itertools 0.14.0", - "lalrpop-util", - "petgraph", + "lalrpop-util 0.22.2", + "petgraph 0.7.1", "regex", "regex-syntax", "sha3", "string_cache", - "term", + "term 1.2.1", "unicode-xid", "walkdir", ] +[[package]] +name = "lalrpop-util" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +dependencies = [ + "regex-automata", +] + [[package]] name = "lalrpop-util" version = "0.22.2" @@ -3768,6 +3991,39 @@ version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +[[package]] +name = "logos" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7251356ef8cb7aec833ddf598c6cb24d17b689d20b993f9d11a3d764e34e6458" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f80069600c0d66734f5ff52cc42f2dabd6b29d205f333d61fd7832e9e9963f" +dependencies = [ + "beef", + "fnv", + "lazy_static", + "proc-macro2", + "quote", + "regex-syntax", + "syn 2.0.119", +] + +[[package]] +name = "logos-derive" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24fb722b06a9dc12adb0963ed585f19fc61dc5413e6a9be9422ef92c091e731d" +dependencies = [ + "logos-codegen", +] + [[package]] name = "lru-cache" version = "0.1.2" @@ -4134,6 +4390,21 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-modular" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc41a1374056e9672221567958a66c16be12d0e2c1b408761e14d901c237d5e0" + +[[package]] +name = "num-order" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6" +dependencies = [ + "num-modular", +] + [[package]] name = "num-rational" version = "0.4.2" @@ -4155,6 +4426,25 @@ dependencies = [ "libm", ] +[[package]] +name = "oauth2" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" +dependencies = [ + "base64 0.21.7", + "chrono", + "getrandom 0.2.17", + "http 1.5.0", + "rand 0.8.7", + "serde", + "serde_json", + "serde_path_to_error", + "sha2 0.10.9", + "thiserror 1.0.69", + "url", +] + [[package]] name = "objc2" version = "0.6.4" @@ -4326,6 +4616,12 @@ 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 = "pem" version = "3.0.6" @@ -4351,13 +4647,65 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pest" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df728be843c7070fab6ab7c328c4e9e9d78e23bf749c0669c86ee7ebfa050a2" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e2dd6fc3b26b3462ee188aac870f5a41d398f1cd5e2408d16531bd71c9591fd" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a7a9205cfb6f596a9e8b689c0a15f9ceb7a1aafae7aaf788150ac65b29975b6" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "pest_meta" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85abd351c0de1e8384fc791a0737111a350394937e92b956b743dac12429f57c" +dependencies = [ + "pest", +] + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset 0.4.2", + "indexmap 2.14.0", +] + [[package]] name = "petgraph" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ - "fixedbitset", + "fixedbitset 0.5.7", "indexmap 2.14.0", ] @@ -4370,6 +4718,12 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project" version = "1.1.13" @@ -4610,7 +4964,7 @@ checksum = "0d22152487193190344590e4f30e219cf3fe140d9e7a3fdb683d82aa2c5f4156" dependencies = [ "arrayvec 0.5.2", "typed-arena", - "unicode-width", + "unicode-width 0.2.2", ] [[package]] @@ -4982,6 +5336,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + [[package]] name = "redox_users" version = "0.5.2" @@ -5189,6 +5554,53 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rmcp" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1f571c72940a19d9532fe52dbea8bc9912bf1d766c2970bb824056b86f3f59" +dependencies = [ + "async-trait", + "base64 0.22.1", + "bytes", + "chrono", + "futures", + "http 1.5.0", + "http-body", + "http-body-util", + "oauth2", + "pastey", + "pin-project-lite", + "rand 0.10.2", + "reqwest 0.13.4", + "rmcp-macros", + "schemars 1.2.2", + "serde", + "serde_json", + "sse-stream", + "thiserror 2.0.19", + "tokio", + "tokio-stream", + "tokio-util", + "tower-service", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "rmcp-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aad0035b69380782d78ea95b508327e6deaa2235909053e596eea8f27b5e1d5" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.119", +] + [[package]] name = "rsa" version = "0.9.10" @@ -5397,7 +5809,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" dependencies = [ "dyn-clone", - "schemars_derive", + "schemars_derive 0.8.22", "serde", "serde_json", ] @@ -5420,8 +5832,10 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" dependencies = [ + "chrono", "dyn-clone", "ref-cast", + "schemars_derive 1.2.2", "serde", "serde_json", ] @@ -5434,10 +5848,22 @@ checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" dependencies = [ "proc-macro2", "quote", - "serde_derive_internals", + "serde_derive_internals 0.29.1", "syn 2.0.119", ] +[[package]] +name = "schemars_derive" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98c67716b46af2f0b8cf752abc930f6f9aecfbf671ecfb531db8a31dbe4e2ba" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals 0.30.0", + "syn 3.0.3", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -5552,12 +5978,24 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "serde_derive_internals" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f852137cce035d6a4df67ccce505ff6b3e9fd3a10e3e52b24dc71e650bb1a9bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + [[package]] name = "serde_json" version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ + "indexmap 2.14.0", "itoa", "memchr", "serde", @@ -5587,6 +6025,15 @@ dependencies = [ "syn 3.0.3", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_tokenstream" version = "0.1.7" @@ -5901,6 +6348,19 @@ dependencies = [ "der", ] +[[package]] +name = "sse-stream" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c123f296ade4ec4b8b0f6162116e6629f5146922ca5ab40ca9d3c2e73ab4761e" +dependencies = [ + "bytes", + "futures-util", + "http-body", + "http-body-util", + "pin-project-lite", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -6143,6 +6603,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "term" version = "1.2.1" @@ -6152,6 +6623,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "termtree" version = "0.5.1" @@ -6268,6 +6748,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinystr" version = "0.8.3" @@ -6402,6 +6891,40 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap 2.14.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tonic" version = "0.14.6" @@ -6664,6 +7187,12 @@ dependencies = [ "serde", ] +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "unicase" version = "2.9.0" @@ -6688,6 +7217,12 @@ version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" +[[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.2" @@ -6738,6 +7273,7 @@ dependencies = [ "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -6803,7 +7339,7 @@ dependencies = [ "getrandom 0.3.4", "getrandom 0.4.3", "jsonschema", - "lalrpop", + "lalrpop 0.22.2", "lz4_flex", "nom-language", "ordered-float", @@ -6969,7 +7505,7 @@ dependencies = [ "bumpalo", "leb128fmt", "memchr", - "unicode-width", + "unicode-width 0.2.2", "wasm-encoder", ] @@ -7218,6 +7754,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.57.1" diff --git a/Cargo.toml b/Cargo.toml index bb65009..8c0cf22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ bench = [ "dep:ic-transport-types", "dep:rand_regex", ] +mcp = ["dep:imcp2"] tokio_console = ["dep:console-subscriber"] debug = [] @@ -50,6 +51,7 @@ ic-http-certification = { version = "3.1.0", optional = true } ic-http-gateway-protocol = { package = "ic-http-gateway-protocol", git = "https://github.com/dfinity/ic-http-gateway-protocol", tag = "v0.6.0" } # TODO: drop & re-export from ic-bn-lib ic-transport-types = { version = "0.49", optional = true } +imcp2 = { git = "https://github.com/dfinity/imcp2", rev = "7f0293a991d8af5a8d18ae765c2ac476ee2783ba", optional = true } isbot = "0.1" itertools = "0.15.0" lazy_static = "1.5.0" diff --git a/src/cli.rs b/src/cli.rs index 5e85648..312cf55 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -117,6 +117,10 @@ pub struct Cli { #[command(flatten, next_help_heading = "SMTP Server")] pub smtp_server: SmtpServerCli, + #[cfg(feature = "mcp")] + #[command(flatten, next_help_heading = "MCP")] + pub mcp: McpCli, + #[cfg(all(target_os = "linux", feature = "sev-snp"))] #[command(flatten, next_help_heading = "SEV-SNP")] pub sev_snp: ic_bn_lib::sev_snp::SevSnpCli, @@ -394,6 +398,11 @@ pub struct Acme { #[clap(env, long)] pub acme_cache_path: Option, + /// Domain names to request during certificate issuance, in addition to + /// those listed in the base domains. + #[clap(env, long, value_delimiter = ',')] + pub acme_additional_names: Vec, + /// ACME account credentials in JSON format. /// If not provided - new account will be created. #[clap(env, long)] @@ -641,6 +650,23 @@ pub struct Prerender { pub prerender_timeout: Duration, } +#[cfg(feature = "mcp")] +#[derive(Args)] +pub struct McpCli { + /// Which II instance to use to authenticate with MCP ("beta" or "prod") + /// Enables MCP. + #[clap(env, long)] + pub mcp_ii_instance: Option, + + /// MCP Public URL, required if MCP is enabled. + #[clap(env, long)] + pub mcp_public_url: Option, + + /// MCP path + #[clap(env, long, default_value = "/mcp")] + pub mcp_path: String, +} + #[cfg(test)] mod test { use super::*; diff --git a/src/core.rs b/src/core.rs index bfdd85c..b0ac1c9 100644 --- a/src/core.rs +++ b/src/core.rs @@ -340,6 +340,18 @@ pub async fn main( Router::new().fallback(redirect_to_https) }; + // Inject MCP router if configured + #[cfg(feature = "mcp")] + let (http_router, mcp) = if cli.mcp.mcp_ii_instance.is_some() { + use crate::mcp::setup_mcp; + + let (router, mcp) = + setup_mcp(&cli.mcp, ic_agent.clone(), http_router).context("unable to set up MCP")?; + (router, Some(mcp)) + } else { + (http_router, None) + }; + // Create HTTP server let http_server = Arc::new( bnhttp::ServerBuilder::new(http_router) @@ -446,6 +458,11 @@ pub async fn main( v.stop().await; } + #[cfg(feature = "mcp")] + if let Some(v) = mcp { + v.shutdown(); + } + Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 72c0020..9b52792 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,8 @@ mod api; mod cli; mod core; pub mod log; +#[cfg(feature = "mcp")] +mod mcp; pub mod metrics; mod policy; pub mod routing; diff --git a/src/main.rs b/src/main.rs index edc4369..57490d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,8 @@ mod api; mod cli; mod core; mod log; +#[cfg(feature = "mcp")] +mod mcp; mod metrics; mod policy; mod routing; diff --git a/src/mcp.rs b/src/mcp.rs new file mode 100644 index 0000000..822d19a --- /dev/null +++ b/src/mcp.rs @@ -0,0 +1,45 @@ +use anyhow::{Error, anyhow}; +use axum::Router; +use imcp2::{Agent, IiInstance, McpConfig, McpServer, SharedClients, auth_callbacks_router}; +use strum::EnumString; + +#[derive(EnumString, Clone, Copy)] +#[strum(serialize_all = "snake_case")] +pub enum IiType { + Prod, + Beta, +} + +use crate::cli::McpCli; + +/// Inject MCP routes into Router +pub fn setup_mcp(cli: &McpCli, agent: Agent, router: Router) -> Result<(Router, McpServer), Error> { + let ii_instance = match cli.mcp_ii_instance.as_ref().unwrap() { + IiType::Beta => IiInstance::beta(), + IiType::Prod => IiInstance::prod(), + } + .map_err(Error::msg)?; + + let config = McpConfig { + agent, + instance: ii_instance, + public_url: cli + .mcp_public_url + .as_ref() + .ok_or_else(|| anyhow!("MCP Public URL not specified"))? + .to_string(), + mcp_path: cli.mcp_path.clone(), + clients: SharedClients::load(), + }; + + let mcp = McpServer::new(config); + mcp.spawn_session_reaper(); + + let router = router + .nest_service(mcp.mcp_path(), mcp.mcp_router()) + .merge(mcp.well_known_router()) + .merge(mcp.root_well_known_router()) + .merge(auth_callbacks_router(&[&mcp])); + + Ok((router, mcp)) +} diff --git a/src/tls/mod.rs b/src/tls/mod.rs index ba20395..9ab1923 100644 --- a/src/tls/mod.rs +++ b/src/tls/mod.rs @@ -35,10 +35,12 @@ use crate::cli::Cli; async fn setup_acme( cli: &Cli, tasks: &mut TaskManager, - domains: Vec, + mut domains: Vec, challenge: &Challenge, dns_resolver: Resolver, ) -> Result, Error> { + domains.extend_from_slice(&cli.acme.acme_additional_names); + let cache_path = cli.acme.acme_cache_path.clone().unwrap(); let resolver: Arc = match challenge { From 7dfe966e79617346c5374086e9a442e0204070f6 Mon Sep 17 00:00:00 2001 From: Igor Novgorodov Date: Mon, 3 Aug 2026 15:20:30 +0000 Subject: [PATCH 2/4] Build with MCP feature --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0f9cf19..3c6434e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,7 +37,7 @@ jobs: run: echo "SOURCE_DATE_EPOCH=$(git log -1 --format=%ct)" >> "$GITHUB_ENV" - name: Build - run: repro-env build --env SOURCE_DATE_EPOCH -- cargo build --features sev-snp,smtp --release --target x86_64-unknown-linux-musl --bin ic-gateway + run: repro-env build --env SOURCE_DATE_EPOCH -- cargo build --features sev-snp,smtp,mcp --release --target x86_64-unknown-linux-musl --bin ic-gateway - name: Generate SHA checksum run: shasum target/x86_64-unknown-linux-musl/release/ic-gateway > ic-gateway.shasum @@ -46,7 +46,7 @@ jobs: run: rm -rf target - name: Build again - run: repro-env build --env SOURCE_DATE_EPOCH -- cargo build --features sev-snp,smtp --release --target x86_64-unknown-linux-musl --bin ic-gateway + run: repro-env build --env SOURCE_DATE_EPOCH -- cargo build --features sev-snp,smtp,mcp --release --target x86_64-unknown-linux-musl --bin ic-gateway - name: Check SHA checksum run: shasum -c ic-gateway.shasum From 4c11cbc464cd801e904134730522c251f43f6e10 Mon Sep 17 00:00:00 2001 From: Igor Novgorodov Date: Wed, 5 Aug 2026 11:22:55 +0000 Subject: [PATCH 3/4] MCP fixes --- src/cli.rs | 4 ++-- src/core.rs | 11 +++++++---- src/mcp.rs | 8 +++++--- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 312cf55..e79d8ac 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -655,11 +655,11 @@ pub struct Prerender { pub struct McpCli { /// Which II instance to use to authenticate with MCP ("beta" or "prod") /// Enables MCP. - #[clap(env, long)] + #[clap(env, long, requires = "mcp_public_url")] pub mcp_ii_instance: Option, /// MCP Public URL, required if MCP is enabled. - #[clap(env, long)] + #[clap(env, long, requires = "mcp_ii_instance")] pub mcp_public_url: Option, /// MCP path diff --git a/src/core.rs b/src/core.rs index b0ac1c9..8841790 100644 --- a/src/core.rs +++ b/src/core.rs @@ -342,11 +342,14 @@ pub async fn main( // Inject MCP router if configured #[cfg(feature = "mcp")] - let (http_router, mcp) = if cli.mcp.mcp_ii_instance.is_some() { - use crate::mcp::setup_mcp; + let (http_router, mcp) = if let Some(v) = cli.mcp.mcp_ii_instance { + warn!( + "Starting MCP at {} (II {v})", + cli.mcp.mcp_public_url.as_ref().unwrap(), + ); - let (router, mcp) = - setup_mcp(&cli.mcp, ic_agent.clone(), http_router).context("unable to set up MCP")?; + let (router, mcp) = crate::mcp::setup_mcp(&cli.mcp, ic_agent.clone(), http_router) + .context("unable to set up MCP")?; (router, Some(mcp)) } else { (http_router, None) diff --git a/src/mcp.rs b/src/mcp.rs index 822d19a..10c6b3a 100644 --- a/src/mcp.rs +++ b/src/mcp.rs @@ -1,9 +1,9 @@ use anyhow::{Error, anyhow}; use axum::Router; use imcp2::{Agent, IiInstance, McpConfig, McpServer, SharedClients, auth_callbacks_router}; -use strum::EnumString; +use strum::{Display, EnumString}; -#[derive(EnumString, Clone, Copy)] +#[derive(EnumString, Clone, Copy, Display)] #[strum(serialize_all = "snake_case")] pub enum IiType { Prod, @@ -27,7 +27,9 @@ pub fn setup_mcp(cli: &McpCli, agent: Agent, router: Router) -> Result<(Router, .mcp_public_url .as_ref() .ok_or_else(|| anyhow!("MCP Public URL not specified"))? - .to_string(), + .to_string() + .trim_end_matches('/') + .into(), mcp_path: cli.mcp_path.clone(), clients: SharedClients::load(), }; From e873859c64485b772827d0c6f99513b28c620a30 Mon Sep 17 00:00:00 2001 From: Igor Novgorodov Date: Wed, 5 Aug 2026 11:50:14 +0000 Subject: [PATCH 4/4] Fix router --- src/core.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core.rs b/src/core.rs index 8841790..af9d56a 100644 --- a/src/core.rs +++ b/src/core.rs @@ -342,17 +342,17 @@ pub async fn main( // Inject MCP router if configured #[cfg(feature = "mcp")] - let (http_router, mcp) = if let Some(v) = cli.mcp.mcp_ii_instance { + let (gateway_router, mcp) = if let Some(v) = cli.mcp.mcp_ii_instance { warn!( "Starting MCP at {} (II {v})", cli.mcp.mcp_public_url.as_ref().unwrap(), ); - let (router, mcp) = crate::mcp::setup_mcp(&cli.mcp, ic_agent.clone(), http_router) + let (router, mcp) = crate::mcp::setup_mcp(&cli.mcp, ic_agent.clone(), gateway_router) .context("unable to set up MCP")?; (router, Some(mcp)) } else { - (http_router, None) + (gateway_router, None) }; // Create HTTP server