Skip to content

Latest commit

 

History

52 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

forge

An MCP server that turns model-generated code into something you can actually run, plus a desktop app that keeps the results.

Ask a model in Cline for a game, an app or a website. It writes the code and calls one of three tools: the page is served at a local URL, or the Python is run in a bubblewrap sandbox with a real window. When something is worth keeping, the model files it into a permanent library of plain folders, and a KDE menu entry — Forge — lists everything saved, plays it, and deletes it.

Built for Cline on KDE Plasma 6 / Wayland. Linux only: the sandbox is bubblewrap and the launcher assumes a freedesktop menu.

The tools

Tool What it does
preview_web(html, css, js, name, libraries) Writes the files to a scratch directory and serves them at http://127.0.0.1:<port>/<id>/. Links style.css/script.js into the page for you, inlines any requested libraries, and warns about references that will not load offline.
run_python_gui(code, name, packages) Runs PySide6/pygame code in a sandbox. Waits 3 seconds so an immediate crash comes back as a traceback the model can fix, then leaves the window open.
save_creation(run_dir, name, prompt, category, packages) Copies a finished run into the permanent library. Saving under a name already there adds a new version instead of replacing it.

packages may only name PySide6, PySide6-Essentials, pygame, pygame-ce, numpy or pillow. run_python_gui installs them into the tool's own venv; save_creation only records them, and installs nothing.

Libraries

Pages are meant to still work offline, and years from now, so nothing is loaded from a CDN. The few libraries worth having are fetched once, pinned by SHA-256, and inlined into the page itself:

.venv/bin/python -m forge.vendor fetch
name what it is for
chart.js charts - line, bar, pie, scatter, radar (global Chart)
alpine declarative interactivity via x-data / x-show / x-on

Then preview_web(html=..., libraries=["chart.js"]). The allowlist is fixed in code: a model naming its own URL to inline would be worse than the CDN link this replaces, since the fetch runs on the host and the result is executed by the browser. A file whose hash does not match its pin is refused rather than inlined, and fetch is the only code in the project that touches the network.

The cost is size - a page using Chart.js is ~200 KB rather than ~30 KB. That is the price of an archive that still runs when the CDN is gone. Everything else (layout, animation, theming, dark mode) is better done in plain CSS.

The sandbox

Generated Python runs under bubblewrap with no network, no access to your home directory, and only its own run directory writable. The tool venv is bound read-only, so sandboxed code cannot install anything. Nothing is ever written into your project directories: scratch runs live under ~/.cache/forge/ and are pruned to the 50 most recent on server start.

This is a guard against generated code getting a path wrong, not a defence against a determined attacker with a kernel exploit. Treat it accordingly.

The library

Saved creations live in ~/Projects/preview-creations/, overridable with the MCP_PREVIEW_LIBRARY environment variable. Projects rather than ~/.local/share because that tree is backed up.

~/Projects/preview-creations/
  pong/
    creation.json          name, kind, category, dates, version list
    README.md              generated: what it is, how to run it
    latest -> v3           symlink, so the newest is obvious in a file manager
    v1/  main.py  PROMPT.md
    v2/  main.py  PROMPT.md
    v3/  main.py  PROMPT.md
  stool-site/
    creation.json  README.md  latest -> v1
    v1/  index.html  style.css  script.js  PROMPT.md

Ordinary directories, deliberately. Nothing about the format needs this project to exist: a creation stays runnable if every line of code here is deleted, which is the whole point of an archive. Version numbers come from creation.json, never from listing directories, so deleting a version folder by hand does not free its number for reuse.

Symlinks in a run directory are never followed when saving — a link is not part of a creation, and dereferencing one would copy a file from outside the sandbox into an archive the launcher serves over loopback HTTP.

The launcher

A PySide6 app in the KDE menu. It lists every creation with a search box and a category filter, shows the prompt and README for the selected version, and:

  • Play — runs a Python creation in the sandbox, against a copy, so a creation can never overwrite its own archived source. If the run changed anything (a high score, a save file), you are asked afterwards whether to keep it as a new version. Web creations open in your browser instead, served read-only from loopback.
  • Files — opens the creation's folder in your file manager.
  • Delete — removes a creation and every version, stating the exact path first.
  • About — what the MCP is and how to ask for things, kept with the code it describes.

It reads the folders directly and never talks to the MCP server, so the library works with Cline closed.

Install

Requires Python >= 3.12 and bwrap (bubblewrap) on PATH.

git clone <this repo> ~/Projects/forge
cd ~/Projects/forge
uv venv                                    # or: python -m venv .venv
.venv/bin/python -m pip install -e '.[gui,dev]'

The gui extra pulls in PySide6, which only the launcher needs; the server itself runs without it. dev adds pytest.

Register the server with Cline — see REGISTER-WITH-CLINE.md, and note that VSCodium must be closed while you edit its settings file.

Install the menu entry:

.venv/bin/python -m forge.vendor fetch      # optional: chart.js, alpine
.venv/bin/python -m forge.launcher.install

That writes ~/.local/share/applications/forge-library.desktop with absolute paths computed at install time, so re-running it is also the fix if you move the project. Run the launcher directly with:

.venv/bin/python -m forge.launcher

Tests

.venv/bin/python -m pytest

The whole suite is headless — no test imports PySide6, and none of them touch your real library or scratch directory.

Layout

forge/
  server.py     the three MCP tools
  sandbox.py    bubblewrap argv, containment checks, process reaping
  web.py        loopback static server, offline-reference warnings
  scratch.py    run directories and their pruning
  venv.py       tool venv and the package allowlist
  library.py    the on-disk creation format
  launcher/
    model.py    every launcher decision, Qt-free and tested headless
    window.py   the view: layout and signal wiring only
    about.py    the in-app explanation
    install.py  the .desktop entry

Docs

  • docs/superpowers/specs/2026-08-30-forge-design.md — server design
  • docs/superpowers/specs/2026-08-30-forge-design.md — library and launcher design

License

MIT - see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages