Skip to main content
Glama

Wii U Remote

An Aroma plugin and MCP server for controlling a physical Wii U: TV/GamePad screenshots, GamePad buttons and sticks, calibrated touch, homebrew launch and upload, native logs and console shell access.

The project contains the console plugin, Python host and a build of the wiiload receiver with interruption and shutdown fixes. A connected physical GamePad is required for injected input. HOME, Wii Remote, motion and arbitrary text injection are not supported.

Build

Install Docker and uv, then run:

make -j24
make host

The Docker recipes pin the open-source toolchain and console dependencies. uv.lock pins Python dependencies.

Output

Purpose

bin/wiiu-remote.wps

Screenshot, input and homebrew-control plugin

bin/wiiload.wps

Compatible application-upload receiver

.build/plugin/wiiu-remote.elf

Console-plugin debug symbols

.build/wiiload/wiiload.elf

Receiver debug symbols

Related MCP server: phone-control

Console installation

Copy both WPS files to sd:/wiiu/environments/aroma/plugins/ and restart Aroma. The remote plugin requires the WUPS backend, MemoryMappingModule, RPXLoadingModule and WUHBUtilsModule. The wiiload receiver additionally requires NotificationModule. See homebrew control.

Remove remote-screenshot.wps and remote-test.wps when replacing those older plugins: they share screenshot ports or inject the same controller input. Building the project does not install files on the console.

Capture and input operate independently of Aroma's debug service. For automatic console discovery, native logs and shell access, enable Aroma Base Plugin's “Enables logging and shell access via TCP” option. Native PPC shell commands also require IOPShellModule.

Host setup

Start one shared server:

uv run --locked wiiu-remote-mcp

Connect MCP clients using Streamable HTTP at http://127.0.0.1:4408/mcp. For a client using mcp_servers TOML configuration:

[mcp_servers.wiiu]
url = "http://127.0.0.1:4408/mcp"
startup_timeout_sec = 20
tool_timeout_sec = 900

Clients connect to this existing service; they should not launch a separate backend per session. Closing a client leaves the shared server running. Simultaneous input from multiple clients is not arbitrated. Uploaded paths refer to files on the server machine.

The host advertises discovery on UDP4445 and listens for Aroma's reverse debug connection on TCP7965. The connection's source address identifies the console. Allow both directions through the firewall and stop other receivers using TCP7965. Auto discovery selects the first enabled console; it can learn a new address on reconnect.

To select a console explicitly, set CONSOLE_ADDRESS to its hostname or IP:

uv run --locked wiiu-remote-mcp --console "$CONSOLE_ADDRESS"

WIIU_HOST supplies the same default. Add --no-debug to disable discovery, log collection and shell access while retaining direct screenshot/input access. --http-bind and --http-port configure the MCP listener; --port selects the console control port. Run wiiu-remote-mcp --help for all options.

The transports have no authentication or encryption and are intended for a local development network. HTTP binds to loopback by default. Other machines can reach the same service through an SSH tunnel.

Persistent host service

make deploy

This installs and starts wiiu-remote.service for the current user. Stop a manually launched server first so it releases TCP4408 and TCP7965. Deployment stops an existing service, copies the host sources and locked dependencies into an ignored deploy/ snapshot, synchronizes that environment and restarts it. Development edits do not affect the running snapshot until the next deployment. Console plugin installation remains separate.

The generated unit is under $XDG_CONFIG_HOME/systemd/user/, normally ~/.config/systemd/user/. Manage it with:

systemctl --user status wiiu-remote.service
systemctl --user restart wiiu-remote.service
journalctl --user -u wiiu-remote.service -f

Native logs use $XDG_STATE_HOME/wiiu-remote/, defaulting to ~/.local/state/wiiu-remote/. --log-dir changes the parent directory; each server run has its own timestamped directory. Deployment does not replace logs.

MCP tools

Tool

Operation

wiiu_get_state

Title, foreground, display sizes, capabilities and last native input

wiiu_screenshot

Fresh TV, DRC or both captures as MCP images

wiiu_act

Ordered, timed buttons, sticks, touch, drags and waits

wiiu_release_all

Cancel input and remove injected state

wiiu_list_homebrew

List SD RPX/WUHB applications and metadata

wiiu_start_homebrew

Launch an SD application by name or path

wiiu_load_homebrew

Upload and launch a host-local RPX/WUHB using Python wiiload

wiiu_stop_application

Request Menu or explicitly submit native force kill

wiiu_debug_status

Discovery, reverse connection and host-log status

