Skip to main content
Glama
euuuuuuan

AssetForge MCP Server

by euuuuuuan

AssetForge

A local, $0 game-asset generator whose first stage is a fail-closed licence gate: if a model is not in the registry, nothing is generated.

License Python Pillow NumPy Tests Licence registry

English · 한국어 · 日本語 · 简体中文 · 繁體中文 · Español · Português (Brasil) · Deutsch · Français · Русский · Italiano · Bahasa Indonesia · Türkçe

Quickstart · The licence gate · Orchestration notes · Credits

IMPORTANT

Apache-2.0 covers the source code in this repository. It says nothing about the models AssetForge drives, or about the assets you generate with them — those are governed by each tool's own licence, which is recorded inforge/licenses.json and summarised in CREDITS.md. Read the gate's verdict before shipping anything it produced.

What this is

AssetForge is a command-line tool, not a game and not a service: one CLI (plus a small MCP server) that generates sprites, atlases, tilesets, maps, textures, meshes, sound effects, music, and procedural content on a local machine at zero marginal cost, as a replacement for paid asset SaaS. Its distinguishing feature is not the generators — it is that every generation request passes a hard licence gate first, and an unknown tool is refused rather than allowed. The deterministic core (gate, scheduler, provenance, L1 lint) is stdlib-only and fully tested; the high-fidelity ML tier is opt-in, needs models you fetch yourself, and is deliberately not exercised by the test suite. Treat this repository as a working tool with a proven deterministic floor, not as a turnkey ML art pipeline.

Related MCP server: PixelVault

Highlights

  • Fail-closed licence gate. forge/licenses.json records 49 tools — 25 ok, 8 gated, 16 blocked — and forge/policy.py refuses anything absent from it with the literal reason unknown tool … (fail-closed). There is no "assume permissive" branch.

  • The gate understands the terms that actually bite, not just an SPDX string: 4 tools require attribution that is copied into provenance, 6 carry a revenue ceiling, 2 are blocked by territory exclusion, and 3 are marked cli-subprocess-only so a GPL tool is invoked as a subprocess and never imported into the calling code.

  • 4 verdict codes double as process exit codes (0 pass, 2 allowed-but-unshippable, 3 gated-needs-ack, 4 blocked), so a script or agent can branch on licensing without parsing prose.

  • Intent is a first-class input. --intent monetized (the default) is the strict path; --intent personal|research permits non-commercial weights but returns a loud non-shippable verdict instead of quietly passing.

  • 9 generation modalities (sprite, atlas, texture, mesh, sfx, music, procedural, tileset, map) share one contract: each module declares its primary tool, every tool to gate, and whether the job is heavy.

  • 12 style presets span 2d, 2.5d and 3d, and the registry is the documented extension point rather than a hard-coded look.

  • ML output is wrapped, not trusted. A deterministic packer supplies structure and frame layout, and 6 L1 lint entry points check PNG, atlas, texture, WAV, OGG and GLB output against 5 declared asset contracts. A silent or empty asset is an error, and a missing optional dependency degrades to an explicit warning rather than a false pass.

  • Provenance is a sidecar, not a promise of byte-stability. Every asset gets a <name>.assetforge.json recording tool, licence, status, attribution, seed, model and prompt hash, plus a line appended to the target project's own LICENSES.txt ledger.

  • Heavy jobs serialise. A residency scheduler takes a lock, honours the host's memory-pressure level, and refuses to run while a game playtest is in progress; a launchd agent drains the queue one job at a time.

  • Engine-neutral export with 3 adapters (Godot, Unity, Unreal): a bundle of atlas PNG, individual frames, Aseprite-format JSON, a manifest, and a STEAM_AI_DISCLOSURE.md, with headless engine import only when explicitly requested.

  • 48 Python modules across forge/ and mcp/, covered by 102 test functions in 16 files — 111 pass and 1 skips on this snapshot (the skip is the opt-in check that needs real ML artifacts). No test requires network or a GPU.

Use it

The whole surface is one executable. Licensing is introspectable before you generate anything:

assetforge policy list --intent monetized     # verdict for all 49 registry entries
assetforge policy check hunyuan3d-2           # -> BLOCKED-ALARM, with the reason
assetforge policy ack stable-fast-3d          # owner ack of attribution + revenue ceiling
assetforge styles --dimension 2.5d            # style presets by dimension

Then generate, lint, and export:

