Build with a coding agent
Let Claude Code, Codex or Cursor write, build, test and install your droplet, with pictures of every surface and a verdict it can read.
Most droplets will be written with a coding agent at the keyboard, and an agent has two problems a person does not. It cannot see the harness window, so it cannot tell whether the widget it just wrote looks right. And it does not know what a droplet is until something tells it, so left alone it reaches for a bare swift build, forgets a width, or lists a surface it never implemented.
DroppyKit answers both. Every scaffold carries a brief the agent reads before it starts: what a droplet is, the loop it has to run after every change, the rules that are not negotiable, and where these guides and the SDK sources are on disk. The harness renders every surface to a PNG and writes a JSON verdict, so the agent can look at what it built and read whether the manifest, the conformances and the activation agree, without anybody opening a window. And an MCP server hands the whole loop to the agent as tools, the pictures included.
One command
droppykit new writes the agent files with the rest of the scaffold. For a droplet that already exists, or after moving one:
cd worldclock
droppykit agentThat writes five files into the package:
| File | Read by | What it is |
|---|---|---|
AGENTS.md | Codex, Cursor, and most agents | The brief. Yours to edit once written; only rewritten with --force. |
CLAUDE.md | Claude Code | Imports AGENTS.md, so there is one brief, not two. |
.cursor/rules/droppykit.mdc | Cursor | The same brief as a rule that applies to every file. |
.mcp.json | Claude Code | The DroppyKit MCP server. |
.cursor/mcp.json | Cursor | The same server. |
The two MCP files carry the absolute path of your SDK checkout and of the package, on purpose: the agent then finds the server whether or not droppykit is on the PATH of the shell that launched it. Run droppykit agent again after moving either, and once on every Mac the droplet is cloned to.
Connecting the agent
Claude Code. Open the package folder and run claude. It reads CLAUDE.md and, the first time, asks you to approve the droppykit server from .mcp.json. To have the server in every project instead of per package:
claude mcp add --scope user droppykit -- /path/to/droppykit/Scripts/droppykit mcpCodex. Codex reads AGENTS.md on its own, and registers MCP servers per user rather than per folder, once:
codex mcp add droppykit -- /path/to/droppykit/Scripts/droppykit mcpStarted that way the server finds the droplet from the folder Codex is running in, so one registration serves every droplet on the Mac.
Cursor. Open the package folder. The rule applies to every file, and the server is in .cursor/mcp.json; if Cursor asks, enable it under Settings, MCP.
Anything else that speaks MCP. droppykit mcp --package <dir> is a stdio server, standard library Python on the interpreter Xcode's command line tools install, so it runs the moment the SDK is cloned and needs nothing built or installed.
The tools
| Tool | What it does |
|---|---|
droppykit_doctor | Xcode, Swift, the SDK version, whether Droppy Playground is installed, the package's manifest, dependency and built bundle. Run first. |
droppykit_new | Scaffold a droplet, agent files included. |
droppykit_build | droppykit build: the bundle Droppy can load, universal, linkage asserted. |
droppykit_validate | droppykit validate: the checks the Store's intake runs. |
droppykit_shots | Every harness page as a picture, returned inline, plus the JSON verdict. |
droppykit_install | Copy the bundle into Droppy Playground, relaunch it, and report whether it loaded, with the loader's log lines. |
droppykit_manifest | A static check of droplet.json against the sources, no build: id agreement, surfaces against conformances, versions, artwork. |
droppykit_docs | These guides as text, and a search across them and the SDK sources. |
droppykit_host_log | The droplet loader's log from Droppy and the Playground. |
droppykit_open_harness | Open the harness window for you to look at. |
droppykit_submit | The prefilled submission URL for the checkout. |
Every tool shells out to the same script a person runs, so an agent and a developer get the same answers.
What the agent sees
droppykit_shots is the one that matters. It runs the harness headless, with --shots and --report, and returns the pictures of the surfaces the droplet declares, downsampled to the page's width in points so a tool result stays small, with the verdict first: every surface as provided, missing or undeclared, whether activate(host:) threw, which host calls were refused and why. The images are the real views, laid out and drawn by the same code Droppy uses. See The harness for what the report carries.
An agent that reads the report and looks at the pictures after every change catches the two failures that otherwise reach a submission: a droplet that validates and then does nothing because its manifest and its conformances disagree, and a widget that compiles and looks wrong.
The loop the brief asks for
The brief tells the agent that a green build is not the end, and that every change goes through, in order: build, validate, shots and report, then an install into Droppy Playground that says the bundle loaded. It also carries the rules that trip an agent working from general knowledge:
- Surfaces in
droplet.jsonand protocol conformances must agree. - Every shelf widget declares both
preferredSoloWidthandpreferredPairedWidth. - Everything
activate(host:)starts,deactivate()stops. - Host calls are gated by
capabilities; declare what you use, and only that. - Never a bare
swift buildfor the bundle. - Dark surfaces, Droppy's tokens, no outlines, no gradients, sentence case.
The full text is Scripts/agent/AGENTS.md.tmpl in the SDK repository, and AGENTS.md in every scaffold.
Without MCP
The same loop is four shell commands, and the brief lists them, so an agent with a shell and no MCP still has the whole thing:
droppykit build
droppykit validate
droppykit run -- --shots ./shots --report ./shots/report.json
cp -R .build/WorldClock.droplet \
~/Library/Application\ Support/Droppy\ Playground/Droplets/worldclock/An agent that can read image files reads the PNGs in ./shots; the report is plain JSON.
Working with it
- Say what you want on which surface. "A shelf widget that shows my next three calendar events, solo and paired" gives the agent a surface and both compositions; "make a calendar droplet" leaves it guessing.
- Ask for the shots by name when you want to see them yourself: they are in
.build/shots/after everydroppykit_shots, anddroppykit runopens the live window. - Ask the agent to install into the Playground and to say whether it loaded. "Loaded" is a real answer from the running host, not an inference from a build.
- Keep
AGENTS.mdyours. Add the things your droplet needs the agent to know: an API it talks to, a design decision, a surface you do not want. It is only rewritten when you pass--force.
When something is off
- The agent does not list the droppykit tools. Run
droppykit agentin the package (the MCP files carry absolute paths and go stale when either the package or the SDK moves), then start a new agent session; MCP servers are read at launch. - No
report.jsonafter the shots. The harness the droplet built against is older than DroppyKit 1.2.0 and ignored--report. Set the dependency inPackage.swifttofrom: "1.2.0"and build again. - The shots are empty or missing. Read the verdict: an
activate(host:)that throws, or a harness target that does not build, produces no pictures. - The Playground says not loaded. The Store row's subtitle in the Playground is the loader's verdict. The usual causes are a
minAppVersionnewer than the Droppy the Playground stands in for, akit.minAPInewer than the DroppyKit it embeds, an id that disagrees betweendroplet.json,Droplet.idand the folder, or a bundle built with a bareswift build.droppykit_host_logreads the same lines.