wiiu_debug_logs

Read retained native output using independent cursors

wiiu_debug_command

Submit one native console shell command and collect output

Input and screenshots

Buttons are A, B, X, Y, LEFT, RIGHT, UP, DOWN, ZL, ZR, L, R, PLUS, MINUS, STICK_L and STICK_R. These are controller buttons, not keyboard characters. Stick axes range from -1 to 1, with +X right and +Y down. Touch coordinates are DRC screenshot pixels, measured from the top-left.

{
  "actions": [
    {"type": "keypress", "keys": ["A"], "duration_ms": 100},
    {"type": "click", "x": 426, "y": 240, "duration_ms": 500},
    {
      "type": "drag",
      "path": [{"x": 100, "y": 120}, {"x": 700, "y": 400}],
      "duration_ms": 600
    }
  ],
  "observe_display": "drc"
}

gamepad actions combine buttons, left_stick, right_stick and optional touch; wait pauses on the console. Input actions include a native neutral release before completion. Drags maintain contact and interpolate on the console. Durations are minimum holds after successful native input delivery, not exact frame counts. A batch allows 256 expanded native steps, and each action lasts at most ten seconds. See input semantics.

During injection, buttons, sticks and touch replace physical values; unrelated motion data passes through. Five seconds without a successful native input read cancels an injected phase. Foreground loss and control disconnection also cancel input. A disconnected or unpolled controller is not emulated.

Screenshots are BMP on the console connection and PNG in MCP, encoded on the host. No screenshot files are stored by the capture operation. TV/DRC captures are sequential and require functioning graphics presentation; capture changes rendering timing. Successful input remains successful if its following capture fails. Do not replay an uncertain input batch automatically.

Homebrew and recovery

Use an exact returned path when application names are ambiguous. WUHB uploads include assets; plain RPX uploads can retain assets from a mounted bundle. Installed-title launch, WPS upload and command-line arguments are unsupported. Lifecycle calls serialize and distinguish submission from observed execution. started requires a new lifetime and matching executable path; application readiness still requires observation. See homebrew semantics.

A graceful stop depends on cooperative application cleanup. Force kill submits cos kill through Aroma's independent debug connection. Termination can leave the title manager waiting, producing recovery_required. No operation implicitly escalates to reboot or replays a failed launch.

Native shell submission confirms host transport submission only, not parsing or completion. Output can interleave with unrelated logs. Debug access may survive a PPC failure, but does not guarantee recovery from every hang. See discovery, logging and shell semantics.

Connections and protocol

Port

Direction and purpose

TCP4405

Host to console: framed state, input and lifecycle control

TCP4406

Host to console: one TV BMP per connection

TCP4407

Host to console: one DRC BMP per connection

TCP4299

Host to console: wiiload application upload

UDP4445

Host discovery beacon

TCP7965

Console to host: Aroma logs and shell

TCP4408

MCP client to host: Streamable HTTP

The combined plugin and host use protocol v3 and must be updated together. See the protocol specification for byte layouts and completion semantics. Network recovery describes listener recreation, connection renewal and read retries. Input and lifecycle mutations are never automatically replayed.

License and attribution

GPL-3.0-or-later; see LICENSE. GX2 capture and the RGB-to-sRGB lookup table derive from Maschell's ScreenshotWUPS. The wiiload recipe pins the upstream wiiload plugin and retains its licensing and attribution with the receiver patch.

The host uses the MCP Python SDK and Pillow. Console dependencies are supplied by the wiiu-env open-source projects.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Local MCP server for automating the Ryubing Nintendo Switch emulator, providing tools for controller input, touch, screenshots, logs, game launch, and session management.
    10
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables controlling an Android device from an MCP client via ADB, including wireless debugging discovery, UI-tree inspection, and performing taps, swipes, typing, app actions, and shell commands without relying on screen coordinates.
    1 npm
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    Lets MCP agents view and control a jailbroken PS5 over a locally running capture app, taking snapshots of live HDMI video and sending virtual DualSense input such as button presses, holds, stick and trigger moves, and timed sequences. It also supports launching, closing, installing, and uninstalling console titles, waiting on on-screen templates, and recording or replaying input sessions.
    24
    14
    MIT
  • A
    license
    B
    quality
    B
    maintenance
    Connects MCP agents to a jailbroken console so they can watch live HDMI video and audio, press buttons through a virtual DualSense, launch or close games, install packages, transfer files, and run visual automation with snapshots and recordings. Multiple agents can share one console through a claim-and-queue system.
    28
    MIT