summer-apps is a skill from the Summer skill library: a guide your AI agent reads when the task matches, so it builds the good version on the first try. Build a Summer App, a team tool inside Summer Studio (balance table, quest editor, tracker), data and tools.
When your agent uses it
- The person asks for a tool for their team (“build us a loot balance tool”, “a board to track creatures”)
- The person needs a UI to decide, rate or edit something they will come back to
- Reading or changing the data of an existing Summer App
- Calling an app’s agent tools or waiting for what an app sends
Install
summer setup installs every skill for your agent. To add only this one:
claude-code for your agent, such as codex or cursor. See Skills for every option.
The skill
A Summer App is a tool the person’s team uses inside Summer Studio: shared with the workspace, live on its assets, game data and engine, dockable in the editor. You build it in minutes; the team keeps it. A call writtenarea.action({...}) is one operation: use the tool summer_area_action when
your client lists it, else summer_call({ op: "area.action", input: {...} }).
summer_search finds any operation and its exact input.
First: does Summer already do it?
A custom app is something the team has to maintain. Before you build one, check whether Summer already does the job: Studio features (the asset library with its collections and groups, Pick one and sound choices, Grow dashboards, Team) and the existing Summer operations (summer_search finds them). If it does, use that and tell the person where it lives.
Build an app only when the need is real and Summer lacks it, or as a Scratch throwaway (a
quick asset review, a UI prototype) that you call throwaway when you make it, and archive
when it is done (apps.update({ appId, status: "archived" })). When
something is missing natively, report it with apps.report_gap (and
summer_call with docs.feedback when a library entry fell short), so Summer can build it for
everyone.
When to build one
- The person asks for a tool (“build us a loot balance tool”, “track every creature from concept to in-engine”, “a board to review playtest clips”).
- They need a UI to decide or edit something they will come back to.
- Not for a one-off answer: reply in chat instead.
- Not when Summer already does it (above).
Layout or code
- Layout (
format: "layout"): a JSON layout of kit nodes. Quick, throwaway, always on brand. Use it to ask the person to pick, rate or fill something now. - Code (default):
index.html+ JS/CSS with the Summer SDK and UI kit. Use it for state, editing, shared data, generation or engine access.
Start from the working app
Read these before writing code; they are exact:- minimal-app.md: a complete working app (manifest,
index.html,app.js) built from kit components, using shared data, live changes, the viewer,page.declare, an agent event, an agent tool and frame-move state. Copy it and change it. - sdk-reference.md: every SDK call with its exact arguments and return shape.
- manifest.md: every manifest field.
- The kit’s component catalog (every
<summer-*>element with attributes, events and a copyable example) isapps.guide({ section: "kit" }), also at/summer-ui/v1/catalog.jsonon the app host. Copy its examples instead of inventing markup.
apps.screenshot({ appId }): Summer renders it headless
with the person’s data and returns the picture plus the console, uncaught errors, refused calls
and the fields and buttons it declared. No Studio tab needed; the person sees it at
/studio/apps/<appId> (apps.create opens it for them). Opening index.html on its own
never connects. There is no offline host: do not build one or mock the SDK.
The app is served from its own host, apps.summerengine.app. Load the SDK and the kit as
ES modules with root paths: <script type="module" src="/summer-sdk/v1.js"> and
<script type="module" src="/summer-ui/v1/ui.js">. Then await summer.ready before
the first call, and reach data only through summer.data.collection(name).
The loop
apps.guide({ section: "overview" })first, thenexamplesand the sections the app needs. Load thesummer-apps-designskill for how it must look.apps.create({ name, purpose, format }). The person’s Studio tab opens it.- Write the files in a local folder, then
apps.publish({ appId, files, manifest, note })with every file as{ path, content, encoding }(index.htmlat the root; orlayoutfor a layout app). Each publish is an immutable version the tab boots. A publish error names the file or rule: fix it and publish again. - Seed starter data with
apps.write_data({ appId, ops, as: "seed" })when the app should start full. Seed rows belong to the team and show no author. - Look at it:
apps.screenshot({ appId }), thenwidth: 390(phone) andtheme: "dark";steps: [{click: "#edit"}]or[{press: "<action id>"}]shows a dialog or a second state. Fix the publishwarningstoo. Fix every error and console line it reports, publish again, look again. It is read-only: writes, sends and paid calls are refused there and work in Studio. apps.events({ appId, after })waits for what the app sends you (summer.agent.send), errors (uncaught andconsole.error) and feedback. Act on them; pass the cursor on.- Change the app by publishing again;
apps.get({ appId })returns the current files.
Data is the API
Read an app’s shared collections withapps.read_data and write them with apps.write_data. Both work with
no app open, and everyone in the app sees your edits live. “Nerf the bow in our
balance sheet” is a data write, not a click.
Declare each collection’s record shape in the manifest collections; apps.tools
and apps.read_data return it, so any agent knows what a record looks like.
apps.write_data: up to 100 ops per call. Send an idempotencyKey and reuse it
only to retry the same write, so it applies once. apps.read_data({ appId, collection, limit, cursor })
reads records (items, then nextCursor for the next page); removed records are not returned. Your writes are recorded as actor { kind: "agent" } (and
as createdBy on records you create), so the app can show which rows an agent made. Add
"as": "seed" for starter data that belongs to the team (kind: "seed", no author). patch merges keys
(null removes one); ifRevision guards against overwriting a newer change.
Agent tools
Logic that lives in the app (a balance formula, an export) can be exposed in the manifestagentTools and implemented with summer.agent.tool(name, handler).
apps.tools lists them; apps.call({ appId, tool, input }) runs one inside the person’s open
app and answers a conflict error when the app is not open (its next is the app’s link for the
person). Prefer data for plain reads and edits. apps.page reads the fields and buttons an open app
declared, and apps.use_page({ appId, fill, press }) fills and presses them; the ones the app marks
critical are the person’s to press.
Rules
- Declare only the permissions the app uses; declare every
engineFilespath and everyagentEventsname. A missing one fails at runtime withpermission_missing. - The app runs sandboxed (
allow-scripts allow-forms allow-popups allow-modals allow-downloads, neverallow-same-origin): no cookies, nolocalStorage, no network unless the workspace allows a domain. Keep state insummer.data. - Paid SDK calls spend the credits of the person using the app. Say so before building an app that generates.
- Use real data (
summer.data,summer.assets,summer.engine.readFile), never placeholder numbers. - Studio moves an idle app to a fresh frame every few minutes. Keep UI state you care
about in shared data, or return it from
summer.app.onSnapshot(() => state)(up to 256 KB) and read it back from(await summer.ready).restored. Wrap Web Audio playback insummer.app.busy(true)/summer.app.busy(false)so a long sound is not cut;<audio>and<video>count on their own. - Call
summer.page.declareso agents can read and use the UI; mark delete, publish, pay and send actionscritical. - When the SDK cannot do what the app needs, say so and call
apps.report_gap. apps.dockdocks an app in the editor (it runs in the Summer MCP on this computer); on engines without app docks it returnsengine_lacks_op: open the app in the Studio tab instead.- Keep, archive, restore, rename and pin with
apps.update(status,name,folder,pinned); copy one withapps.duplicate; add a starter app withapps.add_starter; send the makers a note withapps.feedback.apps.deleteasks the person, andapps.rollback(an older version goes live) andapps.update_starterask the app’s owner: each answersneeds_approvalwith a link to give them, and nothing changes until they approve (jobs.waiton the job).
Done means
The app is published;apps.screenshot shows it with real data in light, dark and at
390 px with no errors; its states (loading, empty, error) work; and the person knows what it
does and where its data goes.
From a shell
No Summer MCP tools in this session? Run each one as a command; whensummer --version is missing or below 4.0, use npx -y summer-engine@latest in place of summer. Fields are flags in kebab-case (--scene-path res://main.tscn) or one --input '<json>', and summer ops describe <op> prints the exact input. The summer-cli skill has the rest.
Files in this skill
Related skills
summer-apps-design: How a Summer App should look: layout, Summer UI kit components, tables, charts, states, pre-ship check.
Source:
library/skills/summer-apps/SKILL.md, rendered from summer-engine@4.3.0 on npm.
