from fastmcp import FastMCP import serial import time import os mcp = FastMCP("Flipper Zero - Universal CLI") FLIPPER_PORT = os.getenv("FLIPPER_PORT", "/dev/cu.usbmodemflip_Il1chih41") def send_flipper_cmd(cmd: str) -> str: """Send ANY Flipper CLI command and return output""" try: with serial.Serial(FLIPPER_PORT, 115200, timeout=3) as ser: time.sleep(0.5) # let port settle ser.reset_input_buffer() # Send command ser.write((cmd + "\r\n").encode()) time.sleep(0.8) # Read response output = "" while ser.in_waiting: output += ser.read(ser.in_waiting).decode(errors="ignore") time.sleep(0.1) # Strip the command echo and prompt lines = [l for l in output.splitlines() if l.strip() and not l.strip().endswith(">:") and cmd not in l] return "\n".join(lines) or f"Executed: {cmd}" except Exception as e: return f"Error: {str(e)}" @mcp.tool() def flipper(cmd: str): """Send ANY Flipper CLI command. Examples: - 'ir universal LG_Hotel_tv Power' - 'nfc scene read' - 'subghz tx 433.92M test.sub' - 'device_info' - 'gpio pins'""" return send_flipper_cmd(cmd) @mcp.tool() def flipper_ir(remote: str, signal: str): """IR shortcut: flipper_ir('LG_Hotel_tv', 'Power')""" return send_flipper_cmd(f"ir universal {remote} {signal}") @mcp.tool() def flipper_list_commands(): """List all available Flipper commands""" return send_flipper_cmd("help") # STORAGE @mcp.tool() def flipper_ls(path: str = "/ext"): """List files: flipper_ls('/ext')""" return send_flipper_cmd(f"storage list {path}") @mcp.tool() def flipper_cat(file: str): """Read file: flipper_cat('/ext/test.txt')""" return send_flipper_cmd(f"storage read {file}") @mcp.tool() def flipper_mkdir(folder: str): """Create folder: flipper_mkdir('/ext/my_folder')""" return send_flipper_cmd(f"storage mkdir {folder}") @mcp.tool() def flipper_rm(file: str): """Delete: flipper_rm('/ext/unwanted.txt')""" return send_flipper_cmd(f"storage remove {file}") # APPS @mcp.tool() def flipper_apps_list(): """List all Flipper apps""" return send_flipper_cmd("loader list") @mcp.tool() def flipper_app_open(app: str): """Open app: flipper_app_open('ir')""" return send_flipper_cmd(f"loader open {app}") @mcp.tool() def flipper_app_close(): """Close current app""" return send_flipper_cmd("loader close") # APPS (loader) @mcp.tool() def flipper_ir(): """Launch Infrared""" return send_flipper_cmd("loader open ir") @mcp.tool() def flipper_nfc(): """Launch NFC""" return send_flipper_cmd("loader open nfc") @mcp.tool() def flipper_subghz(): """Launch Sub-GHz""" return send_flipper_cmd("loader open subghz") @mcp.tool() def flipper_rfid(): """Launch 125kHz RFID""" return send_flipper_cmd("loader open rfid") @mcp.tool() def flipper_rfid_13(): """Launch 13.56MHz RFID""" return send_flipper_cmd("loader open rfid_13") @mcp.tool() def flipper_gpio(): """Launch GPIO""" return send_flipper_cmd("loader open gpio") @mcp.tool() def flipper_bt(): """Launch Bluetooth""" return send_flipper_cmd("loader open bt") @mcp.tool() def flipper_clock(): """Launch Clock""" return send_flipper_cmd("loader open Clock") @mcp.tool() def flipper_archive(): """Launch File Archive""" return send_flipper_cmd("loader open Archive") @mcp.tool() def flipper_settings(): """Launch Settings""" return send_flipper_cmd("loader open Settings") @mcp.tool() def flipper_badusb(): """Launch BadUSB""" return send_flipper_cmd("loader open BadUSB") @mcp.tool() def flipper_i2c(): """Launch I2C""" return send_flipper_cmd("loader open i2c") @mcp.tool() def flipper_onewire(): """Launch 1-Wire""" return send_flipper_cmd("loader open onewire") if __name__ == "__main__": mcp.run(transport="stdio")