npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

game-world-painter-mcp

v0.3.1

Published

MCP server for GameWorld Painter, the layered game world map editor: AI agents (Claude Code, Codex, GitHub Copilot, VS Code, Cursor, Gemini CLI) read, analyze and edit the map open in the app.

Readme

GameWorld Painter MCP server

npm

Lets AI agents — Claude Code, Codex, GitHub Copilot (CLI and VS Code), Cursor, Claude Desktop, Gemini CLI and any other Model Context Protocol client — read and edit the map open in GameWorld Painter. Node.js 18 or newer, no dependencies.

Agent (Claude Code, Codex, Copilot…) ── MCP over stdio or HTTP ──▶ MCP server, 127.0.0.1:38765
MCP server ── tool calls: Server-Sent Events ──▶ GameWorld Painter in the browser
GameWorld Painter ── results: HTTP POST ──▶ MCP server
GameWorld Painter ── autosave ──▶ the project folder (PNG and JSON)

An AI agent's change waiting for review in GameWorld Painter: a bridge and a hut outlined on the map, the agent's text and self-check on a card

The agent starts the server; the app (with AI Agent turned on in its header) connects to it. The tools run in the page, on the open map: what the agent changes appears at once and is one step of the app's undo.

Set up

  1. Add the server to your agent once — it is the npm package game-world-painter-mcp: the agent starts it with npx (Node.js 18 or newer, nothing else to install). npx -y game-world-painter-mcp setup prints these lines:

    | Agent | | |-------|-| | Claude Code | claude mcp add --scope user game-world-painter -- npx -y game-world-painter-mcp | | Codex | codex mcp add game-world-painter -- npx -y game-world-painter-mcp | | GitHub Copilot CLI | copilot mcp add game-world-painter -- npx -y game-world-painter-mcp | | Gemini CLI | gemini mcp add --scope user game-world-painter -- npx -y game-world-painter-mcp | | VS Code | .vscode/mcp.json: {"servers": {"game-world-painter": {"type": "stdio", "command": "npx", "args": ["-y", "game-world-painter-mcp"]}}} | | Cursor, Claude Desktop, Windsurf… | {"mcpServers": {"game-world-painter": {"command": "npx", "args": ["-y", "game-world-painter-mcp"]}}} | | Clients configured by URL | run npx -y game-world-painter-mcp --http and use http://127.0.0.1:38765/mcp |

    On Windows, if a client cannot start npx: "command": "cmd", "args": ["/c", "npx", "-y", "game-world-painter-mcp"]. From a clone of the repository: node /path/to/game-world-painter/mcp/server.mjs instead of npx -y game-world-painter-mcp.

  2. Open the app, open your map and turn on AI Agent in the header. The LED is amber while the app waits for the server, green when an agent is connected, and pulses while the agent works. Click it for the same setup per agent, the skill, the activity log and the settings.

  3. Optional, recommended: the skill, which teaches agents the workflow — gh skill install aleserb/game-world-painter game-world-painter, download it from the app (AI Agent → Skill), or copy skills/game-world-painter into ~/.claude/skills/, ~/.copilot/skills/, ~/.codex/skills/ or a repository's .github/skills/. The server also offers it as the MCP resource gwp://skill.

What the agent can do

