diff --git a/Cargo.lock b/Cargo.lock
index 40e5350..af379aa 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -17,6 +17,15 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "android_system_properties"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "anstream"
version = "1.0.0"
@@ -90,6 +99,12 @@ version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a868f137d0dbaade83da5c4700fd9aef2dfe439fac1763fdb925e5054452a65"
+[[package]]
+name = "autocfg"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
+
[[package]]
name = "bitflags"
version = "2.13.2"
@@ -105,6 +120,18 @@ 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.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
+
[[package]]
name = "cc"
version = "1.5.1"
@@ -127,6 +154,18 @@ 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.7"
@@ -160,6 +199,12 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
+[[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"
@@ -189,6 +234,18 @@ dependencies = [
"crypto-common",
]
+[[package]]
+name = "dyn-clone"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
+
+[[package]]
+name = "equivalent"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826"
+
[[package]]
name = "errno"
version = "0.3.14"
@@ -220,6 +277,94 @@ dependencies = [
"zlib-rs",
]
+[[package]]
+name = "futures"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.5",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d"
+
+[[package]]
+name = "futures-task"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
+
+[[package]]
+name = "futures-util"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "slab",
+]
+
[[package]]
name = "generic-array"
version = "0.14.7"
@@ -241,6 +386,48 @@ dependencies = [
"r-efi",
]
+[[package]]
+name = "hashbrown"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
+
+[[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 = "indexmap"
+version = "2.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+ "serde",
+ "serde_core",
+]
+
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
@@ -253,6 +440,17 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+[[package]]
+name = "js-sys"
+version = "0.3.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5"
+dependencies = [
+ "cfg-if",
+ "futures-util",
+ "wasm-bindgen",
+]
+
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -328,6 +526,15 @@ dependencies = [
"libmimalloc-sys",
]
+[[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"
@@ -340,6 +547,12 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
+[[package]]
+name = "pastey"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4"
+
[[package]]
name = "phf"
version = "0.14.0"
@@ -392,6 +605,12 @@ dependencies = [
"siphasher",
]
+[[package]]
+name = "pin-project-lite"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+
[[package]]
name = "proc-macro2"
version = "1.0.107"
@@ -433,6 +652,26 @@ dependencies = [
"mimalloc",
]
+[[package]]
+name = "ref-cast"
+version = "1.0.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3"
+dependencies = [
+ "ref-cast-impl",
+]
+
+[[package]]
+name = "ref-cast-impl"
+version = "1.0.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.5",
+]
+
[[package]]
name = "regex"
version = "1.13.1"
@@ -462,6 +701,27 @@ version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
+[[package]]
+name = "rmcp"
+version = "3.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab5023804cbe0304cbe06197ac16e645a0a0d0ffa2953bf735c15c2fa0213350"
+dependencies = [
+ "chrono",
+ "futures",
+ "indexmap",
+ "pastey",
+ "pin-project-lite",
+ "schemars",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "tokio-util",
+ "tracing",
+ "uuid",
+]
+
[[package]]
name = "rust_ASC"
version = "0.1.0"
@@ -479,8 +739,14 @@ dependencies = [
"quick-xml",
"radguard",
"regex",
+ "rmcp",
+ "schemars",
+ "serde",
+ "serde_json",
"sha1",
"tempfile",
+ "tokio",
+ "tokio-util",
]
[[package]]
@@ -496,6 +762,38 @@ dependencies = [
"windows-sys",
]
+[[package]]
+name = "rustversion"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
+
+[[package]]
+name = "schemars"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab508826f74a77ca9d5aba6ff19b522583ee3eaf28a19384ff3d0e5835fadf6e"
+dependencies = [
+ "chrono",
+ "dyn-clone",
+ "ref-cast",
+ "schemars_derive",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "schemars_derive"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1c3a92094fa7d61aa124645844facb6b554dfc797136d0f5fd1f890e2bffc69"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde_derive_internals",
+ "syn 3.0.5",
+]
+
[[package]]
name = "serde"
version = "1.0.229"
@@ -526,6 +824,17 @@ dependencies = [
"syn 3.0.5",
]
+[[package]]
+name = "serde_derive_internals"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f852137cce035d6a4df67ccce505ff6b3e9fd3a10e3e52b24dc71e650bb1a9bd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.5",
+]
+
[[package]]
name = "serde_json"
version = "1.0.151"
@@ -562,6 +871,12 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
+[[package]]
+name = "slab"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
+
[[package]]
name = "strsim"
version = "0.11.1"
@@ -623,6 +938,74 @@ dependencies = [
"syn 3.0.5",
]
+[[package]]
+name = "tokio"
+version = "1.53.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce3335fa71841cda333a58d7615b03901380ecf09d59b3296d21f8bbac0dde4e"
+dependencies = [
+ "bytes",
+ "pin-project-lite",
+ "tokio-macros",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.5",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e464cf451ba96ebfc6f9b6542f17ee8b8956e33f1e40d9690624e59d7a7f8a4b"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "libc",
+ "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 2.0.119",
+]
+
+[[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 = "typenum"
version = "1.20.1"
@@ -641,18 +1024,127 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+[[package]]
+name = "uuid"
+version = "1.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cc1186384beb7dd8eedea376413fd654937285ea6c9cfbb928dc3043ea4b606"
+dependencies = [
+ "getrandom",
+ "js-sys",
+ "wasm-bindgen",
+]
+
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.129"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.129"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.129"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn 3.0.5",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.129"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6"
+dependencies = [
+ "unicode-ident",
+]
+
+[[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 2.0.119",
+]
+
+[[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 2.0.119",
+]
+
[[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.61.2"
diff --git a/Cargo.toml b/Cargo.toml
index a843dfe..48c8a3d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,6 +4,10 @@ version = "0.1.0"
edition = "2024"
license = "Apache-2.0"
+[features]
+default = ["mcp"]
+mcp = ["dep:rmcp", "dep:tokio", "dep:tokio-util", "dep:serde", "dep:serde_json", "dep:schemars"]
+
[profile.release]
opt-level = 3 # max speed
lto = "fat" # full link-time optimization
@@ -16,6 +20,12 @@ debug = true
opt-level = 0
[dependencies]
+rmcp = { version = "3.5.1", optional = true, default-features = false, features = ["server", "transport-io"] }
+tokio = { version = "1", optional = true, features = ["rt", "sync", "macros", "time"] }
+tokio-util = { version = "0.7", optional = true, features = ["rt"] }
+serde = { version = "1", optional = true, features = ["derive"] }
+serde_json = { version = "1", optional = true }
+schemars = { version = "1", optional = true }
# getclass keeps chunked flate2 decompression for cooperative early cancellation.
flate2 = { version = "1", default-features = false, features = ["zlib-rs"] }
libdeflater = { version = "1.26.1", features = ["use_rust_alloc"] }
diff --git a/README.md b/README.md
index aae624e..c12d0ea 100644
--- a/README.md
+++ b/README.md
@@ -20,13 +20,21 @@ https://github.com/user-attachments/assets/f1f71a75-1eca-4647-89ce-406ed2c63c6b
- Export pseudocode for every class in an APK with the public API of [MG1937/radguard](https://github.com/MG1937/radguard).
- Handle multi-DEX APKs and DEX 041 logical containers.
- Use memory-mapped APK access and no on-disk cache.
+- Expose four read-only, structured MCP tools over stdio, suitable for Pi CodeMode.
## Build
```bash
+git submodule update --init --recursive
cargo build --release
+
+# Build without MCP and its dedicated dependencies.
+cargo build --release --no-default-features
```
+MCP is enabled by default, including CI/releases. `--no-default-features`
+removes only MCP and its dedicated dependencies; existing CLI commands remain.
+
The executable is written to:
```text
@@ -69,6 +77,43 @@ rust_ASC export --thread 10 --debug app.apk output
`export` writes one `.java` pseudocode file per class under package-relative paths in the output directory. Its multi-threaded APK export pipeline is supplied by [MG1937/radguard](https://github.com/MG1937/radguard). With `--debug`, it reports the APK export lifecycle and worker activity.
+## MCP
+
+Read-only MCP tools over stdio, designed for CodeMode:
+`getclass`, `listclass`, `getmanifest`, and `findrefs`.
+
+Configure:
+
+Ask your agent to install and configure ASC, or follow the manual setup below.
+
+
+Agent setup instructions
+
+1. Confirm the target MCP client and user- or project-level configuration scope.
+2. Locate an MCP-enabled `rust_ASC` executable. If needed, run
+ `git submodule update --init --recursive` followed by `cargo install --path . --locked`
+ from this repository. MCP is enabled by default; the executable is normally installed
+ to `~/.cargo/bin/rust_ASC`.
+3. Register `asc` as a stdio server using the executable's absolute path and
+ `args: ["mcp"]`. Use the client's native configuration format and preserve existing settings.
+4. Reload or restart the client and verify that the four tools listed above are available.
+
+
+
+For manual setup, replace `/absolute/path/to/rust_ASC` with your executable path
+and follow your client's MCP configuration instructions:
+
+```json
+{
+ "mcpServers": {
+ "asc": {
+ "command": "/absolute/path/to/rust_ASC",
+ "args": ["mcp"]
+ }
+ }
+}
+```
+
## CLI Benchmark
The following measurements use CLI commands with no cache, no preprocessing, and direct searches of every code reference to the string `https://`.
diff --git a/src/asc_client/apk/loader.rs b/src/asc_client/apk/loader.rs
index e68d459..99d622d 100644
--- a/src/asc_client/apk/loader.rs
+++ b/src/asc_client/apk/loader.rs
@@ -139,6 +139,63 @@ pub(crate) fn _parse_cd_dex_entries(buf: &[u8]) -> Vec {
entries
}
+/// Strict validation for long-lived query callers. The CLI's fast scanner is
+/// retained, but malformed archives must not become successful empty results.
+pub(crate) fn checked_dex_entries(buf: &[u8]) -> Result, String> {
+ let eocd = _find_eocd(buf).ok_or("EOCD not found")?;
+ if _STRUCT_H::unpack_from(buf, eocd + 4) != 0
+ || _STRUCT_H::unpack_from(buf, eocd + 6) != 0
+ || _STRUCT_H::unpack_from(buf, eocd + 8) != _STRUCT_H::unpack_from(buf, eocd + 10)
+ {
+ return Err("multi-disk ZIP is unsupported".to_owned());
+ }
+ let count = _STRUCT_H::unpack_from(buf, eocd + 10) as usize;
+ if count == u16::MAX as usize {
+ return Err("ZIP64 is unsupported".to_owned());
+ }
+ let comment = _STRUCT_H::unpack_from(buf, eocd + 20) as usize;
+ if eocd + 22 + comment != buf.len() {
+ return Err("bad ZIP comment length".to_owned());
+ }
+ let start = _STRUCT_I::unpack_from(buf, eocd + 16) as usize;
+ let end = start
+ .checked_add(_STRUCT_I::unpack_from(buf, eocd + 12) as usize)
+ .filter(|&end| end <= eocd)
+ .ok_or("bad central directory range")?;
+ let mut pos = start;
+ let mut entries = Vec::new();
+ for _ in 0..count {
+ if pos.checked_add(46).is_none_or(|next| next > end) || &buf[pos..pos + 4] != _CD_SIG {
+ return Err("bad central directory entry".to_owned());
+ }
+ let name_len = _STRUCT_H::unpack_from(buf, pos + 28) as usize;
+ let extra_len = _STRUCT_H::unpack_from(buf, pos + 30) as usize;
+ let comment_len = _STRUCT_H::unpack_from(buf, pos + 32) as usize;
+ let next = pos
+ .checked_add(46 + name_len + extra_len + comment_len)
+ .filter(|&next| next <= end)
+ .ok_or("bad central directory entry range")?;
+ if _STRUCT_H::unpack_from(buf, pos + 8) & 1 != 0 {
+ return Err("encrypted ZIP entries are unsupported".to_owned());
+ }
+ let name = &buf[pos + 46..pos + 46 + name_len];
+ if name.ends_with(_DEX_SUFFIX) && !name.contains(&b'/') {
+ entries.push(DexEntry {
+ name: String::from_utf8_lossy(name).into_owned(),
+ uncomp_size: _STRUCT_I::unpack_from(buf, pos + 24),
+ comp_size: _STRUCT_I::unpack_from(buf, pos + 20),
+ local_header_off: _STRUCT_I::unpack_from(buf, pos + 42),
+ comp_method: _STRUCT_H::unpack_from(buf, pos + 10),
+ });
+ }
+ pos = next;
+ }
+ if pos != end {
+ return Err("central directory size/count mismatch".to_owned());
+ }
+ Ok(entries)
+}
+
fn _inflate_deflate_chunks(compressed: &[u8], stop_event: &AtomicBool) -> Option> {
let mut decoder = DeflateDecoder::new(compressed);
let mut out = Vec::with_capacity(compressed.len() * 3);
@@ -231,6 +288,14 @@ pub(crate) fn _inflate_dex(
}
pub(crate) fn _extract_zip_entry(buf: &[u8], target_name: &str) -> Result, String> {
+ try_extract_zip_entry(buf, target_name)?
+ .ok_or_else(|| format!("{target_name} not found in APK"))
+}
+
+pub(crate) fn try_extract_zip_entry(
+ buf: &[u8],
+ target_name: &str,
+) -> Result