loenn-mcp

السجل الرسمي الخرائط والسفر والطقس

Celeste map editor for AI agents: read, edit, validate, preview and generate .bin maps.

ماذا يمكنك أن تفعل مع loenn-mcp؟

  • list_maps — List all .bin files
  • read_map_overview — Summary of rooms, entities, triggers, stylegrounds
  • read_room — Full room details (tiles, entities, triggers, decals)
  • get_room_tiles — Raw tile grid (FG or BG)
  • read_map_metadata — Quick metadata without full read
  • search_entities — Find entities by type, position, room
  • search_triggers — Find triggers by type
  • compare_rooms — Side-by-side room comparison

التثبيت

أضف هذا إلى إعدادات MCP في عميلك (Claude Desktop: claude_desktop_config.json، وCursor: ‏.cursor/mcp.json، ويستخدم VS Code وغيره الصيغة نفسها «mcpServers»).

MCPB package https://github.com/Magedeline/loenn-mcp/releases/download/v7.2.0/loenn-mcp-7.2.0.mcpb

متغيرات البيئة

  • LOENN_MCP_WORKSPACE Root of the Celeste mod project; map paths are relative to it (default: current directory)

التوثيق

مأخوذ من ملف README للمشروع على GitHub ويُحدَّث أسبوعيًا. تفتح الروابط على GitHub.

المحتويات
  1. loenn-mcp
  2. Contents
  3. Features
  4. Safety: backups, copies and commits
  5. Opening maps in Lönn
  6. Sprite Fusion gray-box rooms
  7. Summer Engine gray-box rooms
  8. Shared PCG core (CelPcg)
  9. seleCte levels
  10. Tool reference
  11. Installation & Setup
  12. JSON round-trip & authoring
  13. pcgscene CLI
  14. Procedural Generation
  15. Image-to-Map Conversion
  16. Seeded Terrain Generation
  17. Analysis & Insights
  18. No AI Inside (v7.0.0)
  19. Configuration
  20. Architecture
  21. Requirements
  22. License

loenn-mcp

A Celeste map editor for AI agents — a Model Context Protocol (MCP) server that brings full Celeste .bin map editing to Claude, GitHub Copilot, and other MCP clients, plus a standalone pcgscene CLI for scripting and CI. Read, edit, analyze, generate, and preview maps without opening Lönn.

"For AI agents" describes who talks to this server (any MCP client), not what runs inside it — every tool here is deterministic and procedural. See No AI Inside.

Works with Everest mods and maps from Lönn or Ahorn.

Contents

Features

MCP tools for complete map manipulation (full list in the tool reference), plus the pcgscene command-line tool for scripting and CI. Every write keeps a timestamped backup, maps open in Lönn are protected from MCP writes, and gray-box rooms painted in Sprite Fusion import directly.

Core Tools

Reading & Querying

  • list_maps — List all .bin files
  • read_map_overview — Summary of rooms, entities, triggers, stylegrounds
  • read_room — Full room details (tiles, entities, triggers, decals)
  • get_room_tiles — Raw tile grid (FG or BG)
  • read_map_metadata — Quick metadata without full read
  • search_entities — Find entities by type, position, room
  • search_triggers — Find triggers by type
  • compare_rooms — Side-by-side room comparison

Editing

  • add_entity / remove_entity — Place or delete entities
  • update_entity / move_entity — Modify entity properties or position
  • add_trigger / remove_trigger — Place or delete triggers
  • set_room_tiles — Replace tile grid
  • add_room / remove_room — Create or delete rooms
  • create_map — Create new .bin file
  • update_room — Modify room properties (music, dark, wind, etc.)
  • clone_room — Duplicate a room
  • batch_add_entities — Add multiple entities at once
  • resize_room — Change room dimensions

JSON & Authoring (see JSON round-trip & authoring)

  • map_to_json / json_to_map — Any .bin to typed JSON and back, byte-identical; refuses lossy writes
  • check_authoring_json — Resolve hand-written content: defaults filled from Lönn plugins, unknown names flagged
  • merge_json_into_map — Add entities/triggers/rooms to a map; dry-run diff + highlighted preview, map-wide unique IDs, placement checks, additions only
  • run_author_script — Run a JavaScript authoring script (js/, lucid-dream style) and merge its output

Decals & Effects

  • list_decals / add_decal / remove_decal — Manage foreground/background decals
  • list_stylegrounds / add_styleground / update_styleground / remove_styleground — Manage map effects

Definitions & Catalog

  • list_entity_definitions / get_entity_definition — Browse entity types; look a definition up by the name placed in maps (e.g. MyMod/CloudBoss)
  • find_missing_definitions — Namespaced entities/triggers in a map that no installed Lönn plugin defines
  • audit_plugin_coverage — C# [CustomEntity] names with no Lönn plugin, and plugins with no C# code (reads the .csproj compile list)
  • list_trigger_definitions / get_trigger_definition — Browse trigger types
  • list_effect_definitions / get_effect_definition — Browse effect types
Analysis & Insights

Basic Analysis

  • analyze_map — Entity counts, type breakdown, world bounds
  • visualize_map_layout — ASCII mini-map
  • preview_map_section — Detailed ASCII preview

Advanced Analysis

  • analyze_entity_usage — Entity stats across entire map
  • analyze_difficulty — Room/map difficulty estimation
  • find_entity_references — Locate all instances of an entity type
  • detect_map_patterns — Identify design archetypes (linear, hub, etc.)
  • analyze_room_connectivity — Adjacency graph analysis

Suggestions & Improvements

  • suggest_improvements — Actionable room suggestions
  • compare_maps — Structural diff between maps

Wiki & Caching

  • wiki_save / wiki_search / wiki_list / wiki_get — Persist and retrieve analysis results

Project Management

  • get_mod_info — Project metadata and structure
  • validate_map / batch_validate_and_fix — Playability validation with auto-fix
  • game_status / game_reload_map / game_teleport / game_log — Drive the running game through Everest's debug server: see where you are, reload an edited map, jump to a room, read load errors
  • game_play_tas — Play CelesteTAS inputs from a room's spawn and report the room, position, deaths and dashes at the end: proof a room is beatable, or that a shortcut fails
  • validate_audio — Every audio event a map references, checked against GUIDs.txt and the game's FMOD banks, with closest-match suggestions
  • export_room_json / import_room_json — JSON room exchange