| Tool | | |------|-| | get_map_info | Start here: unit, bounds, every layer with what it holds, zones, what the user looks at | | get_user_context | The selected area and items, the active layer, the view, the cursor | | get_project_path | The full path of the map's folder on this computer and of every layer file, what the app has not saved | | open_map, create_map | Open a map by its folder path, or make a new one there (bounds, unit, cell size, a set of layers); opens the app in the browser when none is connected | | render_map | An image of the map (north up) with a coordinate grid; a highlight | | describe_region | Everything in a region: size, mask coverage, class shares, heights and slopes, items by kind | | read_layer | A coarse grid of a mask, categories or height layer | | find_items | Objects, notes, paths with filters; distances to other layers, height, slope, zone | | analyze_items | Spacing, too-close pairs, groups, the largest gaps | | find_spots | Open (exposed), enclosed (hidden), high (viewpoints), low, flat, steep, empty, far from / near to layers | | analyze_walkability | Walkable ground, isolated pockets, narrow passages, unreachable items | | find_route | A walking route around obstacles, preferring roads, away from danger; can add it as a path | | scatter_items | Many objects placed naturally: spacing, count, density from a mask, distances to keep, groups, random props | | add_items, update_items, delete_items | Exact edits of objects, notes and paths (deletion can ask the user); objects by their center and yaw, by their two ends a, b, or towards a point | | find_crossing | Where to cross water: the narrowest places, with the bank points a, b for a bridge straight across the flow | | check_change | The agent sees its change before the user does: BEFORE / AFTER images, what changed, checks of every placed object | | paint_layer | Masks (percent) and categories (classes) over a region, with a soft edge and noise | | edit_terrain | Raise, lower, flatten, smooth, slope (from a place to another), noise | | create_layer, update_layer | New layers; names, groups, colors, visibility, classes | | show_on_map | Moves the user's view to something and outlines it, with a message; can select it | | undo | Undoes the agent's latest changes (in review mode: withdraws its proposal) | | begin_change, end_change | Group several calls into one change with a title and a summary: one undo step, or one proposal in review mode | | wait_for_review | Review mode: wait for the user's decision on a proposal |

The changing tools take a comment: the agent's words on the change (what and why), shown to the user with it. get_user_context tells what the user selected — objects (with Select or the Select area tools: rectangle, ellipse, lasso, polygon, same kind) and areas — so "these" and "here" mean something.

Most tools take a region: {"area":"selection"}, {"items":"selection"}, {"zone":"village"}, {"layer":"trees","min":50}, {"near":"roads","distance":8}, {"rect":[x0,z0,x1,z1]}, {"slope":{"max":25}}, … combined with {"all":[…]}, {"any":[…]}, {"not":…} — see the skill's reference.

