#
tokens: 10827/50000 4/4 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── APktool.py
├── gemini.md
├── README.md
└── settings.json
```

# Files

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Apktool MCP Server
  2 | 
  3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
  4 | [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
  5 | [![MCP Compatible](https://img.shields.io/badge/MCP-Compatible-green.svg)](https://modelcontextprotocol.io/)
  6 | [![Gemini CLI](https://img.shields.io/badge/Gemini-CLI-orange.svg)](https://github.com/google-gemini/gemini-cli)
  7 | 
  8 | A powerful **Model Context Protocol (MCP) server** that exposes [Apktool](https://ibotpeaches.github.io/Apktool/) functionality for Android APK analysis and reverse engineering. Integrates seamlessly with **Gemini CLI** to provide AI-powered APK security analysis, privacy auditing, and reverse engineering guidance through natural language commands.
  9 | 
 10 | ## 🚀 Features
 11 | 
 12 | ### 🔍 **Comprehensive APK Analysis**
 13 | - **Decompile APKs** to extract resources, manifest, and smali code
 14 | - **Analyze permissions** and app components for security assessment
 15 | - **Extract string resources** and detect hardcoded secrets
 16 | - **Search smali code** for specific patterns and security vulnerabilities
 17 | - **Recompile modified APKs** after making changes
 18 | 
 19 | ### 🤖 **AI-Powered Workflows**
 20 | - **Natural language commands** for complex APK analysis tasks
 21 | - **Automated security audits** with AI-generated insights
 22 | - **Privacy compliance checking** and GDPR/CCPA analysis
 23 | - **Step-by-step reverse engineering** guidance
 24 | - **Intelligent vulnerability detection** and risk assessment
 25 | 
 26 | ### 🛠 **8 Core Tools**
 27 | | Tool | Description |
 28 | |------|-------------|
 29 | | `decode_apk` | Decompile APK files to extract all components |
 30 | | `build_apk` | Recompile APK from modified source directory |
 31 | | `install_framework` | Install system frameworks for system app analysis |
 32 | | `analyze_manifest` | Parse AndroidManifest.xml for permissions and components |
 33 | | `extract_strings` | Extract string resources with locale support |
 34 | | `list_permissions` | Enumerate all requested permissions |
 35 | | `find_smali_references` | Search for patterns in decompiled smali code |
 36 | | `get_apk_info` | Get basic APK metadata and information |
 37 | 
 38 | ### 📋 **Specialized Analysis Prompts**
 39 | - **Security Analysis**: Comprehensive vulnerability assessment
 40 | - **Privacy Audit**: Data collection and compliance analysis  
 41 | - **Reverse Engineering Guide**: Step-by-step analysis workflows
 42 | 
 43 | ## 📦 Installation
 44 | 
 45 | ### Prerequisites
 46 | 
 47 | **1. Java JDK 8+** (Required by Apktool)
 48 | ```bash
 49 | # Ubuntu/Debian
 50 | sudo apt update && sudo apt install default-jdk
 51 | 
 52 | # macOS (Homebrew)
 53 | brew install openjdk
 54 | 
 55 | # Verify installation
 56 | java -version
 57 | ```
 58 | 
 59 | **2. Apktool** (Core dependency)
 60 | ```bash
 61 | # Option 1: Package manager (recommended)
 62 | # Ubuntu/Debian
 63 | sudo apt install apktool
 64 | 
 65 | # macOS
 66 | brew install apktool
 67 | 
 68 | # Option 2: Manual installation
 69 | # Download from https://ibotpeaches.github.io/Apktool/install/
 70 | 
 71 | # Verify installation
 72 | apktool --version
 73 | ```
 74 | 
 75 | **3. Python 3.10+**
 76 | ```bash
 77 | python3 --version  # Should be 3.10 or higher
 78 | ```
 79 | 
 80 | ### Setup Instructions
 81 | 
 82 | **1. Clone the repository**
 83 | ```bash
 84 | git clone https://github.com/SecFathy/APktool-MCP.git
 85 | cd APktool-MCP
 86 | ```
 87 | 
 88 | **2. Create virtual environment**
 89 | ```bash
 90 | python3 -m venv venv
 91 | source venv/bin/activate  # Linux/macOS
 92 | # or
 93 | venv\Scripts\activate     # Windows
 94 | ```
 95 | 
 96 | **3. Install dependencies**
 97 | ```bash
 98 | pip install -r requirements.txt
 99 | ```
100 | 
101 | **4. Test the installation**
102 | ```bash
103 | python3 apktool_server.py
104 | # Should start the MCP server successfully
105 | ```
106 | 
107 | ## ⚙️ Configuration
108 | 
109 | ### Gemini CLI Integration
110 | 
111 | **1. Install Gemini CLI**
112 | ```bash
113 | # Follow instructions at https://github.com/google-gemini/gemini-cli
114 | ```
115 | 
116 | **2. Configure MCP Server**
117 | 
118 | Edit your Gemini CLI configuration file:
119 | - **Linux/macOS**: `~/.config/gemini-cli/config.json`
120 | - **Windows**: `%APPDATA%\gemini-cli\config.json`
121 | 
122 | ```json
123 | {
124 |   "mcpServers": {
125 |     "apktool": {
126 |       "command": "python3",
127 |       "args": ["/absolute/path/to/apktool_server.py"],
128 |       "env": {
129 |         "APKTOOL_WORK_DIR": "/path/to/workspace"
130 |       }
131 |     }
132 |   }
133 | }
134 | ```
135 | 
136 | ### Claude Desktop Integration (Alternative)
137 | 
138 | Edit Claude Desktop configuration:
139 | - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
140 | - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
141 | - **Linux**: `~/.config/Claude/claude_desktop_config.json`
142 | 
143 | ```json
144 | {
145 |   "mcpServers": {
146 |     "apktool": {
147 |       "command": "python3",
148 |       "args": ["/absolute/path/to/apktool_server.py"],
149 |       "env": {
150 |         "APKTOOL_WORK_DIR": "/path/to/workspace"
151 |       }
152 |     }
153 |   }
154 | }
155 | ```
156 | 
157 | ## 🎯 Usage Examples
158 | 
159 | ### Natural Language Commands
160 | 
161 | ```bash
162 | # Start Gemini CLI
163 | gemini
164 | 
165 | # Security Analysis
166 | > "Analyze the APK at ./suspicious_app.apk for security vulnerabilities"
167 | 
168 | # Permission Analysis  
169 | > "What permissions does ./myapp.apk request and are any of them dangerous?"
170 | 
171 | # Code Analysis
172 | > "Find any hardcoded API keys or secrets in ./social_app.apk"
173 | 
174 | # Privacy Audit
175 | > "Generate a privacy compliance report for ./messenger_app.apk"
176 | 
177 | # Reverse Engineering
178 | > "Help me understand how the authentication works in ./banking_app.apk"
179 | ```
180 | 
181 | ### Direct Tool Usage
182 | 
183 | ```bash
184 | # Decompile an APK
185 | > Use decode_apk to decompile ./sample.apk
186 | 
187 | # Analyze permissions
188 | > Use list_permissions on the decompiled directory ./sample
189 | 
190 | # Search for patterns
191 | > Use find_smali_references to search for "crypto" in ./sample
192 | 
193 | # Extract strings
194 | > Use extract_strings from ./sample for locale "en"
195 | 
196 | # Rebuild APK
197 | > Use build_apk to recompile ./sample into ./sample_modified.apk
198 | ```
199 | 
200 | ### Guided Workflows
201 | 
202 | ```bash
203 | # Run automated security analysis
204 | > Run the security analysis prompt on ./target_app.apk
205 | 
206 | # Perform privacy audit
207 | > Execute privacy audit workflow for ./social_media_app.apk
208 | 
209 | # Get reverse engineering guidance
210 | > Use the reverse engineering guide for analyzing login functionality in ./app.apk
211 | ```
212 | 
213 | ## 📁 Project Structure
214 | 
215 | ```
216 | apktool-mcp-server/
217 | ├── apktool_server.py          # Main MCP server implementation
218 | ├── requirements.txt           # Python dependencies
219 | ├── config.json               # Example Gemini CLI configuration
220 | ├── README.md                 # This file
221 | ├── GEMINI.md                 # AI assistant context file
222 | ├── LICENSE                   # MIT license
223 | ├── examples/                 # Usage examples and samples
224 | │   ├── sample_analysis.py    # Example analysis scripts
225 | │   └── workflows/            # Common workflow examples
226 | ├── tests/                    # Unit tests
227 | │   ├── test_server.py        # Server functionality tests
228 | │   └── test_tools.py         # Individual tool tests
229 | └── docs/                     # Additional documentation
230 |     ├── SECURITY.md           # Security guidelines
231 |     ├── CONTRIBUTING.md       # Contribution guidelines
232 |     └── TROUBLESHOOTING.md    # Common issues and solutions
233 | ```
234 | 
235 | ## 🔒 Security Considerations
236 | 
237 | ### ⚠️ **Important Security Notes**
238 | 
239 | - **Legal Compliance**: Only analyze APKs you own or have explicit permission to analyze
240 | - **Malware Risk**: Unknown APKs may contain malicious code - use in isolated environments
241 | - **Data Privacy**: Decompiled APKs may contain sensitive user information
242 | - **Workspace Isolation**: Configure dedicated workspace with restricted permissions
243 | - **Process Limits**: Server includes timeouts to prevent resource exhaustion
244 | 
245 | ### **Best Practices**
246 | 
247 | ```bash
248 | # Use dedicated workspace
249 | export APKTOOL_WORK_DIR="/secure/isolated/workspace"
250 | 
251 | # Set appropriate permissions
252 | chmod 750 /secure/isolated/workspace
253 | 
254 | # Monitor resource usage
255 | htop  # Watch memory and CPU during analysis
256 | 
257 | # Clean up after analysis
258 | rm -rf /secure/isolated/workspace/*
259 | ```
260 | 
261 | ## 🧪 Testing
262 | 
263 | ### Run Unit Tests
264 | ```bash
265 | # Install test dependencies
266 | pip install pytest pytest-asyncio
267 | 
268 | # Run all tests
269 | pytest tests/
270 | 
271 | # Run with coverage
272 | pytest --cov=apktool_server tests/
273 | ```
274 | 
275 | ### Manual Testing
276 | ```bash
277 | # Test server startup
278 | python3 apktool_server.py
279 | 
280 | # Test with sample APK
281 | # Download a sample APK and test basic functionality
282 | ```
283 | 
284 | ### Integration Testing
285 | ```bash
286 | # Test Gemini CLI integration
287 | gemini
288 | > /tools  # Should list apktool tools
289 | > Use decode_apk to analyze sample.apk
290 | ```
291 | 
292 | ## 🤝 Contributing
293 | 
294 | We welcome contributions! Please see [CONTRIBUTING.md](docs/CONTRIBUTING.md) for details.
295 | 
296 | ### Development Setup
297 | ```bash
298 | # Clone and setup development environment
299 | git clone https://github.com/SecFathy/APktool-MCP.git
300 | cd APktool-MCP
301 | python3 -m venv venv
302 | source venv/bin/activate
303 | pip install -r requirements.txt
304 | pip install -r requirements-dev.txt
305 | 
306 | # Run tests
307 | pytest
308 | 
309 | # Format code
310 | black apktool_server.py
311 | ``` 
312 | 
```

--------------------------------------------------------------------------------
/settings.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "theme": "Dracula",
 3 |   "selectedAuthType": "oauth-personal",
 4 |   "mcpServers": {
 5 |     "myPythonServer": {
 6 |       "command": "python",
 7 |       "args": ["mcp_server.py", "--port", "8080"],
 8 |       "cwd": "./mcp_tools/python",
 9 |       "timeout": 5000
10 |     },
11 |     "myNodeServer": {
12 |       "command": "node",
13 |       "args": ["mcp_server.js"],
14 |       "cwd": "./mcp_tools/node"
15 |     },
16 |     "myDockerServer": {
17 |       "command": "docker",
18 |       "args": ["run", "i", "--rm", "-e", "API_KEY", "ghcr.io/foo/bar"],
19 |       "env": {
20 |         "API_KEY": "$MY_API_TOKEN"
21 |       }
22 |     },
23 |     "apktool": {
24 |       "command": "python3",
25 |       "args": ["/Users/secfathy/Desktop/Workflow/APktool-MCP/APktool.py"],
26 |       "cwd": "/Users/secfathy/Desktop/Workflow/APktool-MCP",
27 |       "env": {
28 |         "APKTOOL_WORK_DIR": "/Users/secfathy/Desktop/Workflow/APktool-MCP",
29 |         "PYTHONPATH": "/Users/secfathy/Desktop/Workflow/APktool-MCP"
30 |       },
31 |       "timeout": 30000,
32 |       "trust": false
33 |     }
34 |   }
35 | }
36 | 
```

--------------------------------------------------------------------------------
/gemini.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Apktool MCP Server: AI-Powered Android APK Analysis
 2 | 
 3 | The **Apktool MCP Server** is a Model Context Protocol (MCP) server designed to expose `Apktool` functionality. It enables AI models, particularly those integrated with the Gemini CLI, to perform advanced Android APK analysis, reverse engineering, and modification workflows.
 4 | 
 5 | ## 🚀 Key Features
 6 | 
 7 | This server bridges the gap between powerful AI capabilities and `Apktool`'s robust set of tools, allowing for automated and intelligent interactions with Android application packages.
 8 | 
 9 | * **Decompilation (`decode_apk`):** Decompile APKs into human-readable resources, AndroidManifest.xml, and Smali code for in-depth analysis.
10 | * **Recompilation (`build_apk`):** Recompile modified Smali code and resources back into an installable APK.
11 | * **Framework Management (`install_framework`):** Install necessary framework APKs for decompiling system applications or specific Android versions.
12 | * **Manifest Analysis (`analyze_manifest`, `list_permissions`):** Extract and analyze crucial information from `AndroidManifest.xml`, including declared permissions, activities, services, and receivers.
13 | * **Resource Extraction (`extract_strings`):** Pull string resources for various locales from decompiled APKs.
14 | * **Smali Code Analysis (`find_smali_references`):** Search for specific patterns, API calls, or sensitive information within the Smali assembly code.
15 | * **APK Information (`get_apk_info`):** Obtain basic metadata about an APK file.
16 | 
17 | ## ✨ Integration with Gemini CLI
18 | 
19 | This server is designed to be consumed by the Gemini CLI. When connected, the Gemini AI can leverage the exposed Apktool functionalities as "tools" and "resources" to facilitate complex APK analysis tasks.
20 | 
21 | ### Exposed Tools (via `list_tools`)
22 | 
23 | The following `apktool-mcp` tools are available for the AI to call:
24 | 
25 | * `decode_apk`: Decompile an APK.
26 | * `build_apk`: Recompile an APK.
27 | * `install_framework`: Install a framework APK.
28 | * `analyze_manifest`: Analyze `AndroidManifest.xml`.
29 | * `extract_strings`: Extract string resources.
30 | * `list_permissions`: List all requested permissions.
31 | * `find_smali_references`: Search Smali code.
32 | * `get_apk_info`: Get basic APK information.
33 | 
34 | ### Exposed Resources (via `list_resources` and `read_resource`)
35 | 
36 | The AI can read specific files from decompiled APKs:
37 | 
38 | * `apktool://apk/<apk_name>/manifest`: Access the `AndroidManifest.xml` of a decompiled APK.
39 | * `apktool://apk/<apk_name>/apktool_yml`: Access the `apktool.yml` configuration file.
40 | 
41 | ### Available Prompts (via `list_prompts` and `get_prompt`)
42 | 
43 | Pre-defined prompts guide the AI in common APK analysis scenarios:
44 | 
45 | * `analyze_security`: Guides the AI to perform a comprehensive security assessment.
46 | * `privacy_audit`: Directs the AI to audit an APK for privacy-related concerns.
47 | * `reverse_engineer_guide`: Provides step-by-step instructions for reverse engineering a target feature within an APK.
48 | 
49 | ## 🛠️ Setup and Running
50 | 
51 | ### Prerequisites
52 | 
53 | * **Python 3.x:** Ensure you have Python 3 installed.
54 | * **Apktool:** The `apktool` executable must be installed and available in your system's PATH. If not, the server will issue a warning.
55 |     * Installation instructions for Apktool can be found here: [https://ibotpeaches.github.io/Apktool/install/](https://ibotpeaches.github.io/Apktool/install/)
56 | * **MCP Library:** Install the Model Context Protocol library:
57 |     ```bash
58 |     pip install google-mcp
59 |     ```
60 | 
61 | ### Running the Server
62 | 
63 | 1.  **Save the server code:** Save your Python server code (the one you provided) as `apktool_server.py`.
64 | 2.  **Run the server:**
65 |     ```bash
66 |     python apktool_server.py
67 |     ```
68 |     The server will run and listen for connections, typically via standard I/O (stdio), which is the default for Gemini CLI integration.
69 | 
70 | ## 🚀 Example Workflow with Gemini CLI (Conceptual)
71 | 
72 | Once the server is running, you can interact with it using the Gemini CLI.
73 | 
74 | 1.  **Connect the server to Gemini CLI:**
75 |     ```bash
76 |     gemini context add --server-command "python apktool_server.py" apktool-server
77 |     ```
78 | 2.  **List available tools/prompts:**
79 |     ```bash
80 |     gemini context list-tools apktool-server
81 |     gemini context list-prompts apktool-server
82 |     ```
83 | 3.  **Use a prompt (e.g., to analyze an APK):**
84 |     First, ensure you have an APK file, for example, `my_app.apk`, in a location accessible by the server (e.g., in the same directory or specified with an absolute path).
85 | 
86 |     ```bash
87 |     gemini prompt apktool-server analyze_security --apk_path my_app.apk
88 |     ```
89 |     The Gemini AI will then use the `apktool-mcp` server to call `decode_apk`, `analyze_manifest`, `list_permissions`, and `find_smali_references` as guided by the prompt, returning its analysis.
90 | 
91 | 4.  **Directly call a tool:**
92 |     ```bash
93 |     gemini tool apktool-server decode_apk --apk_path my_app.apk --output_dir my_app_decompiled
94 |     ```
95 | 
96 | ---
```

--------------------------------------------------------------------------------
/APktool.py:
--------------------------------------------------------------------------------

```python
  1 | #!/usr/bin/env python3
  2 | """
  3 | Apktool MCP Server
  4 | A Model Context Protocol server that exposes Apktool functionality for Android APK analysis and modification.
  5 | Integrates with Gemini CLI for AI-powered APK reverse engineering workflows.
  6 | """
  7 | 
  8 | import asyncio
  9 | import json
 10 | import os
 11 | import subprocess
 12 | import tempfile
 13 | import shutil
 14 | from pathlib import Path
 15 | from typing import Any, Dict, List, Optional
 16 | import logging
 17 | 
 18 | from mcp.server import Server
 19 | from mcp.types import (
 20 |     Tool,
 21 |     TextContent,
 22 |     ImageContent,
 23 |     EmbeddedResource,
 24 |     Resource,
 25 |     Prompt,
 26 |     GetPromptResult,
 27 |     CallToolResult,
 28 |     ListResourcesResult,
 29 |     ListPromptsResult,
 30 |     ListToolsResult,
 31 |     ReadResourceResult,
 32 | )
 33 | 
 34 | # Configure logging
 35 | logging.basicConfig(level=logging.INFO)
 36 | logger = logging.getLogger("apktool-mcp")
 37 | 
 38 | class ApktoolMCPServer:
 39 |     def __init__(self, apktool_path: str = "apktool", work_dir: str = None):
 40 |         """
 41 |         Initialize the Apktool MCP Server
 42 |         
 43 |         Args:
 44 |             apktool_path: Path to apktool executable (default: "apktool")
 45 |             work_dir: Working directory for APK operations (default: temp dir)
 46 |         """
 47 |         self.apktool_path = apktool_path
 48 |         self.work_dir = Path(work_dir) if work_dir else Path(tempfile.mkdtemp())
 49 |         self.work_dir.mkdir(exist_ok=True)
 50 |         
 51 |         # Initialize MCP server
 52 |         self.server = Server("apktool-mcp")
 53 |         self._setup_tools()
 54 |         self._setup_resources()
 55 |         self._setup_prompts()
 56 |         
 57 |     def _setup_tools(self):
 58 |         """Register all available tools with the MCP server"""
 59 |         
 60 |         @self.server.list_tools()
 61 |         async def list_tools() -> ListToolsResult:
 62 |             return ListToolsResult(
 63 |                 tools=[
 64 |                     Tool(
 65 |                         name="decode_apk",
 66 |                         description="Decompile an APK file to extract resources, manifest, and smali code",
 67 |                         inputSchema={
 68 |                             "type": "object",
 69 |                             "properties": {
 70 |                                 "apk_path": {"type": "string", "description": "Path to the APK file"},
 71 |                                 "output_dir": {"type": "string", "description": "Output directory name (optional)"},
 72 |                                 "force": {"type": "boolean", "description": "Force overwrite existing directory", "default": False},
 73 |                                 "no_res": {"type": "boolean", "description": "Do not decode resources", "default": False},
 74 |                                 "no_src": {"type": "boolean", "description": "Do not decode sources", "default": False}
 75 |                             },
 76 |                             "required": ["apk_path"]
 77 |                         }
 78 |                     ),
 79 |                     Tool(
 80 |                         name="build_apk",
 81 |                         description="Recompile/build an APK from decompiled source directory",
 82 |                         inputSchema={
 83 |                             "type": "object",
 84 |                             "properties": {
 85 |                                 "source_dir": {"type": "string", "description": "Path to decompiled APK directory"},
 86 |                                 "output_apk": {"type": "string", "description": "Output APK filename (optional)"},
 87 |                                 "force": {"type": "boolean", "description": "Force build all files", "default": False}
 88 |                             },
 89 |                             "required": ["source_dir"]
 90 |                         }
 91 |                     ),
 92 |                     Tool(
 93 |                         name="install_framework",
 94 |                         description="Install framework APK for system app decompilation",
 95 |                         inputSchema={
 96 |                             "type": "object",
 97 |                             "properties": {
 98 |                                 "framework_path": {"type": "string", "description": "Path to framework APK file"},
 99 |                                 "tag": {"type": "string", "description": "Tag for framework identification (optional)"}
100 |                             },
101 |                             "required": ["framework_path"]
102 |                         }
103 |                     ),
104 |                     Tool(
105 |                         name="analyze_manifest",
106 |                         description="Analyze AndroidManifest.xml from a decompiled APK",
107 |                         inputSchema={
108 |                             "type": "object",
109 |                             "properties": {
110 |                                 "apk_dir": {"type": "string", "description": "Path to decompiled APK directory"}
111 |                             },
112 |                             "required": ["apk_dir"]
113 |                         }
114 |                     ),
115 |                     Tool(
116 |                         name="extract_strings",
117 |                         description="Extract all string resources from a decompiled APK",
118 |                         inputSchema={
119 |                             "type": "object",
120 |                             "properties": {
121 |                                 "apk_dir": {"type": "string", "description": "Path to decompiled APK directory"},
122 |                                 "locale": {"type": "string", "description": "Specific locale (e.g., 'en', 'es')", "default": ""}
123 |                             },
124 |                             "required": ["apk_dir"]
125 |                         }
126 |                     ),
127 |                     Tool(
128 |                         name="list_permissions",
129 |                         description="List all permissions requested by an APK",
130 |                         inputSchema={
131 |                             "type": "object",
132 |                             "properties": {
133 |                                 "apk_dir": {"type": "string", "description": "Path to decompiled APK directory"}
134 |                             },
135 |                             "required": ["apk_dir"]
136 |                         }
137 |                     ),
138 |                     Tool(
139 |                         name="find_smali_references",
140 |                         description="Search for specific patterns in smali code",
141 |                         inputSchema={
142 |                             "type": "object",
143 |                             "properties": {
144 |                                 "apk_dir": {"type": "string", "description": "Path to decompiled APK directory"},
145 |                                 "pattern": {"type": "string", "description": "Search pattern or string"},
146 |                                 "case_sensitive": {"type": "boolean", "description": "Case sensitive search", "default": True}
147 |                             },
148 |                             "required": ["apk_dir", "pattern"]
149 |                         }
150 |                     ),
151 |                     Tool(
152 |                         name="get_apk_info",
153 |                         description="Get basic information about an APK file using aapt",
154 |                         inputSchema={
155 |                             "type": "object",
156 |                             "properties": {
157 |                                 "apk_path": {"type": "string", "description": "Path to the APK file"}
158 |                             },
159 |                             "required": ["apk_path"]
160 |                         }
161 |                     )
162 |                 ]
163 |             )
164 | 
165 |         @self.server.call_tool()
166 |         async def call_tool(name: str, arguments: Dict[str, Any]) -> CallToolResult:
167 |             try:
168 |                 if name == "decode_apk":
169 |                     result = await self._decode_apk(**arguments)
170 |                 elif name == "build_apk":
171 |                     result = await self._build_apk(**arguments)
172 |                 elif name == "install_framework":
173 |                     result = await self._install_framework(**arguments)
174 |                 elif name == "analyze_manifest":
175 |                     result = await self._analyze_manifest(**arguments)
176 |                 elif name == "extract_strings":
177 |                     result = await self._extract_strings(**arguments)
178 |                 elif name == "list_permissions":
179 |                     result = await self._list_permissions(**arguments)
180 |                 elif name == "find_smali_references":
181 |                     result = await self._find_smali_references(**arguments)
182 |                 elif name == "get_apk_info":
183 |                     result = await self._get_apk_info(**arguments)
184 |                 else:
185 |                     raise ValueError(f"Unknown tool: {name}")
186 |                 
187 |                 return CallToolResult(content=[TextContent(type="text", text=result)])
188 |                 
189 |             except Exception as e:
190 |                 error_msg = f"Error executing {name}: {str(e)}"
191 |                 logger.error(error_msg)
192 |                 return CallToolResult(content=[TextContent(type="text", text=error_msg)], isError=True)
193 | 
194 |     def _setup_resources(self):
195 |         """Setup resources for accessing decompiled APK data"""
196 |         
197 |         @self.server.list_resources()
198 |         async def list_resources() -> ListResourcesResult:
199 |             resources = []
200 |             
201 |             # List all decompiled APK directories
202 |             for item in self.work_dir.iterdir():
203 |                 if item.is_dir():
204 |                     resources.append(
205 |                         Resource(
206 |                             uri=f"apktool://apk/{item.name}/manifest",
207 |                             name=f"{item.name} - AndroidManifest.xml",
208 |                             mimeType="application/xml"
209 |                         )
210 |                     )
211 |                     resources.append(
212 |                         Resource(
213 |                             uri=f"apktool://apk/{item.name}/apktool_yml",
214 |                             name=f"{item.name} - apktool.yml",
215 |                             mimeType="application/yaml"
216 |                         )
217 |                     )
218 |             
219 |             return ListResourcesResult(resources=resources)
220 | 
221 |         @self.server.read_resource()
222 |         async def read_resource(uri: str) -> ReadResourceResult:
223 |             try:
224 |                 if uri.startswith("apktool://apk/"):
225 |                     parts = uri.split("/")
226 |                     apk_name = parts[3]
227 |                     resource_type = parts[4]
228 |                     
229 |                     apk_dir = self.work_dir / apk_name
230 |                     
231 |                     if resource_type == "manifest":
232 |                         manifest_path = apk_dir / "AndroidManifest.xml"
233 |                         if manifest_path.exists():
234 |                             content = manifest_path.read_text(encoding='utf-8')
235 |                             return ReadResourceResult(contents=[TextContent(type="text", text=content)])
236 |                     
237 |                     elif resource_type == "apktool_yml":
238 |                         yml_path = apk_dir / "apktool.yml"
239 |                         if yml_path.exists():
240 |                             content = yml_path.read_text(encoding='utf-8')
241 |                             return ReadResourceResult(contents=[TextContent(type="text", text=content)])
242 |                 
243 |                 raise FileNotFoundError(f"Resource not found: {uri}")
244 |                 
245 |             except Exception as e:
246 |                 error_msg = f"Error reading resource {uri}: {str(e)}"
247 |                 return ReadResourceResult(contents=[TextContent(type="text", text=error_msg)])
248 | 
249 |     def _setup_prompts(self):
250 |         """Setup prompts for common APK analysis tasks"""
251 |         
252 |         @self.server.list_prompts()
253 |         async def list_prompts() -> ListPromptsResult:
254 |             return ListPromptsResult(
255 |                 prompts=[
256 |                     Prompt(
257 |                         name="analyze_security",
258 |                         description="Analyze APK for potential security issues",
259 |                         arguments=[
260 |                             {"name": "apk_path", "description": "Path to APK file", "required": True}
261 |                         ]
262 |                     ),
263 |                     Prompt(
264 |                         name="privacy_audit",
265 |                         description="Audit APK for privacy-related permissions and data collection",
266 |                         arguments=[
267 |                             {"name": "apk_path", "description": "Path to APK file", "required": True}
268 |                         ]
269 |                     ),
270 |                     Prompt(
271 |                         name="reverse_engineer_guide",
272 |                         description="Step-by-step guide for reverse engineering an APK",
273 |                         arguments=[
274 |                             {"name": "apk_path", "description": "Path to APK file", "required": True},
275 |                             {"name": "target_feature", "description": "Specific feature to analyze", "required": False}
276 |                         ]
277 |                     )
278 |                 ]
279 |             )
280 | 
281 |         @self.server.get_prompt()
282 |         async def get_prompt(name: str, arguments: Dict[str, str]) -> GetPromptResult:
283 |             if name == "analyze_security":
284 |                 apk_path = arguments.get("apk_path", "")
285 |                 prompt_text = f"""
286 | Please perform a comprehensive security analysis of the APK file: {apk_path}
287 | 
288 | Steps to follow:
289 | 1. Use decode_apk to decompile the APK
290 | 2. Use analyze_manifest to examine permissions and components
291 | 3. Use list_permissions to identify potentially dangerous permissions
292 | 4. Use find_smali_references to search for:
293 |    - Crypto/encryption usage
294 |    - Network communications
295 |    - File I/O operations
296 |    - Sensitive API calls
297 | 5. Look for hardcoded secrets, API keys, or credentials
298 | 6. Analyze app components for potential vulnerabilities
299 | 
300 | Provide a detailed security assessment with:
301 | - Risk level (Low/Medium/High)
302 | - Identified vulnerabilities
303 | - Recommendations for mitigation
304 | """
305 |                 
306 |             elif name == "privacy_audit":
307 |                 apk_path = arguments.get("apk_path", "")
308 |                 prompt_text = f"""
309 | Conduct a privacy audit for the APK file: {apk_path}
310 | 
311 | Analysis should include:
312 | 1. Decompile the APK using decode_apk
313 | 2. Extract and analyze all permissions with list_permissions
314 | 3. Identify data collection patterns in smali code
315 | 4. Check for third-party SDK integrations
316 | 5. Examine network communications and endpoints
317 | 6. Review privacy policy compliance indicators
318 | 
319 | Generate a privacy report covering:
320 | - Personal data types collected
321 | - Data sharing with third parties
322 | - User consent mechanisms
323 | - Compliance with privacy regulations (GDPR, CCPA)
324 | """
325 |                 
326 |             elif name == "reverse_engineer_guide":
327 |                 apk_path = arguments.get("apk_path", "")
328 |                 target_feature = arguments.get("target_feature", "general functionality")
329 |                 prompt_text = f"""
330 | Create a reverse engineering guide for APK: {apk_path}
331 | Target analysis: {target_feature}
332 | 
333 | Provide step-by-step instructions for:
334 | 1. Initial APK decompilation and structure analysis
335 | 2. Understanding the app architecture from AndroidManifest.xml
336 | 3. Identifying key components and entry points
337 | 4. Analyzing smali code for the target feature
338 | 5. Resource analysis (strings, layouts, assets)
339 | 6. Modification strategies if needed
340 | 7. Recompilation and testing approaches
341 | 
342 | Include specific apktool commands and file locations to examine.
343 | """
344 |             
345 |             else:
346 |                 prompt_text = f"Unknown prompt: {name}"
347 |             
348 |             return GetPromptResult(
349 |                 description=f"APK analysis prompt for {name}",
350 |                 messages=[
351 |                     {
352 |                         "role": "user",
353 |                         "content": {
354 |                             "type": "text",
355 |                             "text": prompt_text
356 |                         }
357 |                     }
358 |                 ]
359 |             )
360 | 
361 |     async def _run_command(self, cmd: List[str], cwd: Optional[Path] = None) -> str:
362 |         """Execute a command and return its output"""
363 |         try:
364 |             process = await asyncio.create_subprocess_exec(
365 |                 *cmd,
366 |                 stdout=asyncio.subprocess.PIPE,
367 |                 stderr=asyncio.subprocess.PIPE,
368 |                 cwd=cwd or self.work_dir
369 |             )
370 |             stdout, stderr = await process.communicate()
371 |             
372 |             output = stdout.decode('utf-8', errors='ignore')
373 |             error = stderr.decode('utf-8', errors='ignore')
374 |             
375 |             if process.returncode != 0:
376 |                 raise RuntimeError(f"Command failed: {' '.join(cmd)}\nError: {error}")
377 |             
378 |             return output if output else error
379 |             
380 |         except Exception as e:
381 |             raise RuntimeError(f"Failed to execute command: {' '.join(cmd)}\nError: {str(e)}")
382 | 
383 |     async def _decode_apk(self, apk_path: str, output_dir: str = None, 
384 |                          force: bool = False, no_res: bool = False, 
385 |                          no_src: bool = False) -> str:
386 |         """Decompile an APK file using apktool"""
387 |         apk_file = Path(apk_path)
388 |         if not apk_file.exists():
389 |             raise FileNotFoundError(f"APK file not found: {apk_path}")
390 |         
391 |         if not output_dir:
392 |             output_dir = apk_file.stem
393 |         
394 |         output_path = self.work_dir / output_dir
395 |         
396 |         cmd = [self.apktool_path, "d", str(apk_file)]
397 |         
398 |         if force:
399 |             cmd.append("-f")
400 |         if no_res:
401 |             cmd.append("-r")
402 |         if no_src:
403 |             cmd.append("-s")
404 |         
405 |         cmd.extend(["-o", str(output_path)])
406 |         
407 |         result = await self._run_command(cmd)
408 |         
409 |         return f"Successfully decompiled APK to: {output_path}\n\nOutput:\n{result}"
410 | 
411 |     async def _build_apk(self, source_dir: str, output_apk: str = None, 
412 |                         force: bool = False) -> str:
413 |         """Build/recompile an APK from source directory"""
414 |         source_path = Path(source_dir)
415 |         if not source_path.exists():
416 |             raise FileNotFoundError(f"Source directory not found: {source_dir}")
417 |         
418 |         cmd = [self.apktool_path, "b", str(source_path)]
419 |         
420 |         if force:
421 |             cmd.append("-f")
422 |         
423 |         if output_apk:
424 |             cmd.extend(["-o", output_apk])
425 |         
426 |         result = await self._run_command(cmd)
427 |         
428 |         return f"Successfully built APK from: {source_path}\n\nOutput:\n{result}"
429 | 
430 |     async def _install_framework(self, framework_path: str, tag: str = None) -> str:
431 |         """Install framework APK for system app decompilation"""
432 |         framework_file = Path(framework_path)
433 |         if not framework_file.exists():
434 |             raise FileNotFoundError(f"Framework file not found: {framework_path}")
435 |         
436 |         cmd = [self.apktool_path, "if", str(framework_file)]
437 |         
438 |         if tag:
439 |             cmd.extend(["-t", tag])
440 |         
441 |         result = await self._run_command(cmd)
442 |         
443 |         return f"Successfully installed framework: {framework_path}\n\nOutput:\n{result}"
444 | 
445 |     async def _analyze_manifest(self, apk_dir: str) -> str:
446 |         """Analyze AndroidManifest.xml from decompiled APK"""
447 |         manifest_path = Path(apk_dir) / "AndroidManifest.xml"
448 |         if not manifest_path.exists():
449 |             raise FileNotFoundError(f"AndroidManifest.xml not found in: {apk_dir}")
450 |         
451 |         content = manifest_path.read_text(encoding='utf-8')
452 |         
453 |         # Extract key information
454 |         analysis = []
455 |         lines = content.split('\n')
456 |         
457 |         for line in lines:
458 |             line = line.strip()
459 |             if 'package=' in line:
460 |                 analysis.append(f"Package: {line}")
461 |             elif 'android:name=' in line and 'activity' in line.lower():
462 |                 analysis.append(f"Activity: {line}")
463 |             elif 'android:name=' in line and 'service' in line.lower():
464 |                 analysis.append(f"Service: {line}")
465 |             elif 'android:name=' in line and 'receiver' in line.lower():
466 |                 analysis.append(f"Receiver: {line}")
467 |             elif 'uses-permission' in line:
468 |                 analysis.append(f"Permission: {line}")
469 |         
470 |         analysis_text = '\n'.join(analysis) if analysis else "No key elements found"
471 |         
472 |         return f"AndroidManifest.xml Analysis:\n\n{analysis_text}\n\nFull content:\n{content}"
473 | 
474 |     async def _extract_strings(self, apk_dir: str, locale: str = "") -> str:
475 |         """Extract string resources from decompiled APK"""
476 |         res_dir = Path(apk_dir) / "res"
477 |         if not res_dir.exists():
478 |             raise FileNotFoundError(f"Resources directory not found: {res_dir}")
479 |         
480 |         strings_files = []
481 |         if locale:
482 |             strings_files = list(res_dir.glob(f"values-{locale}/strings.xml"))
483 |         else:
484 |             strings_files = list(res_dir.glob("values*/strings.xml"))
485 |         
486 |         if not strings_files:
487 |             return f"No string files found for locale: {locale or 'default'}"
488 |         
489 |         all_strings = []
490 |         for strings_file in strings_files:
491 |             content = strings_file.read_text(encoding='utf-8')
492 |             all_strings.append(f"\n--- {strings_file.name} ---\n{content}")
493 |         
494 |         return f"Extracted strings from {len(strings_files)} files:\n{''.join(all_strings)}"
495 | 
496 |     async def _list_permissions(self, apk_dir: str) -> str:
497 |         """List all permissions from AndroidManifest.xml"""
498 |         manifest_path = Path(apk_dir) / "AndroidManifest.xml"
499 |         if not manifest_path.exists():
500 |             raise FileNotFoundError(f"AndroidManifest.xml not found in: {apk_dir}")
501 |         
502 |         content = manifest_path.read_text(encoding='utf-8')
503 |         permissions = []
504 |         
505 |         for line in content.split('\n'):
506 |             if 'uses-permission' in line:
507 |                 permissions.append(line.strip())
508 |         
509 |         if not permissions:
510 |             return "No permissions found in AndroidManifest.xml"
511 |         
512 |         return f"Found {len(permissions)} permissions:\n\n" + '\n'.join(permissions)
513 | 
514 |     async def _find_smali_references(self, apk_dir: str, pattern: str, 
515 |                                    case_sensitive: bool = True) -> str:
516 |         """Search for patterns in smali code"""
517 |         smali_dirs = []
518 |         apk_path = Path(apk_dir)
519 |         
520 |         # Find all smali directories
521 |         for item in apk_path.iterdir():
522 |             if item.is_dir() and item.name.startswith('smali'):
523 |                 smali_dirs.append(item)
524 |         
525 |         if not smali_dirs:
526 |             return "No smali directories found"
527 |         
528 |         matches = []
529 |         search_pattern = pattern if case_sensitive else pattern.lower()
530 |         
531 |         for smali_dir in smali_dirs:
532 |             for smali_file in smali_dir.rglob("*.smali"):
533 |                 try:
534 |                     content = smali_file.read_text(encoding='utf-8')
535 |                     search_content = content if case_sensitive else content.lower()
536 |                     
537 |                     if search_pattern in search_content:
538 |                         # Find line numbers
539 |                         lines = content.split('\n')
540 |                         for i, line in enumerate(lines, 1):
541 |                             line_search = line if case_sensitive else line.lower()
542 |                             if search_pattern in line_search:
543 |                                 matches.append(f"{smali_file.relative_to(apk_path)}:{i}: {line.strip()}")
544 |                 except Exception as e:
545 |                     continue
546 |         
547 |         if not matches:
548 |             return f"Pattern '{pattern}' not found in smali code"
549 |         
550 |         return f"Found {len(matches)} matches for '{pattern}':\n\n" + '\n'.join(matches[:50])
551 | 
552 |     async def _get_apk_info(self, apk_path: str) -> str:
553 |         """Get basic APK information using aapt or alternative"""
554 |         apk_file = Path(apk_path)
555 |         if not apk_file.exists():
556 |             raise FileNotFoundError(f"APK file not found: {apk_path}")
557 |         
558 |         try:
559 |             # Try using aapt if available
560 |             cmd = ["aapt", "dump", "badging", str(apk_file)]
561 |             result = await self._run_command(cmd)
562 |             return f"APK Information:\n\n{result}"
563 |         except:
564 |             # Fallback to basic file information
565 |             stat = apk_file.stat()
566 |             return f"APK File Information:\n" \
567 |                    f"Path: {apk_file}\n" \
568 |                    f"Size: {stat.st_size} bytes\n" \
569 |                    f"Modified: {stat.st_mtime}\n" \
570 |                    f"Note: aapt not available for detailed analysis"
571 | 
572 |     async def run(self, transport_type: str = "stdio"):
573 |         """Run the MCP server"""
574 |         if transport_type == "stdio":
575 |             from mcp.server.stdio import stdio_server
576 |             async with stdio_server() as (read_stream, write_stream):
577 |                 await self.server.run(read_stream, write_stream, 
578 |                                     self.server.create_initialization_options())
579 |         else:
580 |             raise ValueError(f"Unsupported transport type: {transport_type}")
581 | 
582 | # Server instance
583 | server_instance = None
584 | 
585 | async def main():
586 |     """Main entry point for the MCP server"""
587 |     import sys
588 |     
589 |     # Check if apktool is available
590 |     try:
591 |         process = await asyncio.create_subprocess_exec(
592 |             "apktool", "--version",
593 |             stdout=asyncio.subprocess.PIPE,
594 |             stderr=asyncio.subprocess.PIPE
595 |         )
596 |         await process.communicate()
597 |         if process.returncode != 0:
598 |             raise FileNotFoundError()
599 |     except:
600 |         print("Warning: apktool not found in PATH. Please install apktool first.", file=sys.stderr)
601 |         print("Visit: https://ibotpeaches.github.io/Apktool/install/", file=sys.stderr)
602 |     
603 |     # Create and run server
604 |     global server_instance
605 |     server_instance = ApktoolMCPServer()
606 |     await server_instance.run()
607 | 
608 | if __name__ == "__main__":
609 |     asyncio.run(main())
```