Diffing

  • summarize_map_diff — Track map evolution with snapshots
Rendering
  • render_map_html — Interactive HTML map preview with tiles, colour-coded entity markers, zoom/pan, search and a minimap; reads a .bin or a typed JSON dump, and can highlight a planned merge (overlay)
Procedural Generation

Pattern-Based Generation

  • build_pattern_library — Extract patterns from existing maps
  • generate_room_from_pattern — Generate rooms with strategy + seed
  • ingest_external_map — Download and extract patterns from GameBanana

Image & Terrain Generation

  • generate_map_from_image — Convert color-mapped images to playable maps
  • generate_terrain_map — Procedural maps with Perlin noise + Voronoi biomes
  • preview_terrain_biomes — Preview biome layout before generation

Safety: backups, copies and commits

No map is overwritten silently.

  • Every write keeps a timestamped backup before the file is replaced, in a backup store outside the mod folder (so Everest never loads it and the mod's git stays clean): %LOCALAPPDATA%\loenn-mcp\backups\<mod>\<path>\<name>.<YYYYMMDD-HHMMSS-micro>.bin. Each tool result ends with a line naming the file written and its backup.
  • New bytes are re-parsed before anything is replaced, then written to a temp file and swapped in atomically.
  • Copy first for chapter maps: map_copy, edit the copy, map_diff, then map_commit (dry run first; can take only some rooms; refuses rooms that fail validation, such as rooms without a player spawn).
  • LOENN_MCP_PROTECT (glob list, ;-separated, relative to the mod) makes matching maps read-only except through map_commit. Recommended for CEL: Maps/CEL/Area/*.bin;Maps/CEL/BSide/*.bin;Maps/CEL/CSide/*.bin;Maps/CEL/DSide/*.bin;Maps/CEL/ZSide/*.bin.
  • Writes are refused while the map is open in an editor started with editor_open, or open in your own Lönn, because Lönn's next save would overwrite them.
  • map_backups lists backups, map_restore puts one back (backing up the current file first).
  • map_diff compares meaning, not bytes: rooms, geometry, fg/bg tile cells, entities and triggers by id, decals, stylegrounds and metadata. Lönn re-encodes the whole file on save, so a byte diff says nothing useful.

Round trips are tested. To check every map in a folder (.bin to tree to .bin, and .bin to typed JSON to .bin, both byte-identical):

LOENN_MCP_TEST_MAPS="<Maps folder>" python -m unittest discover -s tests -k RealMaps

Opening maps in Lönn

editor_open(map, room) starts Lönn on that map and room. editor_wait blocks until you save (and reports the semantic diff of that save) or close Lönn. editor_close closes it, asking Lönn first; force kills it.

What this relies on, read from Lönn 1.0.10's source and tested on Windows:

MechanismBehaviour
Lönn.exe <map.bin>Opens that map instead of the last one.
--storage-directory <dir>Uses <dir> for settings, persistence, backups and plugin settings. The MCP uses an isolated copy (%LOCALAPPDATA%\loenn-mcp\loenn-storage, seeded from your settings and plugin settings), so your recent files are untouched. storage="user" uses your normal Lönn folder.
Room selectionNo command-line option exists. Lönn selects persistence.lastSelectedRoomName on load, so the MCP writes that key (and the camera) before launch.
Detecting a finished editLönn saves to <map>.bin.saving, verifies it, then renames it over the map. The MCP watches the map's size, mtime and hash, ignores it while .saving exists, and only counts bytes that parse.
Reloading in a running LönnNot possible: Lönn has no IPC or single-instance lock. Close and reopen (editor_close, then editor_open).

The editor side is an adapter (loenn_mcp/editors): LoennAdapter today, and RysyAdapter launches Rysy (--map) with the same watching, so the tool surface can move to a Rysy-based setup unchanged.

Sprite Fusion gray-box rooms

Paint gray-box rooms in Sprite Fusion and import them as Celeste rooms.

  1. sf_make_tileset() writes celeste_graybox.png, a legend and a README to %LOCALAPPDATA%\loenn-mcp\sprite-fusion.
  2. In Sprite Fusion, import celeste_graybox.png with tile size 16. Make the map 40x23 cells for one screen (one cell = one 8x8 Celeste tile). Paint ground on a layer with Collider on, background walls on a layer named bg, and player, spikes, spring, refill, strawberry, jump-through or dream block anywhere.
  3. Export as JSON (map.zip), then call sf_import(export_path, map_path, room, output_path=...), dry run first.

Details:

  • Tile ids in an export index the exported spritesheet, so tiles are matched to the tileset by pixels (needs Pillow), falling back to ids.
  • Spikes and jump-throughs merge along runs, dream blocks into rectangles. Entities get their Lönn defaults.
  • Only entities with a Lönn definition are placed (sprites first). A tile can carry Sprite Fusion attributes entity, anchor and entity data to place another entity; unknown names are refused.
  • Tiles on non-collider layers that are not in the tileset are decoration and are ignored, with a count in the report: decorate in Lönn after the gray-box plays well.
  • A new room is added (default: to the right of the existing rooms). An existing room changes only with replace=true, which swaps its fg/bg tiles and entities and keeps triggers, decals and settings.
  • A custom legend (legend_path) can map your own tileset's tiles to kinds and foreground tile characters.

Summer Engine gray-box rooms

Summer Engine is a Godot 4.7 fork. Rooms can be painted there as scenes and come back as Celeste rooms, and Celeste rooms (or Sprite Fusion exports) can go into Summer to be refined.

  • summer_init() creates the project at Documents\CEL-Graybox: the gray-box tileset as a Godot TileSet (custom data kind, collision on solid tiles), rooms/_template.tscn (layers bg, fg, gameplay and an orange 40x23 screen frame) and the bridge scripts in celeste/.
  • summer_open(scene) starts the Summer editor on a room scene. Summer opens on its AI chat panel, which asks you to sign in; switch to the 2D tab to paint. Save with Ctrl+S before exporting.
  • summer_export_room(scene, map_path, room, ...) turns a scene into a Celeste room, with the same checks, dry run and safe write as sf_import. replace=true updates an existing room and keeps each solid cell's tileset.
  • summer_import_room(map_path, room) puts an existing Celeste room into the project; summer_from_sprite_fusion does the same for a Sprite Fusion export.
  • Entities that are not gray-box tiles travel as Marker2D nodes with metadata entity plus their fields.
  • One tile is one 8x8 Celeste tile whatever the Godot tile size; foreign tilesets work too (a layer named fg/solid/terrain, or any tile with collision, counts as solid).

How it runs: Summer.exe --path <project> --summer-editor-script <script.gd> --summer-editor-script-out <dir> runs a GDScript in a headless editor after the import scan and writes result.json. Each call takes about 4 s. No sign-in or network is needed for this.

Shared PCG core (CelPcg)

csharp/ holds the procedural generation core that the MCP, the CEL mod and Summer Engine can all use (design: docs/PCG-CORE-DESIGN.md).

  • CelPcg.Core (netstandard2.0, no dependencies): PCG32 random numbers and seed derivation, the room model, the reach model (ported from CEL's overworld_gen/reach.py: jump 3 up / 6 across, dash 4, springs, refills, jump-throughs; no climbing), a validator (structure + every exit reachable from the entry) and the platform-chain generator.
  • CelPcg.Cli (celpcg): celpcg generate --exits left:16:4,right:10:4 --difficulty 4 --seed 42 prints a celpcg-room JSON document; celpcg validate --room room.json re-checks one.
  • CelPcg.Tests: dotnet test csharp/CelPcg.Tests (determinism, reach limits, and 1,200 generated rooms that must all validate with vanilla-only entities).

pcg_generate_room(map_path, room, exits, difficulty, seed, ...) builds the CLI on first use (needs the .NET 8 SDK or later), generates, and writes through the same checked path as the gray-box importers. A room is only returned when every exit is proven reachable; failed layouts retry with derived seeds, so the same request always gives the same room. The seed is stored on the room (celpcg_seed, celpcg_generator, celpcg_difficulty).

Limits of this first version: the entry must be a left or right exit; rooms are one platform-chain style; no templates or MdMC/WFC fill yet (phase 4 of the design).

seleCte levels

seleCte (Louis Robinet, FDG '25) learned the tile patterns of Celeste's vanilla rooms with a multi-dimensional Markov chain. It draws a level layout, fills each room by sampling from the model, and a Julia script on the Maple library encodes the rooms as a .bin that starts on a spawn and ends on a crystal heart.

selecte_generate(name, nb_rooms, proba, width, height, seed, ...) runs the whole chain in one call: the Python generator (uv run), the Julia encoder, then pcgscene's readiness checklist on the result. The level's data.json, room_<n>.csv, both logs and the .bin land in <state dir>\selecte\<name>. output_path also copies the .bin into the mod through the guarded write (backup, protected maps, open editors), unless the checklist found problems (allow_problems=true copies anyway).

  • Setup: clone seleCte, then uv sync and julia --project=. -e "using Pkg; Pkg.instantiate()" in it (Julia 1.11 or later; the trained model ships with the clone, Celeste itself is not needed). The MCP looks for the clone at SELECTE_DIR, then a seleCte folder next to this repository, then ~/seleCte.
  • width/height 22x14 runs in seconds; 40x23 (one screen) takes minutes per room. proba 0 is a corridor, values near 1 a labyrinth. tries_limit resamples a room that fails seleCte's playability check; reset_skeleton redraws the layout instead of keeping an unplayable room.
  • seed and model need the patched clone (upstream seleCte has no --seed and always uses pe_d5_full); the tool reports the generator's own error when a clone lacks them.
  • seleCte's playability check ignores gravity, jumping and dashing, so rooms are not proven beatable: use game_play_tas or pcg_generate_room's reach model for that. Everest registers a new .bin only after a game restart.

Tool reference

107 tools; 89 are listed by default. Tools marked extra are hidden unless LOENN_MCP_TOOLS=all. Generated by tools/gen_tool_reference.py.

Map Reading Tools
ToolWhat it does
list_mapsList all .bin map files in the project.
read_map_overviewParse a .bin map file and return a summary of rooms and structure.
read_roomGet detailed data for a specific room: tiles, entities, triggers.
get_room_tilesGet the complete tile grid for a room as text.
Map Editing Tools
ToolWhat it does
add_entityAdd an entity to a room and save the map.
remove_entityRemove an entity from a room by its ID.
add_triggerAdd a trigger to a room and save the map.
remove_triggerRemove a trigger from a room by its ID.
set_room_tilesSet tile data for a room. Each row is a line of characters ('0'=air).
add_roomAdd a new empty room to a map.
remove_roomRemove a room from a map.
create_mapCreate a new empty Celeste .bin map file.
Styleground Tools
ToolWhat it does
list_stylegroundsList all stylegrounds (foreground + background effects) in a map.
add_stylegroundAdd a styleground effect to a map.
remove_stylegroundRemove a styleground from a map by its index in the layer.
update_stylegroundMerge properties into an existing styleground without replacing it.
Entity / Trigger Catalog Tools
ToolWhat it does
list_entity_definitionsList available Lönn entity definitions from the project.
get_entity_definitionRead a Lönn entity Lua definition and extract its properties.
list_trigger_definitionsList available Lönn trigger definitions.
list_effect_definitionsList available Lönn effect definitions.
find_missing_definitionsList namespaced entities and triggers in a map that no installed Lönn plugin defines.
audit_plugin_coverageCompare the mod's C# [CustomEntity] names with its Lönn plugin definitions.
validate_audioCheck every audio event a map references against the sound banks.
Analysis Tools
ToolWhat it does
analyze_mapGet detailed statistics about a map file.
visualize_map_layout (extra)Generate an ASCII visualization of all room positions in a map.
Map Preview Tools
ToolWhat it does
preview_map_sectionGenerate an ASCII preview of a map section, like a Lönn mini-map.
render_map_htmlRender an interactive HTML map preview with zoom, pan, search, and detail panel.
Procedural Generation Tools
ToolWhat it does
build_pattern_library (extra)Scan local .bin maps and extract room patterns into a reusable library.
generate_room_from_pattern (extra)Generate a new room using patterns from the library and a randomness strategy.
validate_roomCheck a room for common playability problems.
ingest_external_map (extra)Download a Celeste map from an external URL and extract room patterns.
Image-To-Map And Terrain Generation Tools
ToolWhat it does
generate_map_from_image (extra)Convert a color-mapped image into a full playable Celeste map.
generate_terrain_map (extra)Generate a procedural Celeste map using Perlin noise and Voronoi biomes.
preview_terrain_biomes (extra)Preview the biome layout for a terrain generation without creating the map.
Map Reading Extensions
ToolWhat it does
read_map_metadataRead high-level metadata from a map without listing all room contents.
search_entitiesSearch for entities across all rooms (or a specific room) by type and/or position.
search_triggersSearch for triggers across all rooms by type.
compare_roomsCompare two rooms in the same map side-by-side.
Map Editing Extensions
ToolWhat it does
update_entityUpdate properties of an existing entity by ID.
move_entityMove an entity to a new position.
update_roomUpdate room-level properties (music, dark, wind, etc.).
clone_roomClone an existing room to a new name and position.
batch_add_entitiesAdd multiple entities to a room in one call.
Decals
ToolWhat it does
list_decalsList all decals in a room (foreground or background).
add_decalAdd a decal to a room.
remove_decalRemove a decal by index.
Advanced Analysis
ToolWhat it does
analyze_entity_usage (extra)Analyze entity usage across the entire map.
analyze_difficultyEstimate room or map difficulty from hazard density, nav aids, and tile coverage.
find_entity_references (extra)Find all occurrences of an entity type across the map.
detect_map_patterns (extra)Detect gameplay design archetypes (linear, hub, collectible-rich, etc.).
analyze_room_connectivityAnalyze room adjacency graph showing isolated rooms, dead ends, and hubs.
Suggestions
ToolWhat it does
suggest_improvements (extra)Suggest improvements for a room based on analysis.
compare_maps (extra)Compare two maps and show structural differences.
Wiki / Cache
ToolWhat it does
wiki_save (extra)Save analysis results or notes to the local wiki cache.
wiki_search (extra)Search the wiki cache by key, content, or tags.
wiki_list (extra)List all entries in the wiki cache.
wiki_get (extra)Retrieve a specific wiki entry by key.
Mod Project
ToolWhat it does
get_mod_infoGet information about the current mod project.
validate_mapValidate an entire map for playability issues.
Catalog Extensions
ToolWhat it does
get_trigger_definitionRead the source of a Lönn trigger definition .lua file.
get_effect_definitionRead the source of a Lönn effect definition .lua file.
Import / Export
ToolWhat it does
export_room_jsonExport a room as a JSON file for external editing or sharing.
import_room_jsonImport a room from a JSON file into a map.
Diff & Fix
ToolWhat it does
summarize_map_diff (extra)Create or compare a structural snapshot of the map.
batch_validate_and_fixValidate all rooms in a map and optionally auto-fix issues.
Room Settings
ToolWhat it does
get_room_settingsGet all editable settings for a room (music, physics, wind, camera, flags).
update_room_settingsUpdate room settings (music, physics, wind, camera, flags) by merging a JSON object.
rename_roomRename a room in a map.
move_roomMove a room to a new world-space position.
resize_roomResize a room (snaps to 8-pixel grid, preserves tile data).
Map Metadata & Dependency Settings
ToolWhat it does
get_map_metadataRead the map-level metadata block (Everest/celeste.yaml meta settings).
set_map_metadataSet map-level metadata properties (Everest meta block).
replace_audio_eventFind and replace an FMOD audio event path anywhere in a map.
get_map_dependenciesRead the mod dependencies list from a map's everest.yaml or meta node.
set_map_dependenciesWrite or update the mod dependency list in the map's everest.yaml.
Ml Asset Analysis
ToolWhat it does
analyze_map_assets (extra)ML-style analysis of a map's assets against the Maddie480 graphics dump.
Json Round-Trip And Authoring
ToolWhat it does
map_to_jsonConvert a .bin map to typed JSON that converts back byte-identical.
json_to_mapConvert typed JSON (from map_to_json, possibly edited) back to a .bin map.
check_authoring_jsonResolve authoring JSON without a map: show each object with its defaults filled in from the Lönn definitions, and flag unknown names.
merge_json_into_mapAdd entities, triggers and rooms from authoring JSON to an existing map.
run_author_scriptRun a JavaScript authoring script (js/ package, lucid-dream style) and use its output.
ToolWhat it does
game_statusWhere the running game is: map SID, side, room and player position.
game_reload_mapMake the running game re-read a map after it was edited on disk.
game_teleportLoad a room of a map in the running game (a fresh debug session).
game_play_tasPlay CelesteTAS inputs from a room's spawn and report where the player ended up.
game_logRecent lines of the game's log.txt: errors, warnings and level loads.
Safety: Copies, Commits, Backups, Diff
ToolWhat it does
map_copyCopy a map to a working copy so edits never touch the original. Default target: Maps/<mod>/wip/<name>.copy.bin.
map_commitApply a working copy to the real map: whole file, or only the listed rooms. Dry run shows the diff first.
map_backupsList the timestamped backups of a map, newest first.
map_restoreRestore a map from one of its backups (the current file is backed up first).
map_diffSemantic diff of two maps: rooms, geometry, fg/bg tiles, entities, triggers, decals, stylegrounds, meta.
Assets
ToolWhat it does
check_assetsCheck that a map only uses things that exist: entity/trigger names with a Lönn definition, decal and parallax textures.
check_sprites_xmlCheck a Sprites.xml: it parses, ids are unique, and every Anim/Loop has frames in the atlas. Check this first when a sprite does not show in game.
Editors
ToolWhat it does
editor_openOpen a map in Lönn (or Rysy), optionally on a room. MCP writes to that map are refused until it closes.
editor_waitWait until the user saves in the editor (or it closes), then report what changed. Call again to keep waiting.
editor_statusList editor sessions from this server and any other running Lönn.
editor_closeClose an editor session. Without force, the editor may ask about unsaved changes and stay open.
Sprite Fusion
ToolWhat it does
sf_make_tilesetWrite the Celeste gray-box tileset for Sprite Fusion (PNG, legend JSON, README).
sf_importImport a Sprite Fusion JSON export (map.zip) as a Celeste room: solid/bg tiles plus gray-box entities.
Summer Engine
ToolWhat it does
summer_initCreate or refresh the Summer Engine gray-box project (tileset with collision, room template, bridge scripts).
summer_openOpen the gray-box project in Summer Engine, optionally on a room scene.
summer_roomsList the room scenes in the Summer gray-box project.
summer_export_roomTurn a Summer room scene into a Celeste room (new, or replace=true). Same checks and safe write as sf_import.
summer_import_roomCopy a Celeste room into the Summer project as a scene (fg/bg tiles, gray-box entities as tiles, others as markers).
summer_from_sprite_fusionTurn a Sprite Fusion export (map.zip) into a Summer room scene, to refine it in Summer before export.
Shared Pcg Core
ToolWhat it does
pcg_generate_roomGenerate a gray-box room with the shared C# PCG core and write it into a map (dry run first).
Selecte Levels
ToolWhat it does
selecte_generateGenerate a whole level with seleCte (rooms sampled from a Markov model of the vanilla maps), encode it with Julia and validate the .bin.

Installation & Setup

Install the Python package

The PyPI package loenn-mcp has not been updated since 5.2.0, so pip install loenn-mcp gets an old version. Install the wheel attached to the latest release instead:

pip install https://github.com/Magedeline/loenn-mcp/releases/download/v7.2.0/loenn_mcp-7.2.0-py3-none-any.whl

Or from source:

git clone https://github.com/Magedeline/loenn-mcp
cd loenn-mcp
pip install -e .
Install the MCPB bundle (no PyPI, no Python setup)

Every release attaches loenn-mcp-<version>.mcpb, a bundle for hosts that support MCPB, such as Claude Desktop. It uses the uv runtime: the host runs uv run on the bundle, and uv installs Python and the dependencies by itself, so only uv has to be on the machine. Download the bundle from the releases page, open it in the host, and point its "Celeste mod folder" setting at your mod. python tools/build_mcpb.py builds the same bundle from a checkout (sources in mcpb/), and server.json lists it for the MCP Registry as io.github.Magedeline/loenn-mcp.

Connect to Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "loenn-mcp": {
      "command": "python",
      "args": ["-m", "loenn_mcp.server"],
      "env": {
        "LOENN_MCP_WORKSPACE": "/absolute/path/to/your/mod"
      }
    }
  }
}
Connect to GitHub Copilot (VS Code)

Add to .vscode/mcp.json:

{
  "servers": {
    "loenn-mcp": {
      "type": "stdio",
      "command": "python",
      "args": ["-m", "loenn_mcp.server"],
      "env": {
        "LOENN_MCP_WORKSPACE": "${workspaceFolder}"
      }
    }
  }
}
Preview Maps Locally
python -m loenn_mcp.preview_map Maps/01_City_A.bin
python -m loenn_mcp.preview_map Maps/01_City_A.bin g-   # filter by prefix

The interactive HTML preview supports zoom, pan, room details, search, and minimap with keyboard shortcuts.


JSON round-trip & authoring

Maps can leave the binary format and come back without losing a byte, and new content can be written by hand or in JavaScript and merged into an existing map. Nothing here needs Ruby or Node at runtime; Node is only used if you run JS authoring scripts.

Typed JSON: map_to_json / json_to_map
map_to_json("Maps/MyMod/1-City.bin")                 → Export/1-City.json
json_to_map("Export/1-City.json", "Maps/MyMod/1-City-copy.bin")

Every attribute keeps its binary type, so the round trip is byte-identical:

{
  "format": "celeste-map-json", "version": 1,
  "header": "CELESTE MAP", "package": "1-City",
  "source": {"file": "1-City.bin", "sha256": "…", "tree_sha256": "…"},
  "lookup": ["Map", "levels", "level", "…"],
  "root": {"name": "Map", "children": [
    {"name": "levels", "children": [
      {"name": "level", "attrs": {
        "name": ["lookup", "lvl_a-00"],
        "x": ["i16", -40], "width": ["i32", 320], "dark": ["bool", false],
        "music": ["string", "event:/music/lvl1/main"], "alpha": ["f32", 0.3]},
       "children": [{"name": "solids", "attrs": {"innerText": ["rle", "0000…"]}}]}]}]}
}
  • Types: bool, u8, i16, i32, f32, lookup (string-table string), string (plain string), rle (run-length tile data, shown decoded).
  • Values are what the file holds. A room named lvl_a-00 stays lvl_a-00.
  • lookup is the file's string table in file order; edits only append to it. File quirks a canonical writer would not reproduce (a duplicate table entry, an unusual RLE split) are kept as optional extras ([type, value, {"key_index": 12}]).
  • Refusals. json_to_map writes nothing if a value does not fit its type (["u8", 300]), if an unedited dump would not reproduce its source byte-for-byte (checked with source.sha256), or if the output would not read back as the same map. Writes are atomic (temp file + replace) and keep a timestamped backup (see Safety).
  • Hand-written documents may skip lookup/source and give bare values ("x": 8); those get the type Celeste's own packer would pick.
Authoring JSON

A looser format for content you write yourself. Only names and positions are needed:

{"rooms": [
  {"room": "a-05",
   "entities": [{"name": "strawberry", "x": 120, "y": 64},
                {"name": "CEL/GreenBerry", "x": 200, "y": 40}],
   "triggers": [{"name": "CEL/PinkPlatBerryCollectTrigger", "x": 0, "y": 0,
                 "width": 16, "height": 184}]},
  {"room": "a-99", "x": 0, "y": 2400, "width": 320, "height": 184,
   "solids": "1111…\n1000…", "attrs": {"dark": true},
   "entities": [{"name": "player", "x": 24, "y": 160}]}
]}
  • Defaults come from the Lönn plugin definitions: your mod's Loenn/, other installed mods (when the workspace is in Celeste/Mods), then vanilla Lönn. strawberry above gets winged, moon, checkpointID and order. "placement": "normal_winged" picks another placement's defaults; any other key (flat or under "data") sets an attribute; "nodes": [[x, y], …] adds nodes.
  • Unknown names are flagged (typos, a trigger listed as an entity, a helper that isn't installed). check_authoring_json shows the resolved result without a map.
  • Existing rooms are only added to. Room attributes and tiles are accepted only when creating a room (give x, y, width, height).
  • Room names match with or without lvl_.
Merging: merge_json_into_map
merge_json_into_map("Maps/MyMod/1-City.bin", "plan.json")                  # dry run + preview
merge_json_into_map("Maps/MyMod/1-City.bin", "plan.json", dry_run=false)   # write
  • Dry run by default. It prints a per-room diff and renders an HTML preview with every added object ringed (magenta, or red when a check failed).
  • IDs are unique across the whole map: new ones start above the highest ID in any room, and an explicit "id" that is already used is refused.
  • Placement checks for each added object: inside the room, not inside solid tiles, not in an air pocket sealed off from every room edge and spawn. The existing room validators (validate_room, validate_map) run before and after, and any issue the merge introduces is reported. A write with problems is refused unless allow_problems=true; unknown names need allow_unknown=true.
  • Only additions. The output bytes are re-read and compared with the original; if anything that was already there differs, nothing is written.
Authoring maps from JavaScript

js/ is a small dependency-free Node package whose API follows lucid-dream (see THIRD_PARTY_NOTICES.md). It produces authoring JSON; the Python server does all .bin writing.

// add-berries.js
const { Plan, Room, Entity, Trigger, Tiles, line } = require('loenn-mcp-author'); // or require('./js')

const plan = new Plan();
plan.room('a-05').add(Entity.Strawberry({ x: 120, y: 64 }));          // existing room
for (const [x, y] of line(64, 96, 192, 96, 3)) plan.room('a-06').add(Entity.Strawberry({ x, y }));
plan.room('l-07').add(Trigger('CEL/PinkPlatBerryCollectTrigger', { x: 1700, y: 300, width: 56, height: 104 }));

// a new room, lucid-dream style: pixel position, size in tiles, tiles as text
plan.addRoom(new Room([0, 2400], { name: 'a-99', size: [40, 23], fgTiles: new Tiles(`...`),
  entities: [Entity.Player({ x: 24, y: 160 })] }));

module.exports = plan;          // or: module.exports = async (api) => plan;  or: plan.emit();
  • Entity.Strawberry(...) camel-cases the property to the placed name (strawberry); namespaced names use the call form, Entity('CEL/GreenBerry', {...}).
  • Run it through the server (dry run, with preview): run_author_script("add-berries.js", map_path="Maps/MyMod/1-City.bin").
  • Or from a shell: node js/bin/loenn-author.js add-berries.js --out plan.json, or loenn-json author add-berries.js --map Maps/MyMod/1-City.bin [--write].
  • Tests: node js/test/test.js. Examples: js/examples/.
JSON-driven preview

render_map_html takes a typed JSON dump as well as a .bin, and an overlay (authoring JSON) that is merged in memory only and highlighted. This is the viewer idea from maddievision/celestial built on the existing renderer.

loenn-json CLI
loenn-json map2json Maps/MyMod/1-City.bin -o city.json
loenn-json json2map city.json Maps/MyMod/1-City.bin --overwrite
loenn-json --mod . merge Maps/MyMod/1-City.bin plan.json            # dry run
loenn-json --mod . merge Maps/MyMod/1-City.bin plan.json --write
loenn-json --mod . author add-berries.js --map Maps/MyMod/1-City.bin

More examples: examples/authoring/.


pcgscene CLI

A batch-first command-line tool for scanning and repairing maps outside an MCP client — install once, use from scripts, pre-commit hooks, or CI. Every command that touches a .bin file writes through the same atomic, backed-up, round-trip-validated path as the MCP server, so a bad run can never destroy a map.

pip install loenn-mcp
pcgscene scan MyMap.bin              # score every room + map connectivity
pcgscene validate MyMap.bin          # in-game readiness checklist (exit 0/1)
pcgscene fix MyMap.bin --dry-run     # preview safe auto-repairs
pcgscene fix MyMap.bin               # apply them (backed up first)
pcgscene diff before.bin after.bin   # what changed between two maps
CommandWhat it does
scanScores every room (interestingness, difficulty, exit connectivity, spawn presence, fairness gate) plus map-wide reachability from the start room.
scoreDeep report for a single room (--room NAME).
validateIn-game readiness checklist — bad names, missing spawns, sealed exits, unreachable rooms — with a CI-friendly exit code.
fixSafe auto-repairs: adds missing spawns, sanitizes bad room names, converts strawberry{golden} to real goldenBerry entities. Supports --dry-run and --only spawns,names,berries.
diffRoom/entity/tile changes between two maps.
generatePreset-driven generation (quick / simple_fair / explore / challenge) with a reproducible --seed. Requires the pcg_helper module from the full/Pro build (loenn-mcp-delta) — this build's generate refuses gracefully and tells you so.

Every command accepts --json (and --out FILE) for machine-readable output, so an editor plugin or CI step can consume the report directly.


Procedural Generation

Generation Strategies
StrategyDescription
balancedMix of exploration and challenge (default)
explorationOpen spaces, gentle platforming, few hazards
challengeDense tiles, many hazards, tight jumps
speedrunLinear path, minimal platforms, fast flow
Model Profiles
ProfileBehaviorUse Case
creativeRandom seed each callMaximum variety
deterministicStable seed from strategyReproducible layouts
architectRandom seedEmphasis on shape/connectivity
Quick Start Example
# 1. Build pattern library from existing maps
build_pattern_library()

# 2. Create a new map
create_map("Maps/PCG/Generated.bin", "PCG/Generated")

# 3. Generate rooms
generate_room_from_pattern(
  map_path="Maps/PCG/Generated.bin",
  room_name="a-01",
  strategy="exploration",
  seed=42,
  model_profile="deterministic"
)

# 4. Validate and preview
validate_room("Maps/PCG/Generated.bin", "a-01")
render_map_html("Maps/PCG/Generated.bin")
Seeded Generation

Use seed=<int> + model_profile="deterministic" for reproducible output:

# Both calls produce identical rooms
generate_room_from_pattern(..., strategy="challenge", seed=1234, model_profile="deterministic")
generate_room_from_pattern(..., strategy="challenge", seed=1234, model_profile="deterministic")
GameBanana Integration

Download and extract patterns from community mods:

# Dry-run (preview only)
ingest_external_map(
  source_url="https://gamebanana.com/mods/53774",
  attribution="Spring Collab 2020",
  confirm_download=False
)

# Download and extract
ingest_external_map(
  source_url="https://gamebanana.com/mods/53774",
  attribution="Spring Collab 2020 (various authors)",
  confirm_download=True,
  tags="community,expert"
)

Patterns are saved to PCG/Datasets/ with attribution. Always verify mod licenses permit derivative use.


Image-to-Map Conversion

Convert color-mapped images directly into playable Celeste maps. Each pixel becomes one 8×8 tile.

Default Color Mapping
ColorHexMaps to
Black#000000Solid tile
White#FFFFFFAir (empty)
Red#FF0000Spike hazard
Green#00FF00Player spawn
Blue#0000FFJump-through platform
Yellow#FFFF00Strawberry
Magenta#FF00FFSpring
Cyan#00FFFFRefill crystal
Orange#FF8000Crumble block
Grey#808080Background solid
Usage
# Basic conversion
generate_map_from_image(image_path="Assets/my_level.png")

# Custom colors and scale
generate_map_from_image(
    image_path="Assets/large_map.png",
    output_path="Maps/Custom/level.bin",
    scale=4,  # 4×4 pixel blocks → 1 tile
    color_map_json='{"#FF0000":"solid","#00FF00":"spawn"}'
)

Requires Pillow: pip install loenn-mcp[image]


Seeded Terrain Generation

Generate complete maps with Perlin noise and Voronoi biomes. Inspired by AliShazly/map-generator.

Biomes
BiomeTerrain
mountainDense tiles, tight platforms, spikes
forestModerate density, many platforms, springs
plainsOpen spaces, gentle platforms, collectibles
lakeSparse tiles, jump-throughs, refills
caveEnclosed, crumble blocks, dark rooms
summitSparse platforms, wind effects
Quick Example
# Generate a 4×3 map with seed 42
generate_terrain_map(seed=42, difficulty=3, width_rooms=4, height_rooms=3)

# Preview biome layout before generating
preview_terrain_biomes(seed=42, width_rooms=4, height_rooms=3)
# Output:
# [P] [^] [^] [F]
# [~] [P] [^] [M]
# [C] [~] [P] [F]
Parameters
ParameterDefaultDescription
seed-1 (random)Integer seed for reproducible output
width_rooms4Rooms horizontally
height_rooms3Rooms vertically
frequency8.0Perlin noise frequency (lower = smoother)
voronoi_points12Number of biome region centres
biome_setallComma-separated biome names
difficulty31-5 scale for hazard density

Analysis & Insights

Advanced analysis tools for map design, difficulty, and patterns.

Quick Examples
# Analyze difficulty
analyze_difficulty(map_path="Maps/MyMod/1-City.bin")

# Detect gameplay patterns
detect_map_patterns(map_path="Maps/MyMod/1-City.bin")
# → "standard-level (7-15 rooms)", "linear-horizontal", "checkpointed"

# Get room suggestions
suggest_improvements(map_path="Maps/MyMod/1-City.bin", room_name="lvl_a-01")

# Track map evolution
summarize_map_diff(map_path="Maps/MyMod/1-City.bin")  # save snapshot
# ... edit map ...
summarize_map_diff(map_path="Maps/MyMod/1-City.bin")  # show diff

# Cache results for instant re-use
wiki_save(key="city_difficulty", content="Avg 4.2/10, 3 hard rooms", tags="analysis")
wiki_search(query="difficulty")

# Batch validation
batch_validate_and_fix(map_path="Maps/MyMod/1-City.bin", auto_fix=True)

# Search and clone
search_entities(map_path="Maps/MyMod/1-City.bin", entity_type="strawberry")
clone_room(map_path="Maps/MyMod/1-City.bin", source_room="lvl_a-01", new_name="lvl_a-01-copy")

# Export/import rooms
export_room_json(map_path="Maps/MyMod/1-City.bin", room_name="lvl_a-01")
import_room_json(map_path="Maps/MyMod/2-Resort.bin", json_path="Export/lvl_a-01.json")
Wiki Cache

Analysis results persist in .loenn_mcp_wiki/ as JSON files for instant re-use across sessions.


No AI Inside (v7.0.0)

As of v7.0.0 every tool in loenn-mcp is deterministic and procedural — Markov chains, wave-function collapse, noise, BFS/graph analysis. The server makes no calls to any AI/LLM API, requires no API key, and ships no AI models. The former ai_analyze_map / ai_describe_room / ai_suggest_entities tools and the anthropic dependency were removed.

Disclaimer: this project was developed with the assistance of Claude (Anthropic) as a coding tool, based on the algorithms published in Robinet et al., "Towards a Celeste AI Framework" (FDG '25, DOI 10.1145/3723498.3723796). All output has been human-tested in Lönn and in-game.


Configuration

VariableDefaultDescription
LOENN_MCP_WORKSPACECurrent directoryRoot of your Celeste mod project. All map paths are relative to this. Path traversal is blocked.
LOENN_MCP_LOENN(shipped table)A Lönn install (folder with Lönn.exe, or its src/) to read vanilla entity/trigger defaults from, instead of loenn_mcp/data/vanilla_defs.json
LOENN_MCP_NODEnode on PATHNode.js executable for run_author_script
LOENN_MCP_AUTHOR_CLIbundled js/bin/loenn-author.jsAlternative authoring CLI
LOENN_MCP_STATE_DIR%LOCALAPPDATA%\loenn-mcpBackups, isolated Lönn storage, Sprite Fusion tileset
LOENN_MCP_PROTECT(none);-separated globs of maps that only map_commit may change
LOENN_MCP_TOOLScoreall also lists the extra analysis and older generator tools
LOENN_MCP_LOENN_STORAGEisolatedDefault Lönn storage for editor_open (isolated or user)
LOENN_EXEOlympus' Lönn.exeLönn executable
RYSY_EXEnewest Rysy.exe under Documents\GitHub\RysyRysy executable
SUMMER_EXE%LOCALAPPDATA%\SummerEngine\current\Summer.exeSummer Engine executable
LOENN_MCP_SUMMER_PROJECTDocuments\CEL-GrayboxSummer gray-box project
CELPCG_CLIbuilt csharp/CelPcg.CliUse another celpcg build (exe or dll)
SELECTE_DIRseleCte next to this repo, else ~/seleCteThe seleCte clone for selecte_generate
UV_EXEuv on PATHuv executable that runs seleCte's generator
JULIA_EXEjulia on PATHJulia 1.11+ executable that runs seleCte's encoder
LOENN_MCP_DEBUGRChttp://localhost:32270Everest debug server for the game_* tools

Architecture

Core Modules

celeste_bin.py — Standalone .bin parser

  • Pure Python (no Everest/Lönn required)
  • Lossless read/write: an unedited map is written back byte-for-byte (string table order, value types and RLE data are preserved), and edits only append to the string table
  • Handles all 7 value types: bool, uint8, int16, int32, float32, lookup string, raw string, RLE-encoded string
  • Recursive element tree matching Lönn/Maple format

map_json.py — Typed JSON for .bin maps

  • Every value keeps its type; unedited dumps convert back byte-identical (verified on export)
  • Refuses writes that would not round-trip; atomic writes through celeste_bin

lua_defs.py — Lönn plugin definitions without running Lua

  • Tokenizes plugin files and evaluates literal tables (placements, field types, node limits)
  • Catalog of the mod's own, other installed mods' and vanilla definitions (data/vanilla_defs.json, generated from Lönn by tools/gen_vanilla_defs.py)

authoring.py — Authoring JSON and merges

  • Default filling, unknown-name flags, map-wide ID allocation, placement checks
  • Additions-only verification on the re-read output bytes

js/ — loenn-mcp-author, the JavaScript authoring package (Node, no dependencies)

pcg.py — Procedural generation

  • Pattern extraction from rooms (size, entity density, tile motifs, gameplay tags)
  • JSON pattern library with deduplication
  • Strategy-based generation (balanced, exploration, challenge, speedrun)
  • Seeded randomness for reproducible output
  • Model profiles (deterministic, creative, architect)

image_map.py — Image-to-map conversion

  • Color-to-role mapping (configurable palette)
  • Automatic room splitting
  • Entity placement from pixel colors
  • Scale support (downscaling)
  • Fuzzy color matching

terrain_gen.py — Seeded terrain generation

  • Perlin noise with fractal octaves
  • Voronoi biome partitioning
  • Fully seeded (same seed = identical output)
  • Difficulty scaling (1-5)
  • Biome-aware entities

gdep_tools.py — Game analysis

  • Wiki caching (.loenn_mcp_wiki/)
  • Pattern detection (linear, hub, collectible-rich, etc.)
  • Difficulty analysis (1-10 scale)
  • Room connectivity graphs
  • Map diffing with snapshots
  • Batch validation and auto-fix
  • Actionable suggestions

server.py — MCP server

  • Built with FastMCP
  • Path-traversal protection
  • Atomic map writes
  • Explicit download confirmation

safety.py — write policy: guards, timestamped backup store, per-call write report

mapdiff.py — semantic diff of two maps (rooms, tiles, entities by id, decals, stylegrounds, meta)

editors/ — EditorAdapter with save watching; LoennAdapter (launch, room preselect, isolated storage), RysyAdapter

assets.py — texture index (mod, installed mods, vanilla atlas .meta), Sprites.xml and map asset checks

summer.py — Summer Engine bridge: project setup, headless editor scripts, scene dump to room and back

pcg_core.py — builds and calls the celpcg CLI, turns its rooms into importable conversions

selecte.py — seleCte bridge: finds the clone, runs its generator (uv) and Julia encoder, summarizes the log, validates the .bin

sprite_fusion.py — gray-box tileset, Sprite Fusion export reader, pixel matching, conversion to rooms

cli.py — pcgscene command-line tool

  • scan / score / validate / fix / diff, all --json-capable
  • Shares celeste_bin's atomic-write path — no separate write logic to drift

Requirements

  • Python 3.9+
  • fastmcp >= 3.0.0
  • Pillow >= 9.0 (optional: image-to-map conversion, and pixel matching for Sprite Fusion imports)
  • Node.js 14+ (optional, only for JavaScript authoring scripts)

Install with all optional features:

pip install loenn-mcp[image]

No Celeste installation required.


License

MIT — see LICENSE. Credits for lucid-dream, Lönn and maddievision/celestial: THIRD_PARTY_NOTICES.md.

الأسئلة الشائعة

ما هو loenn-mcp؟

Celeste map editor for AI agents: read, edit, validate, preview and generate .bin maps.

كيف أثبّت loenn-mcp؟

اتبع خطوات التثبيت في توثيقه المعروض في هذه الصفحة وفي مستودعه.

هل يحتاج loenn-mcp إلى مفتاح API؟

لا يذكر إدراجه في السجل أي مفتاح أو سر إلزامي. قد تتطلب بعض الميزات حسابًا لدى الخدمة التي يتصل بها.

هل loenn-mcp مفتوح المصدر؟

نعم. شيفرته متاحة على GitHub بترخيص MIT.

من يصون loenn-mcp؟

ينشره Magedeline. يدرجه AI.tn لكنه لا يصونه ولا يوصي به.

من سجل MCP الرسمي. الإدراج ليس توصية: راجع شيفرة الخادم وصلاحياته قبل منحه الوصول إلى بياناتك.