Shiitakefield journal · shiit.app

Game art over MCP

We built an MCP server over our game's real 3D art. Here’s what six tools taught us, how to connect it, and how it becomes a pixel-art pipeline.

Our game engine is called Vishnu. The games that run on it are cartridges. The first cartridge is a cozy, darkly funny colony game about goblins running a bed and breakfast. All of its props, the benches, bottles, kettles and bookcases, start life as small procedural Three.js builders, then get baked into pixel art for the game.

We wanted an agent to be able to use those same builders. Not a description of them, not a screenshot of them, the actual code that makes the kettle. So we put them behind an MCP server.

Six tools, one owner

The server exposes six tools. Two came first:

  • hive_asset_catalog returns the schema and defaults for every prop.
  • hive_build_scene takes up to 24 configured props and returns a scene.

Four more work on a versioned scene document:

  • hive_scene_create, hive_scene_edit, hive_scene_inspect and hive_scene_export.

Every tool compiles through one shared scene owner in src/asset-pipeline. The game, the browser editor and the MCP server all import the same original geometry. None of them keeps its own copy of what a bench is.

Here’s an agent adding a bench to an empty scene:

hive_scene_edittool call

sent

{
  "document": {
    "version": 1,
    "revision": 0,
    "name": "front porch",
    "nodes": []
  },
  "expectedRevision": 0,
  "operations": [
    {
      "type": "add",
      "node": {
        "id": "bench-1",
        "parentId": null,
        "transform": {
          "position": [0, 0, 0],
          "rotation": [0, 0.4, 0],
          "scale": [1, 1, 1]
        },
        "geometry": {
          "kind": "original",
          "pack": "hive-brewhouse-v1",
          "asset": {
            "builder": "bench",
            "parameters": {
              "width": 1.6
            }
          }
        },
        "material": null
      }
    }
  ]
}

returned

{
  "document": {
    "version": 1,
    "revision": 1,
    "name": "front porch",
    "nodes": [
      {
        "id": "bench-1",
        "…": "the node exactly as added"
      }
    ]
  }
}
Field names match the server’s Zod schema. Bench width, depth and height change the real procedural geometry.

What we got wrong first, and what we kept

We checked it the boring way: a real stdio client and a real Streamable HTTP client list the tools, call them, and compare exported geometry counts against an independent Three.js loader. A browser then loads the exact exported bytes, rotates them, and downloads byte-identical files.

Connect it

It runs on the official MCP TypeScript SDK, over stdio and Streamable HTTP. For now it runs from our repository, so this is the local shape:

{
  "mcpServers": {
    "vishnu-assets": {
      "command": "node",
      "args": ["tools/asset-mcp/server.mjs"]
    }
  }
}

Where it’s going: a pixel-art pipeline

Right now the server gives you 3D. The game shows pixel art. The interesting part is the path between them.

Vishnu’s rule is simple: Three.js is for authoring, fitting and baking. It is not the live game world. The live game draws baked pixel sprites in Pixi over an authoritative simulation. So the bake step is the heart of the pipeline, and today it’s tangled up with the game. It imports the world’s size, disposes the geometry it just rendered, and hands back a Pixi texture directly.

The plan:

  1. Split the bake in two. A capture and outline stage that returns pixels and alpha metadata with no Pixi at all, and a thin Pixi adapter that turns those pixels into a texture and a hit mask.
  2. Give every sprite a manifest. Builder, pack and profile versions, the exact parameters, camera and anchor, alpha bounds, frame layout and content hashes. A sprite you can’t trace back to its recipe is a sprite you can’t fix.
  3. Add a bake tool. hive_scene_bake takes a document and a pixel profile and returns a sprite sheet plus its manifest. Same scene owner, one more output.
  4. Make scenes persistent. Real storage, public read and private write, so a viewer link points at an actual scene and every export points at a revision.
  5. Close the review loop. A person leaves a note on an object at a revision. The agent sees the captured image, fixes the object, and resolves that note only.

If you build games with procedural art and you’d use this, I’d like to hear what you’d feed it. That tells us which builders to open up first.