Skip to main content
Glama
frida-ios-mcp

frida-ios-mcp

by YPYT1

frida-mcp

TypeScript stdio MCP for Frida iOS exploration — Playwright-style loop:

device_list → app_list → session_open → wait_until_texts (TikTok) / wait → screen_snapshot
  → tap(ref) / swipe / type_text → screen_snapshot → … → session_close

If session_open hangs or Cursor cancel leaves MCP half-dead: session_status → session_force_unlock → retry one open. Independent of fleetcontrol (agent JS copied under agent/).

Requirements

Component

Version

Node.js

≥ 22

pnpm

9+

npm frida

17.x (this repo pins ^17.16.2)

iOS frida-server

same major as host npm frida (e.g. both 17.x)

USB

MVP: native USB only (no wecha TCP yet)

Python (media only)

3.x + pymobiledevice3 via FRIDA_MCP_PYTHON

Mismatch between host frida and phone frida-server → inject / session_open fails.

Related MCP server: ya-frida-mcp

Device prerequisites

This MCP targets jailbroken iPhones only. Without a jailbreak and a running frida-server, device_list may still show USB, but inject / touch / UI text collection will fail.

1. Supported jailbreak stacks

Stack

Notes

Dopamine

Common; works well with RootHide. This repo’s default spawn-only path is built for this class of devices.

Waterfall / Serotonin-family

Same requirement: a matching frida-server must run on the phone (daemon or manual start).

RootHide

Recommended for TikTok-like apps (reduces inject fingerprints). Do not rely on attach to an already-running app process (_touchesEvent often stays null).

Not supported: stock (non-jailbroken) devices, Developer Mode alone, pymobiledevice3 without Frida, or remote TCP Frida (USB-only MVP).

2. frida-server must be running on the phone

  1. Download frida-server from Frida releases with the same major as the host npm frida package (e.g. host frida@17.16.x → device frida-server 17.x).

  2. Push it to the device and chmod +x (paths vary by jailbreak; common: /var/jb/usr/sbin/frida-server or /usr/sbin/frida-server).

  3. Start it as root and keep it running, e.g.:

# On-device SSH / terminal (adjust path for your jailbreak)
sudo frida-server -D
# or foreground for debugging:
sudo frida-server
  1. Verify from the PC:

pnpm cli call device_list
# or
npx frida-ps -U

Only then start the MCP. If frida-server stops, later session_open calls will fail or hang.

3. Host machine

  • Node.js ≥ 22; pnpm install && pnpm build in this repo

  • USB cable + trusted computer

  • For Photos import: Python with pymobiledevice3, pointed to by FRIDA_MCP_PYTHON

4. TikTok / touch rules

Approach

Result

session_open (default spawn: kill → inject while suspended → resume)

Touch reliable

attach to already-foreground TikTok

Unreliable (blocked by default; FRIDA_MCP_ALLOW_ATTACH=1 escape hatch only)

Immediate Accessibility dump_tree after launch

Triggers anti-debug; this MCP uses safe text collection instead

Search UI tip: prefer tool tiktok_open_search from For You (taps the top-right magnifier with retries). The top wide field is the text input ([input]). The narrow 搜尋 / 搜索 / Search label on the right is a submit button (tap after typing) — never smart_type_text it.

Spawn-only (this device stack)

RootHide / TikTok / many jailbreak setups cannot use reliable attach (touch _touchesEvent null, or process dies).

Policy

Behavior

Default

Always mode=spawn

mode=attach

Forced to spawn + warning

Escape hatch

FRIDA_MCP_ALLOW_ATTACH=1 (not recommended)

kill old pid → device.spawn(bundleId) suspended → attach(pid) → inject agent → [netEnable?] → resume

Dual parallel: App + SpringBoard (+ Photos side channel)

Channel

Session field

Lock

How to open

App (TikTok)

live

appLock

session_open

SpringBoard

sbLive

sbLock

withSpringBoard:true / sb_ensure / first sb_*

Photos album

photosLive

photosLock

photos_ensure / photos_import_file

Stuck open / half-dead MCP: Cursor cancel does not abort server-side Frida. If device_list works but session_open / ping hang forever, check session_status (appLockBusy, appLockWaiters), then call session_force_unlock or restart the MCP process. CLI can still open apps because it is a different Node process with its own locks.