assetforge sprite  --project mygame --spec spec.json --style retro-pixel --seed 7
assetforge tileset --project mygame --name dungeon
assetforge sfx     --project mygame --preset jump
assetforge lint out/atlas.png
assetforge export  --asset out/atlas.png --engine godot --project /path/to/godot/project
assetforge queue status                       # scheduler, lock, playtest, queue health

Every generation runs the same sequence: licence gate → residency slot → generate → provenance sidecar and ledger line → L1 lint. If the gate refuses, nothing is written.

An MCP server exposes 4 tools (asset_generate, asset_lint, asset_policy, asset_styles) for agent hosts that speak MCP; see docs/ORCHESTRATION.md for the registration snippet.

Quickstart

Prerequisites: Python 3 and a POSIX shell (macOS or Linux; Windows uses install.ps1 and bin\assetforge.cmd). Verified with Python 3.14.6. The deterministic core needs no third-party packages; --venv installs the pinned extras that enable the deeper pixel, audio, and mesh checks.

git clone <your-fork-url> assetforge
cd assetforge
./install.sh --venv        # symlink `assetforge` (+ `af`) into ~/.local/bin, create .venv
assetforge policy list     # confirm the gate loads the registry
assetforge sfx --preset jump --out ./out
assetforge lint ./out/jump.wav

No account, API key, or network call is required for the deterministic tier. ML models are opt-in and fetched separately; assetforge models pull --yes only prepares directories.

Architecture

flowchart TB
  R["Request: assetforge &lt;modality&gt; --intent ..."] --> PL["plan(): primary tool, tools to gate, heavy?"]
  PL --> G{"policy.evaluate()"}
  REG[("forge/licenses.json<br/>49 tools")] --> G
  ACK[("~/.assetforge/acked.json<br/>owner acks")] --> G
  G -->|"unknown tool"| X4["exit 4 BLOCKED — fail-closed"]
  G -->|"blocked / territory-excluded"| X4
  G -->|"gated, no ack"| X3["exit 3 GATED-ALARM"]
  G -->|"non-commercial, personal intent"| X2["exit 2 OK non-shippable"]
  G -->|"ok"| S["Residency slot: lock + memory gate + playtest gate"]
  S --> GEN["Generator: deterministic house code, optional ML tier"]
  GEN --> PK["Deterministic packer: frames, atlas layout, palette"]
  PK --> PV["Provenance sidecar + project LICENSES.txt ledger"]
  PV --> L{"L1 lint vs 5 asset contracts"}
  L -->|fail| AL["ALARM — loud, never a silent fake"]
  L -->|pass| OUT["Asset + sidecar on disk"]
  OUT --> EX["export: Godot / Unity / Unreal bundle"]

The gate is the product. forge/policy.py reads a registry keyed by tool, not by licence name, because the questions that decide shippability are per-tool: does this licence exclude my territory, does it demand attribution in the shipped game, does it cap revenue, and can I only touch it through a subprocess boundary? evaluate() returns a structured Verdict — code, status, reason, licence, attribution, invoke constraint, shippability, alarms — and enforce() raises on anything that did not pass. The unknown-tool branch returns BLOCKED first, before any other reasoning, which is what makes adding a generator without a registry row impossible rather than merely discouraged.

Determinism is a division of labour. ML supplies the look; house code supplies structure, frame layout, and the contract. That split is what makes an unstable dependency tolerable: the sidecar, not the pixels, is the reproducibility record, because MPS sampling is not guaranteed byte-stable across upgrades. Anything the deterministic tier owns — palettes, packing, procedural synthesis, audio synthesis — is seed-reproducible, and the tests assert that.

Loudness over convenience. Three separate places refuse to fake success: the gate (unknown or refused tool), the lint (silent audio, empty image, contract drift, or a missing dependency that would otherwise be reported as a pass), and the scheduler (defers rather than thrashing a memory-pressured host). Each returns a verdict that names the reason.

Project layout

bin/
  assetforge            thin launcher -> python -m forge (plus .cmd for Windows)
  af-queue.sh           serial queue drainer honouring the memory and playtest gates
forge/
  policy.py             the fail-closed licence gate; Verdict + enforce()
  licenses.json         the registry: 49 tools, tiers, ceilings, territories, invoke rules
  scheduler.py          residency slot: lock, memory level, playtest guard
  provenance.py         .assetforge.json sidecars + per-project LICENSES.txt ledger
  modality.py           shared plan()/generate() contract for every generator
  styles.py             12 style presets across 2d / 2.5d / 3d
  sprite|atlas|tileset|game_map|texture|mesh|sfx|music|procedural.py   generators
  curate/               CC0/owned-only training-reference curator + manifest validation
  qa/                   asset_lint.py (L1) + contracts.py (5 asset contracts)
  adapters/             Godot, Unity, Unreal neutral-bundle adapters
  ml/                   opt-in ML clients (kept out of the deterministic test path)
