-
Notifications
You must be signed in to change notification settings - Fork 0
Tools
102 tools across seven groups. Which ones a session sees is controlled by SWITCH_MCP_TOOLS — unset gives the four default groups, all gives everything, or pass a comma-separated list. See Configuration.
Tools marked destructive need a two-call confirmation: call once without confirm to get a token describing exactly what will happen, then call again with it. Set SWITCH_MCP_ALLOW_DESTRUCTIVE=1 to skip that for unattended use.
Connectivity, screen, and input. Registered by default.
What this console's agent will actually permit right now.
Reports the capability tier (observe = read-only, control = drive the console, invasive = NAND/hardware/memory writes), whether it is running on emuMMC (where mistakes are recoverable by restoring an SD image) or sysMMC (where they may not be), and the exact command list allowed at that tier. Check this before planning destructive work: a command absent from the list will be refused no matter how it is called, and the fix is a config change on the SD card plus an agent restart, not a different tool call.
Capture the console's current screen as a JPEG.
Coordinates for tap/swipe are ALWAYS in native 1280x720 space whatever scale you
pass — scaling changes only the returned picture, never the coordinate system. scale
defaults to 0.5 (640x360): that reads menus, text and UI state perfectly well at roughly
a quarter of the token cost. Use scale=1.0 only when you need fine detail, such as small
text or a rendering bug.
One-shot triage snapshot: system info, running app, process list, recent
crash reports, and a screenshot. Use this first when investigating a problem.
Scan the local network for a console running switch-agentd. Useful when
the console's IP changed (DHCP). Uses SWITCH_PORT/SWITCH_TOKEN from the env.
Check connectivity to the console. Returns agent version and firmware,
or a clear error if the console is unreachable (asleep? IP changed?).
Read the PHYSICAL controller state — what the human is holding right now.
Useful to see whether someone is actively using the console before you take it over, and to confirm a real controller is connected.
Record real controller input into a replayable sequence.
Turns "describe the button presses you want" into "do it once on the console and keep
it". The returned sequence is exactly the shape send_input_sequence consumes, so it
can be replayed with no editing, and the gaps between presses are preserved as wait_ms.
Sampled every 50ms; capped at 20s.
Wait until the screen changes, then return the new screen.
Much cheaper than polling capture_screen in a loop: frames are compared on the server with a perceptual hash, and only the final changed screen comes back as an image. Use after pressing a button or launching something to find out when the UI has actually settled. Returns a text verdict plus, if it changed, one image. If nothing changes within timeout_ms it says so and returns no image at all, costing almost nothing — which is also how you detect that an input did not register.
send_input(buttons?, hold_ms=100, left_stick_x=0.0, left_stick_y=0.0, right_stick_x=0.0, right_stick_y=0.0)
Press/hold controller buttons and/or move sticks, then release.
Buttons: A B X Y L R ZL ZR PLUS MINUS DUP DDOWN DLEFT DRIGHT LSTICK RSTICK. Stick axes are -1.0..1.0 (y+ is up). hold_ms caps at 10000.
Run a macro of inputs without per-step round-trips. Each step is
{"buttons": [...], "hold_ms": int, "wait_ms": int}. wait_ms pauses after release before the next step. Ideal for navigating menus.
Capture a short burst of screenshots to 'watch' an animation or transition.
Returns up to frames images (capped at 20) spaced interval_ms apart. With
skip_identical (the default) frames matching the previous one are dropped instead of
returned, so a burst over a static screen costs one image rather than twenty. The
leading text says how many were skipped. Scaled to 0.4 by default: bursts are for
spotting change, not detail.
Swipe the touchscreen from (x, y) to (x2, y2) over duration_ms.
Tap the touchscreen at pixel (x, y). Screen is 1280x720 in handheld.
Many system menus are faster to drive by touch than by D-pad.
Type a string via a virtual USB keyboard. Works wherever the system
software keyboard is open and accepts keyboard input. Unsupported characters are skipped. Open the text field first (tap it) before calling this.
Block on the console until something happens, instead of polling.
One call replaces a polling loop — and on this transport each poll is a Wi-Fi round-trip AND a tool call, so "wait for the game to load" as repeated screenshots costs far more than the answer is worth. event: app_change (default) EDGE: an application starts, exits or switches after this call begins app_start LEVEL: an application is running. Returns immediately if one already is. app_exit LEVEL: no application is running. Returns immediately if none is. button EDGE: the physical controller is pressed app_start/app_exit are level-triggered on purpose. Edge semantics are unusable here: the agent serves one command at a time, so you cannot trigger a transition while a wait holds the only connection. Launch first, then wait for app_start — it answers "is it up yet?" rather than missing the edge. Returns fired=true with details, or fired=false on timeout — which is a real answer, not an error: it is how you learn an action did not take effect. Capped at 20s per call; issue another to keep waiting. IMPORTANT: the agent serves one command at a time, so a wait blocks every other tool for its duration — including from other sessions. Do not start a long wait and then expect to trigger the event with another tool call; that call cannot get through until the wait returns. Trigger first, then wait.
Browsing, transfers, on-device search, and read-only save mounts. Registered by default.
Back up a game's save data to sd:/switch-agentd/saves/<title_id>.
Uses the primary user account.
Delete a file or (empty) directory on the console. DESTRUCTIVE: call
once without confirm to get a token, then repeat with it.
Download a file from the console to the local machine.
Recursively search for files/directories by name, ON the console.
Vastly cheaper than listing directories over the wire and filtering here — an SD card
full of homebrew is thousands of round-trips otherwise. Matching is case-insensitive
substring; an empty name_contains lists everything under path. The walk is depth- and
scan-capped, and the reply says explicitly whether it was truncated
(truncated_by_limit / truncated_by_scan_cap) rather than quietly returning a partial
answer.
Free, used and total bytes for a device ("sd", or "save" when mounted).
Check this before a download, upload or dump.
Search inside a file on the console, returning matching lines.
Returns line number, byte offset and text per match instead of transferring the file. Ideal for config files and logs — much cheaper than fs_read_text on anything large. Long lines are clipped in the reply.
List a directory on the console's SD card ('/' is the SD root).
Create a directory on the console.
List the storage mounts the agent exposes (SD is writable; NAND is
read-only via nand_read).
Read a small text file from the console and return its contents inline.
Use for configs and logs; use fs_download for binaries or large files.
Rename/move a file or directory on the console.
Get type/size/mtime for a single file or directory.
Upload a local file to the console.
Enumerate EVERY save on the console — game saves per user, system saves,
BCAT, device and cache storage — with type, owner, size and IDs. This is the discovery step: it needs no title ID up front, unlike backup_save. Filter with type = account | system | bcat | device | cache | temporary. Account saves carry an application_id and a user uid; system saves carry a system_save_data_id instead (e.g. ...8000000000000010 is system settings, ...30 accounts, ...47 the ticket DB). Feed the IDs into mount_save to browse a save's contents in place.
Mount a save READ-ONLY so it can be browsed in place.
Afterwards use fs_list / fs_read_text / fs_download with device="save" to explore it —
no copying to the SD card first, unlike backup_save. Give either title_id (a game save;
add uid_hi/uid_lo from list_saves for a specific user) or system_save_data_id (a system
save). One save is mounted at a time; mounting again replaces the previous one. Pass
space from the list_saves entry for a system save; without it the plausible spaces are
tried in order. The mount is read-only, and every write command refuses a non-SD device,
so nothing here can corrupt save data. Use restore_save to write a save back.
Read up to length bytes (max 1 MiB) from a NAND BIS partition,
read-only. partition 20 = UserDataRoot. Returns base64 (NAND data is usually binary). NAND is never written by this agent.
Restore a previously backed-up save for a title back into its save data.
Commits the write to NAND and OVERWRITES the current save. Call once without confirm
to get a token, then repeat with it.
Release the read-only save mount from mount_save.
Titles, processes, crash reports, network. Registered by default.
List Atmosphère FATAL reports, or fetch one by name.
Different from get_crash_reports: fatal reports are written when the system itself goes down, not when a single process crashes. Check both when investigating an unexplained reboot or black screen.
List Atmosphère crash reports, or fetch one by name for full contents.
Core of diagnosing crashes/hangs.
Fetch a title's icon as a JPEG image.
Launch a title by its 16-hex-digit title ID (see list_titles).
DO NOT use this for homebrew. 0142b048fd620000 is the hbloader/album takeover program
ID: launching it directly spawns a process that never renders anything, and that dead
process then occupies the application slot so the normal route (Album applet, or holding
R while starting a game) also fails — which looks exactly like "hbmenu will not start".
Verified on hardware: the process reports running=True while the screen stays on the
home menu. If that has already happened, terminate_title() with no argument clears the
stale slot.
List running processes (pid + title ID). Shows what's actually running.
List installed titles (title ID, name, author, version).
The console's network configuration as it sees it: IP, subnet, gateway,
DNS, and whether wireless is on. Useful when the console has become hard to reach — it answers "did the DHCP address change?" from the device rather than by scanning. Note this console drops ICMP but accepts TCP, so ping failing does not mean it is down.
Terminate and relaunch a title.
Report the currently running foreground application (title ID + pid).
Firmware, uptime, battery, temperatures, storage, memory, network.
Tail a log file on the SD card. Defaults to the agent's own log.
Close a title by ID, or the current foreground app if no ID is given.
Permanently uninstall a title (save data is kept). DESTRUCTIVE. Requires
allow_nand_write=true in the agent config. Call once without confirm to get a token,
then repeat with it. Installs are out of scope; use DBI/Awoo.
Power, settings, album, and the safety tooling. Registered by default.
album_download(application_id, year, month, day, hour, minute, second, id=0, content_type=0, storage=1, local_path?)
Download an album file using the fields from album_list. content_type:
0=screenshot 1=movie. Screenshots return inline; movies over 1 MiB are refused. Pass local_path to save to disk.
List capture-album files (screenshots/videos). storage: 1=SD, 0=NAND.
Each entry includes the address fields needed by album_download.
Serial number, nickname, theme, and region of the console.
Disable auto-sleep (handheld + docked → Never). Essential for a headless
console: sleep drops Wi-Fi and makes the agent unreachable. Persists across reboots.
Copy a file on the SD card, on-device (no round-trip through the client).
Read the current screen brightness (0.0–1.0).
Read current CPU/GPU/memory clock rates (Hz).
Read the console's current system time (Unix timestamp).
Read the system master output volume (0.0–1.0).
Read the on-device audit trail of everything that changed the console.
Every mutating command is appended with its outcome, on the SD card rather than in memory — the case you actually need it for is reconstructing what happened just before a console stopped booting, when nothing in RAM survived.
List connected controllers with battery/charging/power state.
Check whether NOW is a safe moment for something irreversible.
Reports battery, charger, SD free space and whether this is emuMMC (where a mistake is recoverable by restoring an SD image) or sysMMC (where it may not be), plus a list of concrete warnings. Call this before a NAND write, a payload reboot or a long dump. The classic way to end up with an unbootable console is a power loss part-way through a write that was started at 8% battery.
Reboot the console. DESTRUCTIVE: interrupts anything running and loses
unsaved game progress. Call once without confirm to get a token.
Reboot into the configured payload (hekate/fusee) for recovery.
DESTRUCTIVE: interrupts anything running, and the console may stop at a payload menu
needing physical input. Call once without confirm first.
Enable or disable automatic (ambient) brightness control.
Set screen brightness, 0.0 (dim) to 1.0 (max).
Set auto-sleep timeouts explicitly. Handheld plan: 0=1min 1=3min 2=5min
3=10min 4=30min 5=Never. Console (docked) plan: 0=1h 1=2h 2=3h 3=6h 4=12h 5=Never.
Set the console's system clock to a Unix timestamp (seconds).
Set the system master output volume (0.0–1.0).
Enable/disable wireless (Wi-Fi) communication. Note: disabling it while
connected over Wi-Fi will cut off the agent until re-enabled locally.
Power off the console. DESTRUCTIVE: it goes offline until someone
physically powers it on — you cannot wake it remotely, so this ends the session. Call
once without confirm to get a token.
Update the on-device agent: upload a new switch-agentd.nsp to the
Atmosphère contents path, verify it landed intact, then reboot to load it. DESTRUCTIVE
(reboots). Call once without confirm to get a token. If the new build is bad, recover
by removing the file from the SD card and rebooting (see the Troubleshooting wiki page).
How many times the console has booted without any client connecting.
A rising count means the agent is starting but nobody can reach it — or is not starting at all. Either way the most recent config or agent change is the first thing to undo. Resets as soon as a client authenticates.
Attach, memory, registers, modules, backtraces. Off by default — SWITCH_MCP_TOOLS=all or add debug.
Attach the debugger to a process (default: foreground app). pause=True
freezes the target (best for inspection); pause=False keeps it running while attached (for live memory/cheat work). Returns pid and main module base.
Walk a stopped thread's frame-pointer chain into a call stack.
Turns "it faulted somewhere" into the path it took to get there. Requires debug_attach first (the thread must be stopped). thread_id 0 = first thread. Addresses are absolute — pair with debug_modules and subtract the module base to get RVAs. The walk stops on its own when the chain stops looking like real frames, so a short backtrace usually means the frame pointer was optimised away rather than that something failed.
Break into (pause) a running attached target.
Free a hardware breakpoint or watchpoint slot.
Slots are a scarce hardware resource and stay armed until cleared, so a forgotten slot silently costs you the next one. Set watchpoint=True to clear a data watchpoint rather than an instruction breakpoint.
Resume a paused attached target without detaching.
Detach the debugger and RESUME the target process.
Dump the attached process's memory map (mapped regions with addr/size/
type/permissions). Requires debug_attach first.
List a process's loaded modules with base address, size and build ID.
This is what turns raw addresses into something you can act on: subtract a module base
from an address to get an RVA like main+0x1a2f4, which is the form Ghidra and IDA use.
The build ID identifies that exact binary, so it is the key for matching a symbol map or
an existing RE database to what is running. Works without attaching (uses ldr:dmnt). pid
0 = the attached process, or the foreground app if nothing is attached. Call this before
debug_read_mem or debug_backtrace so the addresses mean something.
Drain queued debug events from the attached process — exceptions/faults,
thread & process create/exit, user breaks. With a non-pausing attach, poll this repeatedly for a live fault/log stream. resume=True keeps it running.
Read length bytes (max 1 MiB) from the attached process at addr
(int or '0x...'). Returns hex. Requires debug_attach first.
Read a thread's CPU registers (x0–x28, fp, lr, sp, pc). thread_id 0 =
first thread. Requires debug_attach first.
Search the attached process's writable memory for a little-endian integer
value of width bytes (1/2/4/8). Returns matching addresses — the basis of cheat/mod
development. Requires debug_attach first.
Break when an instruction address is executed (hardware breakpoint).
addr must be 4-byte aligned. Hits arrive via debug_poll_events. Only about 6
breakpoint slots exist in hardware; free them with debug_clear_breakpoint. Requires
debug_attach first.
Break when a memory address is accessed — the fastest way to find what
writes a value. Instead of repeatedly reading memory and diffing, set a watchpoint on the address and let the hardware catch the exact instruction that touches it; the hit arrives via debug_poll_events with the faulting PC. This is how you go from "the health value lives at 0x… " to "this code decrements it". mode: "write" (default), "read", or "rw". size: 1, 2, 4 or 8 bytes, and the range may not cross an 8-byte boundary. slot: hardware register index — only about 4 watchpoint slots exist, so free them with debug_clear_breakpoint when done. Requires debug_attach first.
List thread IDs of the attached process. Requires debug_attach first.
Write raw bytes (given as a hex string, e.g. '1f2003d5') to the attached
process at addr. Patches live memory. Requires debug_attach first.
Write CPU registers of a stopped thread.
registers names only what should change, e.g. {"pc": "0x8001234"} or {"x0": 1};
everything else keeps its current value. Use to redirect execution, skip a faulting
instruction, or force a return value. Accepts ints or "0x..." strings. thread_id 0 =
first thread. Requires debug_attach first, and the invasive tier.
Unpaused memory access and the narrowing value search. Off by default — add cheat.
List loaded cheats (id, name, enabled, opcode count).
Whether a cheat process is attached, plus cheat and frozen-address counts.
Requires the dmnt cheat module enabled on the console.
Enable/disable a cheat by id (see cheat_list).
Start a narrowing memory search for value in the running game.
This is the first step of the standard value hunt. A single scan is not enough on its
own — searching for 100 will match tens of thousands of addresses. Follow with
narrow_search after changing the value in-game: find_value(100) ->
40k candidates (take damage in game) narrow_search(op="decreased") -> 300 candidates
narrow_search(op="eq", value=87) -> 2 candidates Candidates are kept ON the console
between calls, so each step costs one round-trip instead of shipping addresses back and
forth. Only real mappings are scanned — the reserved heap extent is mostly unmapped, so
a naive sweep would be thousands of failed reads. region="" (default) covers everything
readable+writable; "heap" or "main" narrow it. width: 1, 2, 4 or 8 bytes. max_bytes
bounds the scan so a large game cannot tie up the console for minutes. The reply says
explicitly when the candidate cap or the byte budget was hit, rather than looking
complete.
Freeze a memory address at its current value (classic cheat primitive).
width in bytes (1/2/4/8). Returns the frozen value.
List the running game's actual memory mappings (address, size, perms, type).
Worth knowing why this matters: the heap extent reported by live_meta is a reserved ~8 GB address range that is almost entirely unmapped. Only these mappings are real. find_value walks them automatically, but this is how you see what there is to search and pick a region deliberately.
Title ID, main-module base/size, heap extents and build ID of the running
game — WITHOUT attaching a debugger and without pausing it. Start here for any memory work on a live game: it gives the regions find_value should search and the build ID that identifies the binary.
Read memory of the RUNNING game without pausing it.
This is the important difference from debug_read_mem: attaching a debugger freezes the target, so anything that only exists while the game is moving — a draining health bar, a countdown — cannot be observed that way. Returns hex. Requires Atmosphère's cheat module.
Write memory of the RUNNING game without pausing it. Invasive tier.
Patches a live process — the effect is immediate and there is no undo beyond writing the old bytes back, so read the region first.
Filter the surviving candidates from find_value against a new observation.
Operators needing a value: eq, ne, gt, lt. Operators comparing against the previous scan: changed, unchanged, increased, decreased — these are what make the search converge, because you rarely know the exact new number but always know which way it moved. Repeat until a handful of addresses remain, then read them with live_read_mem to confirm.
Discard the candidate set and free the memory it holds on the console.
List the surviving candidate addresses and their current values.
Stop freezing a previously frozen address.
i2c and GPIO. Off by default, and additionally gated by allow_hardware.
Read a GPIO pad value (default 25 = Vol+ button). Needs allow_hardware=true.
Read from an i2c device (default 2 = Tmp451 temp sensor). Optionally
write reg first (register read). Returns hex. Needs allow_hardware=true.
Write raw bytes (hex string) to an i2c device. DANGEROUS — writes to the
PMIC and similar can physically damage the console and are not undoable. Needs
allow_hardware=true. Call once without confirm first.