Env

Default

Meaning

FRIDA_MCP_OPEN_TIMEOUT_MS

60000

spawn/attach/inject total timeout

FRIDA_MCP_CLOSE_TIMEOUT_MS

8000

soft timeout for script unload/detach (won't pin the lock)

FRIDA_MCP_LOCK_WAIT_MS

90000

max wait to acquire appLock/sbLock

FRIDA_MCP_TOOLS

all

core = hide net/photos/dual extras from MCP tool list

FRIDA_MCP_ALLOW_DEBUG_TOOLS

unset (on)

Default all registers debug tools. Set 0/false/off to hide rpc_call / dump_modal / set_text_at_point only

session_open also has a hold timeout (~open+close+5s): if Frida close/spawn hangs but the event loop is alive, the lock is released with APP_LOCK_HOLD_TIMEOUT instead of pinning forever. On hold/open timeout the server best-effort kills inFlightPid / last app pid and sets orphanFridaOpPossible. Soft-close timeout on the app channel also kills that pid (SpringBoard is never killed).

Stuck / orphan recovery: session_status (look for orphanFridaOpPossible / inFlightPid) → session_force_unlock (kills orphan pid, clears flag) → one session_open. Do not immediately re-open while orphan is set.

  • Held in parallel — App + SB Frida scripts; Photos is a temporary third channel.

  • RPCs concurrent — separate locks; dual_ping / Promise.all([app…, sb…]) run together.

  • Not multi-app business — one app + SpringBoard; Photos is import/clear only.

  • photos_ never closes TikTok* — spawn Photos may briefly steal foreground.

Media import (PhotoKit, no fleetcontrol HTTP)

Requires Python 3 with pymobiledevice3 on the interpreter MCP actually runs (AFC).
MCP does not pip install for you. Missing deps → stage: afc within ~5s (preflight), not a 120s hang.

Pin the interpreter (recommended on Windows):

# CLI
set FRIDA_MCP_PYTHON=C:\Users\You\AppData\Local\Programs\Python\Python312\python.exe
"%FRIDA_MCP_PYTHON%" -m pip install pymobiledevice3

Cursor / Claude MCP config example:

{
  "mcpServers": {
    "frida-ios": {
      "command": "node",
      "args": ["D:/Project/tk/frida-mcp/dist/index.js"],
      "env": {
        "FRIDA_MCP_PYTHON": "C:\\Users\\You\\AppData\\Local\\Programs\\Python\\Python312\\python.exe"
      }
    }
  }
}
# image or small mp4 (prefer no other session_open during video import)
pnpm cli call photos_import_file --localPath D:\path\to\clip.mp4 --mediaType video
pnpm cli call photos_list --mediaType video
pnpm cli call photos_clear

Video tip: concurrent App sessions (e.g. Preferences open via session_open) can delay sqlite verify → needsRetry:true with a valid localIdentifier. Close other sessions and photos_list / re-import; do not treat needsRetry as silent success.

Tool

Role

photos_import_file

Upload + ensure Photos + PhotoKit import (+ sqlite verify); image or video

media_upload / photos_ensure / photos_import

Split steps for retry

photos_list

Untrashed assets; optional mediaType / idPrefix

photos_clear

Trash untrashed media (Recently Deleted), optional DCIM cleanup

AFC helper: scripts/afc_tool.py (preflight / push / list-untrashed / rm-dcim). Host = Photos.app only. SQLite query aligns with fleetcontrol (ZKIND in (0,1,2) + extensions).

session_open { bundleId: TikTok, withSpringBoard: true }
dual_ping                    # both channels pong at once
# later, can issue app + sb work concurrently if client allows

Install & build

cd D:\Project\tk\frida-mcp
pnpm install
pnpm build

Product surface

Surface

Role

MCP (frida-mcp)

Interactive AI/human probe (main)

CLI (cli/frida-ios.mjs)

Scripts / CI / one-shot

Core (src/backend.ts + session)

Shared API — not shared memory by default

MCP vs CLI sessions (read this)

  1. Embedded MCP = its own Node process + own sessionStore (Cursor/Grok default).

  2. CLI = another process; cannot see the MCP session.

  3. To share one Frida session: run the daemon, then set FRIDA_MCP_MODE=daemon on both MCP and CLI.

  4. Without daemon, open/close in CLI is independent of Cursor.

  5. App acts are serialized in-process (AI parallel tap+swipe will queue, not race).

# CLI (after build) — separate process unless daemon
pnpm cli help
pnpm cli open --bundleId com.ss.iphone.ugc.Ame --withSpringBoard
pnpm cli call wait --ms 4000
pnpm cli snap --limit 20
pnpm cli call net_dump --summaryOnly
pnpm cli close

Open-source safety (net_dump defaults):

  • Redact Authorization / Cookie / *Token*

  • Drop data: URLs (base64 images)

  • Fold binary / octet-stream body previews

  • summaryOnly: true for host counts only

  • redact:false / includeDataUrls / includeBinaryBodies only on trusted local machines — never paste raw dumps into issues/PRs.

Typing (real input path): Feed → tiktok_open_search (preferred; retries magnifier points) → smart_type_text on the wide [input] search bar → then tap the narrow 搜尋 / 搜索 / Search submit button to run the query.
Never smart_type_text on nav tabs or on the submit 搜尋 button itself.
Nav / composer chips (e.g. “What's on your mind”) are not fields → NOT_INPUT.

SB test alert: sb_alert_trigger → sb_alert_list (hasAlert) → single sb_alert_dismiss (post-settle cleared) or stacked sb_alert_dismiss({ all: true }); if needsRetry re-list / retry all.

Debug tools (rpc_call, dump_modal, set_text_at_point): registered by default in FRIDA_MCP_TOOLS=all (prefixed [debug]). Hide with FRIDA_MCP_ALLOW_DEBUG_TOOLS=0, or hide advanced+debug with FRIDA_MCP_TOOLS=core.

Modes

Session lives inside the MCP process. No daemon, no env:

node dist/index.js
# or
pnpm dev

Daemon + thin MCP (NSSM) — shared session with CLI

  1. Daemon holds Frida session on 127.0.0.1:18765

  2. stdio MCP and CLI forward when FRIDA_MCP_MODE=daemon (or FRIDA_MCP_DAEMON=1)

pnpm start:daemon
# other terminal / Cursor:
set FRIDA_MCP_MODE=daemon
node dist/index.js

Grok config (~/.grok/config.toml)

Why no args? MCP spawns command + optional args. We ship bin/frida-mcp.cmd which already runs node dist/index.js, so config only needs the launcher path:

[mcp_servers.frida-ios]
command = "D:/Project/tk/frida-mcp/bin/frida-mcp.cmd"
enabled = true

Optional daemon mode (daemon must be running separately):

[mcp_servers.frida-ios]
command = "D:/Project/tk/frida-mcp/bin/frida-mcp.cmd"
enabled = true

[mcp_servers.frida-ios.env]
FRIDA_MCP_MODE = "daemon"

Cursor mcp.json

{
  "mcpServers": {
    "frida-ios": {
      "command": "D:/Project/tk/frida-mcp/bin/frida-mcp.cmd"
    }
  }
}

TikTok red lines

  • Never dump_tree / find_view / find_buttons / dump_login_gate (MCP gates these).

  • Read UI only via screen_snapshot → collectTextsWithFrames.

  • Prefer session_open mode=spawn for reliable touch.

  • After open, wait 3000–5000 ms before first snapshot.

  • Refs only valid for last snapshot — re-snapshot after UI changes.

Tools

Tool

Purpose

device_list

Frida devices (default USB-only; usbOnly=false for all)

app_list

Apps + pid. Default userFacing=true filters Apple services; runningOnly / query supported

session_open

spawn | attach + inject

session_status / session_respawn / session_close / session_force_unlock

lifecycle + stuck-lock recovery (appLockBusy, openInFlight, refsValid)

wait / wait_until_texts

blind sleep vs poll until text; TikTok use preset:"tiktok_feed" (multi-locale)

ping

agent liveness

screen_window

simplified {width,height,x,y,cx,cy,className}

screen_snapshot / screen_search

texts refs are generation-scoped (g3t8); tree mode does not wipe texts refs

screen_shot

lockdown pixel screenshot (pymobiledevice3); visual assist — not for tap refs

tap / swipe / press_home / wait

act — swipe prefer durationMs (agent seconds; duration>10 = ms)

probe_help

Recommended probe loop + tool tiers (tools.core / advanced)

type_text

Humanized per-char typing into focused field (resnapshot default true)

smart_type_text

Preferred: tap → focus → humanized typing

clear_text / first_responder / human_pause

focus / clear / step-gap pause

double_tap

double-tap like at ref/x,y

set_otp

TikTok OTP fill (setOtpCode)

set_text_at_point

coordinate setText (not humanized; debug)

dump_modal

mid-screen modal (blocked on TikTok; debug)

rpc_call

whitelisted agent RPC ([debug]; on by default in all, hide with ALLOW_DEBUG_TOOLS=0)

process_list

device processes (pid/name)

sb_alert_trigger / sb_alert_list / sb_alert_tap / sb_alert_dismiss / sb_close

SpringBoard system alerts

net_enable / net_disable / net_clear / net_status / net_dump

in-process NSURLSession + TTNet/Cronet capture (TLS plaintext after app decrypt)

tiktok_inbox / tiktok_reply / tiktok_im / tiktok_posts / tiktok_sign

Inbox+MR read / composer reply / IM advanced / self posts / MetaSec sign I/O

Refs expire after tap/swipe and across snapshot generations. Off-screen / zero-size nodes are marked and rejected on tap.

Humanized typing (inputText)

Agent: agent/text_input/comment.js (same approach as fleetcontrol).

MCP tool

fleetcontrol counterpart

Behavior

type_text

TypeTextAction / HumanTypeInField

Field already focused; per-char inputText

smart_type_text

SmartTypeTextAction

tap → wait firstResponder → human_pause → type

human_pause

human_pause(min,max)

Random gap between steps (not inter-key delay)

  • Default perCharDelayMs=90; agent adds randomDelay(base, jitter≈base) (jitter ≥ 30ms).

  • Insert fallbacks: insertText → replaceRange → inner insertText → setText + notify.

  • Nav tabs / chips like “Home” or “What's on your mind” are not inputs → NOT_INPUT (session stays alive).

  • Prefer smart_type_text on a real field ref; use first_responder if unsure (canInsertText).

  • screen_snapshot defaults: onScreenOnly=true, limit=40; optional search / showDiff.

  • tap / swipe / smart_type_text default resnapshot=true (returns snapshot).

  • Errors return { code, recovery[] } (e.g. SCRIPT_DESTROYED → respawn).

  • Start probes with probe_help; prefer first-class tools over [debug] rpc_call (hide debug with FRIDA_MCP_ALLOW_DEBUG_TOOLS=0).

Network capture (reverse-engineering)

# Best: capture launch traffic (hooks before resume)
session_open {
  bundleId,
  captureNet: true,
  netOptions: { captureMode: "all", maxBody: 16384, captureResponse: true, signTrace: true }
}
  → use app (Inbox / Profile)
  → net_dump({ redact:false, dedupe:false, query:"imapi|inbox|/im/|profile/self|security-argus", includeBinaryBodies:true, limit:100 })
  → tiktok_sign({ action: "last" })   # recent sign headers (+ backtrace if signTrace)
  • captureMode: nsurl | ttnet | all (default).

  • Signing model (general capability): MCP does not reimplement offline X-Gorgon / X-Argus. Instead it uses in-process TTNet (TTNetworkManager JSON request) so the App's MetaSec signs, and net_* / tiktok_sign capture x-security-argus / x-Tt-Token / x-metasec-* I/O (+ optional signTrace backtrace).

  • iOS sign headers (45.x): x-security-argus, x-Tt-Token, x-metasec-*, ticket/device-guard — classic X-Gorgon / X-Argus names are often absent on this build.

  • Responses: captureResponse:true uses TTNet onReadResponseData + setIsCompleted. Do not hook onURLFetchComplete (kills script). Many JSON/IM payloads may still arrive empty via this path (protobuf/other channels); binary/CDN bodies usually populate. Prefer ttnetRequest callback JSON for posts/list APIs.

  • DM / works URLs seen on device: imapi-*.tiktokv.com/v1/message/get_by_user, …/v2/message/get_by_user_init, tiktok/v1/im/inbox_data/get, aweme/v1/user/profile/self, feed/post endpoints.

  • IM inbox/reply:

    • tiktok_inbox — refresh Inbox + Message Requests and return username, real text content, conversationId, peerUid.

    • tiktok_reply / tiktok_im send_text — default dryRun:true. With dryRun:false, opens the real chat composer (ChatInputTextView + 傳送), and returns sent:true only after the exact text is re-read from live AWEIMTextMessage models (blank bubbles / network-callback-only success are rejected). transport:"sdk" is disabled.

    • tiktok_im action: messages — list recent messages with real text when available; open_chat accepts conversationId or peerUid.

  • Posts (tiktok_posts): self post list via TTNet to aweme/v1/aweme/post/ (override url/userId if needed).

  • Add Phone popup attribution (no dedicated tool):

    1. session_open with captureNet + reproduce the popup

    2. net_dump({ query: "passport/account|bind_phone|mobile", redact:false, dedupe:false }) — avoid bare phone (matches iPhone device_type noise)

    3. tiktok_im({ action: "phone_status" }) — on this build: AWEUserService.sharedService → currentLoginUser:

      • havePhoneNumber / isPhoneBinded false + empty bindPhone ⇒ account unbound (not just a cosmetic prompt)

      • canShowThirdPartyPhoneBindingPopup / is3pBindPopupRequestShow distinguish third-party bind UI vs mandatory add-phone

    4. Boot traffic often includes passport/account/info/v2 and passport/token/beat/v2

  • RE dump tip: redact:false, dedupe:false, includeBinaryBodies:true.

Advanced TikTok tools (not core)

Tool

Purpose

tiktok_inbox

Read Inbox + Message Requests/notification messages (username/content/conversation ID)

tiktok_reply

Reply via real chat composer; succeeds only after exact text re-read

tiktok_im

Advanced IM diagnostics / compatibility actions

tiktok_posts

Self works list (in-app signed TTNet)

tiktok_sign

last sign headers or enable_trace

NSSM (only after device tools work)

# Admin
cd D:\Project\tk\frida-mcp
pnpm build
.\scripts\install-nssm-service.ps1
nssm start FridaMcpDaemon

Uninstall: .\scripts\uninstall-nssm-service.ps1

Logs: logs/daemon.stdout.log, logs/daemon.stderr.log

Typical probe

  1. device_list

  2. app_list → find TikTok bundle

  3. session_open { "bundleId": "…", "mode": "spawn" }

  4. wait { "ms": 4000 }

  5. ping → pong

  6. screen_snapshot → refs

  7. tap { "ref": "t3" } → screen_snapshot again

Agent

  • Source: agent/agent_main.js (+ imports), includes ping

  • Override: FRIDA_AGENT_ENTRY=path/to/agent_main.js

  • Compile: Frida Compiler, projectRoot = repo root (frida-objc-bridge in node_modules)

License

Private / internal use.

Available Tools

53 tools
app_listA

Enumerate apps: identifier, name, pid. Default userFacing=true filters noisy Apple services. Use runningOnly for live apps, query for name/id substring.

Inputs: udid, query, userFacing, runningOnly.

clear_textA

Clear current firstResponder text field (setText empty).

Inputs: no named parameters.

device_listA

List Frida devices. Default USB-only. Need matching frida-server on phone (17.x with this package).

Inputs: usbOnly.

double_tapC

Double-tap (like) at ref or x,y. gapMs default 140. resnapshot default true.

Inputs: x, y, ref, gapMs, +1 more.

dual_pingA

[advanced] Parallel health: app ping + SpringBoard ping at the same time (proves dual inject + concurrent locks).

Inputs: no named parameters.

dump_modalD

[debug] dumpModalView. BLOCKED on TikTok. Daily probe: do not use.

Inputs: no named parameters.

first_responderA

Current firstResponder info (className/frame/canInsertText). Check focus before type_text.

Inputs: no named parameters.

human_pauseA

Random step gap sleep (fleetcontrol human_pause). Not typing delay — use between actions.

Inputs: maxMs, minMs.

media_uploadC

[advanced] AFC upload PC file to /DCIM/100APPLE/{IMG|VID}_XXXX.ext. Needs Python + pymobiledevice3. stage=upload on failure.

Inputs: localPath (required), mediaType (required), udid.

net_clearB

[advanced] Clear captured network buffer.

Inputs: no named parameters.

net_disableA

[advanced] Stop recording new network entries (buffer retained until net_clear).

Inputs: no named parameters.

net_dumpB

[advanced] Quiet HTTP dump. Entries may include stack=nsurl|ttnet, signHeaders, query, backtrace (if signTrace). rawCount=buffer; returned(=count)=entries after filter. Default: redact, DROP data: URLs, FOLD binary, dedupe method+url. RE tip: redact:false dedupe:false query:"tiktokv" or query:"sign_header". Add Phone: query:"phone|bind|mobile|passport|verify".

Inputs: limit, query, dedupe, redact, +3 more.

net_enableA

[advanced] Start in-process HTTP capture. captureMode: nsurl | ttnet | all (default all). ttnet hooks TikTok TTHttpTaskChromium AFTER request filters (api.tiktokv.com + headers/sign fields). signTrace:true attaches module+offset backtrace on sign_header writes (MetaSec RE). captureResponse wraps TTNet onReadResponseData+setIsCompleted (stable). NSURLSession wrap skipped when TTHttpTaskChromium present. Each call RESETS opts. Typical RE: session_open({captureNet:true, netOptions:{signTrace:true}}) → use app → net_dump / tiktok_sign.

Inputs: maxBody, signTrace, urlFilter, captureMode, +1 more.

net_statusB

[advanced] Network capture status: enabled, hooksInstalled, count, options.

Inputs: no named parameters.

photos_clearA

[advanced] PhotoKit trash all untrashed image/video (Recently Deleted), verify count=0, optional DCIM source cleanup. needsRetry if leftover.

Inputs: udid, clearDcim.

photos_ensureA

[advanced] Spawn+resume Photos.app (com.apple.mobileslideshow), settle ~4s, inject photos agent. Does not close TikTok/app session. May steal foreground briefly.

Inputs: udid, settleMs.

photos_importB

[advanced] PhotoKit import already-on-device file (devicePath or remotePath under /DCIM). Host=Photos.app only. Returns localIdentifier.

Inputs: mediaType (required), udid, devicePath, remotePath, +1 more.

photos_import_fileA

[advanced] One-shot: AFC upload + Photos ensure + PhotoKit import + optional sqlite verify. Preferred for AI. Needs FRIDA_MCP_PYTHON with pymobiledevice3 (no auto pip). Missing deps → stage=afc in ≤5s. Accepts image or small mp4 (mediaType=video). Video: avoid parallel session_open other apps or expect needsRetry + photos_list.

Inputs: localPath (required), mediaType (required), udid, verify.

photos_listB

[advanced] Pull Photos.sqlite via AFC; list untrashed assets (not Recently Deleted). Optional mediaType=image|video and idPrefix/localIdentifier filter. Default = all untrashed. Needs FRIDA_MCP_PYTHON with pymobiledevice3 (fast-fail stage=afc if missing).

Inputs: udid, idPrefix, mediaType, localIdentifier.

pingB

Agent liveness probe (returns pong). Do not use wrong RPC names as probes.

Inputs: no named parameters.

press_homeA

Background current app (suspend) so SpringBoard shows. Session may remain attached to previous app.

Inputs: no named parameters.

probe_helpA

Recommended probe loop and which tools to prefer/avoid. Call first in a new session.

Inputs: no named parameters.

process_listA

[advanced] List device processes (pid, name). query is LITERAL substring only (not regex; SpringBoard ok, a|b is wrong).

Inputs: udid, limit, query.

rpc_callC

[debug] Whitelisted agent RPC. Prefer first-class tools + probe_help.

Inputs: name (required), args.

sb_alert_dismissA

Dismiss SB alert. Default policy=deny. all=false: one layer after ~300ms settle (trust cleared; empty → cleared:true rounds:0). Stacked: all=true (loop until clear or maxRounds=5). Returns cleared/remaining/rounds/needsRetry. If needsRetry or cleared=false after settle → re-list or all:true again. Do not parallel tap+dismiss.

Inputs: all, policy, maxRounds.

sb_alert_listA

List SpringBoard alerts. Live actionViewCount + actionViewCountRaw (raw may be higher; live can undercount stacks). hasAlert if either count path shows UI. After force: do not trust actionViewCount===1 — use sb_alert_dismiss({all:true}). After dismiss → app screen_snapshot.

Inputs: no named parameters.

sb_alert_tapA

[advanced] Tap SpringBoard alert button by title. Then call app screen_snapshot. Do not parallel with dismiss.

Inputs: title (required).

sb_alert_triggerA

[advanced] Create a test system alert (SBAlertItemTestRecipe). Default force=false: skip if alert already present (no stack). force:true stacks another. Next: sb_alert_list → sb_alert_dismiss({all:true}) or sb_alert_tap.

Inputs: force.

sb_closeA

Detach SpringBoard Frida session (app session_open stays open).

Inputs: no named parameters.

sb_ensureA

[advanced] Attach SpringBoard now without listing alerts. Use to warm dual session; then app tools + sb_* can run in parallel.

Inputs: no named parameters.

screen_shotA

Pixel screenshot via lockdown ScreenshotService (pymobiledevice3) — NOT Accessibility, not Frida UI dump. Use when texts are sparse / visual layout unclear. Still prefer screen_snapshot for tap refs. Needs FRIDA_MCP_PYTHON + pymobiledevice3 (+ optional Pillow for JPEG). Returns image + meta.

Inputs: udid, quality.

screen_snapshotA

Read screen → generation-scoped refs (g3t8). PRIMARY read tool. Defaults: onScreenOnly=true, limit=40 (token-safe). TikTok: texts only (tree blocked). search= substring by default; a|b auto-enables regex. showDiff=true compares to previous snapshot. Do not parallelize app acts (tap/swipe/type).

Inputs: mode, limit, search, showDiff, +2 more.

screen_windowA

Key window size: {width,height,x,y,cx,cy,className}. Safe on TikTok.

Inputs: no named parameters.

session_closeA

Close app session. Default also closes SpringBoard and clears Photos side channel (photosAlive). closeSpringBoard=false keeps SB; closePhotos=false keeps Photos.

Inputs: closePhotos, closeSpringBoard.

session_force_unlockA

Emergency: reset stuck locks, detach sessions, kill in-flight/last app pid. Use when orphanFridaOpPossible or open hangs. Then ONE session_open.

Inputs: no named parameters.

session_openA

Open long-lived Frida session (spawn-only on this stack). ALWAYS uses mode=spawn: kill → spawn suspended → inject agent → resume. mode=attach is ignored/forced to spawn unless FRIDA_MCP_ALLOW_ATTACH=1 (unreliable: touch/net). TikTok: after open, wait 3–5s before screen_snapshot. Never dump_tree/find_view. spawn restarts the process (login UI state may reset). captureNet=true installs NSURLSession + TTNet hooks before resume (launch traffic). withSpringBoard=true attaches SpringBoard in parallel (dual inject; App+SB concurrent RPCs).

Inputs: bundleId (required), mode, udid, captureNet, +2 more.

session_respawnA

Force spawn+inject+resume for current bundle. Kills the app process. Prefer only when session is dead.

Inputs: no named parameters.

session_statusB

Session health: alive, refsValid/hasSnapshot, lastSnapshotGeneration, openInFlight, appLockBusy/waiters, recovery[].

Inputs: no named parameters.

set_otpA

Fill TikTok OTP (TMVerificationCodeInputView / TUXPinField). Pass full code string e.g. 123456.

Inputs: code (required), source.

set_text_at_pointB

[debug] setText at point — NOT 拟人. Prefer type_text / smart_type_text. Prefer first-class tools for daily probes.

Inputs: text (required), x, y, ref.

smart_type_textA

PREFERRED typing: tap real input (ref|x,y) → wait typable FR → 拟人逐字. Rejects chrome/chips (好友/有什麼好事). TikTok AWESearchBar may canInsertText=false but still types. Prefer wide search-bar [input]; avoid hot-search chips. retryOnFail default false. resnapshot default true.

Inputs: text (required), x, y, ref, +4 more.

swipeA

Swipe direction or path. Prefer durationMs (e.g. 280). Agent uses seconds; duration>10 is treated as ms (avoids 280→280s lock traps). Clamped ~0.15–2.5s. resnapshot default true (feed browse: set false then one snapshot).

Inputs: x0, x1, y0, y1, +4 more.

tapA

Tap by ref (gNtM) or x,y. Default resnapshot=true returns new screen_snapshot in result.snapshot. Set resnapshot=false only when chaining many acts then one snapshot.

Inputs: x, y, ref, resnapshot.

tiktok_imB

[advanced] In-process TikTok IM network runtime. action: status | conversations | inbox | send_text | messages | open_chat | peer_conversation | phone_status. inbox refreshes normal chats and Message Requests/notification pages without UI navigation; returns username + newest content. send_text defaults dryRun:true. With dryRun:false, transport defaults to the real chat composer and succeeds only after the exact text is re-read from live message models. messages: peer nickname/content/time (best-effort). open_chat: open message VC. peer_conversation: build id from peerUid.

Inputs: action (required), text, limit, dryRun, +6 more.

tiktok_inboxA

[advanced] Refresh TikTok Inbox plus Message Requests/notification entries without UI navigation. Returns username, content, conversationId, peerUid, and isMessageRequest.

Inputs: limit, timeoutMs, onlyUnread.

tiktok_postsC

[advanced] List current user's posts via in-process TTNet (App MetaSec signs). Returns awemeId, desc, createTime, shareUrl, stats.

Inputs: url, count, cursor, userId.

tiktok_replyB

[advanced] Reply through TikTok's real chat composer. With dryRun:false, opens the chat, types into ChatInputTextView, taps 傳送, and succeeds only after the exact text is re-read from live message models.

Inputs: text (required), dryRun, peerUid, conversationId, +1 more.

tiktok_signB

[advanced] MetaSec sign observability (NOT offline Argus recompute). action: last | enable_trace. last → recent sign_header / signHeaders entries (x-security-argus, x-Tt-Token, …). enable_trace → net_enable({signTrace:true, captureMode:ttnet}). Prefer session_open captureNet+signTrace.

Inputs: limit, action.

type_textA

拟人逐字 into ALREADY-FOCUSED field (TypeTextAction). Default perCharDelayMs=90 + jitter. If field not focused → use smart_type_text instead. resnapshot default true.

Inputs: text (required), resnapshot, perCharDelayMs.

waitA

Sleep N milliseconds. Prefer wait_until_texts after TikTok session_open instead of blind wait.

Inputs: ms (required).

wait_until_textsA

Poll screen_snapshot until on-screen text matches pattern or preset (or timeout). TikTok: prefer preset "tiktok_feed" (EN/ZH-Hant/ZH-Hans/JA/KO) — do not hardcode one language. Custom pattern still allowed for page-specific probes.

Inputs: preset, pattern, timeoutMs, intervalMs, +2 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.2/5.0

    Scored across 53 tools

    Disambiguation5/5

    Tools are highly specific with detailed descriptions, making each one clearly distinct. Even overlapping areas like typing (type_text vs smart_type_text) are well-differentiated.

    Naming Consistency4/5

    Most tools follow a consistent snake_case convention, but there are minor inconsistencies like screen_snapshot vs screen_shot and verb_noun vs noun_verb order. Prefixes for subdomains (tiktok_, sb_, photos_) aid clarity.

    Tool Count3/5

    With 53 tools, the set is extensive and covers a broad domain, but the count is high compared to typical MCP servers. While many are advanced or debug-specific, it still feels heavy for a single server.

    Completeness4/5

    The tool set covers device management, session handling, UI interaction, network capture, and TikTok-specific features, leaving few obvious gaps. Advanced tools add depth for power users.

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      An MCP-compliant server that enables AI systems to interact with mobile and desktop applications through Frida's dynamic instrumentation capabilities, allowing for process management, device control, JavaScript execution, and script injection.
      465
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      A comprehensive MCP server for Frida dynamic instrumentation, enabling AI agents to manage devices, processes, scripts, memory, and ADB operations.
      39
      37
      MIT
    • A
      license
      C
      quality
      D
      maintenance
      A Frida MCP server for authorized dynamic analysis and application security research, exposing device/session management, script injection, process and memory inspection, and platform-specific workflows.
      2
      100
      4
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      MCP server that wraps the Frida dynamic instrumentation toolkit, allowing users to attach to processes, hook functions, enumerate modules and exports, and manage scripts through natural language.
      10
      1
      MIT