Server data from the Official MCP Registry
Celeste map editor for AI agents: read, edit, validate, preview and generate .bin maps.
About
Celeste map editor for AI agents: read, edit, validate, preview and generate .bin maps.
Security Report
Valid MCP server (0 strong, 2 medium validity signals). No known CVEs in dependencies. Imported from the Official MCP Registry.
6 files analyzed · No issues found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
Permissions Required
This plugin requests these system permissions. Most are normal for its category.
What You'll Need
Set these up before or after installing:
Environment variable: LOENN_MCP_WORKSPACE
Documentation
View on GitHubFrom the project's GitHub README.
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
- Safety: backups, copies and commits
- Opening maps in Lönn
- Sprite Fusion gray-box rooms
- Summer Engine gray-box rooms
- Shared PCG core (CelPcg)
- seleCte levels
- Tool reference
- Installation & setup
- JSON round-trip & authoring
pcgsceneCLI- Procedural generation
- Image-to-map conversion
- Seeded terrain generation
- Analysis & insights
- No AI inside (v7.0.0)
- Configuration
- Architecture
- Requirements
- License
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.binfilesread_map_overview— Summary of rooms, entities, triggers, stylegroundsread_room— Full room details (tiles, entities, triggers, decals)get_room_tiles— Raw tile grid (FG or BG)read_map_metadata— Quick metadata without full readsearch_entities— Find entities by type, position, roomsearch_triggers— Find triggers by typecompare_rooms— Side-by-side room comparison
Editing
add_entity/remove_entity— Place or delete entitiesupdate_entity/move_entity— Modify entity properties or positionadd_trigger/remove_trigger— Place or delete triggersset_room_tiles— Replace tile gridadd_room/remove_room— Create or delete roomscreate_map— Create new.binfileupdate_room— Modify room properties (music, dark, wind, etc.)clone_room— Duplicate a roombatch_add_entities— Add multiple entities at onceresize_room— Change room dimensions
JSON & Authoring (see JSON round-trip & authoring)
map_to_json/json_to_map— Any.binto typed JSON and back, byte-identical; refuses lossy writescheck_authoring_json— Resolve hand-written content: defaults filled from Lönn plugins, unknown names flaggedmerge_json_into_map— Add entities/triggers/rooms to a map; dry-run diff + highlighted preview, map-wide unique IDs, placement checks, additions onlyrun_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 decalslist_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 definesaudit_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 typeslist_effect_definitions/get_effect_definition— Browse effect types
Analysis & Insights
Basic Analysis
analyze_map— Entity counts, type breakdown, world boundsvisualize_map_layout— ASCII mini-mappreview_map_section— Detailed ASCII preview
Advanced Analysis
analyze_entity_usage— Entity stats across entire mapanalyze_difficulty— Room/map difficulty estimationfind_entity_references— Locate all instances of an entity typedetect_map_patterns— Identify design archetypes (linear, hub, etc.)analyze_room_connectivity— Adjacency graph analysis
Suggestions & Improvements
suggest_improvements— Actionable room suggestionscompare_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 structurevalidate_map/batch_validate_and_fix— Playability validation with auto-fixgame_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 errorsgame_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 failsvalidate_audio— Every audio event a map references, checked against GUIDs.txt and the game's FMOD banks, with closest-match suggestionsexport_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.binor a typed JSON dump, and can highlight a planned merge (overlay)
Procedural Generation
Pattern-Based Generation
build_pattern_library— Extract patterns from existing mapsgenerate_room_from_pattern— Generate rooms with strategy + seedingest_external_map— Download and extract patterns from GameBanana
Image & Terrain Generation
generate_map_from_image— Convert color-mapped images to playable mapsgenerate_terrain_map— Procedural maps with Perlin noise + Voronoi biomespreview_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, thenmap_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 throughmap_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_backupslists backups,map_restoreputs one back (backing up the current file first).map_diffcompares 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:
| Mechanism | Behaviour |
|---|---|
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 selection | No 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 edit | Lö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önn | Not 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.
sf_make_tileset()writesceleste_graybox.png, a legend and a README to%LOCALAPPDATA%\loenn-mcp\sprite-fusion.- In Sprite Fusion, import
celeste_graybox.pngwith 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 namedbg, and player, spikes, spring, refill, strawberry, jump-through or dream block anywhere. - Export as JSON (
map.zip), then callsf_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,anchorand 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 atDocuments\CEL-Graybox: the gray-box tileset as a GodotTileSet(custom datakind, collision on solid tiles),rooms/_template.tscn(layersbg,fg,gameplayand an orange 40x23 screen frame) and the bridge scripts inceleste/.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 assf_import.replace=trueupdates 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_fusiondoes the same for a Sprite Fusion export.- Entities that are not gray-box tiles travel as
Marker2Dnodes with metadataentityplus 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'soverworld_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 42prints a celpcg-room JSON document;celpcg validate --room room.jsonre-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 syncandjulia --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 atSELECTE_DIR, then aseleCtefolder next to this repository, then~/seleCte. width/height22x14 runs in seconds; 40x23 (one screen) takes minutes per room.proba0 is a corridor, values near 1 a labyrinth.tries_limitresamples a room that fails seleCte's playability check;reset_skeletonredraws the layout instead of keeping an unplayable room.seedandmodelneed the patched clone (upstream seleCte has no--seedand always usespe_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_tasorpcg_generate_room's reach model for that. Everest registers a new.binonly 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
| Tool | What it does |
|---|---|
list_maps | List all .bin map files in the project. |
read_map_overview | Parse a .bin map file and return a summary of rooms and structure. |
read_room | Get detailed data for a specific room: tiles, entities, triggers. |
get_room_tiles | Get the complete tile grid for a room as text. |
Map Editing Tools
| Tool | What it does |
|---|---|
add_entity | Add an entity to a room and save the map. |
remove_entity | Remove an entity from a room by its ID. |
add_trigger | Add a trigger to a room and save the map. |
remove_trigger | Remove a trigger from a room by its ID. |
set_room_tiles | Set tile data for a room. Each row is a line of characters ('0'=air). |
add_room | Add a new empty room to a map. |
remove_room | Remove a room from a map. |
create_map | Create a new empty Celeste .bin map file. |
Styleground Tools
| Tool | What it does |
|---|---|
list_stylegrounds | List all stylegrounds (foreground + background effects) in a map. |
add_styleground | Add a styleground effect to a map. |
remove_styleground | Remove a styleground from a map by its index in the layer. |
update_styleground | Merge properties into an existing styleground without replacing it. |
Entity / Trigger Catalog Tools
| Tool | What it does |
|---|---|
list_entity_definitions | List available Lönn entity definitions from the project. |
get_entity_definition | Read a Lönn entity Lua definition and extract its properties. |
list_trigger_definitions | List available Lönn trigger definitions. |
list_effect_definitions | List available Lönn effect definitions. |
find_missing_definitions | List namespaced entities and triggers in a map that no installed Lönn plugin defines. |
audit_plugin_coverage | Compare the mod's C# [CustomEntity] names with its Lönn plugin definitions. |
validate_audio | Check every audio event a map references against the sound banks. |
Analysis Tools
| Tool | What it does |
|---|---|
analyze_map | Get detailed statistics about a map file. |
visualize_map_layout (extra) | Generate an ASCII visualization of all room positions in a map. |
Map Preview Tools
| Tool | What it does |
|---|---|
preview_map_section | Generate an ASCII preview of a map section, like a Lönn mini-map. |
render_map_html | Render an interactive HTML map preview with zoom, pan, search, and detail panel. |
Procedural Generation Tools
| Tool | What 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_room | Check 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
| Tool | What 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
| Tool | What it does |
|---|---|
read_map_metadata | Read high-level metadata from a map without listing all room contents. |
search_entities | Search for entities across all rooms (or a specific room) by type and/or position. |
search_triggers | Search for triggers across all rooms by type. |
compare_rooms | Compare two rooms in the same map side-by-side. |
Map Editing Extensions
| Tool | What it does |
|---|---|
update_entity | Update properties of an existing entity by ID. |
move_entity | Move an entity to a new position. |
update_room | Update room-level properties (music, dark, wind, etc.). |
clone_room | Clone an existing room to a new name and position. |
batch_add_entities | Add multiple entities to a room in one call. |
Decals
| Tool | What it does |
|---|---|
list_decals | List all decals in a room (foreground or background). |
add_decal | Add a decal to a room. |
remove_decal | Remove a decal by index. |
Advanced Analysis
| Tool | What it does |
|---|---|
analyze_entity_usage (extra) | Analyze entity usage across the entire map. |
analyze_difficulty | Estimate 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_connectivity | Analyze room adjacency graph showing isolated rooms, dead ends, and hubs. |
Suggestions
| Tool | What 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
| Tool | What 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
| Tool | What it does |
|---|---|
get_mod_info | Get information about the current mod project. |
validate_map | Validate an entire map for playability issues. |
Catalog Extensions
| Tool | What it does |
|---|---|
get_trigger_definition | Read the source of a Lönn trigger definition .lua file. |
get_effect_definition | Read the source of a Lönn effect definition .lua file. |
Import / Export
| Tool | What it does |
|---|---|
export_room_json | Export a room as a JSON file for external editing or sharing. |
import_room_json | Import a room from a JSON file into a map. |
Diff & Fix
| Tool | What it does |
|---|---|
summarize_map_diff (extra) | Create or compare a structural snapshot of the map. |
batch_validate_and_fix | Validate all rooms in a map and optionally auto-fix issues. |
Room Settings
| Tool | What it does |
|---|---|
get_room_settings | Get all editable settings for a room (music, physics, wind, camera, flags). |
update_room_settings | Update room settings (music, physics, wind, camera, flags) by merging a JSON object. |
rename_room | Rename a room in a map. |
move_room | Move a room to a new world-space position. |
resize_room | Resize a room (snaps to 8-pixel grid, preserves tile data). |
Map Metadata & Dependency Settings
| Tool | What it does |
|---|---|
get_map_metadata | Read the map-level metadata block (Everest/celeste.yaml meta settings). |
set_map_metadata | Set map-level metadata properties (Everest meta block). |
replace_audio_event | Find and replace an FMOD audio event path anywhere in a map. |
get_map_dependencies | Read the mod dependencies list from a map's everest.yaml or meta node. |
set_map_dependencies | Write or update the mod dependency list in the map's everest.yaml. |
Ml Asset Analysis
| Tool | What it does |
|---|---|
analyze_map_assets (extra) | ML-style analysis of a map's assets against the Maddie480 graphics dump. |
Json Round-Trip And Authoring
| Tool | What it does |
|---|---|
map_to_json | Convert a .bin map to typed JSON that converts back byte-identical. |
json_to_map | Convert typed JSON (from map_to_json, possibly edited) back to a .bin map. |
check_authoring_json | Resolve 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_map | Add entities, triggers and rooms from authoring JSON to an existing map. |
run_author_script | Run a JavaScript authoring script (js/ package, lucid-dream style) and use its output. |
Live Game Link
| Tool | What it does |
|---|---|
game_status | Where the running game is: map SID, side, room and player position. |
game_reload_map | Make the running game re-read a map after it was edited on disk. |
game_teleport | Load a room of a map in the running game (a fresh debug session). |
game_play_tas | Play CelesteTAS inputs from a room's spawn and report where the player ended up. |
game_log | Recent lines of the game's log.txt: errors, warnings and level loads. |
Safety: Copies, Commits, Backups, Diff
| Tool | What it does |
|---|---|
map_copy | Copy a map to a working copy so edits never touch the original. Default target: Maps//wip/.copy.bin. |
map_commit | Apply a working copy to the real map: whole file, or only the listed rooms. Dry run shows the diff first. |
map_backups | List the timestamped backups of a map, newest first. |
map_restore | Restore a map from one of its backups (the current file is backed up first). |
map_diff | Semantic diff of two maps: rooms, geometry, fg/bg tiles, entities, triggers, decals, stylegrounds, meta. |
Assets
| Tool | What it does |
|---|---|
check_assets | Check that a map only uses things that exist: entity/trigger names with a Lönn definition, decal and parallax textures. |
check_sprites_xml | Check 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
| Tool | What it does |
|---|---|
editor_open | Open a map in Lönn (or Rysy), optionally on a room. MCP writes to that map are refused until it closes. |
editor_wait | Wait until the user saves in the editor (or it closes), then report what changed. Call again to keep waiting. |
editor_status | List editor sessions from this server and any other running Lönn. |
editor_close | Close an editor session. Without force, the editor may ask about unsaved changes and stay open. |
Sprite Fusion
| Tool | What it does |
|---|---|
sf_make_tileset | Write the Celeste gray-box tileset for Sprite Fusion (PNG, legend JSON, README). |
sf_import | Import a Sprite Fusion JSON export (map.zip) as a Celeste room: solid/bg tiles plus gray-box entities. |
Summer Engine
| Tool | What it does |
|---|---|
summer_init | Create or refresh the Summer Engine gray-box project (tileset with collision, room template, bridge scripts). |
summer_open | Open the gray-box project in Summer Engine, optionally on a room scene. |
summer_rooms | List the room scenes in the Summer gray-box project. |
summer_export_room | Turn a Summer room scene into a Celeste room (new, or replace=true). Same checks and safe write as sf_import. |
summer_import_room | Copy a Celeste room into the Summer project as a scene (fg/bg tiles, gray-box entities as tiles, others as markers). |
summer_from_sprite_fusion | Turn a Sprite Fusion export (map.zip) into a Summer room scene, to refine it in Summer before export. |
Shared Pcg Core
| Tool | What it does |
|---|---|
pcg_generate_room | Generate a gray-box room with the shared C# PCG core and write it into a map (dry run first). |
Selecte Levels
| Tool | What it does |
|---|---|
selecte_generate | Generate 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-00stayslvl_a-00. lookupis 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_mapwrites nothing if a value does not fit its type (["u8", 300]), if an unedited dump would not reproduce its source byte-for-byte (checked withsource.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/sourceand 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 inCeleste/Mods), then vanilla Lönn.strawberryabove getswinged,moon,checkpointIDandorder."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_jsonshows 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 unlessallow_problems=true; unknown names needallow_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, orloenn-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
| Command | What it does |
|---|---|
scan | Scores every room (interestingness, difficulty, exit connectivity, spawn presence, fairness gate) plus map-wide reachability from the start room. |
score | Deep report for a single room (--room NAME). |
validate | In-game readiness checklist — bad names, missing spawns, sealed exits, unreachable rooms — with a CI-friendly exit code. |
fix | Safe auto-repairs: adds missing spawns, sanitizes bad room names, converts strawberry{golden} to real goldenBerry entities. Supports --dry-run and --only spawns,names,berries. |
diff | Room/entity/tile changes between two maps. |
generate | Preset-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
| Strategy | Description |
|---|---|
balanced | Mix of exploration and challenge (default) |
exploration | Open spaces, gentle platforming, few hazards |
challenge | Dense tiles, many hazards, tight jumps |
speedrun | Linear path, minimal platforms, fast flow |
Model Profiles
Documentation truncated — see the full README on GitHub.
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Rea
Freeby Morluto · Developer Tools
Reverse engineer anything from your terminal or agent with one CLI and MCP server.
Paperclip
Freeby Paperclipai · Developer Tools
Trending hip-hop artist momentum scores across four cultural dimensions.
Fetch
Freeby Modelcontextprotocol · Developer Tools
Web content fetching and conversion for efficient LLM usage
Git
Freeby Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
MCP Marketplace
Freeby mcp-marketplace · Developer Tools
Search and install MCP servers from inside your AI client.
