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

forma-dsl-mcp

v0.4.12

Published

A stdio MCP server that knows the forma DSL and writes .forma documents that render.

Downloads

2,744

Readme

forma-dsl-mcp

Release npm M8ven Verified

A Model Context Protocol server that knows forma - the declarative DSL for 3D solids - and builds it with the real geometry kernel, so a .forma document is measured before anyone is told it works.

It carries the language with it: the guide, the block reference, the worked examples and the full manual are bundled, so a model learns the syntax from the server rather than guessing at it.

It speaks over stdio, and there is nothing to configure.

The site · Try the editor · Read the reference

Installing it

Claude Code

claude mcp add forma -- npx -y forma-dsl-mcp

Codex

codex mcp add forma -- npx -y forma-dsl-mcp

or, in ~/.codex/config.toml:

[mcp_servers.forma]
command = "npx"
args = ["-y", "forma-dsl-mcp"]

Claude Desktop, and anything else that takes a JSON block

{
  "mcpServers": {
    "forma": {
      "command": "npx",
      "args": ["-y", "forma-dsl-mcp"]
    }
  }
}

Where the files go

The server does not choose. It asks the client over MCP's roots capability, and reads and writes only inside the directories the client hands over. A relative path is taken against the first of them, and every write reports the directory it landed in.

Every path is confined to those directories - an absolute path outside them, a .. that climbs out, or a symlink pointing out of the tree is refused rather than followed. When the client says its roots have changed, the server re-reads them before the next call.

A client that does not implement roots gets the working directory it launched the server in, which is the one location it did choose. Codex can set that explicitly:

[mcp_servers.forma]
command = "npx"
args = ["-y", "forma-dsl-mcp"]
cwd = "/absolute/path/to/your/models"

Why it is not just a prompt

A model that has read the language's documentation still writes forma that does not build. The difference this server makes is that it runs the geometry kernel. Every tool compiles the document, solves it with Manifold and reports back the bounding box, the volume, the triangle count and the genus of every part - so the question "is this the size that was asked for, and is it actually a solid?" is answered before the work is handed over. A part that silently solved to nothing is called out by name, and an error comes back with the line, the column and an excerpt with a caret on it.

forma_write refuses to write a document that does not render.

Refusals carry the vocabulary too: an unknown block comes back with every block there is, and an unknown attribute with the ones that block reads. That is the backstop, not the teacher - it is enough to correct a misspelling and not enough to learn a language, since it gives the name of a block and nothing about what that block means or what it takes. forma_guide, forma_blocks and forma_reference are what answer that, and they are bundled so they answer offline.

Everything the server says about the language is answerable to the language. The block and function catalogue is merged with the library's own registries at load time, and the tests assert the two agree: a block added, renamed or given a new dimensionality in forma-dsl fails this package's build rather than becoming a confident wrong answer. Every worked example is rendered by the test suite.

Tools

Learning the language

| Tool | What it does | | --- | --- | | forma_guide | The language in one page: structure, nesting, dimensionality, units, syntax, the mistakes that cost the most time | | forma_blocks | Every block with its attributes, defaults, dimensionality and caveats | | forma_functions | Every builtin function and constant, with arity | | forma_examples | Complete documents that render, each showing one way of building a real part | | forma_reference | The full reference manual, searchable and readable page by page |

Building with it

| Tool | What it does | | --- | --- | | forma_write | Check, then write a .forma file | | forma_read | Read a document back, checked | | forma_export_stl | Render and write a binary STL |

Resources

The same knowledge in the form a person can attach to a conversation by hand:

  • forma://guide - the language in brief
  • forma://catalogue - every block and function, as JSON
  • forma://reference/{path} - one page of the manual
  • forma://example/{name} - one worked document

Asking for a part

Install it in Codex as above, cd to wherever you keep your models, and ask:

you - build me a forma file with a tree in it

codex - Where should I put it?

you - the current folder

Codex reads the language before it writes any: forma_guide for the shape of a document, forma_blocks for the attributes of the blocks it is about to use - here cone, translate and for - and forma_examples for a document of a similar shape. Then it writes, and forma_write builds what it wrote with the real kernel before anything reaches the disk.

