Skip to main content
Glama
README.md
# @gbs-toolkit/mcp-server

MCP server for structured GB Studio 4.x project editing — read/patch scripts, scenes, actors, triggers, variables; build ROMs; sprite generation pipeline.

Designed to be driven by an LLM coding assistant (Claude Code, Cursor, etc.) so the model can mutate `.gbsres` resources safely instead of dropping bytes into JSON by hand.

## Install

```bash
npm install -g @gbs-toolkit/mcp-server
```

Or invoke on demand via `npx`:

```bash
npx -y @gbs-toolkit/mcp-server
```

## Run as an MCP server

The server speaks stdio MCP. Most users wire it up in their LLM client's `.mcp.json`:

```json
{
  "mcpServers": {
    "gbs": {
      "command": "npx",
      "args": ["-y", "@gbs-toolkit/mcp-server"],
      "env": {
        "GBS_PROJECT_ROOT": "./gbsproj/demo"
      }
    }
  }
}
```

## Environment variables

| Variable | Required | Default | Notes |
|---|---|---|---|
| `GBS_PROJECT_ROOT` | yes | — | Directory containing `<name>.gbsproj` |
| `GBS_CLI_PATH` | no | probes common install locations | Absolute path to `gb-studio-cli.js` |
| `GBS_MGBA_RUNNER` | no | none | Path to a libmgba-linked runner binary used by `run_emulator` (build it from `native/build.sh`) |
| `GBS_ROM_OUT` | no | `<root>/build` | Output directory for `build_rom` |
| `GBS_LOG_PATH` | no | `<root>/build/compile.log` | Captured compile log |
| `GBS_SCREENSHOT_DIR` | no | `<root>/build/screenshots` | Where `run_emulator` drops PNGs |
| `SPRITE_PROVIDER` | no | `openai` | `openai` / `gemini` / `replicate` / `fal` |
| `OPENAI_API_KEY` | conditional | — | Required if `SPRITE_PROVIDER=openai` and `generate_sprite` is used |
| `GEMINI_API_KEY` | conditional | — | Required for `gemini` provider |
| `REPLICATE_API_TOKEN` | conditional | — | Required for `replicate` provider |
| `FAL_KEY` | conditional | — | Required for `fal` provider |

## Tool inventory

| Tool | Kind |
|---|---|
| `list_scenes` / `read_scene` / `list_actors` / `read_script` / `read_compile_log` | read |
| `patch_script` | write (insert / replace / delete, applied in order) |
| `set_variable` | write (upserts a `VARIABLE_SET_TO_VALUE` event in the start scene's onInit) |
| `create_scene` / `create_actor` / `create_trigger` / `create_variable` / `create_custom_event` / `set_start_scene` | write |
| `delete_scene` / `delete_actor` / `delete_trigger` / `delete_custom_event` / `delete_variable` | write (cross-script reference scan; refuses unless `force: true`) |
| `generate_sprite` | write (text-to-image → 4-colour DMG quantise → emits `(asset.png, asset.png.gbsres)`) |
| `convert_image_to_sprite` | write (no AI; same pipeline applied to a user-supplied PNG/JPG) |
| `build_rom` | subprocess (spawns GB Studio CLI's `make:rom`) |
| `run_emulator` | subprocess (drives a libmgba-linked C runner) |
| `screenshot` | read image (returns base64 PNG as multimodal MCP content) |

## What is NOT exposed

Creation/import of binary assets stays GUI-side: `backgrounds`, `tilesets`, `emotes`, `avatars`, `fonts`, `music`, `sounds`, plus `actorPrefabs` / `triggerPrefabs` / `palettes` / `notes`. The MCP layer can read and patch scripts that **reference** these resources by id, but cannot create their `.png` / `.uge` / `.wav` payloads. Sprites are the one exception (see `generate_sprite` / `convert_image_to_sprite`).

## Built-in guardrails

The server enforces two checks that previously had to live in prompts:

1. **Dialogue width audit** — `patch_script` validates every `EVENT_TEXT` / `EVENT_MENU` / `EVENT_CHOICE` / `EVENT_DIALOGUE` / `EVENT_MARQUEE` string against the GB Studio default-font line budget (18 columns; `$var$` placeholders reserve 5 chars to cover signed-16-bit values). Multi-box `text: string[]` form, CJK width (2 columns / char), and recursive `children` branches are all walked. A violation rejects the patch with `DIALOGUE_WIDTH_EXCEEDED` and a per-line breakdown. Pass `widthBudget` to override (e.g. for a custom narrow font) or `force: true` to bypass.

2. **Sprite quality check** — `generate_sprite` and `convert_image_to_sprite` analyse the post-quantisation RGBA buffer (opaque ratio, effective DMG colour count, edge density, single-shade dominance). Clearly degenerate outputs (near-empty frame, ≤1 effective shade) refuse to write the asset; borderline cases (2 shades, low edge density, >85% one shade) save with a `quality.level: "warn"` field in the response. Pass `force: true` to skip the check.

Both checks return structured metadata so the LLM can self-correct rather than ploughing through.

## Building the emulator runner

`run_emulator` requires a libmgba-linked C binary. Stock mGBA CLIs do **not** accept a startup `--script` flag, so we ship a small dedicated runner. From the cloned repo:

```bash
cd native
MGBA_SRC=/path/to/mgba MGBA_BUILD=/path/to/mgba/build ./build.sh
```

Then point the env var at the result: `GBS_MGBA_RUNNER=/path/to/gbs-mgba-runner`.

If you don't need `run_emulator`, ignore this — every other tool works without it.

## License

MIT

TDQS

A4.2/5.0

Scored across 23 tools

Disambiguation5/5

Each tool targets a distinct resource and action (e.g., delete_custom_event vs delete_scene, create_actor vs create_trigger). Descriptions clearly differentiate overlapping operations like read_script vs patch_script, and list_scenes vs read_scene. No two tools serve the same purpose.

Naming Consistency5/5

All tools follow a consistent verb_noun pattern in snake_case (e.g., list_scenes, create_variable, delete_actor). Verbs like list, read, create, delete, set, build, run, generate, convert are used predictably without mixing styles.

Tool Count5/5

23 tools cover a comprehensive range of operations for GB Studio development (scenes, actors, triggers, variables, scripts, sprites, build/run). This count is well-scoped for the toolkit's purpose—neither too few to be incomplete nor too many to be unwieldy.

Completeness4/5

The toolkit covers CRUD for scenes, actors, triggers, variables, custom events, and scripts, plus building, emulation, and sprite generation. Minor gaps include lack of dedicated list tools for variables and custom events (though scoped via read_scene and inference), and no direct background/palette management despite backgroundId references.

Maintenance

ActivityInactive
ResponsivenessNo issues