mgba_screenshot
Take a screenshot of the current GBA emulator display and save it as a PNG. Accepts an optional file path; returns the saved file path.
Instructions
Take a screenshot of the current display and save it to a file. Returns the saved file path.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | Absolute file path to save the PNG (optional — defaults to a temp file) |
Implementation Reference
- src/tools.ts:208-220 (schema)Schema/tool definition for mgba_screenshot — declares the tool name, description, and optional 'path' input parameter.
{ name: "mgba_screenshot", description: "Take a screenshot of the current display and save it to a file. Returns the saved file path.", inputSchema: { type: "object", properties: { path: { type: "string", description: "Absolute file path to save the PNG (optional — defaults to a temp file)", }, }, }, }, - src/tools.ts:380-383 (handler)Handler for mgba_screenshot — calls the mGBA bridge's 'screenshot' RPC method with an optional path, and returns the saved file path.
case "mgba_screenshot": { const path = await mgba.call<string>("screenshot", p.path ? { path: p.path } : {}); return ok(`Screenshot saved: ${path}`); } - src/tools.ts:258-411 (registration)Registration function that wires up both ListToolsRequestSchema (serving TOOLS array) and CallToolRequestSchema (routing the 'mgba_screenshot' case to the handler).
export function registerTools(server: Server, mgba: MgbaClient): void { server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS })); server.setRequestHandler(CallToolRequestSchema, async (req) => { const { name, arguments: args = {} } = req.params; const p = args as Record<string, unknown>; switch (name) { case "mgba_ping": { const r = await mgba.call<string>("ping"); return ok(r); } case "mgba_get_info": { const r = await mgba.call<{ title?: string; code?: string; frame?: number; platform?: number | string; capabilities?: Record<string, boolean>; }>("get_info"); const lines = [ `Title: ${r.title ?? "(unavailable)"}`, `Code: ${r.code ?? "(unavailable)"}`, `Platform: ${r.platform ?? "(unavailable)"}`, `Frame: ${r.frame ?? "(unavailable)"}`, ]; if (r.capabilities) { const present = Object.entries(r.capabilities).filter(([, v]) => v).map(([k]) => k); const missing = Object.entries(r.capabilities).filter(([, v]) => !v).map(([k]) => k); lines.push(""); lines.push(`Capabilities present: ${present.length ? present.join(", ") : "(none)"}`); if (missing.length) lines.push(`Missing on this build: ${missing.join(", ")}`); } return ok(lines.join("\n")); } case "mgba_read8": { const v = await mgba.call<number>("read8", { address: p.address }); return ok(`0x${(p.address as number).toString(16).toUpperCase()}: ${formatHex(v)}`); } case "mgba_read16": { const v = await mgba.call<number>("read16", { address: p.address }); return ok(`0x${(p.address as number).toString(16).toUpperCase()}: ${formatHex(v)}`); } case "mgba_read32": { const v = await mgba.call<number>("read32", { address: p.address }); return ok(`0x${(p.address as number).toString(16).toUpperCase()}: ${formatHex(v)}`); } case "mgba_write8": { await mgba.call("write8", { address: p.address, value: p.value }); return ok(`Wrote ${formatHex(p.value)} → 0x${(p.address as number).toString(16).toUpperCase()}`); } case "mgba_write16": { await mgba.call("write16", { address: p.address, value: p.value }); return ok(`Wrote ${formatHex(p.value)} → 0x${(p.address as number).toString(16).toUpperCase()}`); } case "mgba_write32": { await mgba.call("write32", { address: p.address, value: p.value }); return ok(`Wrote ${formatHex(p.value)} → 0x${(p.address as number).toString(16).toUpperCase()}`); } case "mgba_read_range": { const bytes = await mgba.call<number[]>("read_range", { address: p.address, length: p.length, }); const hex = bytes .map((b) => b.toString(16).padStart(2, "0").toUpperCase()) .join(" "); const addr = (p.address as number).toString(16).toUpperCase(); return ok(`0x${addr} [${bytes.length} bytes]:\n${hex}`); } case "mgba_write_range": { const r = await mgba.call<{ written: number }>("write_range", { address: p.address, bytes: p.bytes, }); const addr = (p.address as number).toString(16).toUpperCase(); return ok(`Wrote ${r.written} bytes → 0x${addr}`); } case "mgba_press_buttons": { const r = await mgba.call<{ queued: boolean; queue_size: number }>("press_buttons", { buttons: p.buttons, frames: p.frames ?? 1, release_frames: p.release_frames ?? 1, }); const keys = (p.buttons as string[]).join("+"); return ok( `Queued press: ${keys} ` + `(hold ${p.frames ?? 1}f, release ${p.release_frames ?? 1}f). ` + `Queue size: ${r.queue_size}`, ); } case "mgba_advance_frames": { const frame = await mgba.call<number>("advance_frames", { count: p.count ?? 1 }); return ok(`Advanced ${p.count ?? 1} frame(s). Current frame: ${frame}`); } case "mgba_pause": { await mgba.call("pause"); return ok("Emulation paused"); } case "mgba_unpause": { await mgba.call("unpause"); return ok("Emulation resumed"); } case "mgba_reset": { await mgba.call("reset"); return ok("ROM reset"); } case "mgba_screenshot": { const path = await mgba.call<string>("screenshot", p.path ? { path: p.path } : {}); return ok(`Screenshot saved: ${path}`); } case "mgba_save_state": { if (p.slot === undefined && p.path === undefined) { throw new Error("provide either `slot` (0-9) or `path`"); } const r = await mgba.call<{ slot?: number; path?: string }>("save_state", { ...(p.slot !== undefined ? { slot: p.slot } : {}), ...(p.path !== undefined ? { path: p.path } : {}), }); return ok(r.path ? `Saved state to ${r.path}` : `Saved state to slot ${r.slot}`); } case "mgba_load_state": { if (p.slot === undefined && p.path === undefined) { throw new Error("provide either `slot` (0-9) or `path`"); } const r = await mgba.call<{ slot?: number; path?: string }>("load_state", { ...(p.slot !== undefined ? { slot: p.slot } : {}), ...(p.path !== undefined ? { path: p.path } : {}), }); return ok(r.path ? `Loaded state from ${r.path}` : `Loaded state from slot ${r.slot}`); } default: throw new Error(`Unknown tool: ${name}`); } }); } - src/mgba.ts:89-132 (helper)MgbaClient.call() — the generic RPC helper used by the mgba_screenshot handler to invoke the 'screenshot' method on the mGBA bridge over a TCP socket.
async call<T = unknown>( method: string, params?: Record<string, unknown>, ): Promise<T> { // Lazy (re)connect — bridge.lua reloads kill the socket, and the user // shouldn't have to restart the MCP host every time they edit the script. if (!this.socket || this.socket.destroyed) { try { await this.connect(); } catch (err) { throw new Error( `Cannot reach mGBA bridge at ${this.host}:${this.port}. ` + `Make sure mGBA is running with bridge.lua loaded (Tools > Scripting). ` + `Underlying error: ${(err as Error).message}`, ); } } return new Promise<T>((resolve, reject) => { const sock = this.socket; if (!sock) { reject(new Error("socket vanished after connect")); return; } const id = this.nextId++; this.pending.set(id, (resp) => { if (resp.error) { reject(new Error(`mGBA RPC error [${resp.error.code}]: ${resp.error.message}`)); } else { resolve(resp.result as T); } }); const msg = JSON.stringify({ id, method, params: params ?? {} }) + "\n"; sock.write(msg, (err) => { if (err) { this.pending.delete(id); reject(err); } }); }); } }