Where the vocabulary runs out, the refusals carry it. A guess at a block that does not exist comes back with every block that does, and a guess at an attribute with the ones that block reads:

unknown block "frustum" - the blocks are "align", "for", "if", "part", "box", "circle",
  "cone", "cylinder", "difference", … "union"

cone: unknown attribute "bottom_radius" - cone reads "center", "height", "radius",
  "segments", "top_radius"

Then it lands, and the tool answers with the part rather than with a claim about it:

Wrote `tree.forma` under `/home/you/models` - 537 bytes.

| Part     | Colour  | Opacity | Size (X × Y × Z) | Volume    | Triangles | Genus |
| `trunk`  | #6b4423 |       1 | 8 × 8 × 30       | 1004.682  |       108 |     0 |
| `canopy` | #2f7d32 |       1 | 28 × 28 × 30     | 5193.4555 |       358 |     0 |

Overall bounding box: 28 × 28 × 51 (min -14, -14, 0, max 14, 14, 51)
Triangles: 466

tree.forma:

param trunk_height { type = number  default = 30  min = 10  max = 60 }
param canopy_layers { type = number  default = 3  min = 1  max = 6 }

local canopy_base = trunk_height * 0.7

model "tree" {
  part "trunk" {
    color = "#6b4423"
    cone { radius = 4  top_radius = 2.5  height = var.trunk_height }
  }

  part "canopy" {
    color = "#2f7d32"
    for i in range(var.canopy_layers) {
      translate {
        offset = [0, 0, canopy_base + i * 8]
        cone { radius = 14 - i * 3  top_radius = 0  height = 14 }
      }
    }
  }
}

The document above is a test in this repo rather than an illustration: test/server.test.js builds it, checks both parts came out solid, drives trunk_height to the top of its declared range, and exports it. The wrong guesses are asserted to name their own corrections.

More to ask for

Model a wall bracket that carries a 35 mm pipe 60 mm off the wall. Two M5 screw holes in the backplate, 50 mm apart. Make the pipe diameter and the standoff params, and write it to bracket.forma.

Write enclosure.forma: a 90 × 60 × 25 mm box with 2 mm walls, open at the top, with a lid that drops in on a 0.3 mm clearance lip. Wall thickness and clearance are params. Give the body and the lid separate parts so I can see them apart.

Read bracket.forma and re-check it at both ends of every param's declared range. Anything that solves to nothing or changes the bounding box in a way the document did not intend, tell me about before you change it.

Take enclosure.forma, set the wall thickness to 3 mm, and export just the lid to lid.stl.

Two things are worth asking for by name, because they are what the kernel is there to answer: the bounding box, to confirm the part is the size you meant, and the genus, to confirm a part that should be a plain solid has not ended up with a hole through it.

The bundled manual

src/reference/ is a copy of apps/docs/content, which is a private workspace and cannot be depended on from something that ships to npm. The copy is what lets the server answer offline. It is made by npm run sync:reference -w forma-dsl-mcp and compared byte for byte against the original by the tests, so it cannot drift: edit the manual, re-run the sync, and the test passes again.

Running it from source

npm install
npm test -w forma-dsl-mcp                  # including the protocol end to end
node packages/forma-dsl-mcp/src/bin.js     # the server, on stdio

Point a client at node /absolute/path/to/packages/forma-dsl-mcp/src/bin.js to use the working tree rather than the published package.

Privacy

This server makes no network requests. It imports no HTTP, socket or DNS module and calls no fetch; the geometry kernel is WebAssembly loaded from disk and the manual is bundled in this tarball, so it works with no network at all. There is no telemetry, no analytics, no update check and no account.

It writes only inside the directories your client declares as MCP roots, never replaces a file unless told to, and leaves nothing elsewhere - no cache, no config, no logs.

Your MCP client is a separate program with its own policy. Documents you ask a model to write travel wherever that client sends them, which this server neither controls nor sees.

Full text: PRIVACY.md.

License

MIT - see LICENSE.