Dan Matthew

Program note · Writing · 19 Aug 2026

Scaffolding as a native tool an agent can call

I wanted an agent to scaffold a project by calling a typed tool, not by guessing at a CLI. Packkit's MCP server exposes every generator as one tool surface, and adding a language to it is a two line change.

#packkit #mcp #agents #tooling #architecture

When an agent scaffolds a project today, it usually does one of two clumsy things. It shells out to a CLI and parses the text that comes back, or it writes the files itself from memory and gets the details wrong: the tsconfig that does not quite compile, the CI file with a stale action version, the lockfile it forgets entirely. Neither is a tool. Both are the agent guessing.

I already had generators that knew how to write a correct project. I wanted an agent to reach them the way it reaches any other capability, as a typed tool it calls, with a schema it can read. That is packkit-mcp, an MCP server that puts the Packkit generators in front of an agent as first-class tools.

What it is

An MCP server exposing six tools: list the available generators, list a generator's presets, get a preset's option schema, generate a project, plan an upgrade of an existing one, and compose a full stack repo from two of them. An agent connected to it can ask what stacks exist, inspect exactly which options a preset takes, and get back a real file tree, without ever constructing a command line.

The interesting part is what the server does not contain. It holds no generation logic at all. It is a registry of generators plus six thin handlers that call @packkit/core. When I added the Go generator to the ecosystem, teaching the MCP server about Go was a two line change: register the generator, and it appears in list_generators with all its presets. The server fronts JavaScript, Python, and Go through one surface, and it stays honest about maturity, an experimental generator like Go is hidden unless the caller opts in to see it. That is the same rule from the rest of the platform showing up again: the surface does not model the languages, it drives a protocol that already forgot them.

An agent can build across two languages

The tool I like most is compose_fullstack. An agent hands it a front end spec and a back end spec, each just a generator id and a preset, and gets back one repo with both wired together. Because it runs on the composition primitive core already had, an agent can build a React front end with a Python FastAPI back end in a single call, and neither generator knows the other language exists. The agent does not orchestrate two scaffolds and glue them, it asks for a composed thing and receives it.

The option schema tool matters more than it sounds. The reason agents get scaffolding wrong is that they guess flags. get_generator_schema hands back the real option set for a preset, so the agent inspects what is available and fills it in, instead of inventing --lint biome and hoping. It turns a guess into a lookup.

Discovery has no rename button

Here is the honest, unglamorous part I did not expect to spend time on: getting the server discoverable, and keeping it that way through renames.

The server publishes itself to the Model Context Protocol registry from CI, using short-lived OIDC so there is no token to store, and directories like Glama crawl it and build their own listing. The canonical entry, io.github.PackkitLabs/packkit-mcp, is correct and current. The catch is that I renamed things twice while building this: my personal account's repo moved into an org, and then the org itself was renamed. GitHub redirects the repositories, but the discovery infrastructure does not follow a rename the way a browser does. Each old name left an orphaned entry in the registry and on Glama, under a namespace I can no longer authenticate against, because the identity that owned it no longer exists by that name.

None of it is dangerous, nobody can impersonate the orphans, because publishing under them would still require proving ownership of the npm package, which is mine. But they sit there, stale copies pointing at dead paths, and cleaning them up is a support request per platform rather than a command I can run. If I were starting again, I would pick the final org name before publishing anything to a registry, because the registries remember every name you ever used and give you no clean way to forget the wrong ones.

What is still rough

The server is a surface, so it inherits its generators' limits. Go is experimental behind it because Go is experimental everywhere in the platform, and the tool honestly reports that rather than pretending otherwise. And a schema an agent can read does not make the agent choose well, it just stops it guessing the option names. A good scaffold still needs a caller that knows what it wants; the tool's job is to make sure that once it knows, it cannot fat-finger the request.

The server is on npm as packkit-mcp, it runs over stdio, and the code lives with the rest of the platform under the PackkitLabs org. If you drive agents and want them to scaffold real projects instead of approximating them, point one at it.