The server also offers the skill and the project format as MCP resources (gwp://skill, gwp://project-format).

How it works

  • Agents start server.mjs and speak MCP over stdio. One process owns the port (38765): the app connects there. When a second agent starts another process, it finds the port taken by a GameWorld Painter server and relays its calls through it; when the first one exits, the next takes the port over and the app reconnects.
  • The app holds a Server-Sent Events stream (/app/events) and posts results (/app/result). One tab at a time (the latest).
  • Protocol: dual-era MCP. Legacy clients get the initialize handshake (2024-11-05 to 2025-11-25). Modern requests carry _meta per request (2026-07-28, with server/discover). Streamable HTTP at /mcp serves both: sessions for legacy clients, stateless with header checks for modern ones. JSON-RPC batches are accepted.
  • Maps by path (open_map, create_map): browsers cannot open a folder from a path, so the app reads and writes such a map through this server (/fs/stat, /fs/read, /fs/write — atomic, /fs/remove). The server serves only folders the agent opened or created, only to the connected app tab (its session), never outside the folder (no .., no links out). When no app is connected it opens the default browser at https://aleserb.github.io/game-world-painter/?mcp=<port>&map=<path> (the app on that link turns AI Agent on with this local server and waits for the map). After a reload the app opens the map again once the server is connected.
  • Self-check: the changing calls return checks for the objects they placed — overlaps, objects in water or on roads, uneven ground, bridges (both ends on dry land, over the water, about 90° to the flow) — and check_change adds two images of the place (BEFORE and AFTER, the agent's objects outlined and labeled, problems in red). end_change runs the checks and keeps the change open while problems are left (unless the agent gives ignore_problems with a reason); in review mode a single call with problems is not shown for review. The card shows the result of the self-check. Bridges and other crossings are walkable for find_route and analyze_walkability.
  • Changes on a card: begin_change … end_change groups calls; a call alone is a change too. The card at the top right of the map (the user can drag it elsewhere) shows the agent's title, description or summary, and each step with its comment (lines, - lists, **bold**, `code`).
  • Review mode (AI Agent → Settings → Review the agent's changes, on by default): a change is applied but held — shown on the map with the card (title, what changed, Before / After), not saved, its layers locked for the user — until the user clicks Accept (kept and saved), Change… (undone; their comment goes to the agent as feedback) or Reject (undone). The changing call waits for the decision up to 45 s (agents such as Codex give up on a call after 60 s); after that it returns pending and the agent calls wait_for_review. An accepted proposal is one undo step. With review mode off, changes apply at once; a group is one undo step, and the card shows it with Undo for a while.
  • Undo: each tool call that changes the map is one step named “AI: …” in the app (a group: one step named after it). The agent's undo only undoes its own latest steps.
  • The folder on disk (get_project_path): browsers do not reveal paths, so the app sends what identifies its folder (the name, a SHA-256 of metadata.json, the sizes and dates of the layer files) and the server finds it on disk. It looks in the path the app remembers, the agent's workspace roots (MCP roots/list), its working directory, the folders in GWP_PROJECT_DIRS, then the home folder (GWP_SEARCH_HOME=0 turns that off; on macOS Desktop, Documents and Downloads come last, as macOS may ask before they are read). Tool and system folders (node_modules, .git, Library…) are skipped; the search stops after 8 s. The app remembers the path and shows it (the folder in the bottom bar, AI Agent → Settings, where the user can also type it).

Security

  • The server listens on 127.0.0.1 only, and refuses a Host that is not a loopback name (DNS rebinding).
  • Browsers may connect only from allowed origins: the hosted app (https://aleserb.github.io) and pages on localhost / 127.0.0.1. Add others with --allow-origin <origin> (null for a page opened from the disk). The relay endpoints refuse browsers entirely.
  • In the app: AI Agent is off until the user turns it on. Settings let the agent only read, and ask the user before it deletes. Locked layers stay locked.

Options

npx -y game-world-painter-mcp [--port 38765] [--http | --stdio] [--allow-origin <origin>]… [--timeout 120] [--quiet]
npx -y game-world-painter-mcp setup        print the setup for each agent

| Option | | |--------|-| | --port | The port the app and the agents meet at (env GWP_MCP_PORT). Change it in the app too (Settings). | | --http | Run alone, with the Streamable HTTP endpoint /mcp (the default when started in a terminal) | | --stdio | MCP over stdin / stdout (the default when an agent starts it) | | --allow-origin | Another web origin allowed to connect as the app (repeat) | | --timeout | Seconds a tool call may take in the app (deletions that wait for the user: 10×) | | --app-url | The app opened for open_map / create_map when none is connected (default the hosted one, or the last one that connected; env GWP_APP_URL) | | --no-browser | Never open a browser: the tool returns the link instead (env GWP_OPEN_BROWSER=0) | | --quiet | No log on stderr (the log never goes to stdout) |

Environment: GWP_MCP_PORT (the port), GWP_PROJECT_DIRS (folders to search for the map's folder, separated by : or ; on Windows), GWP_SEARCH_HOME=0 (do not search the home folder).

Troubleshooting

  • The LED stays amber: no server at the app's URL. Agents start it when they start; check that the agent lists the server (/mcp in Claude Code and Copilot CLI) or run npx -y game-world-painter-mcp in a terminal.
  • Chrome asks to “access other apps and services on this device” (Local Network Access): allow it — the page talks to the server on this computer.
  • “GameWorld Painter is not connected” in the agent: turn on AI Agent in the app and keep the tab open.
  • Port in use by another program: --port 38766 in the agent's command and the same port in the app.
  • A page opened from the disk (file://): start the server with --allow-origin null, or serve the app over HTTP.
  • An old version keeps starting: npx caches packages; npx -y game-world-painter-mcp@latest (or pin a version, e.g. [email protected]) in the agent's command.

Development

node --test mcp/test/*.test.mjs     the protocol, the bridge (with a fake app), origins, relays, HTTP, the folder search, the package
node tests/agent.mjs                the app in headless Chrome + the server + every tool (Node 22+, Chrome)

The tools are declared in lib/tools.mjs and implemented in the app (js/agent-*.js). The package bundles the skill, the project format and the license when it is packed (scripts/bundle.mjs).

Releasing: raise the version in mcp/package.json and write the changelog, push to main — the npm workflow tests the package, publishes it to npm with provenance (trusted publishing, no tokens), tags the commit and makes a GitHub release.