Skip to main content
Glama

bizhawk_press_buttons

Set joypad button state for one player for the next emulated frame only. Interleave with frame advance to hold buttons across multiple frames.

Instructions

PURPOSE: Set the joypad button state for one player for EXACTLY the next emulated frame. USAGE: Drive games with input. Each call sets joypad state for ONE frame only — the very next frame BizHawk processes. After that frame, BizHawk's input goes back to whatever the human user is holding (typically nothing). To HOLD a button across N consecutive frames, INTERLEAVE: call bizhawk_press_buttons + bizhawk_frame_advance(count=1) N times in a loop. DO NOT call bizhawk_press_buttons once and then bizhawk_frame_advance(count=N) — only the first of those N frames sees the button; the rest are no-input. Verified empirically against SNES Super Metroid in May 2026: a 60-frame advance after a single press_buttons(Right) moved Samus the same +1 pixel as a 10-frame advance, because frames 2-60 had no input. To release a button mid-hold, just stop calling press_buttons for it; the next frame_advance will see it released. BEHAVIOR: Modifies emulator input state for the next frame poll only — no other side effects. Returns an error if the loaded core doesn't expose joypad.set. Button names that aren't valid for the active core are silently ignored by BizHawk (no error). RETURNS: Single line 'Set joypad N: BUTTON+BUTTON+...' or '... (all released)' if nothing was pressed.

Button names vary per system. Common names across cores: A, B, X, Y, Up, Down, Left, Right, Start, Select, L, R, L1, R1, L2, R2, L3, R3, C, Z, C-Up, C-Down, C-Left, C-Right, Mode. Use whatever names the active core understands — if unsure, try a name and check BizHawk's input display, or use bizhawk_get_info to confirm joypad_set is available.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
buttonsYesMap of button name (string, case-sensitive per the active core) → pressed (boolean: true=pressed, false=released). Example: {"A": true, "Up": true} presses A and Up while leaving everything else released. Names not recognized by the active core are silently ignored.
playerNoPlayer number (1-based). Default 1. For multi-controller cores (e.g. N64 with 4 controllers) pass 2/3/4 to address other players.

