Skip to main content
Glama

mgba_write_range

Write a contiguous range of bytes to emulated RAM for seeding SRAM, patching code, or installing cheats. Supports up to 4096 bytes per call.

Instructions

Write a contiguous range of bytes to emulated RAM in one call. Useful for seeding SRAM, patching code blocks, or installing cheats. Maximum 4096 bytes per call.

NOTE: writes use mGBA's debug-direct memory access, which bypasses the cartridge bus model. On Game Boy with an MBC cartridge, this means writes to ROM region (0x0000-0x7FFF) won't trigger MBC bank-switch / RAM-enable commands, and writes to SRAM (0xA000-0xBFFF) hit the underlying buffer regardless of MBC enable state. To seed cartridge SRAM cleanly, use mgba_save_state / mgba_load_state with a pre-prepared state file.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
addressYesStart address
bytesYesArray of byte values (0-255). Length cannot exceed 4096.

Implementation Reference

  • Handler for the mgba_write_range tool — calls the mGBA bridge's 'write_range' RPC method with address and bytes, then returns the count of bytes written.
    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}`);
    }
  • Schema definition for mgba_write_range — requires 'address' (integer) and 'bytes' (array of integers 0-255, length 1-4096).
    {
      name: "mgba_write_range",
      description: `Write a contiguous range of bytes to emulated RAM in one call. Useful for seeding SRAM, patching code blocks, or installing cheats. Maximum 4096 bytes per call.\n\n${MBC_CAVEAT}`,
      inputSchema: {
        type: "object",
        required: ["address", "bytes"],
        properties: {
          address: { type: "integer", description: "Start address" },
          bytes: {
            type: "array",
            items: { type: "integer", minimum: 0, maximum: 255 },
            minItems: 1,
            maxItems: 4096,
            description: "Array of byte values (0-255). Length cannot exceed 4096.",
          },
        },
      },
    },
  • src/tools.ts:258-411 (registration)
    Registration of all tools via server.setRequestHandler, including mgba_write_range as a case in the CallToolRequestSchema switch.
    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}`);
        }
      });
    }
Behavior5/5

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

No annotations provided, but the description fully discloses the tool's behavior: maximum 4096 bytes per call, memory access bypassing cartridge bus model, and specific effects on different memory regions for Game Boy MBC cartridges.

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

Conciseness5/5

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

The description is concise with two clear paragraphs. The first paragraph states purpose and use cases, the second provides critical behavioral details. No fluff; every sentence earns its place.

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 the complexity of the tool (direct memory access, MBC interactions) and lack of output schema, the description is complete. It covers all necessary behavioral aspects and differentiates from sibling tools adequately.

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

Parameters3/5

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

Schema coverage is 100%, so baseline is 3. The description does not add significant new meaning beyond the schema's parameter descriptions (address and bytes), though it reiterates the byte limit and provides context.

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 states the tool writes a contiguous range of bytes to emulated RAM, with specific use cases (seeding SRAM, patching code, installing cheats). It clearly distinguishes from siblings like mgba_write8/16/32 which write single values, and mgba_read_range which reads.

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?

The description explicitly explains when to use the tool (seeding SRAM, patching, cheats) and when not to (writes to ROM region won't trigger MBC commands; writes to SRAM bypass enable state). It provides an alternative method (mgba_save_state/mgba_load_state) for clean SRAM seeding.

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-mgba'

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