Ios Files
About
A local MCP server that lets AI clients safely read and write files on jailbroken iOS devices over SSH/SFTP.
Explore
- Read‑only by default; optional write capability with approval system.
- List, stat, search, and hash files on the iOS device.
- Read text files, plists, SQLite databases, and React Native bundles.
- Detect installed apps and resolve their containers.
- Inspect and decode Hermes bytecode bundles.
- Static analysis via radare2 (device‑side or local fallback).
- Supports all major MCP clients with automatic config setup.
Requirements:
- Node.js 20+
- OpenSSH running on your iOS device
- Your computer can SSH to the device
Find the iOS device IP in Settings -> Wi-Fi -> your network -> IP Address, then test:
ssh [email protected]
Run the command for your coding agent. Replace 192.168.1.23 and change-me. Your default ssh password is alpine if you haven't changed it.
The installer writes this command:
{
"command": "npx",
"args": ["--yes", "--quiet", "github:xtofuub/ios-files-mcp"],
"env": {
"IOS_FILES_MCP_HOST": "192.168.1.23",
"IOS_FILES_MCP_USERNAME": "mobile",
"IOS_FILES_MCP_PASSWORD": "change-me"
}
}
Use that under mcpServers.ios-files for Claude/Cline-style clients, or under servers.ios-files for VS Code.
For an explicit package install:
npm install github:xtofuub/ios-files-mcp
To make npm install also write MCP config, set installer env vars first:
$env:IOS_FILES_MCP_INSTALL_CLIENTS="codex"
$env:IOS_FILES_MCP_HOST="192.168.1.23"
$env:IOS_FILES_MCP_USERNAME="mobile"
$env:IOS_FILES_MCP_PASSWORD="change-me"
npm install github:xtofuub/ios-files-mcp
Add this env var if you also want Hermes decoders:
$env:IOS_FILES_MCP_INSTALL_HERMES="true"
Useful env vars:
IOS_FILES_MCP_HOST
IOS_FILES_MCP_PORT
IOS_FILES_MCP_USERNAME
IOS_FILES_MCP_PASSWORD
IOS_FILES_MCP_KEY_PATH
IOS_FILES_MCP_ALLOWED_ROOTS
IOS_FILES_MCP_READ_ONLY
IOS_FILES_MCP_ALLOW_WRITES
IOS_FILES_MCP_REQUIRE_WRITE_APPROVAL
IOS_FILES_MCP_ENABLE_R2
IOS_FILES_MCP_R2_MODE
IOS_FILES_MCP_R2_DEVICE_R2_PATH
IOS_FILES_MCP_R2_DEVICE_RABIN2_PATH
IOS_FILES_MCP_R2_PATH
IOS_FILES_MCP_RABIN2_PATH
IOS_FILES_MCP_R2_TIMEOUT_MS
IOS_FILES_MCP_R2_MAX_OUTPUT_BYTES
IOS_FILES_MCP_R2_MAX_BINARY_SIZE
IOS_FILES_MCP_SFTP_OP_TIMEOUT_MS
IOS_FILES_MCP_CONFIG
Most users should use MCP env. JSON config files are only needed for advanced/local setups.
Minimal example:
{
"host": "192.168.1.23",
"port": 22,
"username": "mobile",
"password": "change-me",
"readOnly": true,
"allowWrites": false
}
See ios-files-mcp.config.example.json for every option.
Point MCP at the config file with IOS_FILES_MCP_CONFIG:
{
"servers": {
"ios-files": {
"command": "npx",
"args": [
"--yes",
"--quiet",
"github:xtofuub/ios-files-mcp"
],
"env": {
"IOS_FILES_MCP_CONFIG": "/path/to/ios-files-mcp/ios-files-mcp.config.json"
}
}
}
}
Or pass it as an arg:
{
"mcpServers": {
"ios-files": {
"command": "npx",
"args": [
"--yes",
"--quiet",
"github:xtofuub/ios-files-mcp",
"--config",
"/path/to/ios-files-mcp/ios-files-mcp.config.json"
]
}
}
}
By default these tools detect radare2 on the iOS device and run it there over SSH — no binary copy needed. If r2 is not installed on the device, the MCP falls back to running r2/rabin2 on your computer after copying the binary to a temporary local folder. Run ios_r2_check to see which runner is active.
Recommended install (on the iOS device, via Sileo):
1. Open Sileo on the jailbroken device.
2. Install the radare2 package from the Procursus repo (default on modern jailbreaks like Dopamine and palera1n).
3. From your computer, run ssh mobile@<device-ip> 'r2 -v' to confirm.
Common device paths after Sileo install:
/usr/bin/r2 (rootful jailbreaks: unc0ver, checkra1n, classic palera1n)
/var/jb/usr/bin/r2 (rootless jailbreaks: Dopamine, palera1n rootless)
The MCP probes command -v r2 over SSH, so it picks up whatever is on the device's $PATH. Override with IOS_FILES_MCP_R2_DEVICE_R2_PATH=/your/path if the binary is in a non-standard location.
Modes:
- IOS_FILES_MCP_R2_MODE=auto (default) — try device first, fall back to local.
- IOS_FILES_MCP_R2_MODE=device — require device-side r2; fail fast if missing.
- IOS_FILES_MCP_R2_MODE=local — always run on this computer (copies binary to a temp folder).
Optional env:
IOS_FILES_MCP_ENABLE_R2=true
IOS_FILES_MCP_R2_MODE=auto
IOS_FILES_MCP_R2_DEVICE_R2_PATH=/var/jb/usr/bin/r2
IOS_FILES_MCP_R2_DEVICE_RABIN2_PATH=/var/jb/usr/bin/rabin2
IOS_FILES_MCP_R2_PATH=r2
IOS_FILES_MCP_RABIN2_PATH=rabin2
IOS_FILES_MCP_R2_TIMEOUT_MS=30000
IOS_FILES_MCP_R2_MAX_OUTPUT_BYTES=16777216
IOS_FILES_MCP_R2_MAX_BINARY_SIZE=134217728
Migration note: the previous host-side installer (ios-files-mcp-install-radare2) and the IOS_FILES_MCP_INSTALL_R2 postinstall flag have been removed. Install radare2 on the iOS device via Sileo, or install locally with your own package manager if you prefer IOS_FILES_MCP_R2_MODE=local.
When to use:
- Use ios_r2_app_triage(bundleId) for the fastest overview of an installed app.
- Use ios_r2_binary_info(remotePath) when you already know the binary path.
- Use ios_r2_strings(remotePath, query, limit) for endpoints, secrets, Firebase, URLs, debug strings, and feature flags.
- Use ios_r2_imports(remotePath, query, limit) for framework/API usage such as Keychain, crypto, networking, SQLite, WebKit, device integrity, and anti-debug checks.
- Use ios_r2_functions(remotePath, limit) to map available functions.
- Use ios_r2_function_disasm(remotePath, functionNameOrAddress) to inspect one selected function or address.
Recommended analysis flow:
1. ios_find_app("App Name") or ios_resolve_app_container("com.example.app")
2. ios_r2_app_triage("com.example.app")
3. ios_r2_strings(remotePath, query, limit) with queries like http, api, firebase, token, auth, key, debug
4. ios_r2_imports(remotePath, query, limit) with queries like SecItem, CommonCrypto, CryptoKit, NSURLSession, SQLite, WKWebView
5. ios_r2_functions(remotePath, limit)
6. ios_r2_function_disasm(remotePath, functionNameOrAddress) on a specific interesting function or address
| Tool | What it does |
| --- | --- |
| ios_r2_check() | Shows whether r2 support is enabled and whether local r2/rabin2 are available. |
| ios_r2_binary_info(remotePath) | Returns Mach-O metadata and linked libraries for one binary path. |
| ios_r2_app_triage(bundleId) | Resolves an installed app, finds its executable, and returns binary info, interesting imports/strings, functions preview, and next actions. |
| ios_r2_strings(remotePath, query, limit) | Searches binary strings for URLs, endpoints, tokens, Firebase config, debug text, and feature flags. |
| ios_r2_imports(remotePath, query, limit) | Searches imported symbols/framework APIs such as Keychain, crypto, networking, SQLite, WebKit, and anti-debug calls. |
| ios_r2_functions(remotePath, limit) | Lists function names and addresses before deeper inspection. |
| ios_r2_function_disasm(remotePath, functionNameOrAddress) | Returns structured JSON disassembly for one selected function or address. |
MCP stdio server for controlled SSH/SFTP access to an iOS device filesystem.
AI MCP client -> ios-files-mcp on your computer -> SSH/SFTP -> iOS device
Quick Install
Requirements:
- Node.js 20+
- OpenSSH running on your iOS device
- Your computer can SSH to the device
Find the iOS device IP in Settings -> Wi-Fi -> your network -> IP Address, then test:
ssh [email protected]
Run the command for your coding agent. Replace 192.168.1.23 and change-me. Your default ssh password is alpine if you haven't changed it.
Codex
npx -p github:xtofuub/ios-files-mcp iosfiles-mcp --client codex --host 192.168.1.23 --password change-me
Writes to ~/.codex/config.toml.
Claude Desktop
npx -p github:xtofuub/ios-files-mcp iosfiles-mcp --client claude --host 192.168.1.23 --password change-me
Writes to Claude Desktop's MCP config.
OpenCode
npx -p github:xtofuub/ios-files-mcp iosfiles-mcp --client opencode --host 192.168.1.23 --password change-me
Writes to ~/.config/opencode/opencode.json.
VS Code
Run this from the workspace folder where you want the MCP server enabled.
npx -p github:xtofuub/ios-files-mcp iosfiles-mcp --client vscode --host 192.168.1.23 --password change-me
Writes to .vscode/mcp.json.
All Supported Clients
npx -p github:xtofuub/ios-files-mcp iosfiles-mcp --client all --host 192.168.1.23 --password change-me
Supported --client values:
codex -> ~/.codex/config.toml
claude -> Claude Desktop config
opencode -> ~/.config/opencode/opencode.json
vscode -> .vscode/mcp.json in the current folder
all -> all supported clients
The installer writes an ios-files MCP server entry and backs up existing config files to .bak.
Install with optional Hermes bytecode decoders:
npx -p github:xtofuub/ios-files-mcp iosfiles-mcp --client codex --host 192.168.1.23 --password change-me --install-hermes
Hermes decoders are only needed for React Native Hermes bytecode bundle decoding. The flag installs hermes-dec with Python/pipx when available.
For radare2 static analysis, see the radare2 section below — recommended path is to install radare2 on the iOS device itself via Sileo.
USB SSH
Forward iOS device SSH to a local port with iproxy, then install using localhost:
ssh -p 2222 [email protected]
npx -p github:xtofuub/ios-files-mcp iosfiles-mcp --client codex --host 127.0.0.1 --port 2222 --password change-me
USB SSH still uses normal SSH auth, so use a password or SSH key.
Manual MCP Config
The installer writes this command:
{
"command": "npx",
"args": ["--yes", "--quiet", "github:xtofuub/ios-files-mcp"],
"env": {
"IOS_FILES_MCP_HOST": "192.168.1.23",
"IOS_FILES_MCP_USERNAME": "mobile",
"IOS_FILES_MCP_PASSWORD": "change-me"
}
}
Use that under mcpServers.ios-files for Claude/Cline-style clients, or under servers.ios-files for VS Code.
For an explicit package install:
npm install github:xtofuub/ios-files-mcp
To make npm install also write MCP config, set installer env vars first:
$env:IOS_FILES_MCP_INSTALL_CLIENTS="codex"
$env:IOS_FILES_MCP_HOST="192.168.1.23"
$env:IOS_FILES_MCP_USERNAME="mobile"
$env:IOS_FILES_MCP_PASSWORD="change-me"
npm install github:xtofuub/ios-files-mcp
Add this env var if you also want Hermes decoders:
$env:IOS_FILES_MCP_INSTALL_HERMES="true"
Useful env vars:
IOS_FILES_MCP_HOST
IOS_FILES_MCP_PORT
IOS_FILES_MCP_USERNAME
IOS_FILES_MCP_PASSWORD
IOS_FILES_MCP_KEY_PATH
IOS_FILES_MCP_ALLOWED_ROOTS
IOS_FILES_MCP_READ_ONLY
IOS_FILES_MCP_ALLOW_WRITES
IOS_FILES_MCP_REQUIRE_WRITE_APPROVAL
IOS_FILES_MCP_ENABLE_R2
IOS_FILES_MCP_R2_MODE
IOS_FILES_MCP_R2_DEVICE_R2_PATH
IOS_FILES_MCP_R2_DEVICE_RABIN2_PATH
IOS_FILES_MCP_R2_PATH
IOS_FILES_MCP_RABIN2_PATH
IOS_FILES_MCP_R2_TIMEOUT_MS
IOS_FILES_MCP_R2_MAX_OUTPUT_BYTES
IOS_FILES_MCP_R2_MAX_BINARY_SIZE
IOS_FILES_MCP_SFTP_OP_TIMEOUT_MS
IOS_FILES_MCP_CONFIG
Optional JSON Config File
Most users should use MCP env. JSON config files are only needed for advanced/local setups.
Minimal example:
{
"host": "192.168.1.23",
"port": 22,
"username": "mobile",
"password": "change-me",
"readOnly": true,
"allowWrites": false
}
See ios-files-mcp.config.example.json for every option.
Point MCP at the config file with IOS_FILES_MCP_CONFIG:
{
"servers": {
"ios-files": {
"command": "npx",
"args": [
"--yes",
"--quiet",
"github:xtofuub/ios-files-mcp"
],
"env": {
"IOS_FILES_MCP_CONFIG": "/path/to/ios-files-mcp/ios-files-mcp.config.json"
}
}
}
}
Or pass it as an arg:
{
"mcpServers": {
"ios-files": {
"command": "npx",
"args": [
"--yes",
"--quiet",
"github:xtofuub/ios-files-mcp",
"--config",
"/path/to/ios-files-mcp/ios-files-mcp.config.json"
]
}
}
}
Local Test
This should print help and exit:
npx --yes --quiet github:xtofuub/ios-files-mcp --help
This starts the MCP server and waits for an MCP client:
$env:IOS_FILES_MCP_HOST="192.168.1.23"
$env:IOS_FILES_MCP_USERNAME="mobile"
$env:IOS_FILES_MCP_PASSWORD="change-me"
npx --yes --quiet github:xtofuub/ios-files-mcp
Press Ctrl+C to stop it.
Development
From a clone:
npm install
npm run build
npm run typecheck
node dist/index.js --help
For local MCP testing without NPX, point your MCP client at node dist/index.js with an absolute path.
First MCP Calls
If app directories look empty, start here:
ios_connection_doctor()
ios_doctor()
ios_diagnose_roots()
Check local MCP client config:
ios_mcp_config_status()
ios_config()
To find YouTube:
ios_find_app("YouTube")
ios_find_app("com.google.ios.youtube")
ios_snapshot_app("com.google.ios.youtube")
ios_app("com.google.ios.youtube")
To inspect an app plist:
ios_read_plist("/private/var/containers/Bundle/Application/<UUID>/YouTube.app/Info.plist")
App Paths
App data containers:
/var/mobile/Containers/Data/Application/<UUID>
/private/var/mobile/Containers/Data/Application/<UUID>
App Store .app bundles:
/var/containers/Bundle/Application/<UUID>/<AppName>.app
/private/var/containers/Bundle/Application/<UUID>/<AppName>.app
Info.plist is usually in the .app bundle, not the data container.
Safety
The server is read-only by default. Writes require both:
{
"readOnly": false,
"allowWrites": true
}
When writes are enabled, write approval is still required by default:
{
"requireWriteApproval": true,
"writeApprovalTtlMs": 300000
}
Write-capable tools do not write on the first call. They return an approval request with an approvalId. If you approve the exact operation, call the same tool again with the same arguments plus that approvalId.
Approval ids are:
one-use
time-limited
bound to the exact tool name and arguments
Example:
ios_write_file("/var/mobile/test.txt", "hello")
Returns an approval request. Then, only if approved:
ios_write_file("/var/mobile/test.txt", "hello", approvalId="the-id-from-the-request")
Blocked by default:
/var/Keychains
/var/mobile/Library/Accounts
/var/mobile/Library/SMS
/var/mobile/Library/Mail
/private/var/db
/System
/usr
/bin
/sbin
Every operation is logged to ios-files-mcp.log. File contents and secrets are not logged.
Tools
Basic Filesystem
…
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.



