StaticApkAuditor MCP
A Model Context Protocol (MCP) server that exposes Apktool-based decompilation plus a suite of custom static-analysis heuristics for Android APK security assessment, through natural-language commands in Claude Code / Claude Desktop.
What this is for
You point it at an APK. It decompiles it, then gives an AI assistant a set of tools to analyze the result — attack surface, permissions, hardcoded secrets, crypto usage, native libraries, business logic candidates — and cross-checks findings from other tools like MobSF. Every call is logged to disk as a markdown report, so you end up with an audit trail, not just a chat transcript.
This is a static-analysis aid, not a complete pentest. Several tools produce candidates for manual/dynamic verification, not confirmed vulnerabilities — this is called out explicitly in tool descriptions and in the built-in prompts. Runtime behavior (race conditions, workflow bypasses, actual data exfiltration) needs dynamic testing (Frida, Burp/mitmproxy, a real device) to confirm.
Quick Start
Assumes Java, Apktool, and Python 3.10+ are already installed (see Prerequisites below if not).
# 1. Set up the project folder
mkdir static-apk-auditor && cd static-apk-auditor
# 2. Create a venv and install the one dependency
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
# 3. Sanity check — should hang waiting for stdio input, Ctrl+C to stop
python3 server.py
# 4. Register with Claude Code (adjust paths to your actual location)
claude mcp add static-apk-auditor \
--env APKTOOL_WORK_DIR=$(pwd)/data \
-- $(pwd)/venv/bin/python3 $(pwd)/server.py
# 5. Verify it connected
claude mcp list
Then, inside a claude session, in the same folder you ran claude mcp add from (registration defaults to that folder's scope):
/mcp__static-apk-auditor__security_audit
/absolute/path/to/some.apk
or just ask in plain language — the model will call decode_apk and the rest of the tools on its own:
Decompile /absolute/path/to/some.apk and give me a quick security triage.
See Prompts and Tools below for everything available, or run /mcp in a Claude Code session to browse them interactively.
Project structure
static-apk-auditor/ # your folder can be named anything — this is just an example
├── server.py # the MCP server — single file, FastMCP-based
├── requirements.txt # just "mcp"
├── README.md # this file
├── prompts/
│ ├── security_audit.md # 16-point checklist, OWASP Mobile Top 10 mapping
│ └── mobsf_review.md # MobSF finding validation workflow
├── venv/ # created locally, not part of the repo
└── data/ # created automatically on first decode_apk call
# (this is APKTOOL_WORK_DIR — see Configuration below)
Runtime data layout (created automatically)
Every APK you analyze gets its own self-contained folder under APKTOOL_WORK_DIR:
$APKTOOL_WORK_DIR/<apk_filename_without_extension>/
├── source/
│ └── <original>.apk # copy of the APK you pointed the tool at
├── decoded/
│ ├── AndroidManifest.xml
│ ├── smali/, smali_classes2/, ...
│ ├── res/, assets/, lib/
│ └── ... # apktool's decompiled output
├── reports/
│ ├── 20260706_175359_809_decode_apk.md
│ ├── 20260706_175412_112_analyze_manifest.md
│ ├── 20260706_175430_501_check_root_detection.md
│ └── ... # one markdown file per tool call, ever
└── final_reports/
├── 20260706_223500_123_security_audit.md
└── 20260706_224100_456_mobsf_review.md
# the finished, synthesized reports
# (Summary + Detailed Findings + Risk Table),
# saved explicitly via save_final_report
Every tool call — regardless of which tool, regardless of success or failure — writes a timestamped markdown file to that APK's reports/ folder. This isn't optional per-call logging; it's a decorator (@logged_tool) applied to every registered tool, so nothing calling into this server goes unrecorded. Each report contains the tool name, the parameters it was called with, and its full output.
reports/ and final_reports/ serve different purposes: reports/ is the raw call log (every individual tool invocation, useful for auditing exactly what was checked and when), while final_reports/ holds only the polished, human-readable write-ups — the actual deliverable you'd hand to a developer or client. The security_audit and mobsf_review prompts both end with a mandatory call to save_final_report, which is what populates final_reports/; without that call, the synthesized report only exists in the chat response and isn't persisted anywhere on disk.
Prerequisites
1. Java JDK 8+ (required by Apktool)
# Ubuntu/Debian
sudo apt update && sudo apt install default-jdk
# macOS
brew install openjdk
java -version
2. Apktool
# Ubuntu/Debian
sudo apt install apktool
# macOS
brew install apktool
apktool --version
3. Python 3.10+
python3 --version
4. (Optional) aapt — for full get_apk_info metadata without needing to decode first
brew install aapt # or: brew install --cask android-commandlinetools
If aapt isn't installed, get_apk_info falls back to parsing the already-decoded AndroidManifest.xml instead of failing silently.
Installation
git clone <your-repo-or-just-copy-the-files> static-apk-auditor
cd static-apk-auditor
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
Sanity check:
python3 server.py
# Should hang waiting for stdio input (that's normal) — Ctrl+C to stop.
Connecting to Claude Code
claude mcp add apktool-pro \
--env APKTOOL_WORK_DIR=/absolute/path/to/static-apk-auditor/data \
-- /absolute/path/to/static-apk-auditor/venv/bin/python3 /absolute/path/to/static-apk-auditor/server.py
Verify:
claude mcp list
claude mcp get apktool-pro # should show "✔ Connected"
Note: claude mcp add registers the server scoped to the current directory by default (local scope). If you want it available from any project, add --scope user.
Connecting to Claude Desktop (alternative)
Edit your config file (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"apktool-pro": {
"command": "/absolute/path/to/static-apk-auditor/venv/bin/python3",
"args": ["/absolute/path/to/static-apk-auditor/server.py"],
"env": {
"APKTOOL_WORK_DIR": "/absolute/path/to/static-apk-auditor/data"
}
}
}
}
Restart Claude Desktop after saving.
Configuration (environment variables)
| Variable | Default | Purpose |
|---|---|---|
APKTOOL_PATH |
apktool |
Path to the apktool executable, if not on PATH |
AAPT_PATH |
aapt |
Path to aapt, if not on PATH |
APKTOOL_WORK_DIR |
~/.static-apk-auditor |
Root folder for all per-APK source/decoded/reports data |
Tools
Core (decompilation & manifest)
| Tool | Parameters | Description |
|---|---|---|
decode_apk |
apk_path, force=False |
Decompiles an APK with apktool. Output persists under APKTOOL_WORK_DIR/<slug>/decoded/ (not a tempdir), and a copy of the source APK is kept in .../source/. |
get_apk_info |
apk_path |
Basic metadata: package, version, SDK levels, file size. Uses aapt if available, otherwise falls back to parsing the decoded manifest. |
build_apk |
decoded_dir, output_apk=None |
Rebuilds an APK from a (possibly modified) decoded directory. Output is unsigned — sign separately with apksigner. |
install_framework |
framework_apk |
Installs a system framework APK so apktool can decode system/OEM apps that reference it. |
analyze_manifest |
decoded_dir |
Parses AndroidManifest.xml directly via xml.etree: package/version/SDK, exported components (Activity/Service/Receiver/Provider), intent-filters/deep links, debuggable/allowBackup/taskAffinity. |
list_permissions |
decoded_dir |
Lists all requested permissions, flags which are classified "dangerous" by the Android permission model. |
extract_strings |
decoded_dir, locale="en" |
Extracts string resources for a given locale. |
find_smali_references |
decoded_dir, pattern, max_matches=200 |
Regex search across all decompiled .smali files, with file + line for each match. |
Security heuristics
| Tool | Parameters | Description |
|---|---|---|
list_native_libraries |
decoded_dir |
Lists .so libraries and CPU architectures shipped in the APK — flags code that smali search can't see into. |
check_ssl_pinning |
decoded_dir |
Scans for custom TrustManager/HostnameVerifier implementations that could defeat certificate validation, plus CertificatePinner usage and network_security_config.xml contents. |
check_pending_intents |
decoded_dir |
Finds PendingIntent.getActivity/getBroadcast/getService calls and flags ones without FLAG_IMMUTABLE nearby. |
extract_js_bundle_strings |
decoded_dir, pattern=<url regex> |
For React Native / Expo apps: searches assets/*.bundle and assets/*.js for a pattern (defaults to extracting URLs — usually where real backend endpoints leak). |
check_root_detection |
decoded_dir |
Scans for root/emulator detection and anti-tampering signatures: su-binary paths, RootBeer/RootTools, SafetyNet, Play Integrity, test-keys build tag, generic isRooted-style method names. Presence tells you what you'll need to bypass for dynamic testing; absence is itself a finding for sensitive apps. |
Business logic reconnaissance
| Tool | Parameters | Description |
|---|---|---|
map_sensitive_flows |
decoded_dir, extra_keywords=None |
Locates methods/classes whose names suggest sensitive operations (payments, entitlements, roles, discounts, quotas) and shows nearby invoke-* calls as lightweight call context. A candidate map for manual/dynamic review — not a confirmed vulnerability list. |
find_client_side_only_checks |
decoded_dir |
Heuristic: flags sensitive-keyword hits where a local comparison (if-*/cmp-*) appears without an adjacent network call — suggests the decision may be made client-side and potentially bypassable by patching + rebuilding. |
MobSF report validation
| Tool | Parameters | Description |
|---|---|---|
load_mobsf_report |
report_path |
Loads and normalizes a MobSF static-analysis JSON report (code_analysis, manifest_analysis, permissions, urls/emails/firebase_urls, network_security, certificate_analysis). Does not judge validity by itself. |
cross_validate_mobsf_finding |
decoded_dir, finding_pattern, mobsf_file_hint=None |
Cross-checks one MobSF finding against your own decompiled output (smali + string resources) to catch false positives. Returns evidence for the model to classify — Confirmed / False Positive / Needs Manual Review — it does not classify on its own. |
Prompts (guided workflows)
Prompts are invoked explicitly as slash commands in Claude Code (/apktool-pro:<name>), unlike tools, which the model calls on its own as needed.
| Prompt | Arguments | What it does |
|---|---|---|
security_audit |
apk_path |
Runs the full 16-point checklist (attack surface, permissions, secrets, crypto, WebView, storage, SSL pinning, task hijacking, root detection, exported receivers/providers, JS bundle, native libs, logging, dependency versions, PendingIntent mutability, business logic) and outputs: Summary → Detailed Findings (with Confidence rating + adb PoC steps) → Risk Table mapped to OWASP Mobile Top 10 (M1–M10). |
mobsf_review |
apk_path, mobsf_report_path |
Loads a MobSF JSON report, cross-validates every finding against the actual decompiled code, classifies each as Confirmed/False Positive/Needs Manual Review, re-assesses severity independently of MobSF's own rating, and produces a Prioritized Action List of Confirmed findings only. |
quick_triage |
apk_path |
Fast pass: exported components + dangerous permissions + obvious hardcoded secrets. For when you don't need the full audit. |
Usage examples
Use decode_apk to decompile /path/to/app.apk
/apktool-pro:security_audit apk_path="/path/to/app.apk"
Use check_root_detection on the decompiled output
/apktool-pro:mobsf_review apk_path="/path/to/app.apk" mobsf_report_path="/path/to/mobsf_report.json"
To see all available tools and their exact parameters at any time, run /mcp in a Claude Code session and select apktool-pro.
Getting a MobSF report
If you're running MobSF locally:
# upload the APK, get back a hash
curl -F 'file=@/path/to/app.apk' http://localhost:8000/api/v1/upload \
-H "Authorization: <MOBSF_API_KEY>"
# fetch the JSON report using that hash
curl -X POST http://localhost:8000/api/v1/report_json \
-H "Authorization: <MOBSF_API_KEY>" \
--data "hash=<hash_from_upload>" -o mobsf_report.json
Known limitations
- Static analysis only. Nothing here executes the app or intercepts real traffic. Business-logic findings and some "confirmed" static findings still need dynamic verification before you treat them as proven.
build_apkoutput is unsigned. Sign withapksignerbefore installing on a device.- Regex-based heuristics (
check_ssl_pinning,check_root_detection,map_sensitive_flows, etc.) can miss obfuscated/renamed code and can false-positive on unrelated matches — treat their output as a shortlist to review, not a verdict. install_frameworkis only needed for decoding system/OEM APKs that referenceframework-res.apk; most third-party app analysis won't need it.
Design notes
APKTOOL_WORK_DIRis always respected — every APK gets a persistent, self-contained folder (source/,decoded/,reports/,final_reports/); nothing writes to a throwaway tempdir.get_apk_infofalls back to parsing the decoded manifest ifaaptisn't installed, rather than returning nothing useful.- Every tool call is auto-logged (
reports/); finished, synthesized reports are saved separately viasave_final_report(final_reports/). - Built on
FastMCPfor straightforward tool/prompt registration.
Legal / responsible use
Only analyze APKs you own or have explicit written permission to test. Decompiled APKs may contain sensitive user data — handle output accordingly and clean up APKTOOL_WORK_DIR after an engagement if it contains client data. This tool does not perform any network exfiltration or active exploitation on its own; PoC commands in security_audit output (e.g. adb shell am start ...) are meant to be run manually against a device/emulator you control.