Implementation Reference

  • src/tools.ts:234-263 (registration)
    Tool definition (metadata + inputSchema) for bizhawk_press_buttons as part of the TOOLS array. Registers the tool name, description, and input schema (required 'buttons' map and optional 'player' integer).
    {
      name: "bizhawk_press_buttons",
      description:
        "PURPOSE: Set the joypad button state for one player for EXACTLY the next emulated frame. " +
        "USAGE: Drive games with input. Each call sets joypad state for ONE frame only — the very next frame BizHawk processes. After that frame, BizHawk's input goes back to whatever the human user is holding (typically nothing). " +
        "To HOLD a button across N consecutive frames, INTERLEAVE: call bizhawk_press_buttons + bizhawk_frame_advance(count=1) N times in a loop. " +
        "DO NOT call bizhawk_press_buttons once and then bizhawk_frame_advance(count=N) — only the first of those N frames sees the button; the rest are no-input. Verified empirically against SNES Super Metroid in May 2026: a 60-frame advance after a single press_buttons(Right) moved Samus the same +1 pixel as a 10-frame advance, because frames 2-60 had no input. " +
        "To release a button mid-hold, just stop calling press_buttons for it; the next frame_advance will see it released. " +
        "BEHAVIOR: Modifies emulator input state for the next frame poll only — no other side effects. Returns an error if the loaded core doesn't expose joypad.set. Button names that aren't valid for the active core are silently ignored by BizHawk (no error). " +
        "RETURNS: Single line 'Set joypad N: BUTTON+BUTTON+...' or '... (all released)' if nothing was pressed. " +
        `\n\nButton names vary per system. Common names across cores: ${VALID_BUTTONS.join(", ")}. Use whatever names the active core understands — if unsure, try a name and check BizHawk's input display, or use bizhawk_get_info to confirm joypad_set is available.`,
      inputSchema: {
        type: "object",
        required: ["buttons"],
        properties: {
          buttons: {
            type: "object",
            description: "Map of button name (string, case-sensitive per the active core) → pressed (boolean: true=pressed, false=released). Example: {\"A\": true, \"Up\": true} presses A and Up while leaving everything else released. Names not recognized by the active core are silently ignored.",
            additionalProperties: { type: "boolean" },
          },
          player: {
            type: "integer",
            minimum: 1,
            default: 1,
            description: "Player number (1-based). Default 1. For multi-controller cores (e.g. N64 with 4 controllers) pass 2/3/4 to address other players.",
          },
        },
        additionalProperties: false,
      },
    },
  • Input schema for bizhawk_press_buttons: requires 'buttons' (object mapping button name strings to booleans) and optional 'player' (integer, min 1, default 1).
    inputSchema: {
      type: "object",
      required: ["buttons"],
      properties: {
        buttons: {
          type: "object",
          description: "Map of button name (string, case-sensitive per the active core) → pressed (boolean: true=pressed, false=released). Example: {\"A\": true, \"Up\": true} presses A and Up while leaving everything else released. Names not recognized by the active core are silently ignored.",
          additionalProperties: { type: "boolean" },
        },
        player: {
          type: "integer",
          minimum: 1,
          default: 1,
          description: "Player number (1-based). Default 1. For multi-controller cores (e.g. N64 with 4 controllers) pass 2/3/4 to address other players.",
        },
      },
      additionalProperties: false,
    },
  • Handler implementation for bizhawk_press_buttons: calls bh.call('press_buttons', ...) to set joypad state via the BizHawk Lua bridge, then returns a summary line showing which buttons were pressed.
    case "bizhawk_press_buttons": {
      await bh.call("press_buttons", { buttons: p.buttons, player: p.player ?? 1 });
      const pressed = Object.entries(p.buttons as Record<string, boolean>)
        .filter(([, v]) => v).map(([k]) => k);
      return ok(`Set joypad ${p.player ?? 1}: ${pressed.length ? pressed.join("+") : "(all released)"}`);
    }
  • src/tools.ts:486-660 (registration)
    registerTools function that sets up ListToolsRequestSchema and CallToolRequestSchema handlers, routing tool calls (including 'bizhawk_press_buttons') via a switch statement.
    export function registerTools(server: Server, bh: BizhawkServer): 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>;
        const a = () => p.address as number;
        const dom = () => p.domain ? { domain: p.domain } : {};
    
        switch (name) {
          case "bizhawk_ping": {
            const r = await bh.call<string>("ping");
            return ok(r);
          }
    
          case "bizhawk_get_info": {
            const r = await bh.call<{
              rom_name?: string;
              rom_hash?: string;
              framecount?: number;
              memory_domains?: string[];
              current_memory_domain?: string;
              capabilities?: Record<string, boolean>;
            }>("get_info");
            const lines = [
              `ROM:        ${r.rom_name ?? "(unavailable)"}`,
              `ROM hash:   ${r.rom_hash ?? "(unavailable)"}`,
              `Framecount: ${r.framecount ?? "(unavailable)"}`,
            ];
            if (r.memory_domains?.length) {
              lines.push("");
              lines.push(`Memory domains: ${r.memory_domains.join(", ")}`);
              if (r.current_memory_domain) {
                lines.push(`Active domain (used when 'domain' is omitted): ${r.current_memory_domain}`);
              }
            }
            if (r.capabilities) {
              const missing = Object.entries(r.capabilities).filter(([, v]) => !v).map(([k]) => k);
              if (missing.length) {
                lines.push("");
                lines.push(`Missing capabilities on this BizHawk build: ${missing.join(", ")}`);
              }
            }
            return ok(lines.join("\n"));
          }
    
          case "bizhawk_list_memory_domains": {
            const r = await bh.call<string[]>("list_memory_domains");
            return ok("Memory domains:\n  " + r.join("\n  "));
          }
    
          case "bizhawk_read8":  return ok(`${addrHex(a())}: ${fmtHex(await bh.call<number>("read8", { address: a(), ...dom() }))}`);
          case "bizhawk_read16": return ok(`${addrHex(a())}: ${fmtHex(await bh.call<number>("read16", { address: a(), ...dom() }))}`);
          case "bizhawk_read32": return ok(`${addrHex(a())}: ${fmtHex(await bh.call<number>("read32", { address: a(), ...dom() }))}`);
    
          case "bizhawk_read_range": {
            const bytes = await bh.call<number[]>("read_range", { address: a(), length: p.length, ...dom() });
            const hex = bytes.map((b) => b.toString(16).padStart(2, "0").toUpperCase()).join(" ");
            return ok(`${addrHex(a())} [${bytes.length} bytes${p.domain ? `, ${p.domain}` : ""}]:\n${hex}`);
          }
    
          case "bizhawk_write8": {
            await bh.call("write8", { address: a(), value: p.value, ...dom() });
            return ok(`Wrote ${fmtHex(p.value)} → ${addrHex(a())}${p.domain ? ` (${p.domain})` : ""}`);
          }
          case "bizhawk_write16": {
            await bh.call("write16", { address: a(), value: p.value, ...dom() });
            return ok(`Wrote ${fmtHex(p.value)} → ${addrHex(a())}${p.domain ? ` (${p.domain})` : ""}`);
          }
          case "bizhawk_write32": {
            await bh.call("write32", { address: a(), value: p.value, ...dom() });
            return ok(`Wrote ${fmtHex(p.value)} → ${addrHex(a())}${p.domain ? ` (${p.domain})` : ""}`);
          }
          case "bizhawk_write_range": {
            const r = await bh.call<{ written: number }>("write_range", { address: a(), bytes: p.bytes, ...dom() });
            return ok(`Wrote ${r.written} bytes → ${addrHex(a())}${p.domain ? ` (${p.domain})` : ""}`);
          }
    
          case "bizhawk_press_buttons": {
            await bh.call("press_buttons", { buttons: p.buttons, player: p.player ?? 1 });
            const pressed = Object.entries(p.buttons as Record<string, boolean>)
              .filter(([, v]) => v).map(([k]) => k);
            return ok(`Set joypad ${p.player ?? 1}: ${pressed.length ? pressed.join("+") : "(all released)"}`);
          }
    
          case "bizhawk_play_input_sequence": {
            const params: Record<string, unknown> = { frames: p.frames };
            if (p.screenshot_every       !== undefined) params.screenshot_every       = p.screenshot_every;
            if (p.screenshot_dir         !== undefined) params.screenshot_dir         = p.screenshot_dir;
            if (p.screenshot_prefix      !== undefined) params.screenshot_prefix      = p.screenshot_prefix;
            if (p.observe_memory         !== undefined) params.observe_memory         = p.observe_memory;
            if (p.stop_on_memory_change  !== undefined) params.stop_on_memory_change  = p.stop_on_memory_change;
            const r = await bh.call<{
              played: number;
              final_framecount?: number;
              stopped_early?: boolean;
              stop_reason?: string;
              observations?: {
                frame_offset: number;
                path?: string;
                memory?: Record<string, number>;
              }[];
            }>("play_input_sequence", params);
    
            const obs = r.observations ?? [];
            const lines = [
              `Played ${r.played} frames. Final framecount: ${r.final_framecount ?? "(unavailable)"}.`,
            ];
            if (r.stopped_early) {
              lines.push(`Stopped early — reason: ${r.stop_reason ?? "(unspecified)"}.`);
            }
            lines.push(`Captured ${obs.length} observation${obs.length === 1 ? "" : "s"}.`);
            // Per-observation lines so the agent can correlate inline images with state
            for (let i = 0; i < obs.length; i++) {
              const o = obs[i];
              const memStr = o.memory
                ? ` memory={${Object.entries(o.memory).map(([k, v]) => `${k}=${v}`).join(", ")}}`
                : "";
              const imgStr = o.path ? ` (image ${i + 1})` : "";
              lines.push(`  obs[${i}] frame_offset=${o.frame_offset}${memStr}${imgStr}`);
            }
    
            // Build the multi-content response: text summary + per-observation
            // inline image blocks. We read each PNG from disk (Lua wrote it),
            // base64-encode for MCP transport.
            const content: ({ type: "text"; text: string } | { type: "image"; data: string; mimeType: string })[] = [
              { type: "text", text: lines.join("\n") },
            ];
            const fs = await import("node:fs");
            for (const o of obs) {
              if (!o.path) continue;
              try {
                const bytes = fs.readFileSync(o.path);
                content.push({
                  type: "image",
                  data: bytes.toString("base64"),
                  mimeType: "image/png",
                });
              } catch (err) {
                content.push({
                  type: "text",
                  text: `(failed to read observation at frame ${o.frame_offset} from ${o.path}: ${(err as Error).message})`,
                });
              }
            }
            return { content };
          }
    
          case "bizhawk_pause":         await bh.call("pause");          return ok("Emulation paused");
          case "bizhawk_unpause":       await bh.call("unpause");        return ok("Emulation resumed");
          case "bizhawk_reset":         await bh.call("reset");          return ok("Core reset");
          case "bizhawk_frame_advance": {
            const f = await bh.call<number>("frame_advance", { count: p.count ?? 1 });
            return ok(`Advanced ${p.count ?? 1} frame(s). Framecount: ${f}`);
          }
    
          case "bizhawk_screenshot": {
            const path = await bh.call<string>("screenshot", { path: p.path });
            return ok(`Screenshot saved: ${path}`);
          }
    
          case "bizhawk_save_state": {
            const r = await bh.call<{ path: string }>("save_state", { path: p.path });
            return ok(`Saved state to ${r.path}`);
          }
          case "bizhawk_load_state": {
            const r = await bh.call<{ path: string }>("load_state", { path: p.path });
            return ok(`Loaded state from ${r.path}`);
          }
    
          default:
            throw new Error(`Unknown tool: ${name}`);
        }
      });
    }
  • VALID_BUTTONS constant listing common button names referenced in the tool description for documentation purposes.
    const VALID_BUTTONS = [
      "A", "B", "X", "Y",                 // Face buttons
      "Up", "Down", "Left", "Right",      // D-pad
      "Start", "Select",                  // System
      "L", "R", "L1", "R1", "L2", "R2", "L3", "R3", // Shoulders/triggers/sticks
      "C", "Z", "C-Up", "C-Down", "C-Left", "C-Right",  // N64
      "Mode",                             // Genesis 6-button
    ];
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided; description fully covers behavior: sets input for exactly one frame, then reverts to human input, silently ignores invalid button names, returns a status line, and describes error conditions. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with clear sections (PURPOSE, USAGE, BEHAVIOR, RETURNS). Front-loaded with purpose. Slightly verbose due to empirical example, but each part serves a purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no annotations and no output schema, the description is remarkably complete: covers purpose, usage patterns, behavioral details, return format, error handling, and button name guidance. No gaps for effective use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema covers both parameters with 100% description coverage. The description adds minimal extra meaning but includes an example for buttons and clarifies the player default. Could be slightly more detailed about button name variability.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'set joypad button state for one player for exactly the next emulated frame.' It provides a specific verb-resource-scope and distinguishes from siblings like bizhawk_frame_advance by clarifying frame-level granularity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly tells when to use (drive games) and when not to (avoid single press+multiple frame_advance). Provides the correct interleaving pattern and explains how to release buttons. Includes empirical verification.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dmang-dev/mcp-bizhawk'

If you have feedback or need assistance with the MCP directory API, please join our Discord server