mcp/server.py           MCP server exposing 4 asset tools
tests/                  16 pytest files, deterministic, no network and no GPU
launchd/                queue drain agent
docs/ORCHESTRATION.md   how agents and engines reach the CLI

How this was built

This repository is a tool built by AI agents for AI agents to use, and its shape is a direct response to failures observed while doing that.

  • The gate exists because agents are optimistic. An agent asked to "make a monster sprite" will reach for whatever model is fashionable. Encoding licence policy as data with a fail-closed default converts a judgement call into a lookup that an agent cannot talk its way past, which is why the unknown-tool branch is checked before anything else and why the registry carries last_verified and a revisit_trigger telling the next reader when the terms need re-checking.

  • ALARM-never-fake as a build rule. The recurring defect in agent-generated asset work is a plausible-looking empty result — a silent WAV, a flat grey PNG, a mesh with no faces. So the L1 lint is a gate rather than a report, and a missing dependency is a warning with a named reason rather than a pass. The rule is that no layer may report success it did not verify.

  • Deterministic gates, not screenshots of success. 102 test functions across 16 files run with no network and no GPU, so the suite proves exactly the part that is supposed to be reproducible and stays silent about the part that is not. The one opt-in test that needs real ML artifacts is marked and skips by default rather than being quietly deleted.

  • Adversarial review between agents. Implementation and audit alternated between agents with the owner as tie-breaker, and the handoff documents from those rounds are deliberately excluded from this snapshot: they are working notes full of half-verified claims, and publishing them would ship the noise instead of the result. The publication gate asserts that 0 of them survive.

  • Fail-closed publication, mirroring the tool's own doctrine. This snapshot is built by a sanitising pipeline that never writes to the private repository: it exports a git archive, drops the excluded working notes, applies publication-only documentation as an overlay, rewrites machine-specific paths into ${ASSETFORGE_ROOT}-style placeholders, and replaces internal decision-record identifiers whose target documents are not published. Then it refuses to publish unless every gate passes — secret scanning, credential-shaped filenames, absolute home paths, e-mail-shaped strings, a client-name wordlist expanded across spacing and Unicode-normalisation variants, and a claims file that recomputes every number in this README from the tree being published. The counts above are derived, not typed.

Development

PYTHONPATH=. .venv/bin/python -m pytest -q      # 111 passed, 1 skipped
bash tests/test-offline.sh                       # offline CLI smoke checks
python3 -m compileall -q forge mcp                # byte-compile check used by publication
PYTHONPATH=. .venv/bin/python -m pytest -q -m e2e  # opt-in: needs real ML artifacts

When adding a generator: give it a plan() that declares every tool to gate, add the tool to forge/licenses.json with its real terms (the gate will refuse it otherwise, by design), add an asset contract and lint path if it emits a new file type, and add a deterministic test that does not need network access. When adding a tool, set last_verified and read the upstream LICENSE file rather than a model card summary.

Localization

This English README is canonical. 12 translations live under docs/i18n/ and are listed in docs/i18n/README-INDEX.md; keep commands, paths, numbers and links synchronised with this file — a change here that touches a number is not finished until the translations carry the same number. The CLI itself, its verdict strings, and the registry are English-only, because verdict text is copied verbatim into provenance records and shipped disclosure files.

Credits

This repository ships no third-party media. CREDITS.md explains what that means, records the licence terms of the tools the gate governs, and defines the four redistribution tiers that apply to anything you generate with it. Read it before shipping generated assets: the code licence here does not license the models' output.

License

Source code is licensed under the Apache License 2.0; modification notices are recorded in NOTICE. Generated assets and the models that produce them are not covered by Apache-2.0 and remain governed by CREDITS.md and forge/licenses.json.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Licensed, rights-cleared content for AI agents - verifiable license keys + EU AI Act attestation.

  • OCR, transcription, file extraction, and image generation for AI agents via MCP.

  • Security firewall for AI agents — scans MCP calls for injection, secrets, and risks.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/euuuuuuan/assetforge-public'

If you have feedback or need assistance with the MCP directory API, please join our Discord server