Charming
Server Details
Build personal interactive apps with real URLs and persistent storage, using any AI.
- Status
- Healthy
- OAuth
- Works in Glama
- Last Tested
- Transport
- Streamable HTTP
- URL
- Repository
- tambo-labs/charming-mcp
- GitHub Stars
- 1
- Server Listing
- Charming
TDQS
Scored across 33 tools
Most tools target distinct resources and actions, and descriptions clarify overlaps. However, deprecated aliases set_remixable/unset_remixable duplicate set_template/unset_template, and the multiple set_* tools (set_public, set_template, set_starter_prompt, set_handle) require careful reading to avoid misselection.
All tool names use consistent snake_case and follow a verb_noun pattern (e.g., create_app, get_app_source, list_feedback). Deprecated aliases still follow the same convention, with no mixing of camelCase or alternate styles.
With 33 tools, the server exceeds the 25+ threshold for 'too many' and includes two deprecated aliases that duplicate existing functionality. While the platform is broad, the set feels bloated and could be consolidated to reduce confusion.
The surface covers app lifecycle, builds, routines, sharing, templates, feedback, assets, and docs thoroughly. A notable gap is the lack of a claim_app operation despite several tools referencing the need to claim anonymous apps first, which creates a dead end for some workflows.
Available Tools
33 toolsacknowledge_feedback_responsesAIdempotentInspect
Acknowledge feedback responses after the agent has processed them. Use this to stop processed responses from appearing in advisories and list_feedback_responses. Acknowledgement is durable and idempotent.
| Name | Required | Description | Default |
|---|---|---|---|
| response_ids | Yes | Response UUIDs returned by list_feedback_responses. |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
| acknowledged | Yes | Response IDs acknowledged by this call. |
| already_acknowledged | Yes | Response IDs that were already acknowledged. Safe idempotent retries land here. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already provide idempotentHint=true and destructiveHint=false; the description adds that acknowledgment is durable and changes visibility in advisories and list_feedback_responses. This goes beyond the structured hints, although it does not describe the output shape or any side effects on the underlying feedback records.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise statements are packed with relevant information: the action, the precondition, the effect on other tools, and the tool's durability/idempotence. No filler or redundant elaboration.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation with a single well-described parameter and an existing output schema, this description covers what the tool does, when to use it, and what side effects to expect. An agent has enough context to call it appropriately.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema fully documents the only parameter (response_ids as UUIDs returned by list_feedback_responses), and the tool description reinforces the purpose but does not add parameter-level detail. With 100% schema coverage, the baseline applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly specifies the action ('Acknowledge feedback responses'), its timing ('after the agent has processed them'), and its observable effect ('stop processed responses from appearing in advisories and list_feedback_responses'). It is easy to distinguish from sibling tools like list_feedback_responses or submit_feedback.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly tells an agent when to call this tool ('after the agent has processed them') and what outcome to expect. It does not name alternative tools or edge cases, but the intended trigger and post-condition are clear enough for a competent agent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
cancel_app_buildAIdempotentInspect
Use this to cancel an accepted app build before it publishes. Repeating cancellation returns the same terminal status. Cancellation leaves accepted desired source editable and keeps the last active app running.
| Name | Required | Description | Default |
|---|---|---|---|
| build_id | Yes | Build ID returned by create_app or update_app. |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | |
| url | No | App URL. Present only after successful publication. |
| appId | No | Existing target app ID, or the created app ID after publication. |
| error | No | |
| state | Yes | |
| intent | Yes | The accepted operation: create, update, migrate, restore, or copy. |
| source | No | Exact accepted source, returned only when include_source is true. |
| buildId | Yes | Durable build ID. Use get_app_build to inspect progress or source. |
| attempts | Yes | |
| deadline | Yes | |
| revision | No | Published app source revision. |
| elapsedMs | Yes | |
| lockState | Yes | |
| statusUrl | Yes | Authenticated HTTP status URL for this build. |
| updatedAt | Yes | |
| acceptedAt | Yes | |
| finishedAt | Yes | |
| lockDigest | No | |
| sourceEtag | Yes | ETag identifying the immutable input accepted for this build. |
| inputDigest | Yes | |
| queueDeadline | Yes | |
| activeRevision | No | |
| desiredRevision | No | |
| retryAfterSeconds | No | Wait at least this many seconds before polling again. |
| inspectionExpiresAt | Yes | |
| idempotencyExpiresAt | Yes | |
| resolvedDependencies | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds side effects beyond annotations: 'leaves accepted desired source editable' and 'keeps the last active app running'. The idempotency statement ('Repeating cancellation returns the same terminal status') reinforces idempotentHint=true but adds detail. No contradictions with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, no filler. The primary purpose and key side effects are front-loaded. Every sentence adds value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the presence of an output schema and simple operation, the description covers the main behavior, idempotency, and side effects. It could detail error conditions or what happens to the build object, but for a cancel operation this is adequate. The description is complete enough for an agent to use it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Only one parameter (build_id) is present, and the schema description ('Build ID returned by create_app or update_app') already provides full coverage. The description does not add further semantic nuance about the parameter, but with 100% schema coverage, the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'cancel' and the resource 'accepted app build', and adds the condition 'before it publishes'. This distinguishes it from delete_app (which removes the app) and get_app_build (which retrieves). The scope is precise and actionable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states when to use it ('accepted app build before it publishes') and implies that once published or not accepted, this tool is inappropriate. It does not name alternatives, but the context is strong enough to route an agent. Could improve by explicitly saying when not to use (e.g., for deleting the app).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_appADestructiveInspect
Create a new interactive web app and render it inline. Use this to build a new app from a request. Existing-contract apps return after publication. Eligible authors can select the ESM manifest schema with an idempotency_key to accept a background build; that result contains a buildId to poll with get_app_build, and has no app URL before publication. If module reuses a manifest.id the caller already owns, the new app replaces the existing app module, UI, and styles in place; use update_app to edit a known app ID by UUID. Apps persist state through a backend env.storage (Workers KV) binding and call backend operations from the frontend via window.charming.api. The module, ui, and description parameter docs carry the authoring rules — including the fill-viewport outer-container rule on ui; the charming:app-guide prompt has the full guide and a canonical example. Read the public docs without a browser using read_docs({ path: "build-mcp.md" }); read_docs({}) lists available pages. If a request needs in-app AI calls, external integrations, native apps, scheduled jobs, SQL, notifications, or view-only sharing, do not fake support. Build the closest local-state version, then log the gap with submit_feedback. The charming:app-guide prompt has alternatives. The charming:design-guide prompt documents Charming's default visual style for generated ui (single accent, warm neutrals, real type hierarchy; no gradients or decorative emoji) — a default that yields to any user-requested aesthetic. When telling the user where to open or share the app, always give them shareUrl from the result — never the url field, which is machine-only and embeds a write-capable access token.
| Name | Required | Description | Default |
|---|---|---|---|
| ui | No | Under the explicit ESM contract, this is an ES module: import declared client packages and import { api, onStateChange } from "charming:ui/app@1.0" to call this app and subscribe to its state changes. Under the existing contract, follow the classic JavaScript rules below. Frontend JavaScript rendered into #app. The #app mount point starts empty, so (a) populate its innerHTML before attaching event listeners, and (b) call operations as `window.charming.api('<manifest-id>').<opName>(input)` (operation name = method name, strips the { ok, value } envelope — you receive the value only, never ok) — not `api.operation(name, params)` and not raw fetch(), which fails in the null-origin srcdoc iframe. The signed-in caller is exposed synchronously as `window.charming.user` (`{ id, handle?, name?, image? }`, or `null` for an anonymous visitor) — read `user.name` to greet, attribute, or personalize, instead of asking the user to type their name. PUBLIC fields only (never email); it is a convenience signal, not enforcement. The outermost container MUST fill the viewport — use `<main class="min-h-screen">` (or a grid/flex layout that spans width) as the root shell. Do NOT wrap the root in `max-w-md`, `max-w-2xl`, or `container mx-auto`: those cap the entire app to a narrow central column with wide empty margins on 2K+ monitors — the loudest "AI-generated app" tell. If the view is text-heavy (a note, an article, a form with long prose), cap the reading measure on an INNER wrapper only, e.g. `<main class="min-h-screen"><div class="mx-auto max-w-2xl">…</div></main>`. Dashboards, kanban, tables, canvases, galleries, and split views should use the full width. For live updates when an agent mutates state from another session, register `window.charming.onStateChange((e) => { ... })` and update the DOM surgically rather than wiping #app. See the charming:app-guide prompt for a canonical example. | |
| module | Yes | Eligible ESM authors may instead select `$schema: "https://charm.ing/schema/app-manifest/2026-09-05.json"`, declare target-specific `dependencies.server` and `dependencies.client`, and use ordinary package imports plus exact versioned platform imports. For ESM persistence, use `import { kv } from 'charming:storage/kv@1.0'` and call `kv.get`/`kv.put` inside route handlers; omit `capabilities`. ESM creation takes its name from `manifest.meta.name`; omit `display_name`. For the existing contract, export a strict canonical `manifest` with `$schema: "https://charm.ing/schema/app-manifest/2026-07-31.json"`, `id`, and `meta: { name, icon? }`. The server may insert the exact schema URL on create when the rest of the source is canonical. Unknown manifest keys are rejected. To persist state in the existing contract, `capabilities.imports` must include "charming:storage/kv@1.0" (without it `env.storage` is undefined and every read/write throws `storage capability not granted`). A route handler in `export const routes = [...]` receives `(input, { env, ctx, request })` and returns exactly the value declared by `outputSchema`; for an array schema, use `handler: async (_input, { env }) => (await env.storage.get("key")) ?? []`. Charming creates the transport envelope. Do not add a `{ ok, value }` or `{ value }` envelope unless those fields belong to `outputSchema` itself. A named context reads storage as `context.env.storage`. The optional unmatched-request fallback has the separate signature `export default { fetch(request, env, ctx) { ... } }`, where the second argument is the environment itself; it adds no discoverable route metadata. `env.user` is always present, not gated by any import: the caller's public identity (`{ id, handle?, name?, image? }`) or `null`. It lives only on `env` — read `env.user` (or `context.env.user`); there is no `ctx.user`. Full reference: call read_docs with path `llms-full.txt` (https://charm.ing/docs/llms-full.txt). For the existing contract, persistence goes through `env.storage` using Workers KV semantics — `get(key)`, `put(key, value)`, `delete(key)`, `list()`; `.set`/`.add`/`.write`/`.setItem`/`.removeItem` do not exist and throw `TypeError: env.storage.<x> is not a function`. env.storage stores JSON-compatible values directly; do not JSON.stringify before put or JSON.parse after get. Use env.storage for ALL persistence — it is the only storage that survives inside Claude/ChatGPT and syncs across devices. Do NOT keep app data or user state in localStorage/sessionStorage/IndexedDB: those APIs are empty inside chat hosts, so their data silently vanishes there (the most common cause of an app that appears not to save). The "charming:browser/storage@1.0" capability (claim-gated) unlocks them but only for throwaway, web-only caching; anything the user expects to keep belongs in env.storage. Export a `routes` array with unique `op` values and `handler` functions. Canonical route fields are `inputSchema`, `outputSchema`, and `annotations`; `method` defaults to `POST`, `path` defaults to `/api/<op>`, input defaults to a closed empty-object schema, and `public` defaults to true. Set all four MCP annotation hints when their defaults do not fit; Charming does not infer them from the HTTP method. A default `fetch` handler is an unmatched-request fallback only and is not discoverable. The following capability declarations apply only to the existing contract. Apps that use a sensitive browser capability must declare its import in `capabilities.imports` — "charming:browser/microphone@1.0" (getUserMedia audio), "charming:browser/camera@1.0" (getUserMedia video), "charming:browser/geolocation@1.0", "charming:browser/clipboard-read@1.0" (reading the clipboard), "charming:browser/display-capture@1.0" (getDisplayMedia screen share), "charming:browser/midi@1.0" (Web MIDI, navigator.requestMIDIAccess), "charming:browser/device-motion@1.0" (device orientation/motion: DeviceOrientationEvent/DeviceMotionEvent + iOS requestPermission), "charming:browser/ambient-light@1.0" (ambient light, new AmbientLightSensor), or "charming:browser/storage@1.0" (native client storage — localStorage/sessionStorage/IndexedDB — web-only); access is granted only after the app is claimed/authenticated. To call external HTTPS APIs from backend code, declare "charming:network/fetch@1.0" and list each exact origin in `manifest.permissions.server.fetch`; both are required and public-only. For an endpoint that needs an API key, declare "charming:secrets/fetch@1.0" to get `env.fetch` (claimed apps only) — a sealed outbound fetch that substitutes `{{secret:NAME}}` references in request HEADER values or query-parameter VALUES host-side (never a parameter name, the host, path, fragment, or body), so the key never enters app source or the sandbox; write the placeholder literally in the URL string — `URLSearchParams.set(...)` or `encodeURIComponent(...)` percent-encodes it first and it will NOT resolve; the app OWNER opens App settings, then Secrets, at `/<owner-handle>/~/apps/<app-name>/settings/secrets`, while `/app/<id>/secrets` remains the machine HTTP API. The agent only references the NAME. Never embed API keys in source. To render remote images, list each exact https origin in `manifest.permissions.browser["img-src"]`. To make an image render in ANY host — standalone, ChatGPT, AND Claude inline (their injected CSP blocks a cross-origin `<img src>`) — set the src from `const src = await window.charming.images.load(remoteUrl)` (it fetches through Charming and returns a `data:` URL every embed CSP allows). `window.charming.images.proxy(remoteUrl)` returns a same-origin proxy URL that works standalone/ChatGPT but NOT in Claude inline; prefer `images.load(...)` when the app may be embedded. Both enforce the declared origins; neither bypasses them. | |
| styles | No | Optional CSS for the rendered app | |
| team_id | No | Optional destination team id. Only a team owner or admin can create an App there. Omit it to create a personal App. | |
| description | Yes | Search-friendly summary of what this app does, surfaced by list_apps so a future session can match user intent (e.g. "open my protein tracker", "log food") to this app even when display_name is ambiguous. Write 1-2 sentences covering: (a) what the app tracks/does, (b) the key actions it supports, and (c) synonyms or alternative phrasings the user might say. Example: "Tracks daily protein intake. Log meals, view weekly totals, set a daily goal. Synonyms: meals, food log, nutrition tracker, calorie counter." Limit 500 characters. | |
| display_name | No | Optional display name override | |
| idempotency_key | No | Required for ESM builds: 8–128 visible ASCII characters. Retry the exact request with the same key to recover its build; use a new key for changed source. | |
| migrate_contract | No | Set true to migrate an existing legacy manifest.id to ESM, together with expected_revision. Source submissions do not roll back contracts. History can explicitly restore a retained validated existing-contract revision. | |
| expected_revision | No | Required when ESM source reuses an existing manifest.id. Pass the desired revision from get_app_source. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | UUID of the created or updated app. |
| ok | Yes | |
| url | No | App URL. Present only after successful publication. |
| icon | No | The effective home-screen icon stored for the app. Canonical source declares it at `manifest.meta.icon`. |
| appId | No | Existing target app ID, or the created app ID after publication. |
| error | No | |
| state | No | |
| intent | No | The accepted operation: create, update, migrate, restore, or copy. |
| source | No | Exact accepted source, returned only when include_source is true. |
| appName | No | URL-safe slug for the app, unique per owner. Distinct from manifestId and displayName, and stable across edits — use rename_app to change it (the title changing does NOT change the slug). |
| buildId | No | Durable build ID. Use get_app_build to inspect progress or source. |
| attempts | No | |
| deadline | No | |
| revision | No | Server-owned app source revision. Historical null counters read as 0; new apps start at 1; each successful source write advances it once. Pass this value through `expected_revision` when guarding update_app. |
| shareUrl | No | The link to give humans whenever the user wants to open or share the app. Friendly /<handle>/<app-name> form when the OWNER has a live handle and the app a slug, /app/<uuid> otherwise. Always token-free — safe to show, paste, and send. Not an API base: appending /api/<op> to the friendly form 404s; use `url` for machine calls. |
| warnings | No | Non-blocking publish feedback (#1126): present when static validation found UI/backend contract mismatches or legacy icon input needed a fallback. The write succeeded; fix the named source field. |
| elapsedMs | No | |
| lockState | No | |
| statusUrl | No | Authenticated HTTP status URL for this build. |
| updatedAt | No | |
| acceptedAt | No | |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
| finishedAt | No | |
| lockDigest | No | |
| sourceEtag | No | ETag identifying the immutable input accepted for this build. |
| inputDigest | No | |
| queueDeadline | No | |
| activeRevision | No | |
| desiredRevision | No | |
| retryAfterSeconds | No | Wait at least this many seconds before polling again. |
| inspectionExpiresAt | No | |
| idempotencyExpiresAt | No | |
| resolvedDependencies | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description discloses significant behavioral details: reusing a manifest.id the caller owns replaces the existing app module/UI/styles in place; idempotency_key enables background builds with a buildId to poll; the shareUrl field is user-facing while the url field embeds a write-capable token. It also documents storage semantics, capability declarations, and failure modes (e.g., localStorage silently vanishes). The destructiveHint annotation is consistent with the described replacement behavior, with no contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is exceptionally long and dense, but nearly every sentence carries a rule or operational constraint necessary for this tool's dual-contract complexity. It is well front-loaded with the core purpose and key side-effect warnings. It loses one point because it duplicates some content that already lives in the rich parameter docs and could be tightened without losing meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity—two authoring contracts, background builds, destructive replace semantics, capability declarations, storage constraints, and a machine-only URL security warning—the description is remarkably complete. The presence of an output schema reduces the need to explain return values, and the description still covers the critical result fields (buildId, shareUrl). Nothing an agent needs to call this tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3, but the description adds substantial operational meaning: idempotency_key is tied to background builds and retries, expected_revision is required for reuse of an existing manifest.id, team_id is owner/admin-gated, and the module/ui/description params carry distinct authoring contracts. The description also explains result semantics (shareUrl vs url) that are not in the schema. It goes far beyond merely restating parameter names.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Create a new interactive web app and render it inline. Use this to build a new app from a request.' It immediately distinguishes itself from the sibling update_app by saying 'use update_app to edit a known app ID by UUID,' and it clarifies the existing-contract vs ESM contract behavior. An agent can confidently select this tool for new-app creation rather than mutation or query tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance: build a new app from a request, with a clear alternative for edits (update_app). It also handles unsupported requests explicitly: 'do not fake support. Build the closest local-state version, then log the gap with submit_feedback.' It points to read_docs for full documentation and to the charming:app-guide prompt for canonical examples. This is exemplary routing and exclusion guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_routineAInspect
Use this when the user wants an app op to run on its own, on a timer — no chat needs to be open. Schedules one of an app's declared ops as a Routine. Interval is hourly, daily, or weekly (no cron syntax). The target op is invoked with empty input ({}), so it must not declare any required input fields. Rejects a duplicate Routine on the same (app, op) pair, and enforces a 3-per-app / 25-per-user cap.
| Name | Required | Description | Default |
|---|---|---|---|
| op | Yes | The declared op to invoke on each run | |
| app_id | Yes | The app ID (UUID) to schedule a Routine on | |
| interval | Yes | How often the Routine runs |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| routine | Yes | |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate readOnlyHint=false and destructiveHint=false, so the description correctly implies a write operation without destruction. It adds valuable behavioral context: the target op is invoked with empty input, duplicate Routines are rejected, and there are hard caps (3 per app, 25 per user). This goes beyond the structured annotations and helps the agent anticipate side effects and failure conditions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact, two sentences, and front-loads the primary use case before detailing constraints. Every sentence contributes: the first defines the scenario, the second explains mechanics, input requirements, and limits. There is no filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has an output schema (indicated by 'Has output schema: true'), so return values are covered there. The description covers the key aspects an agent needs: when to use, allowed intervals, the empty-input requirement, duplicate rejection, and caps. For a creation tool with clear schema and annotations, this is complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so each parameter (app_id, op, interval) is already described. The description adds meaning by clarifying that interval accepts only 'hourly', 'daily', or 'weekly' and explicitly rules out cron syntax. It also explains that the op is called with empty input, which informs the agent that op must not have required fields—a semantic detail not present in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool creates a Routine that schedules an app op on a timer, and distinguishes it from chat-based operations. It specifies the verb 'schedules' and the resource (an app's declared op), and the opening phrase 'Use this when the user wants an app op to run on its own, on a timer' immediately clarifies the primary use case, setting it apart from siblings like update_routine or delete_routine.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives a clear trigger condition ('when the user wants an app op to run on its own, on a timer') and explicitly notes it is not a chat operation. It also states constraints like 'no cron syntax' and that the op must not have required input fields, which guides correct usage. However, it does not explicitly mention when to use an alternative tool (e.g., update_routine to modify an existing Routine), though this is implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_appADestructiveInspect
Permanently delete a Charming app the caller owns, optionally also purging its stored data. Use this to remove an app the caller no longer wants. Deletion is irreversible and requires confirmation: the host is asked to show a confirmation prompt, and when it cannot, the call fails closed until re-invoked with confirm: true.
| Name | Required | Description | Default |
|---|---|---|---|
| app_id | Yes | The app ID (UUID) to delete | |
| confirm | No | Explicit confirmation that the caller intends to permanently delete this app. Fallback for hosts that cannot show a confirmation prompt: set true only after the deletion has been confirmed with the user through another channel. On hosts that do support a prompt, the prompt is authoritative and this field is ignored. | |
| purge_storage | No | Also delete stored app data |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | UUID of the targeted app. |
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| deleted | Yes | True if the app was deleted, false if deletion was cancelled by the user. |
| cancelled | No | Set to true when the user declined the elicitation confirmation prompt. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the destructiveHint annotation by spelling out irreversibility, the host-side confirmation prompt, and the fail-closed behavior that requires confirm: true when the prompt is unavailable. This gives the agent a complete picture of side effects and safeguards.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with the core action, then the conditional nuance. No redundant filler; every clause carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers the action, ownership constraint, optional data purge, irreversibility, and confirmation edge-case behavior. With a full input schema and clear sibling distinctions, nothing essential is missing for an agent to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Input schema already covers all three parameters at 100% coverage; the description adds real value for confirm by explaining the fail-closed fallback semantics. It does not deepen app_id or purge_storage much beyond the schema, but the confirm addition justifies a strong score.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource ('Permanently delete a Charming app the caller owns'), adds the optional purge-storage nuance, and clearly distinguishes this from siblings like update_app or delete_routine. No ambiguity about what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Use this to remove an app the caller no longer wants' gives a clear call-context, and the ownership condition further scopes when it applies. It does not explicitly name alternatives or say when not to use it, so it falls short of a full 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_routineADestructiveInspect
Use this to stop and remove a Routine.
| Name | Required | Description | Default |
|---|---|---|---|
| routine_id | Yes | The Routine id (`routine_<uuid>` form) to delete |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| deleted | Yes | True when a Routine was removed; false when it was already gone. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and readOnlyHint=false, which signal this is a mutation that destroys data. The description aligns with these traits by saying 'stop and remove,' but it adds no extra behavioral context such as irreversibility, required permissions, or consequences beyond what annotations already convey. With annotations covering the primary behavioral flags, the description provides minimal additional value.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence with no filler, directly stating the tool's purpose. It is concise and front-loaded, though it could be slightly more precise by explicitly using 'delete' instead of 'stop and remove.' Still, it is efficient and earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple delete tool with one well-documented parameter and annotations indicating destructive behavior, the description is largely complete. An output schema exists, so return values need not be described. It lacks mention of edge cases (e.g., whether deleting a nonexistent routine errors), but these are not critical for basic usage. Overall, it meets the needs of an agent selecting and invoking the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema fully describes the single required parameter, including its format ('routine_<uuid>') and meaning. Since schema coverage is 100%, the description does not need to add parameter details. The description contributes nothing beyond the schema, so the baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('stop and remove') and names the resource ('Routine'), which clearly identifies the action. It distinguishes itself from sibling tools like 'create_routine' and 'update_routine' by implying deletion, though 'delete' is not explicitly used. The phrasing is slightly indirect but still unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description states the tool is for stopping and removing a Routine, giving a clear general use case. However, it provides no explicit guidance on when not to use it or how it compares to alternatives (e.g., whether updating is preferred over deletion in certain contexts). The usage is implied but not detailed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_appARead-onlyInspect
Fetch an existing app by ID and render it inline. Use this to open, re-render, or continue working with an app the caller already has. Returns the live embedded UI plus its callable API operations, which another agent can invoke with query_app (read-only) or mutate_app (writes) to query or update saved app data. Unlike list_apps, which returns metadata including IDs and URLs, get_app renders the app; call list_apps first to find the ID. For synchronous results, give the user shareUrl to open or share the app; the url field is machine-only and embeds a write-capable access token.
| Name | Required | Description | Default |
|---|---|---|---|
| app_id | Yes | The app ID (UUID) |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | UUID of the opened app. |
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| api | Yes | Full API surface — same shape as `_meta.charming.api`, but model-facing. |
| url | Yes | Machine/API URL for the app (stable /app/<uuid> form). Embeds a write-capable ?t= access token — NEVER show, paste, or send it to the user. Append /api/<op> to its path for out-of-band operation calls. For anything user-facing, use shareUrl instead. |
| $schema | No | Exact dated canonical manifest schema URL. Omitted for legacy apps. |
| appName | No | URL-safe slug for the app, unique per owner. Distinct from manifestId and displayName, and stable across edits — use rename_app to change it (the title changing does NOT change the slug). |
| revision | Yes | Server-owned app source revision. Historical null counters read as 0; new apps start at 1; each successful source write advances it once. Pass this value through `expected_revision` when guarding update_app. |
| shareUrl | Yes | The link to give humans whenever the user wants to open or share the app. Friendly /<handle>/<app-name> form when the OWNER has a live handle and the app a slug, /app/<uuid> otherwise. Always token-free — safe to show, paste, and send. Not an API base: appending /api/<op> to the friendly form 404s; use `url` for machine calls. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
| appVersion | Yes | Canonical apps return decimal String(revision). Legacy apps return their stored effective SemVer. |
| manifestId | Yes | Stable manifest identifier declared by the app module. Distinct from `id` (DB primary key) — surface this when discussing the app with the user. |
| description | Yes | Search-friendly summary of what the app does. Null on apps created before descriptions existed; agents are nudged to backfill on the next update_app. |
| displayName | Yes | Human-readable name shown in app listings and headers. |
| recentIssues | No | Runtime failures recorded against the current app revision since it was published (#1133): contract misses, runtime JS errors, CSP violations, and failed API calls. Present only when the current revision has at least one such event — absence means a clean render. The same summary is appended to the text content. Fetch full detail with GET /app/:id/activity. |
| manifestVersion | Yes | Deprecated public contract marker. Canonical apps return null; legacy apps return their stored contract date. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/destructive/openWorld, but the description goes well beyond them: it discloses that the response contains a live embedded UI plus callable API operations, and warns that the `url` field is machine-only and embeds a write-capable access token while `shareUrl` is the user-safe one. That token-handling caveat is a meaningful behavioral disclosure not available anywhere in structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, and every subsequent sentence carries routing or safety information rather than filler. It is dense but slightly long, packing five distinct ideas into one paragraph where minor trimming would still preserve meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although an output schema exists, the description still tells the agent what comes back (embedded UI plus invokable operations) and which return field is safe to hand to a user versus which is token-bearing. For a render tool with a downstream query/mutate workflow, nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There is a single parameter with 100% schema description coverage, so the schema fully documents app_id including its UUID format and pattern. The description's 'by ID' adds no format or constraint detail beyond that, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Fetch an existing app by ID and render it inline') and immediately distinguishes itself from the closest sibling by contrasting rendering against list_apps' metadata-only return. An agent can tell exactly what this does and what it is not.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use ('open, re-render, or continue working with an app the caller already has'), names the prerequisite alternative ('call list_apps first to find the ID'), and points to the downstream alternatives query_app and mutate_app with their read/write split.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_app_buildARead-onlyInspect
Use this to inspect an accepted app build without running or publishing it. Use include_source to recover the exact submitted source. Status remains available after authoring access to the ESM cohort is removed.
| Name | Required | Description | Default |
|---|---|---|---|
| build_id | Yes | Build ID returned by create_app or update_app. | |
| include_source | No | Include the immutable source accepted for this build. |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | |
| url | No | App URL. Present only after successful publication. |
| appId | No | Existing target app ID, or the created app ID after publication. |
| error | No | |
| state | Yes | |
| intent | Yes | The accepted operation: create, update, migrate, restore, or copy. |
| source | No | Exact accepted source, returned only when include_source is true. |
| buildId | Yes | Durable build ID. Use get_app_build to inspect progress or source. |
| attempts | Yes | |
| deadline | Yes | |
| revision | No | Published app source revision. |
| elapsedMs | Yes | |
| lockState | Yes | |
| statusUrl | Yes | Authenticated HTTP status URL for this build. |
| updatedAt | Yes | |
| acceptedAt | Yes | |
| finishedAt | Yes | |
| lockDigest | No | |
| sourceEtag | Yes | ETag identifying the immutable input accepted for this build. |
| inputDigest | Yes | |
| queueDeadline | Yes | |
| activeRevision | No | |
| desiredRevision | No | |
| retryAfterSeconds | No | Wait at least this many seconds before polling again. |
| inspectionExpiresAt | Yes | |
| idempotencyExpiresAt | Yes | |
| resolvedDependencies | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds meaningful behavioral context beyond the readOnlyHint annotation by stating the operation is non-running/non-publishing and that status persists after authoring access is removed. This clarifies side-effect-free inspection and historical availability, though it does not address auth or rate limits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the primary purposeaint, then the include_source guidance, then the persistence guarantee. No filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a full output schema and complete parameter descriptions, the description covers the key operational context: accepted build inspection, no run/publish side effects, source recovery, and availability after authoring access removal. Minor ambiguity with get_app/get_app_source remains unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the description only restates the include_source purpose ('recover the exact submitted source') that the schema already conveys. The description adds little semantic value beyond what the parameter schema provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the operation as inspecting an accepted app build without running or publishing it, which is specific and distinct from generic retrieval. It does not explicitly contrast with sibling tools like get_app or get_app_source, so it stops short of full differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context for when to use the tool ('inspect an accepted app build') and explicitly instructs using include_source to recover the exact submitted source. It does not name alternatives or state when not to use it, but the usage scenario is well defined.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_app_sourceARead-onlyInspect
Return an app's raw persisted source (module, frontend ui script, and styles) plus its current revision for optimistic-concurrency edits. Pass that revision through update_app's expected_revision input. Use this when you need the source to inspect, debug, or edit an app the caller owns.
| Name | Required | Description | Default |
|---|---|---|---|
| app_id | Yes | The app ID (UUID) |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | UUID of the inspected app. |
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| url | Yes | Public app URL without an access token, built from the OWNER's handle when available (friendly form) and the UUID form otherwise. Safe to show the user. Browsers open via session cookie; agents should call get_app to mint a per-app token for write access. |
| source | Yes | Persisted source bundle. The same fields accepted by create_app/update_app. |
| $schema | No | Exact dated canonical manifest schema URL. Omitted for legacy apps. |
| claimed | Yes | True if the app is owned by an authenticated account. False for anonymous apps still in the unclaimed grace window. |
| revision | Yes | Server-owned app source revision. Historical null counters read as 0; new apps start at 1; each successful source write advances it once. Pass this value through `expected_revision` when guarding update_app. |
| expiresAt | Yes | ISO timestamp when an unclaimed app will be hard-deleted. Null for claimed apps. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
| appVersion | Yes | Canonical apps return decimal String(revision). Legacy apps return their stored effective SemVer. |
| manifestId | Yes | Effective manifest id. May differ from `sourceManifestId` when a conflicting id was rewritten on save. |
| description | Yes | Search-friendly summary set by the creating agent. Null on apps created before descriptions existed. |
| displayName | Yes | Human-readable name from the manifest. |
| capabilities | Yes | Capability declarations parsed from the app manifest. |
| manifestVersion | Yes | Deprecated public contract marker. Canonical apps return null; legacy apps return their stored contract date. |
| sourceManifestId | Yes | Manifest id as authored in the persisted module source. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safe-read profile is conveyed. The description adds value beyond that by disclosing that the result includes a revision token intended for optimistic-concurrency edits and by naming the next-step integration with update_app. Failure/auth behavior is missing, but it is a minor complete a minor gap given annotations and output schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, each earning its place: first defines the exact return current revision, second gives the downstream update_app instruction, third states the when-to-use context. No filler or repeated annotation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter read tool with a known output schema, this description covers the return contents, the purpose of the revision, the downstream update call, and the caller-ownership condition. Failure/error behavior (e.g., invalid app_id or non-owned app) is omitted, but calling the tool correctly does not require that.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%: the sole required app_id is already fully documented as a UUID in the schema. The description contributes fewer intro fields names (e.g., module, ui script, styles, revision) to provide context, but does not add per-parameter syntax or constraints. With high schema coverage, baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description's verb 'Return' and resource specification is precise: 'raw persisted source (module, frontend ui script, and styles)' plus 'current revision for optimistic-concurrency edits.' It further distinguishes this tool from siblings by naming the downstream consumer (update_app) and use cases (inspect, debug, edit), so no other schema or description is needed to tell it apart.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states when to use this tool: 'Use this when you require the source to inspect, debug, or edit an app the caller owns.' It also gives concrete downstream instruction by having the returned revision passed into update_app's expected_revision. It does not enumerate alternative tools or when not to use it, but the context is clear and actionable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_appsARead-onlyInspect
List the caller's Charming apps — owned ones plus apps shared with them (accepted invites, marked [shared with you]; a read-only share also reads (view-only)). Use this to find an app's id before fetching, editing, or calling it. Returns per app: id, revision, role (owner | collaborator | end-user | viewer | team-admin | team-member), displayName, description (a search-friendly summary), url (machine API base), shareUrl (the link to give the user), lastUpdatedAt (ISO-8601 of the last write), claimed (always true for now — exposed for forward-compat), and capabilities (imports + exports). A collaborator can edit and run a shared app; an end-user can run it and write its data but cannot edit the app source; a viewer can only open and read it. The description disambiguates apps with similar display names; capabilities.exports lists each app's callable operations, exposing the cross-app integration surface without N+1 get_app_source calls. Paginate with cursor from a prior call's nextCursor line. Apps with description: null predate descriptions and have not yet been backfilled. When telling the user where to open or share an app, always give them shareUrl — never construct links for humans from the url field.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max apps to return (1-100, default 50). | |
| cursor | No | Opaque cursor from a prior call's `nextCursor` to fetch the next page. |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| apps | Yes | Apps on this page, ordered by creation time newest-first. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
| nextCursor | No | Opaque cursor for the next page. Pass back as `cursor` on the next list_apps call. Absent on the final page. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Rich behavioral detail beyond readOnlyHint: includes owned+shared scoping, role semantics (collaborator/end-user/viewer), read-only share convention, url vs shareUrl distinction, and forward-compat note on description. This goes well beyond a generic 'list' description.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with core behavior and guidance on when to call it/newline; very detailed but each sentence adds information. Slightly long and field-heavy, but no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Since there is no output schema, the description compensates by enumerating the returned fields and their semantics and tells the agent how to use the result (capabilities for integration, shareUrl for sharing). Missing explicit error scenarios or permission boundaries, but strong overall.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is high (limit is described with max and default). The description adds meaningful cross-field guidance: cursor-based pagination uses the prior call's nextCursor, which clarifies a relationship not inferable from the parameter list alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Begins with a specific verb and resource: 'List the caller's Charming apps — owned plus shared.' It clearly defines the scope (owned and shared, with view-only shared items noted) and states the primary use case (finding an app's id before fetch/edit/call). This is unambiguous and distinct from sibling tools like get_app or get_app_source.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs agents to use this to get an app's ID before fetching, editing, or calling itaine — a clear decision rule. It also implies the cross-tool benefit of capabilities.exports for discovering callable operations without separately using other toolsaint list_app_shares or get_app_source. However, it doesn't explicitly state when NOT to use this tool vs. siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_feedbackARead-onlyInspect
List feedback rows for the caller's apps. Use this to review feedback previously recorded for an app. When app_id is set, returns rows for that one app (owner-checked); when omitted, returns rows across every app the caller owns. Paginate with cursor from a prior call's cursor field. Newest rows come first.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max rows to return (1-50, default 20). | |
| since | No | Optional inclusive lower bound on `created_at` as an ISO-8601 timestamp. | |
| app_id | No | Optional app ID (UUID) to restrict to. When omitted, returns feedback across every app the caller owns. | |
| cursor | No | Opaque cursor from a prior call's `cursor` to fetch the next page. | |
| category | No | Optional category filter. One of: bug, crash, enhancement, praise, other. |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| items | Yes | Feedback rows ordered by `created_at DESC, id DESC` (newest-first). |
| cursor | Yes | Opaque cursor for the next page. Null when this page is the last. Pass back as `cursor` on the next list_feedback call. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already provide readOnlyHint=true and destructiveHint=false, and the description adds valuable behavior beyond that: owner-checked access, scope broadening when app_id is omitted, ordering (newest first), and cursor-based pagination. This gives the agent a concrete picture of the tool's behavior without contradicting the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences and each one earns its place: statement of purpose, usage guidance, and behavior/pagination details. It is front-loaded with the main verb and resource, and avoids unnecessary flourishes. It could be shortened slightly by merging the first two sentences, but it is still appropriately sized.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present and 5 parameters all documented in the input schema, the description's job is mainly to add scope, pagination, and ordering, which it does. It fully covers the owner-checked behavior and how pagination works with cursor. It doesn't discuss filtering capabilities like since or category, but those are already in the schema, so no meaningful gap remains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds only a little meaning beyond the schema: it reframes app_id's owner-scoping in prose and implicitly communicates cursor usage, but the schema already explains each parameter in similar terms. No substantial extra semantics are added for limit, since, or category.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('List feedback rows') and clarifies that it targets the caller's apps, which is clear and distinct enough. It does not explicitly distinguish itself from sibling tools like list_feedback_responses or acknowledge_feedback_responses, but the focus on 'feedback rows' plus the caller scope communicates the purpose effectively.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear when-to-use guidance: 'Use this to review feedback previously recorded for an app.' It also explains the two major usage modes (with app_id vs. without), giving users the key behavioral switch without needing to read the schema. It does not name alternatives or explicitly say when not to use it, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_feedback_responsesARead-onlyInspect
Fetch unread Charming staff responses to feedback submitted by this authenticated account. Use this when a feedback-responses-available advisory appears. Responses are ordered oldest-first. Reading does not acknowledge them; call acknowledge_feedback_responses with the returned response IDs after processing them.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max responses to return (1-50, default 20). | |
| cursor | No | Opaque cursor returned by the prior page. |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| items | Yes | Unread staff responses ordered by `created_at ASC, id ASC`. |
| cursor | Yes | Opaque cursor for the next oldest-first page. Null on the final page. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark readOnlyHint=true, but the description adds meaningful behavioral context: responses are unread, ordered oldest-first, and reading does not acknowledge them. The note that acknowledge_feedback_responses must be called separately prevents a common misuse, going well beyond structured annotation data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tightly scoped sentences deliver the core purpose, usage trigger, ordering, non-acknowledgment behavior, and follow-up instruction with no filler. Key operational facts are front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a full output schema, 100% parameter schema coverage, and annotations carrying safety semantics, everything an agent needs is present: when to call, what it returns semantically, ordering, and post-processing action. Nothing important is left to inference.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Input schema description coverage is 100%, so the schema already documents limit and cursor. The description adds context on response ordering and the acknowledgment follow-up, but no additional parameter-level semantics beyond what the schema provides; baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Fetch') and resource ('unread Charming staff responses to feedback'), clearly distinguishing this read tool from the sibling acknowledge_feedback_responses. It also conveys the unique unread/acknowledgment semantics that separate it from other feedback tooling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly tells the agent when to use the tool ('when a feedback-responses-available advisory appears') and instructs the follow-up action of calling acknowledge_feedback_responses after processing. This gives clear, decisive routing guidance relative to its sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_routinesARead-onlyInspect
Use this to see the caller's Routines across their apps.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| routines | Yes | The caller's Routines across their apps. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the description only adds caller scope and cross-app aggregation. It does not describe pagination, sorting, or other behavior, but for a simple read-only list with an output schema this is acceptable. No contradiction exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence with no filler, front-loading the action and scope. Every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a parameterless read-only listing tool with annotations and an output schema, the description covers the essential semantic scoping (caller, across apps). It is complete enough, though it does not explicitly discuss output contents or alternative tools.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and schema coverage is effectively complete; there is nothing for the description to add. The baseline for zero-parameter tools is 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('see') and resource ('caller's Routines across their apps'), making the tool's purpose clear. It is distinguishable from list_apps by targeting routines, though it does not explicitly name sibling alternatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description directly tells the agent when to use it ('Use this to see...'), but it does not provide guidance on alternatives or exclusions. Usage context is implied rather than fully explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mutate_appADestructiveInspect
Run a mutating backend operation on a Charming app and return its result, without editing code. Use this for operations that change state — including operations whose read/write safety can't be determined. For read-only operations, use query_app. If you don't know an operation's name or which tool it needs, call get_app — each operation lists its tool.
| Name | Required | Description | Default |
|---|---|---|---|
| op | Yes | Operation name, routed to /api/:op inside the app module | |
| app_id | Yes | The app ID (UUID) | |
| params | No | Optional JSON params |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | True if the operation handler returned a successful envelope. False when the app reported an operation error — distinct from a transport/protocol error. |
| error | No | Set when ok=false. Always inspect kind before retrying. |
| value | No | Operation return value when ok=true. Shape depends on the app and op called. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag readOnlyHint=false and destructiveHint=true; the description adds useful nuance by covering operations whose read/write safety cannot be determined, stating that it returns the operation result, and clarifying that it does not edit code. No contradictions with the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences: the primary action, the selection rule for when to use it, and the disambiguation fallback via get_app. Front-loaded and without redundant schema repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description fully resolves the key agent decision—when to choose this tool over query_app and how to resolve unknown operations via get_app. With an output schema present and only three parameters, no further information is needed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so parameters are already fully described. The description reinforces that op is an operation name and that params are part of the operation, but it doesn't add much beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and object: 'Run a mutating backend operation on a Charming app and return its result, without editing code.' It is immediately distinguishable from read-only siblings, and it tells the agent that this tool is for state-changing work rather than source editing.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly directs agents to use this tool for state-changing operations, including ambiguous ones, and to use query_app for read-only operations. It also tells agents to call get_app when the operation or tool is unknown, covering the main routing ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
query_appARead-onlyInspect
Run a read-only backend operation on a Charming app and return its result, without editing code. Use this for operations that only read state. For operations that change state, use mutate_app. If you don't know an operation's name or which tool it needs, call get_app — each operation lists its tool.
| Name | Required | Description | Default |
|---|---|---|---|
| op | Yes | Operation name, routed to /api/:op inside the app module | |
| app_id | Yes | The app ID (UUID) | |
| params | No | Optional JSON params |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | True if the operation handler returned a successful envelope. False when the app reported an operation error — distinct from a transport/protocol error. |
| error | No | Set when ok=false. Always inspect kind before retrying. |
| value | No | Operation return value when ok=true. Shape depends on the app and op called. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The readOnlyHint and destructiveHint annotations already communicate the safe, read-only nature, and the description reinforces this with 'read-only' and 'without editing code.' However, the description does not add much beyond the annotations, such as auth requirements, error behavior, or rate limits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded: the core purpose and read-only constraint appear in the first sentence. The following sentences give essential routing guidance without filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description does not need to explain return values. The description, annotations, and schema together provide everything an agent needs to select the tool, invoke it, and recover from uncertainty about operation names.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the input schema fully documents app_id, op, and params. The description adds minimal parameter-level meaning beyond saying operations are routed and that get_app lists available operations, which is useful context but not required for parameter understanding.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action ('Run a read-only backend operation'), a clear resource ('a Charming app'), and the result ('return its result'). It also explicitly distinguishes itself from mutate_app, making the purpose unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear when-to-use guidance: use this tool for read-only state operations. It explicitly names mutate_app for state-changing operations and get_app as the fallback when the operation name or required tool is unknown. This fully covers selection among siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_docsARead-onlyInspect
Read Charming documentation without web access. Call without arguments to get the docs index, then pass a page path from that index (for example capabilities/data-storage.md). Use this when building an app or looking up the runtime, API, or capability reference. Returns Markdown in chunks of up to 20,000 characters; pass next_offset as offset with the same path to continue. For the full authoring reference, use llms-full.txt. Only public Charming docs can be read. Limited to 60 reads per caller per minute.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | Path relative to /docs/, such as capabilities/data-storage.md or llms-full.txt. A copied docs/ prefix is accepted. Omitted or empty paths default to llms.txt (the docs index). Page slugs without .md also work. No full URLs, query strings, or fragments. | |
| offset | No | Character offset returned as next_offset by the prior read. Defaults to 0. |
Output Schema
| Name | Required | Description |
|---|---|---|
| url | Yes | |
| path | Yes | |
| markdown | Yes | |
| next_offset | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds substantial behavioral details beyond annotations: pagination via offset (chunks of up to 20,000 characters), default to llms.txt when path is omitted, acceptance of paths without .md, rejection of URLs/query strings/fragments, and rate limiting. No contradiction exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is structured efficiently: it starts with the primary purpose and usage flow, then goes into when to use it, return format, limitations, and alternatives. Every sentence adds critical information—no filler. Information is front-loaded so an agent quickly grasps how to call it correctly.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (read-only, 2 params, output schema exists), the description is fully complete. It covers the invocation flow, pagination mechanics, accepted inputs, exclusions, and rate limits. With annotations covering safety and an output schema handling return values, nothing an agent needs is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the schema itself provides detailed descriptions for both 'path' and 'offset'. The description adds valuable semantics: the default path when omitted (llms.txt), that a 'docs/' prefix is accepted, that slugs without .md work, and that offset should be taken from prior next_offset. This goes beyond the schema, though the schema already covers the basics, so it earns a 4 rather than 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Read') and resource ('Charming documentation') with a clear scope: 'without web access'. It also mentions the intended use cases ('building an app or looking up the runtime, API, or capability reference'), which differentiates it from all sibling tools—no other tool reads documentation. This makes the purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides explicit usage guidance: call without arguments to get the index, then pass a page path from that index. It names an alternative for a specific need ('For the full authoring reference, use llms-full.txt'), implying when not to use this tool. It also states limitations like 'Only public Charming docs can be read' and 'Limited to 60 reads per caller per minute', giving clear boundaries.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
rename_appAInspect
Change a Charming app's URL slug — the human-readable, URL-safe name, distinct from its title/displayName and from its machine manifest id. Use this to give an app a different public URL. Titles and slugs are independent: editing the title with update_app does not change the slug; this tool does. Renaming changes the public URL, and the old URL keeps working by redirecting to the new one. Slug rules: lowercase letters, digits, and hyphens; 2-48 chars; may start with a letter or digit; no leading, trailing, or consecutive hyphens. When telling the user where to open or share the renamed app, always give them shareUrl from the result — never the url field, which is machine-only and embeds a write-capable access token.
| Name | Required | Description | Default |
|---|---|---|---|
| app_id | Yes | The app ID (UUID) to rename. | |
| app_name | Yes | The new URL slug. Lowercase letters, digits, and hyphens; 2-48 chars; may start with a letter or digit; no leading/trailing or consecutive hyphens. Input is lowercased and trimmed. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | UUID of the renamed app. |
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| url | Yes | Machine/API URL for the app (stable /app/<uuid> form). Reflects the new slug.. Embeds a write-capable ?t= access token — NEVER show, paste, or send it to the user. Append /api/<op> to its path for out-of-band operation calls. For anything user-facing, use shareUrl instead. |
| appName | Yes | The new live slug for the app. |
| shareUrl | Yes | The link to give humans whenever the user wants to open or share the app. Friendly /<handle>/<app-name> form when the OWNER has a live handle and the app a slug, /app/<uuid> otherwise. Always token-free — safe to show, paste, and send. Not an API base: appending /api/<op> to the friendly form 404s; use `url` for machine calls. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
| previousAppName | Yes | The slug the app had before this rename. Retained as an alias so old URLs keep resolving (a redirect to the new slug). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations, the description explains that renaming changes the public URL, old URLs redirect, and the url field is machine-only with a write-capable token (a serious security-relevant behavioral note). It also implies a mutating operation without destructive side effects, consistent with readOnlyHint=false and destructiveHint=false.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is slightly long, but each sentence contributes meaningful information: identity distinction, redirect behavior, slug rules, and security guidance. The key usage guidance is front-loaded before the slug validation rules.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutating tool with output schema, it fully covers the operation's semantics, parameter meaning, security-relevant urn field warning, and redirect behavior. It even teaches the safe way to share URLs, which closes the loop on how an agent should use the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers both parameters with 100% coverage, including slug constraints and lowercasing/trimming. The description adds semantic value by contrasting the slug with title/displayName and machine id, and ties the result's shareUrl to usage guidance.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the specific operation (
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The text gives direct guidance: it contrasts this tool with update_app by stating that editing the title via update_app does not change the slug, while this tool does. It also specifies the context (giving an app a different public URL) and the fact that the old URL redirects, so an agent can choose correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_templatesARead-onlyInspect
Search the public Charming Template directory by keyword. Use this to check for an existing Template before calling create_app, or when the user asks what Templates exist. Returns the resolved listing title, summary, Markdown detail, category, cover, starter prompt, creator, builder count, canonical Template page, and browser copy URL. The copy URL starts browser navigation where the user chooses to copy and signs in before owned creation; it is not an MCP or REST write. After browser creation, find the copy with list_apps or GET /app and read it with get_app_source or GET /app/:id/source.
| Name | Required | Description | Default |
|---|---|---|---|
| sort | No | `builders` ranks by builders reached; `recent` (default) is newest-listed first. | |
| limit | No | Max templates to return (1-100). | |
| query | No | Case-insensitive match against listing title, summary, detail, category, and starter prompt. |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| templates | Yes | Listed templates matching the query, most relevant first per `sort`. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, and the description reinforces this by explicitly stating the copy URL 'starts browser navigation... it is not an MCP or REST write.' This preempts a likely misreading of the copy URL as a write-capable endpoint.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than average but each sentence earns its place: purpose, usage context, return values, the critical browser-copy caveat, and the follow-up workflow. It is front-loaded with the main action and well organized, though slightly dense.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return-value details are already structured, and the description still adds the essential browser-navigation caveat and the post-copy discovery flow. The tool is fully specified for an agent to select and invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all three parameters are already documented. The description adds high-level purpose ('search by keyword') but does not add substantive parameter-level meaning beyond the schema baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Search the public Charming Template directory by keyword.' It further differentiates from siblings by explicitly positioning it as the pre-create_app check and as the answer to 'what Templates exist,' which separates it from list_apps and create_app.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use guidance: before calling create_app, or when the user asks what Templates exist. It also explains the post-copy workflow with list_apps and get_app_source, effectively telling the agent which sibling tools to use after the browser flow.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_handleAInspect
Change the signed-in user's handle (their username) — the first segment of their friendly app URLs (https://charm.ing/<handle>/<app-name>), so changing it changes the prefix of every app URL they share. Use this to give the current user a different handle. The old handle keeps working by redirecting to the new one, so links already shared stay valid. Acts on the current signed-in user only and takes no user id. Handle rules: lowercase letters, digits, and hyphens; 3-32 chars; must start with a letter; no leading, trailing, or consecutive hyphens; some words are reserved. Rate-limited to 3 changes per rolling 30 days.
| Name | Required | Description | Default |
|---|---|---|---|
| handle | Yes | The desired new handle. Lowercase letters, digits, and hyphens; 3-32 chars; must start with a letter; no leading/trailing or consecutive hyphens. Input is lowercased and trimmed. |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| handle | Yes | The new live handle for the signed-in user. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
| previousHandle | Yes | The handle the user had before this change. Retained as an alias so old friendly URLs (`/<previousHandle>/<app>`) keep resolving (a redirect to the new handle). Empty string when the user had no prior handle. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations, the description discloses that URLs change, the old handle redirects so existing links remain valid, the tool is scoped to the signed-in user, reserved words may apply, and there is a rolling 30-day rate limit. This gives the agent substantial behavioral context that structured annotations do not convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is focused and front-loads the key purpose before adding constraints, but it is somewhat dense and repeats the validation rules already present in the schema. Still, each sentence contributes a distinct fact, so the length is justified.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the simple one-parameter schema, the output schema, and annotations, the description is sufficiently complete: it covers scope, semantics, constraints, side effects, and limits. An agent can call this tool correctly without needing additional edge-case information.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers handle validation and trimming at 100%, so high schema coverage raises the baseline. The description still adds meaning beyond the schema by tying the parameter to the user's app URLs and mentioning reserved words and the rate limit, which affect how the handle value should be chosen.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource: changing the signed-in user's handle, and explains what a handle is and how it affects app URLs. It clearly differentiates this from sibling set_* and rename-style tools by scoping it to the current user and handle specifically.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"Use this to give the current user a different handle" is an explicit usage statement, and the description adds that it acts on the current signed-in user with no user id. It provides clear context and exclusions, though it does not name alternative tools or give a when-not case beyond the current-user scope.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_publicAInspect
Make a Charming app Public: anyone with the URL can open the live hosted App Viewer with no login and read its data. Public visitors cannot change data or source. Use this for a read-only public surface such as a dashboard, menu, schedule, or status page. Surface the returned message verbatim so the user understands the access granted. For write access, share with the end-user role or choose the signed-in audience setting. Template and Listed are separate settings. Idempotent: calling on an already-public app returns the same URL without re-firing analytics. Owner-only; anonymous apps must be claimed first.
| Name | Required | Description | Default |
|---|---|---|---|
| app_id | Yes | The app ID (UUID) to make public |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| message | Yes | Consequence copy the agent should surface verbatim to the user. It states that anyone with the URL can view the live App with no login but cannot change its data or source. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
| public_url | Yes | Public URL for the now-Public App. Friendly `/<handle>/<app-name>` form when available, `/app/<uuid>` otherwise. Anyone can open this URL with no login and view the live App and its data, but cannot change data or source. Free of write-capable `?t=` tokens. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, it discloses that the operation is idempotent and does not re-fire analytics, that only the owner can perform it, and that anonymous apps must be claimed first. It also explains the read-only nature of the public viewer, which is critical side-effect information an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The most important behavior is stated first, and every subsequent sentence adds a distinct operational fact: use case, write-access alternative, separate settings, idempotency, and owner prerequisite. There is no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter tool with an output schema, the description is fully sufficient. It covers prerequisites, side effects, idempotency, use case, and how to interpret the result, leaving no important context for the agent to infer.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single parameter app_id is already fully described by the schema, so the description adds no extra parameter-level meaning. The 100% schema coverage makes the baseline score of 3 appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb phrase ('Make a Charming app Public') and clearly defines the outcome: anyone with the URL can open the app in the viewer without login, but visitors cannot modify data. It is immediately distinguishable from the sibling mutation and sharing tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states when to use this tool (read-only public surface) and when not to (write access should use sharing with the end-user role or the signed-in audience option). It also separates Template and Listed settings, so an agent can rule out alternatives without opening other schemas.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_remixableAInspect
Deprecated: use set_template instead. This metadata-blind alias accepts only app_id, enables Template for people who can already read the source App, and leaves listing metadata and Listed state unchanged. The original is never mutated. Use this when an older workflow requires the deprecated alias; use set_template for new work. Each copy belongs to the signed-in visitor from creation. Idempotent: calling on an already-remixable app returns the same URL without re-firing analytics. Anonymous apps cannot be made remixable; the app must be claimed first.
| Name | Required | Description | Default |
|---|---|---|---|
| app_id | Yes | The app ID (UUID) to share as remixable |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| message | Yes | Consequence copy the agent should surface verbatim to the user when reporting that the share was created. Spells out that other people will get their own remix — the user's data is untouched. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
| public_url | Yes | Canonical `/templates/<handle>/<app-name>` Template page when the owner and App have public names. A person who can already read the source App can choose to create an editable copy after signing in; the original is never mutated. Free of write-capable `?t=` tokens. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond what annotations provide by disclosing that the original is never mutated, listing metadata and Listed state are untouched, each copy belongs to the signed-in visitor from creation, and the call is idempotent with no analytics refire on repeat calls. The deprecated-alias and metadata-blind traits are also important non-obvious behaviors.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than minimal but every clause earns its place: deprecation, replacement, input contract, side effects, ownership, idempotence, and the anonymous-app constraint. It is front-loaded with the most critical routing decision (deprecated, use `set_template`) and then flows into details without repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter, deprecated alias tool with an output schema, the description covers selection guidance, behavioral side effects, idempotence, ownership, and a failure condition (anonymous apps). An agent has enough information to decide whether to call it and what will happen when it does.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already fully documents `app_id` as a UUID, and the description reinforces that this deprecated alias accepts only that single parameter. It adds semantic meaning beyond the schema by clarifying that the parameter is the only input and that the app must have been claimed, which helps avoid misuse.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states this is a deprecated alias for `set_template`, taking only `app_id` and enabling Template for readers of the source App while leaving listing metadata unchanged. It names the replacement sibling tool, making the tool's place among `set_template`, `unset_remixable`, and `share_app` immediately understandable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives an explicit when-to-use rule: use this only when an older workflow requires the deprecated alias, and use `set_template` for new work. It also adds a hard prerequisite—anonymous apps must be claimed first—which is exactly the kind of usage constraint an agent needs before selecting the tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_starter_promptADestructiveInspect
Set or clear an app's authored starter prompt — a short getting-started instruction prefilled in the chat host when a visitor clicks "Open in Claude" or "Open in ChatGPT" on the app, replacing the generic "I'm using this Charming app" body. Use this to frame a shared/remixable app's intended first action for visitors. Write a generic instruction — do NOT embed the app's URL: the app name, description, and URL (and, for unclaimed apps, an access token) are appended automatically at render time, so a hardcoded URL would point every remixer at the template instead of their own copy. Pass starter_prompt: null (or an empty string) to clear and revert to the generic default. Owner-only; anonymous apps must be claimed first. Idempotent (re-setting the same value is a no-op). Length cap: 2000 characters.
| Name | Required | Description | Default |
|---|---|---|---|
| app_id | Yes | The app ID (UUID) whose starter prompt should change. | |
| starter_prompt | Yes | Getting-started instruction the chat host receives when a visitor clicks "Open in Claude" / "Open in ChatGPT". The authored text leads; the app's name, description, URL, and (for unclaimed apps) an access token are appended automatically as context — so write a generic 1–3 sentence instruction (the first action you want the visitor to take, plus any persona/voice), NOT a self-contained prompt and NOT the app's URL. Pass null or "" to clear and revert to the generic default. Max 2000 characters. |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| message | Yes | Consequence copy the agent should surface to the user. Explains where the prompt is rendered (Open in Claude / Open in ChatGPT) and how to clear it. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
| starter_prompt | Yes | The current starter prompt on the app row after this call. Null when the prompt was cleared (or normalized away from whitespace-only input). Otherwise the verbatim authored string. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false, openWorldHint=true, and destructiveHint=true, but the description adds substantial behavioral context beyond those flags: it explains the automatic appending of name/description/URL/access token at render time, the idempotency (re-setting same value is a no-op), the length cap, and the owner-only requirement. It also clarifies that clearing reverts to the generic default. This is rich behavioral disclosure that goes well beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but well-organized, front-loading the core purpose and then covering constraints, clearing behavior, ownership, idempotency, and length cap. Every sentence earns its place, though it is somewhat long. The structure is logical: purpose, usage guidance, anti-pattern warning, clearing semantics, access control, idempotency, and limit. Slightly verbose but not wasteful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (mutation with ownership constraints, clearing semantics, and a critical anti-pattern about URLs), the description covers everything an agent needs to call it correctly: what to write, what to avoid, how to clear, who can call it, and the length limit. The output schema exists, so return values don't need explanation. The sibling list shows many app-related tools, and this description clearly distinguishes set_starter_prompt from them.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents both parameters thoroughly. The description adds value by explaining the render-time auto-append behavior and the 'do NOT embed URL' rule, which directly informs how to fill starter_prompt. It also clarifies the null/empty-string clearing semantics. The only minor gap is that app_id semantics are fully covered by the schema, so the description doesn't add much there, but the starter_prompt guidance is strong.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Set or clear') and resource ('an app's authored starter prompt'), and explains the exact purpose: prefilling a getting-started instruction in the chat host when a visitor clicks 'Open in Claude' or 'Open in ChatGPT'. It clearly distinguishes this from generic app updates by focusing on the starter prompt behavior and even contrasts it with the generic default. This is unambiguous and differentiates the tool from siblings like update_app or mutate_app.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance: 'Use this to frame a shared/remixable app's intended first action for visitors.' It also provides clear exclusions and constraints: do NOT embed the app's URL, write a generic instruction, pass null/empty string to clear, owner-only, anonymous apps must be claimed first, and idempotent behavior. This is comprehensive and leaves little to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_templateAInspect
Enable a Charming Template for people who can already read the source App, and optionally publish it in Charming's public Template directory so anyone can discover and copy it. Listed grants no access to the live source App or its data. Use this when the user wants independent copies or a public Template listing, not access to the live App. Each copy belongs to the signed-in visitor from creation. Optional listing fields are a patch: title, summary, content_markdown, category, cover fields, and gallery change only when supplied. Publishing with listed: true requires a nonblank resulting title and summary; every cover or gallery image needs nonblank alt text and must reference an image asset from this App. Pass listed: true to also mark it for discovery, or listed: false to unlist it. Omitting listed is a patch, not a reset: it leaves the current listed state exactly as it is (an already-listed app stays listed; a never-listed app stays unlisted), so re-affirming that an app is a template never surprises the owner by delisting it. Idempotent: calling with the same effective listed state as the current one returns the same URL without re-firing analytics. Anonymous apps cannot be made into templates; the app must be claimed first.
| Name | Required | Description | Default |
|---|---|---|---|
| title | No | Template listing title; null clears it | |
| app_id | Yes | The app ID (UUID) to publish as a template | |
| listed | No | Whether to also list the app in Charming's public template directory for discovery. `true` lists it, `false` unlists it. Omit this to leave the current listed state untouched — it is NOT a default-false reset, so omitting it never unlists an already-listed app. | |
| gallery | No | Ordered screenshot records; null or an empty array clears the gallery | |
| summary | No | Template listing summary; null clears it | |
| category | No | Template listing category; null clears it | |
| cover_alt | No | Nonblank cover alt text; supply with cover_asset_key | |
| cover_caption | No | Optional cover caption; supply with cover_asset_key | |
| cover_asset_key | No | App asset key for the cover image; null removes the cover | |
| content_markdown | No | Long-form Template listing content; null clears it |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| listed | Yes | Whether the app is currently listed in the public template directory, reflecting the ACTUAL resulting state — not necessarily the call's `listed` input, since an omitted `listed` leaves the prior state unchanged. A template can be copyable without being listed. |
| message | Yes | Consequence copy the agent should surface verbatim to the user when reporting the change. Spells out that other people will get their own copy — the user's data is untouched — and, when listed, that strangers can discover it. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
| public_url | Yes | Canonical `/templates/<handle>/<app-name>` Template page. The URL stays the same while the Template is listed or unlisted. Existing App readers can copy an unlisted Template; anyone can copy it when Listed. The original is never mutated. Free of write-capable `?t=` tokens. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description discloses unique behavioral traits: the difference between 'listed' and live access, that each copy belongs to the signed-in visitor, idempotency, and odd patch semantics for `listed` omission. It adds valuable behavioral context entirely absent from the schema/annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is wordy but each sentence carries a distinct, non-redundant idea—purpose, access scope, usage, ownership, patch behavior, listing requirements, idempotency, anonymous restriction. It is front-loaded with the primary purpose and maintains a logical left-to-right order that maximizes comprehension.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool that both mutates a property and has optional performance community pages, the definition gives all necessary : use cases, side effects, parameter behavior, validation constraints, and an edge case ('Anonymous apps cannot be made into templates'). Output schema being present, no return-value description is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
While schema coverage is 100%, the description enriches parameter understanding with patch semantics ('change only when supplied'), the nonblank title/summary prerequisite for listing, alt text and asset-key validation for images, and the non-reset behavior of omitting `listed`. These constraints are cross-field and not captured by per-parameter schemas.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence names the verb ('Enable') and resource ('Charming Template'), and immediately distinguishes the tool from siblings by noting it is 'not access to the live App or its data'. This clear separation from set_public, share_app, or search_templates is self-evident.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly provides the 'when' in the second sentence: 'Use this when the user wants independent copies or a public Template listing, not access to the live App.' It also supplies a prerequisite and excludes anonymous apps, giving a clear decision boundary between this and the alternative tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
submit_feedbackAInspect
Record agent-authored feedback about a Charming app. Use this to log observed bugs, suggested enhancements, caught crash reports, or qualitative notes. Source is server-enforced to "agent" — agents cannot impersonate user or auto-crash sources. The caller must have app:run access.
| Name | Required | Description | Default |
|---|---|---|---|
| text | No | Free-form feedback body (up to 16384 chars). Optional, but at least one of text / structured_data / crash_data should usually be set so the row is actionable. | |
| app_id | Yes | The app ID (UUID) the feedback is attached to. | |
| category | Yes | Feedback category. One of: bug, crash, enhancement, praise, other. Pass "crash" only when crash_data carries the captured error. | |
| crash_data | No | Required when category=crash. Optional otherwise. | |
| structured_data | No | Optional JSON object with caller-supplied structured context (e.g. route, repro inputs). |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | UUID of the newly-inserted feedback row. |
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| app_id | Yes | UUID of the app the feedback was attached to. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
| created_at | Yes | ISO-8601 timestamp when the row was inserted. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only indicate non-read-only and non-destructive behavior. The description adds meaningful behavioral constraints beyond the annotations: source is server-enforced to 'agent', impersonation of user or auto-crash sources is not possible, and app:run permission is required.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded with the main purpose, followed by behavioral constraints. The first two sentences are slightly redundant ('Record' vs. 'Use this to log...'), but no unnecessary detail is included.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the schema covers parameter details and annotations cover read/write traits, the description adds the crucial server-enforcement and permission context. It doesn't describe response semantics, but the output schema likely covers that; overall it is sufficiently complete for an agent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides full descriptions for all parameters, including category enum values and crash_data requirements. The description does not add additional parameter-level meaning, so the baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb ('record'), the resource ('agent-authored feedback about a Charming app'), and the scope (bugs, enhancements, crash reports, qualitative notes). This is specific enough to distinguish it from sibling tools like list_feedback or acknowledge_feedback_responses.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says when to use the tool ('Use this to log observed bugs, suggested enhancements, caught crash reports, or qualitative notes') and adds the constraint that callers must have app:run access. It does not mention explicit exclusions or alternatives, but the context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unset_publicADestructiveInspect
Make a public Charming app PRIVATE again. Use this to close anonymous access: visitors can no longer open the app without logging in (the URL requires login). Data already written by anonymous visitors is retained. Idempotent: calling on an already-private app is a no-op.
| Name | Required | Description | Default |
|---|---|---|---|
| app_id | Yes | The app ID (UUID) to make private |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| message | Yes | Consequence copy the agent should surface verbatim. Tells the user the app now requires login again; data already written by anonymous visitors is retained. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (destructiveHint=true, readOnlyHint=false), the description adds crucial behavior: anonymous visitor data is retained, the app URL now requires login, and idempotency. These details give an agent a clear picture of the side effects without needing to infer them from the annotation flags.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise sentences with zero filler. The primary purpose is stated first, followed by the behavioral consequence for visitors and data, and finally the idempotency note. Every sentence earns its place and the text is easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter, well-annotated mutation with an output schema, the description covers the key aspects an agent needs: what changes, who is affected, what remains, and edge-case idempotency. There is no obvious missing information that would prevent correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers the single parameter app_id with a description and strong validation. The tool description does not add anything new about the parameter itself—it just reflects the same usage. With 100% schema coverage, the baseline is 3, and the description provides no additional semantic value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific action: making a public Charming app private again. It differentiates itself from siblings like set_public (the inverse) by explicitly explaining the effect on anonymous access and login requirements, leaving no ambiguity about what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description specifies the exact use case: to close anonymous access and require login. It also notes the idempotent no-op behavior on already-private apps. However, it does not explicitly compare to sibling tools like set_public or unset_remixable to state when NOT to use this one, so it falls just short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unset_remixableADestructiveInspect
Deprecated: use unset_template instead (identical effect). Stop allowing remixes of a Charming app. Use this to stop offering copies to new visitors; existing copies survive untouched. Also clears any public template listing, since a listed app must stay copyable. Idempotent: calling on an already-non-remixable app is a no-op.
| Name | Required | Description | Default |
|---|---|---|---|
| app_id | Yes | The app ID (UUID) to stop sharing as remixable |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| message | Yes | Consequence copy the agent should surface verbatim. Tells the user new visitors can no longer remix, while existing remixes are unaffected. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only indicate readOnly=false and destructive=true; the description adds crucial non-obvious behavior: existing copies survive untouched, a public template listing is cleared because listed apps must stay copyable, and the operation is idempotent. These warnings go beyond the annotations and help the agent reason about side effects.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded with the most important guidance: deprecation and the preferred alternative. Each sentence adds distinct value—effect, side effects, and idempotency—without redundancy. This is an exemplary concise definition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the annotations already flag destructive behavior, the description supplies the missing operational details: existing copies survive, template listing is cleared, and repeated calls are harmless. It also names the successor tool, making the overall context complete for an agent deciding whether and how to call it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already fully documents `app_id` as a required UUID with 100% coverage, so the description has little parameter meaning to add. It only refers to 'a Charming app' implicitly through the tool semantics. This is acceptable baseline behavior when the schema carries the parameter documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific action—'Stop allowing remixes of a Charming app'—and immediately clarifies that this tool is deprecated in favor of `unset_template` with an identical effect. It also spells out what the tool does: stop offering copies to new visitors, keep existing copies, and clear any public template listing. This leaves no ambiguity about the tool's role among its many sibling template/remix tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The first sentence gives direct routing guidance: 'Deprecated: use `unset_template` instead (identical effect).' It then specifies when this operation applies ('stop offering copies to new visitors') and notes that an already-non-remixable app can be safely targeted due to idempotency. This is sufficient usage guidance for a single-parameter tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unset_templateADestructiveInspect
Stop treating a Charming app as a template. Use this to turn off copying for new visitors and clear any public listing; existing copies survive untouched. Idempotent: calling on an app that is neither copyable nor listed is a no-op.
| Name | Required | Description | Default |
|---|---|---|---|
| app_id | Yes | The app ID (UUID) to stop publishing as a template |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| message | Yes | Consequence copy the agent should surface verbatim. Tells the user new visitors can no longer copy or discover the app, while existing copies are unaffected. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds meaningful behavioral context beyond the annotations: it explains that existing copies survive untouched, that the operation is idempotent, and that calling it on an app that is neither copyable nor listed is a no-op. This is valuable transparency for a destructive operation. The annotations already declare destructiveHint=true, and the description does not contradict that.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded. The first sentence states the core purpose, the second clarifies the scope, and the third explains idempotency. Every sentence earns its place with no wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with a full schema and an output schema, the description covers the essential behavioral aspects: what is turned off, what survives, and the no-op case. It could arguably mention the output/return value, but the presence of an output schema reduces that burden. The description is complete enough for an agent to invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already provides 100% coverage for the single parameter, app_id, with a clear description ('The app ID (UUID) to stop publishing as a template'). The description does not add additional parameter-level detail, but with full schema coverage, the baseline of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb ('Stop treating'), the resource ('a Charming app'), and the effect ('turn off copying for new visitors and clear any public listing'). It also distinguishes itself from related operations by noting that existing copies survive untouched, which helps an agent understand what this tool does and does not do.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context for when to use this tool: when you want to stop treating an app as a template. It also notes the idempotent behavior, which is useful guidance. However, it does not explicitly name sibling alternatives like set_template or unset_public, so the when-not-to-use guidance is implied rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_appADestructiveInspect
Use this to update an existing Charming app identified by UUID. Existing-contract saves publish synchronously. ESM saves require idempotency_key and expected_revision, return a durable buildId promptly, and keep the last active app running while the build completes. Poll with get_app_build. Two mutually exclusive input shapes: (A) full-source — pass module/ui/styles to replace those buckets, following the same authoring rules as create_app; (B) edits[] — exact-string find/replace operations plus expected_revision from your last source read. If the app's behavior changed materially (new operation, new data tracked, new use-case), also pass an updated description so list_apps stays accurate; backfill one if an older app has none. For synchronous results, give the user shareUrl to open or share the app; the url field is machine-only and embeds a write-capable access token.
| Name | Required | Description | Default |
|---|---|---|---|
| ui | No | Full-source path only. Optional replacement frontend JavaScript. Follow the create_app rules for the selected contract. Existing-contract UI must (a) populate #app innerHTML BEFORE attaching event listeners, and (b) call operations as `window.charming.api('<manifest-id>').<opName>(input)` (operation name = method name, strips the { ok, value } envelope — you receive the value only, never ok) — NOT `api.operation(name, params)` and NOT raw fetch(). The signed-in caller is exposed synchronously as `window.charming.user` (`{ id, handle?, name?, image? }`, or `null` for an anonymous visitor) — read `user.name` to greet, attribute, or personalize, instead of asking the user to type their name. PUBLIC fields only (never email); it is a convenience signal, not enforcement. The outermost container MUST fill the viewport — use `<main class="min-h-screen">` (or a grid/flex layout that spans width) as the root shell. Do NOT wrap the root in `max-w-md`, `max-w-2xl`, or `container mx-auto`: those cap the entire app to a narrow central column with wide empty margins on 2K+ monitors. Cap the reading measure on an INNER wrapper only for text-heavy views, e.g. `<main class="min-h-screen"><div class="mx-auto max-w-2xl">…</div></main>`. Mutually exclusive with `edits`. | |
| edits | No | Edits path. Array of exact-string find/replace operations applied atomically. Pass the last-read revision through `expected_revision` to gate optimistic concurrency. Mutually exclusive with full-source fields (`module` / `ui` / `styles`). | |
| app_id | Yes | The app ID (UUID) to update | |
| module | No | Full-source path only. Optional replacement ES module source. Same selected contract as create_app. For the existing contract, declare capabilities.imports including "charming:storage/kv@1.0" for persistence. For ESM, keep the exact ESM schema and import { kv } from "charming:storage/kv@1.0"; omit capabilities. A route handler in `export const routes = [...]` receives `(input, { env, ctx, request })` and returns exactly the value declared by `outputSchema`; for an array schema, use `handler: async (_input, { env }) => (await env.storage.get("key")) ?? []`. Charming creates the transport envelope. Do not add a `{ ok, value }` or `{ value }` envelope unless those fields belong to `outputSchema` itself. A named context reads storage as `context.env.storage`. The optional unmatched-request fallback has the separate signature `export default { fetch(request, env, ctx) { ... } }`, where the second argument is the environment itself; Charming supplies a generic 404 handler when it is absent. `env.user` is always present, not gated by any import: the caller's public identity (`{ id, handle?, name?, image? }`) or `null`. It lives only on `env` — read `env.user` (or `context.env.user`); there is no `ctx.user`. Full reference: call read_docs with path `llms-full.txt` (https://charm.ing/docs/llms-full.txt). Keep persisted state in backend storage (env.storage for the existing contract, imported kv for ESM); do not move it into localStorage/sessionStorage/IndexedDB, which are empty inside Claude/ChatGPT and lose the data. env.storage stores JSON-compatible values directly; do not JSON.stringify before put or JSON.parse after get. Mutually exclusive with `edits`. Migrating a legacy app to the existing dated contract requires the complete canonical manifest with the exact dated `$schema: "https://charm.ing/schema/app-manifest/2026-07-31.json"` plus `migrate_contract: true`; on this full-source path only, an omitted `$schema` is inserted automatically, but a wrong one still fails. ESM migration instead selects `https://charm.ing/schema/app-manifest/2026-09-05.json` through full-source or edits and requires `migrate_contract: true`, `expected_revision`, and `idempotency_key`. | |
| styles | No | Full-source path only. Optional replacement CSS. Mutually exclusive with `edits`. | |
| description | No | Optional replacement description. Omit to preserve the existing description, pass `null` to clear, pass a string to replace. Same shape and intent as create_app.description (1-2 sentences covering purpose + key actions + synonyms). Limit 500 characters. | |
| idempotency_key | No | Required for ESM saves. Retry the exact request with the same key; use a new key and current expected_revision for a new save. | |
| expected_version | No | Deprecated compatibility input. Use `expected_revision`; while legacy hosts remain active, expected_version maps to the same revision precondition. | |
| migrate_contract | No | Set true only when replacing a legacy app with a complete valid dated manifest and routes contract. Source submissions do not roll back contracts. History can explicitly restore a retained validated existing-contract revision. | |
| expected_revision | No | Revision precondition. Required for every ESM save and for existing-contract edits[]. Pass revision from your last get_app_source response. A new request against a stale desired revision fails with revision_mismatch; an identical idempotent retry returns its original build. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | UUID of the created or updated app. |
| ok | Yes | |
| url | No | App URL. Present only after successful publication. |
| icon | No | The effective home-screen icon stored for the app. Canonical source declares it at `manifest.meta.icon`. |
| appId | No | Existing target app ID, or the created app ID after publication. |
| error | No | |
| state | No | |
| intent | No | The accepted operation: create, update, migrate, restore, or copy. |
| source | No | Exact accepted source, returned only when include_source is true. |
| appName | No | URL-safe slug for the app, unique per owner. Distinct from manifestId and displayName, and stable across edits — use rename_app to change it (the title changing does NOT change the slug). |
| buildId | No | Durable build ID. Use get_app_build to inspect progress or source. |
| attempts | No | |
| deadline | No | |
| revision | No | Server-owned app source revision. Historical null counters read as 0; new apps start at 1; each successful source write advances it once. Pass this value through `expected_revision` when guarding update_app. |
| shareUrl | No | The link to give humans whenever the user wants to open or share the app. Friendly /<handle>/<app-name> form when the OWNER has a live handle and the app a slug, /app/<uuid> otherwise. Always token-free — safe to show, paste, and send. Not an API base: appending /api/<op> to the friendly form 404s; use `url` for machine calls. |
| warnings | No | Non-blocking publish feedback (#1126): present when static validation found UI/backend contract mismatches or legacy icon input needed a fallback. The write succeeded; fix the named source field. |
| elapsedMs | No | |
| lockState | No | |
| statusUrl | No | Authenticated HTTP status URL for this build. |
| updatedAt | No | |
| acceptedAt | No | |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
| finishedAt | No | |
| lockDigest | No | |
| sourceEtag | No | ETag identifying the immutable input accepted for this build. |
| inputDigest | No | |
| queueDeadline | No | |
| activeRevision | No | |
| desiredRevision | No | |
| retryAfterSeconds | No | Wait at least this many seconds before polling again. |
| inspectionExpiresAt | No | |
| idempotencyExpiresAt | No | |
| resolvedDependencies | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description richly discloses behavior: synchronous publishing for existing-contract saves, async ESM builds that keep the last active app running, atomic edits with revision gating, and the warning that `url` is machine-only with a write-capable token while `shareUrl` is for users. This goes well beyond the annotations' destructive/openWorld hints and does not contradict them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but front-loaded with the core update purpose and sync/async behavior, and every paragraph adds new guidance (migration, docs reference, sharing semantics). It earns its length for a 10-parameter tool, though it repeats some 'mutually exclusive' information that already lives in the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool this complex, the description covers input shapes, concurrency controls, polling, contract migration, output fields, and a pointer to full docs, while the output schema covers return values. Nothing an agent needs in order to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 100% schema coverage, the schema already documents the parameters, and the description builds on it by explaining when to pass an updated `description`, when `idempotency_key`/`expected_revision` are required, and how `shareUrl` vs `url` relate to the result. It adds cross-cutting semantics without needing to re-document each parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource ('update an existing Charming app identified by UUID') and immediately lays out the two input shapes, so an agent knows what the tool does. It distinguishes from create_app by emphasizing existing apps, but it never differentiates from the sibling mutate_app, so it is not quite a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives concrete when-to-use context: existing-contract saves publish synchronously, ESM saves require idempotency_key/expected_revision and should be polled via get_app_build, and full-source vs edits[] are mutually exclusive. It does not explicitly name alternatives or say when not to use this tool versus mutate_app or other siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_routineAInspect
Use this to change a Routine's interval, or enable/disable it. Re-enable is {enabled: true} — there is no separate re-enable action. Re-enabling also resets the consecutive-failure counter.
| Name | Required | Description | Default |
|---|---|---|---|
| enabled | No | `true` re-enables the Routine; `false` disables it (owner-disabled) | |
| interval | No | New schedule interval | |
| routine_id | Yes | The Routine id (`routine_<uuid>` form) to update |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| routine | Yes | |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations establish that this is a non-read, non-destructive mutation, and the description adds a genuinely useful behavioral detail: re-enabling resets the consecutive-failure counter. This goes beyond the schema and informs the agent of a side effect that affects downstream reasoning.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short, information-dense sentences with no filler. The most important usage instruction is front-loaded, and the inline JSON example communicates the re-enable semantics more efficiently than prose would.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the small parameter set, 100% schema description coverage, existing output schema, and annotations already indicating a safe mutation, the description covers the essential operational and behavioral context. An agent has everything needed to invoke this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds meaning to the `enabled` parameter by making the exact payload shape explicit (`{enabled: true}`) and by explaining the reset behavior on re-enable, which is not inferable from the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific action ('change'), a specific resource ('a Routine'), and narrows the scope to exactly two operations: modifying the interval or toggling enabled state. The re-enable example further clarifies exactly what the tool does, leaving no ambiguity about its role relative to create/delete/list siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use the tool ('Use this to change a Routine's interval, or enable/disable it') and clarifies that re-enabling is done through this same tool ('there is no separate re-enable action'). It doesn't name alternative sibling tools, but the CRUD context makes the selection obvious.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
upload_assetADestructiveInspect
UPLOAD A STATIC ASSET TO AN APP so app code stays small and the app reads it back same-origin. Use this when an app needs a large or static file — an image, PDF, audio clip, or a dataset bigger than a few KB — that would otherwise be inlined into module/ui and blow the 256 KiB source cap. Provide EXACTLY ONE of: sourceUrl (PREFERRED for any binary — the SERVER fetches the remote file, follows redirects, and the bytes never transit this tool call), text (UTF-8 dataset/JSON/CSV — no encoding needed), or dataBase64 (LAST RESORT, tiny binaries only: large base64 arguments can stall inside some MCP clients before ever reaching the server, so keep it under ~16 KB and use sourceUrl for anything bigger). The app reads the asset via window.charming.assets.getUrl(key) (for /) or env.assets.get(key) in its backend (the app must declare charming:storage/blob@1.0 in manifest.capabilities.imports to use env.assets). Caps: 10 MiB/asset, 50 assets/app, 100 MiB/app.
| Name | Required | Description | Default |
|---|---|---|---|
| key | Yes | Asset name, e.g. "dataset_v1.json". URL-safe [a-zA-Z0-9._-], <=128 chars, no leading dot. | |
| text | No | UTF-8 text payload (datasets, JSON, CSV). No base64. | |
| appId | Yes | The app id (app_modules.id) to attach the asset to. Must be an app you own. | |
| sourceUrl | No | https URL the server fetches and stores. Bytes never pass through this tool call. | |
| dataBase64 | No | Base64 of a SMALL binary. Last resort — prefer sourceUrl: large base64 tool arguments can stall in some MCP clients before reaching the server. Keep the encoded string under ~16 KB; the server-side hard cap is 64 KiB decoded. | |
| contentType | No | MIME type. Defaults: text/plain for text, sniffed for sourceUrl, application/octet-stream otherwise. |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | Indicates success. Errors arrive as content with isError:true. |
| key | Yes | The asset key it was stored under. Same value passed in. |
| url | Yes | Same-origin URL serving the asset. Use in <img src>/<a href>/fetch, or read in the backend via env.assets.get(key). Equivalent to window.charming.assets.getUrl(key). |
| appId | Yes | UUID of the asset-owning app. |
| bytes | Yes | Stored byte length of the asset payload after decode/fetch. |
| advisories | No | Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path. |
| contentType | Yes | Effective MIME type stored with the asset (supplied, sniffed from sourceUrl, or defaulted). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description adds meaningful behavioral detail: the server fetches sourceUrl and follows redirects, bytes never transit through the tool call, base64 payloads can stall MCP clients around 16 KB, and caps are 10 MiB/asset, 50 assets/app, 100 MiB/app. It does not disclose what happens when uploading with an existing key, though the destructiveHint annotation is present but not elaborated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense and front-loaded with the core purpose, then immediately moves into how to invoke it, then into retrieval and caps. Every sentence earns its place, although it is still a long single paragraph that could be easier to scan as bullets or as separate paragraphs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the 6-parameter schema, annotations, output schema, and caps, the description covers the contract well: source selection, binary safety, retrieval APIs, and size constraints. The main gap is that it does not explain the overwrite/destructive behavior of reuploading an existing key, and it assumes the reader knows their app ownership requirement is enough.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so baseline is 3, but the description adds selection semantics above the schema: lasering xactly one way, source precedence, and size guidance for source vs base64. Some of this duplicates the parameter-level descriptions in the schema, so not a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening phrase 'UPLOAD A STATIC ASSET TO AN APP' gives a specific verb, resource, and target. It also distinguishes itself from code inlining by mentioning the 256 KiB source cap and static files (images, PDFs, datasets), which an agent can use it aside from sibling app-mutation tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says 'Use this when an app needs a large or static file... would otherwise be inlined into module/ui'. It also tells the agent to provide EXACTLY ONE of sourceUrl/text/dataBase64, and gives clear selection rules ('PREFERRED for any binary', 'LAST RESORT', 'use sourceUrl for anything bigger'). This gives the agent direct guidance on when and how to invoke.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
- Changed
get_app3 fields changed- added
Output schema / properties / api / properties / operations / items / properties / idempotentAdded value: +{ + "description": "True when repeating the call with the same input has no further effect.", + "type": "boolean" +} - added
Output schema / properties / api / properties / operations / items / properties / openWorldAdded value: +{ + "description": "True when the route may reach beyond the app, such as the network. Default true.", + "type": "boolean" +} - changed
Output schema / properties / api / properties / operations / items / requiredPrevious value: -[ - "op", - "method", - "path", - "url", - "readOnly", - "destructive", - "public", - "tool", - "discoveredFrom" -]New value: +[ + "op", + "method", + "path", + "url", + "readOnly", + "destructive", + "idempotent", + "openWorld", + "public", + "tool", + "discoveredFrom" +]
33 tool updates
- Added
acknowledge_feedback_responses - Added
cancel_app_build - Changed
create_app41 fields changed- added
Input schema / properties / expected_revisionAdded value: +{ + "description": "Required when ESM source reuses an existing manifest.id. Pass the desired revision from get_app_source.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +} - added
Input schema / properties / idempotency_keyAdded value: +{ + "description": "Required for ESM builds: 8–128 visible ASCII characters. Retry the exact request with the same key to recover its build; use a new key for changed source.", + "type": "string" +} - added
Input schema / properties / migrate_contractAdded value: +{ + "description": "Set true to migrate an existing legacy manifest.id to ESM, together with expected_revision. Source submissions do not roll back contracts. History can explicitly restore a retained validated existing-contract revision.", + "type": "boolean" +} - changed
Input schema / properties / module / descriptionPrevious value: -"ES module source for the app backend. Export a `manifest`; to persist any state its `capabilities.imports` must include \"buildy:storage/kv@1.0\" (without it `env.storage` is undefined and every read/write throws `storage capability not granted`). All persistence goes through `env.storage` using Workers KV semantics — `get(key)`, `put(key, value)`, `delete(key)`, `list()`; `.set`/`.add`/`.write`/`.setItem`/`.removeItem` do not exist and throw `TypeError: env.storage.<x> is not a function`. env.storage stores JSON-compatible values directly; do not JSON.stringify before put or JSON.parse after get. Use env.storage for ALL persistence — it is the only storage that survives inside Claude/ChatGPT and syncs across devices. Do NOT keep app data or user state in localStorage/sessionStorage/IndexedDB: those APIs are empty inside chat hosts, so their data silently vanishes there (the most common cause of an app that appears not to save). The \"buildy:browser/storage@1.0\" capability (claim-gated) unlocks them but only for throwaway, web-only caching; anything the user expects to keep belongs in env.storage. Export `routes` (recommended) or a default `fetch` handler serving `/api/<opName>`, each returning `{ ok: true, value }` or `{ ok: false, error: { kind, message } }`. Apps that use a sensitive browser capability must declare its import in `capabilities.imports` — \"buildy:browser/microphone@1.0\" (getUserMedia audio), \"buildy:browser/camera@1.0\" (getUserMedia video), \"buildy:browser/geolocation@1.0\", \"buildy:browser/clipboard-read@1.0\" (reading the clipboard), \"buildy:browser/display-capture@1.0\" (getDisplayMedia screen share), \"buildy:browser/midi@1.0\" (Web MIDI, navigator.requestMIDIAccess), \"buildy:browser/device-motion@1.0\" (device orientation/motion: DeviceOrientationEvent/DeviceMotionEvent + iOS requestPermission), \"buildy:browser/ambient-light@1.0\" (ambient light, new AmbientLightSensor), or \"buildy:browser/storage@1.0\" (native client storage — localStorage/sessionStorage/IndexedDB — web-only); access is granted only after the app is claimed/authenticated. To call external HTTPS APIs from backend code, declare \"buildy:network/fetch@1.0\" — backend `fetch` is blocked by default and this enables it (public-only: private/loopback/cloud-metadata addresses stay blocked; claim-gated). Restrict it to the hosts the app needs with `manifest.capabilities.fetchHosts` (concrete https hosts, like imageHosts) — default-deny, any non-listed host is blocked; omit to allow any public host. For an endpoint that needs an API key, declare \"buildy:secrets/fetch@1.0\" to get `env.fetch` (claimed apps only) — a sealed outbound fetch that substitutes `{{secret:NAME}}` references in request HEADER values host-side, so the key never enters app source or the sandbox; the app OWNER sets the value in the dashboard at `/app/<id>/secrets` and the agent only references the NAME. Never embed API keys in source. To render remote images, list each https host in `manifest.capabilities.imageHosts`. To make an image render in ANY host — standalone, ChatGPT, AND Claude inline (their injected CSP blocks a cross-origin `<img src>`) — set the src from `const src = await window.buildy.images.load(remoteUrl)` (it fetches through Charming and returns a `data:` URL every embed CSP allows). `window.buildy.images.proxy(remoteUrl)` returns a same-origin proxy URL that works standalone/ChatGPT but NOT in Claude inline; prefer `images.load(...)` when the app may be embedded. Both enforce the imageHosts allowlist; neither bypasses it."New value: +"Eligible ESM authors may instead select `$schema: \"https://charm.ing/schema/app-manifest/2026-09-05.json\"`, declare target-specific `dependencies.server` and `dependencies.client`, and use ordinary package imports plus exact versioned platform imports. For ESM persistence, use `import { kv } from 'charming:storage/kv@1.0'` and call `kv.get`/`kv.put` inside route handlers; omit `capabilities`. ESM creation takes its name from `manifest.meta.name`; omit `display_name`. For the existing contract, export a strict canonical `manifest` with `$schema: \"https://charm.ing/schema/app-manifest/2026-07-31.json\"`, `id`, and `meta: { name, icon? }`. The server may insert the exact schema URL on create when the rest of the source is canonical. Unknown manifest keys are rejected. To persist state in the existing contract, `capabilities.imports` must include \"charming:storage/kv@1.0\" (without it `env.storage` is undefined and every read/write throws `storage capability not granted`). A route handler in `export const routes = [...]` receives `(input, { env, ctx, request })` and returns exactly the value declared by `outputSchema`; for an array schema, use `handler: async (_input, { env }) => (await env.storage.get(\"key\")) ?? []`. Charming creates the transport envelope. Do not add a `{ ok, value }` or `{ value }` envelope unless those fields belong to `outputSchema` itself. A named context reads storage as `context.env.storage`. The optional unmatched-request fallback has the separate signature `export default { fetch(request, env, ctx) { ... } }`, where the second argument is the environment itself; it adds no discoverable route metadata. `env.user` is always present, not gated by any import: the caller's public identity (`{ id, handle?, name?, image? }`) or `null`. It lives only on `env` — read `env.user` (or `context.env.user`); there is no `ctx.user`. Full reference: call read_docs with path `llms-full.txt` (https://charm.ing/docs/llms-full.txt). For the existing contract, persistence goes through `env.storage` using Workers KV semantics — `get(key)`, `put(key, value)`, `delete(key)`, `list()`; `.set`/`.add`/`.write`/`.setItem`/`.removeItem` do not exist and throw `TypeError: env.storage.<x> is not a function`. env.storage stores JSON-compatible values directly; do not JSON.stringify before put or JSON.parse after get. Use env.storage for ALL persistence — it is the only storage that survives inside Claude/ChatGPT and syncs across devices. Do NOT keep app data or user state in localStorage/sessionStorage/IndexedDB: those APIs are empty inside chat hosts, so their data silently vanishes there (the most common cause of an app that appears not to save). The \"charming:browser/storage@1.0\" capability (claim-gated) unlocks them but only for throwaway, web-only caching; anything the user expects to keep belongs in env.storage. Export a `routes` array with unique `op` values and `handler` functions. Canonical route fields are `inputSchema`, `outputSchema`, and `annotations`; `method` defaults to `POST`, `path` defaults to `/api/<op>`, input defaults to a closed empty-object schema, and `public` defaults to true. Set all four MCP annotation hints when their defaults do not fit; Charming does not infer them from the HTTP method. A default `fetch` handler is an unmatched-request fallback only and is not discoverable. The following capability declarations apply only to the existing contract. Apps that use a sensitive browser capability must declare its import in `capabilities.imports` — \"charming:browser/microphone@1.0\" (getUserMedia audio), \"charming:browser/camera@1.0\" (getUserMedia video), \"charming:browser/geolocation@1.0\", \"charming:browser/clipboard-read@1.0\" (reading the clipboard), \"charming:browser/display-capture@1.0\" (getDisplayMedia screen share), \"charming:browser/midi@1.0\" (Web MIDI, navigator.requestMIDIAccess), \"charming:browser/device-motion@1.0\" (device orientation/motion: DeviceOrientationEvent/DeviceMotionEvent + iOS requestPermission), \"charming:browser/ambient-light@1.0\" (ambient light, new AmbientLightSensor), or \"charming:browser/storage@1.0\" (native client storage — localStorage/sessionStorage/IndexedDB — web-only); access is granted only after the app is claimed/authenticated. To call external HTTPS APIs from backend code, declare \"charming:network/fetch@1.0\" and list each exact origin in `manifest.permissions.server.fetch`; both are required and public-only. For an endpoint that needs an API key, declare \"charming:secrets/fetch@1.0\" to get `env.fetch` (claimed apps only) — a sealed outbound fetch that substitutes `{{secret:NAME}}` references in request HEADER values or query-parameter VALUES host-side (never a parameter name, the host, path, fragment, or body), so the key never enters app source or the sandbox; write the placeholder literally in the URL string — `URLSearchParams.set(...)` or `encodeURIComponent(...)` percent-encodes it first and it will NOT resolve; the app OWNER opens App settings, then Secrets, at `/<owner-handle>/~/apps/<app-name>/settings/secrets`, while `/app/<id>/secrets` remains the machine HTTP API. The agent only references the NAME. Never embed API keys in source. To render remote images, list each exact https origin in `manifest.permissions.browser[\"img-src\"]`. To make an image render in ANY host — standalone, ChatGPT, AND Claude inline (their injected CSP blocks a cross-origin `<img src>`) — set the src from `const src = await window.charming.images.load(remoteUrl)` (it fetches through Charming and returns a `data:` URL every embed CSP allows). `window.charming.images.proxy(remoteUrl)` returns a same-origin proxy URL that works standalone/ChatGPT but NOT in Claude inline; prefer `images.load(...)` when the app may be embedded. Both enforce the declared origins; neither bypasses them." - added
Input schema / properties / team_idAdded value: +{ + "description": "Optional destination team id. Only a team owner or admin can create an App there. Omit it to create a personal App.", + "type": "string" +} - changed
Input schema / properties / ui / descriptionPrevious value: -"Frontend JavaScript rendered into #app. The #app mount point starts empty, so (a) populate its innerHTML before attaching event listeners, and (b) call operations as `window.buildy.api('<manifest-id>').<opName>(input)` (operation name = method name, strips the { ok, value } envelope — you receive the value only, never ok) — not `api.operation(name, params)` and not raw fetch(), which fails in the null-origin srcdoc iframe. The signed-in caller is exposed synchronously as `window.buildy.user` (`{ id, handle?, name?, image? }`, or `null` for an anonymous visitor) — read `user.name` to greet, attribute, or personalize, instead of asking the user to type their name. PUBLIC fields only (never email); it is a convenience signal, not enforcement. The outermost container MUST fill the viewport — use `<main class=\"min-h-screen\">` (or a grid/flex layout that spans width) as the root shell. Do NOT wrap the root in `max-w-md`, `max-w-2xl`, or `container mx-auto`: those cap the entire app to a narrow central column with wide empty margins on 2K+ monitors — the loudest \"AI-generated app\" tell. If the view is text-heavy (a note, an article, a form with long prose), cap the reading measure on an INNER wrapper only, e.g. `<main class=\"min-h-screen\"><div class=\"mx-auto max-w-2xl\">…</div></main>`. Dashboards, kanban, tables, canvases, galleries, and split views should use the full width. For live updates when an agent mutates state from another session, register `window.buildy.onStateChange((e) => { ... })` and update the DOM surgically rather than wiping #app. See the charming:app-guide prompt for a canonical example."New value: +"Under the explicit ESM contract, this is an ES module: import declared client packages and import { api, onStateChange } from \"charming:ui/app@1.0\" to call this app and subscribe to its state changes. Under the existing contract, follow the classic JavaScript rules below. Frontend JavaScript rendered into #app. The #app mount point starts empty, so (a) populate its innerHTML before attaching event listeners, and (b) call operations as `window.charming.api('<manifest-id>').<opName>(input)` (operation name = method name, strips the { ok, value } envelope — you receive the value only, never ok) — not `api.operation(name, params)` and not raw fetch(), which fails in the null-origin srcdoc iframe. The signed-in caller is exposed synchronously as `window.charming.user` (`{ id, handle?, name?, image? }`, or `null` for an anonymous visitor) — read `user.name` to greet, attribute, or personalize, instead of asking the user to type their name. PUBLIC fields only (never email); it is a convenience signal, not enforcement. The outermost container MUST fill the viewport — use `<main class=\"min-h-screen\">` (or a grid/flex layout that spans width) as the root shell. Do NOT wrap the root in `max-w-md`, `max-w-2xl`, or `container mx-auto`: those cap the entire app to a narrow central column with wide empty margins on 2K+ monitors — the loudest \"AI-generated app\" tell. If the view is text-heavy (a note, an article, a form with long prose), cap the reading measure on an INNER wrapper only, e.g. `<main class=\"min-h-screen\"><div class=\"mx-auto max-w-2xl\">…</div></main>`. Dashboards, kanban, tables, canvases, galleries, and split views should use the full width. For live updates when an agent mutates state from another session, register `window.charming.onStateChange((e) => { ... })` and update the DOM surgically rather than wiping #app. See the charming:app-guide prompt for a canonical example." - added
Output schema / anyOfAdded value: +[ + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "advisories": { + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" + }, + "appName": { + "description": "URL-safe slug for the app, unique per owner. Distinct from manifestId and displayName, and stable across edits — use rename_app to change it (the title changing does NOT change the slug).", + "type": [ + "string", + "null" + ] + }, + "icon": { + "additionalProperties": false, + "description": "The effective home-screen icon stored for the app. Canonical source declares it at `manifest.meta.icon`.", + "properties": { + "bg": { + "description": "The icon background as a hex color (e.g. \"#1d8a4e\").", + "type": "string" + }, + "emoji": { + "description": "The single emoji rendered on the icon.", + "type": "string" + } + }, + "required": [ + "emoji", + "bg" + ], + "type": "object" + }, + "id": { + "description": "UUID of the created or updated app.", + "type": "string" + }, + "ok": { + "const": true, + "description": "Indicates success. Errors arrive as content with isError:true.", + "type": "boolean" + }, + "revision": { + "description": "Server-owned app source revision. Historical null counters read as 0; new apps start at 1; each successful source write advances it once. Pass this value through `expected_revision` when guarding update_app.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "shareUrl": { + "description": "The link to give humans whenever the user wants to open or share the app. Friendly /<handle>/<app-name> form when the OWNER has a live handle and the app a slug, /app/<uuid> otherwise. Always token-free — safe to show, paste, and send. Not an API base: appending /api/<op> to the friendly form 404s; use `url` for machine calls.", + "type": "string" + }, + "url": { + "description": "Machine/API URL for the app (stable /app/<uuid> form). Embeds a write-capable ?t= access token — NEVER show, paste, or send it to the user. Append /api/<op> to its path for out-of-band operation calls. For anything user-facing, use shareUrl instead.", + "type": "string" + }, + "warnings": { + "description": "Non-blocking publish feedback (#1126): present when static validation found UI/backend contract mismatches or legacy icon input needed a fallback. The write succeeded; fix the named source field.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "ok", + "id", + "url", + "shareUrl", + "revision", + "icon" + ], + "type": "object" + }, + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "acceptedAt": { + "type": "string" + }, + "activeRevision": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "appId": { + "description": "Existing target app ID, or the created app ID after publication.", + "type": "string" + }, + "attempts": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "buildId": { + "description": "Durable build ID. Use get_app_build to inspect progress or source.", + "type": "string" + }, + "deadline": { + "type": "string" + }, + "desiredRevision": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "elapsedMs": { + "minimum": 0, + "type": "number" + }, + "error": { + "additionalProperties": false, + "properties": { + "column": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "kind": { + "type": "string" + }, + "line": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "message": { + "type": "string" + }, + "retryable": { + "type": "boolean" + }, + "specifier": { + "type": "string" + }, + "target": { + "type": "string" + } + }, + "required": [ + "kind", + "message", + "retryable" + ], + "type": "object" + }, + "finishedAt": { + "type": [ + "string", + "null" + ] + }, + "idempotencyExpiresAt": { + "type": [ + "string", + "null" + ] + }, + "inputDigest": { + "type": "string" + }, + "inspectionExpiresAt": { + "type": [ + "string", + "null" + ] + }, + "intent": { + "description": "The accepted operation: create, update, migrate, restore, or copy.", + "type": "string" + }, + "lockDigest": { + "type": "string" + }, + "lockState": { + "enum": [ + "locked", + "unresolved" + ], + "type": "string" + }, + "ok": { + "const": true, + "type": "boolean" + }, + "queueDeadline": { + "type": "string" + }, + "resolvedDependencies": { + "additionalProperties": false, + "properties": { + "client": { + "additionalProperties": { + "type": "string" + }, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "server": { + "additionalProperties": { + "type": "string" + }, + "propertyNames": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "server", + "client" + ], + "type": "object" + }, + "retryAfterSeconds": { + "description": "Wait at least this many seconds before polling again.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "revision": { + "description": "Published app source revision.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "source": { + "additionalProperties": false, + "description": "Exact accepted source, returned only when include_source is true.", + "properties": { + "description": { + "type": [ + "string", + "null" + ] + }, + "module": { + "type": "string" + }, + "styles": { + "type": [ + "string", + "null" + ] + }, + "ui": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "module", + "ui", + "styles", + "description" + ], + "type": "object" + }, + "sourceEtag": { + "description": "ETag identifying the immutable input accepted for this build.", + "type": "string" + }, + "state": { + "enum": [ + "queued", + "resolving", + "building", + "validating", + "published", + "failed", + "superseded", + "canceled", + "expired" + ], + "type": "string" + }, + "statusUrl": { + "description": "Authenticated HTTP status URL for this build.", + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "url": { + "description": "App URL. Present only after successful publication.", + "type": "string" + } + }, + "required": [ + "ok", + "buildId", + "intent", + "state", + "sourceEtag", + "statusUrl", + "attempts", + "acceptedAt", + "updatedAt", + "finishedAt", + "queueDeadline", + "deadline", + "inspectionExpiresAt", + "idempotencyExpiresAt", + "elapsedMs", + "lockState", + "inputDigest" + ], + "type": "object" + } +] - added
Output schema / properties / acceptedAtAdded value: +{ + "type": "string" +} - added
Output schema / properties / activeRevisionAdded value: +{ + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - changed
Output schema / properties / advisories / descriptionPrevious value: -"Structured advisories attached when the tool detected a non-fatal authoring issue (e.g. legacy bridge usage). Each advisory.summary is also appended to the text content for the LLM path."New value: +"Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path." - added
Output schema / properties / appIdAdded value: +{ + "description": "Existing target app ID, or the created app ID after publication.", + "type": "string" +} - removed
Output schema / properties / appName / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / appName / typeAdded value: +[ + "string", + "null" +] - added
Output schema / properties / attemptsAdded value: +{ + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / buildIdAdded value: +{ + "description": "Durable build ID. Use get_app_build to inspect progress or source.", + "type": "string" +} - added
Output schema / properties / deadlineAdded value: +{ + "type": "string" +} - added
Output schema / properties / desiredRevisionAdded value: +{ + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / elapsedMsAdded value: +{ + "minimum": 0, + "type": "number" +} - added
Output schema / properties / errorAdded value: +{ + "additionalProperties": false, + "properties": { + "column": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "kind": { + "type": "string" + }, + "line": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "message": { + "type": "string" + }, + "retryable": { + "type": "boolean" + }, + "specifier": { + "type": "string" + }, + "target": { + "type": "string" + } + }, + "required": [ + "kind", + "message", + "retryable" + ], + "type": "object" +} - added
Output schema / properties / finishedAtAdded value: +{ + "type": [ + "string", + "null" + ] +} - changed
Output schema / properties / icon / descriptionPrevious value: -"The effective home-screen icon stored for the app, after normalization. Read this back to confirm what stuck: if you sent a `manifest.icon` and this is the default `{ emoji: \"🧱\", bg: \"#3b82f6\" }`, your icon was invalid and rejected — see `warnings`."New value: +"The effective home-screen icon stored for the app. Canonical source declares it at `manifest.meta.icon`." - added
Output schema / properties / idempotencyExpiresAtAdded value: +{ + "type": [ + "string", + "null" + ] +} - added
Output schema / properties / inputDigestAdded value: +{ + "type": "string" +} - added
Output schema / properties / inspectionExpiresAtAdded value: +{ + "type": [ + "string", + "null" + ] +} - added
Output schema / properties / intentAdded value: +{ + "description": "The accepted operation: create, update, migrate, restore, or copy.", + "type": "string" +} - added
Output schema / properties / lockDigestAdded value: +{ + "type": "string" +} - added
Output schema / properties / lockStateAdded value: +{ + "enum": [ + "locked", + "unresolved" + ], + "type": "string" +} - removed
Output schema / properties / ok / descriptionRemoved value: -"Indicates success. Errors arrive as content with isError:true." - added
Output schema / properties / queueDeadlineAdded value: +{ + "type": "string" +} - added
Output schema / properties / resolvedDependenciesAdded value: +{ + "additionalProperties": false, + "properties": { + "client": { + "additionalProperties": { + "type": "string" + }, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "server": { + "additionalProperties": { + "type": "string" + }, + "propertyNames": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "server", + "client" + ], + "type": "object" +} - added
Output schema / properties / retryAfterSecondsAdded value: +{ + "description": "Wait at least this many seconds before polling again.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" +} - added
Output schema / properties / revisionAdded value: +{ + "description": "Server-owned app source revision. Historical null counters read as 0; new apps start at 1; each successful source write advances it once. Pass this value through `expected_revision` when guarding update_app.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / sourceAdded value: +{ + "additionalProperties": false, + "description": "Exact accepted source, returned only when include_source is true.", + "properties": { + "description": { + "type": [ + "string", + "null" + ] + }, + "module": { + "type": "string" + }, + "styles": { + "type": [ + "string", + "null" + ] + }, + "ui": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "module", + "ui", + "styles", + "description" + ], + "type": "object" +} - added
Output schema / properties / sourceEtagAdded value: +{ + "description": "ETag identifying the immutable input accepted for this build.", + "type": "string" +} - added
Output schema / properties / stateAdded value: +{ + "enum": [ + "queued", + "resolving", + "building", + "validating", + "published", + "failed", + "superseded", + "canceled", + "expired" + ], + "type": "string" +} - added
Output schema / properties / statusUrlAdded value: +{ + "description": "Authenticated HTTP status URL for this build.", + "type": "string" +} - added
Output schema / properties / updatedAtAdded value: +{ + "type": "string" +} - changed
Output schema / properties / url / descriptionPrevious value: -"Machine/API URL for the app (stable /app/<uuid> form). Embeds a write-capable ?t= access token — NEVER show, paste, or send it to the user. Append /api/<op> to its path for out-of-band operation calls. For anything user-facing, use shareUrl instead."New value: +"App URL. Present only after successful publication." - removed
Output schema / properties / versionRemoved value: -{ - "description": "Monotonic int version of the app. 0 on truly-legacy rows that predate versioning (still editable — pass `expected_version: 0`); otherwise a positive int bumped on every successful update_app/PUT. Pass back as `expected_version` on edits-aware update_app and as `If-Match: \"v<N>\"` on PATCH /app/:id/source.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" -} - changed
Output schema / properties / warnings / descriptionPrevious value: -"Non-blocking publish feedback (#1126): present when static validation found UI/backend contract mismatches, or when a provided `manifest.icon` was invalid and coerced to the default. The write succeeded; fix by adding the backend op, renaming the UI call, or correcting the icon `{ emoji, bg }`."New value: +"Non-blocking publish feedback (#1126): present when static validation found UI/backend contract mismatches or legacy icon input needed a fallback. The write succeeded; fix the named source field." - changed
Output schema / requiredPrevious value: -[ - "ok", - "id", - "url", - "shareUrl", - "version", - "icon" -]New value: +[ + "ok" +]
- Added
create_routine - Changed
delete_app1 field changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +}
- Added
delete_routine - Changed
get_app25 fields changed- added
Output schema / properties / $schemaAdded value: +{ + "description": "Exact dated canonical manifest schema URL. Omitted for legacy apps.", + "enum": [ + "https://charm.ing/schema/app-manifest/2026-07-31.json", + "https://charm.ing/schema/app-manifest/2026-09-05.json" + ], + "type": "string" +} - added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +} - changed
Output schema / properties / api / descriptionPrevious value: -"Full API surface — same shape as `_meta.buildy.api`, but model-facing."New value: +"Full API surface — same shape as `_meta.charming.api`, but model-facing." - changed
Output schema / properties / api / properties / operations / items / properties / input / descriptionPrevious value: -"JSON Schema for the field. Same shape passed to `route.input`/`route.output`."New value: +"JSON Schema for the field. Canonical source declares it as `route.inputSchema` or `route.outputSchema`." - removed
Output schema / properties / api / properties / operations / items / properties / nameRemoved value: -{ - "description": "Back-compat alias of `op`. Read either; prefer `op` for new consumers.", - "type": "string" -} - changed
Output schema / properties / api / properties / operations / items / properties / output / descriptionPrevious value: -"JSON Schema for the field. Same shape passed to `route.input`/`route.output`."New value: +"JSON Schema for the field. Canonical source declares it as `route.inputSchema` or `route.outputSchema`." - removed
Output schema / properties / appName / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / appName / typeAdded value: +[ + "string", + "null" +] - changed
Output schema / properties / appVersion / descriptionPrevious value: -"Author-declared version string for the app (whatever the manifest source set as `manifest.version`, e.g. \"0.0.1\"). Distinct from the concurrency `version` int and from `manifestVersion` (Charming contract date)."New value: +"Canonical apps return decimal String(revision). Legacy apps return their stored effective SemVer." - removed
Output schema / properties / description / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / description / typeAdded value: +[ + "string", + "null" +] - changed
Output schema / properties / manifestVersion / descriptionPrevious value: -"Charming manifest contract date (`YYYY-MM-DD`) the app is pinned to. The runtime branches on this value to pick contract-specific behaviour."New value: +"Deprecated public contract marker. Canonical apps return null; legacy apps return their stored contract date." - changed
Output schema / properties / manifestVersion / typePrevious value: -"string"New value: +[ + "string", + "null" +] - changed
Output schema / properties / recentIssues / descriptionPrevious value: -"Runtime failures recorded against the current app version since it was published (#1133): contract misses, runtime JS errors, CSP violations, and failed API calls. Present only when the current version has at least one such event — absence means a clean render. The same summary is appended to the text content. Fetch full detail with GET /app/:id/activity."New value: +"Runtime failures recorded against the current app revision since it was published (#1133): contract misses, runtime JS errors, CSP violations, and failed API calls. Present only when the current revision has at least one such event — absence means a clean render. The same summary is appended to the text content. Fetch full detail with GET /app/:id/activity." - added
Output schema / properties / recentIssues / properties / counts / properties / externalResourceFailuresAdded value: +{ + "description": "Failed external image requests, including failures caught by app code.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +} - changed
Output schema / properties / recentIssues / properties / counts / requiredPrevious value: -[ - "contractMisses", - "runtimeErrors", - "cspViolations", - "apiFailures", - "appLoadErrors" -]New value: +[ + "contractMisses", + "runtimeErrors", + "cspViolations", + "apiFailures", + "externalResourceFailures", + "appLoadErrors" +] - changed
Output schema / properties / recentIssues / properties / examples / items / properties / category / enumPrevious value: -[ - "contractMisses", - "runtimeErrors", - "cspViolations", - "apiFailures", - "appLoadErrors" -]New value: +[ + "contractMisses", + "runtimeErrors", + "cspViolations", + "apiFailures", + "appLoadErrors", + "externalResourceFailures" +] - changed
Output schema / properties / recentIssues / properties / examples / items / properties / kind / descriptionPrevious value: -"Underlying durable event kind: `diag_report` (runtime errors and CSP violations, distinguished by category), `contract_validation` (contract misses), `api_proxy_result` (API failures), or `app_load_error` (caught load failures)."New value: +"Underlying durable event kind: `diag_report` (runtime errors and CSP violations, distinguished by category), `contract_validation` (contract misses), `api_proxy_result` (API failures), `app_load_error` (caught load failures), or `image_proxy_result` (external image failures)." - added
Output schema / properties / recentIssues / properties / sinceRevisionAdded value: +{ + "description": "The app revision the issues are attributed to (the current revision at call time). Legacy nulls normalize to 0.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - removed
Output schema / properties / recentIssues / properties / sinceVersionRemoved value: -{ - "anyOf": [ - { - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - { - "type": "null" - } - ], - "description": "The app version the issues are attributed to (the current version at call time). Null on legacy rows that predate versioning." -} - changed
Output schema / properties / recentIssues / properties / total / descriptionPrevious value: -"Issue events counted for the current version, capped at 500 — a value of 500 means 500 or more. Use GET /app/:id/activity for the exact, paginated list."New value: +"Issue events counted for the current revision, capped at 500 — a value of 500 means 500 or more. Use GET /app/:id/activity for the exact, paginated list." - changed
Output schema / properties / recentIssues / requiredPrevious value: -[ - "sinceVersion", - "total", - "counts", - "examples" -]New value: +[ + "sinceRevision", + "total", + "counts", + "examples" +] - added
Output schema / properties / revisionAdded value: +{ + "description": "Server-owned app source revision. Historical null counters read as 0; new apps start at 1; each successful source write advances it once. Pass this value through `expected_revision` when guarding update_app.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - removed
Output schema / properties / versionRemoved value: -{ - "description": "Monotonic int version of the app. 0 on truly-legacy rows that predate versioning (still editable — pass `expected_version: 0`); otherwise a positive int bumped on every successful update_app/PUT. Pass back as `expected_version` on edits-aware update_app and as `If-Match: \"v<N>\"` on PATCH /app/:id/source.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" -} - changed
Output schema / requiredPrevious value: -[ - "ok", - "id", - "url", - "shareUrl", - "displayName", - "description", - "manifestId", - "version", - "appVersion", - "manifestVersion", - "api" -]New value: +[ + "ok", + "id", + "url", + "shareUrl", + "displayName", + "description", + "manifestId", + "revision", + "appVersion", + "manifestVersion", + "api" +]
- Added
get_app_build - Changed
get_app_source17 fields changed- added
Output schema / properties / $schemaAdded value: +{ + "description": "Exact dated canonical manifest schema URL. Omitted for legacy apps.", + "enum": [ + "https://charm.ing/schema/app-manifest/2026-07-31.json", + "https://charm.ing/schema/app-manifest/2026-09-05.json" + ], + "type": "string" +} - changed
Output schema / properties / advisories / descriptionPrevious value: -"Structured advisories attached when the tool detected a non-fatal authoring issue (e.g. legacy bridge usage). Each advisory.summary is also appended to the text content for the LLM path."New value: +"Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path." - changed
Output schema / properties / appVersion / descriptionPrevious value: -"Author-declared version string for the app (whatever the manifest source set as `manifest.version`, e.g. \"0.0.1\"). Distinct from the concurrency `version` int and from `manifestVersion` (Charming contract date)."New value: +"Canonical apps return decimal String(revision). Legacy apps return their stored effective SemVer." - changed
Output schema / properties / capabilities / properties / imports / descriptionPrevious value: -"WIT-style capability ids the app requests (e.g. \"buildy:storage/kv@1.0\"). Empty if the app declares none."New value: +"WIT-style capability ids the app requests (e.g. \"charming:storage/kv@1.0\"). Empty if the app declares none." - removed
Output schema / properties / description / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / description / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / expiresAt / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / expiresAt / typeAdded value: +[ + "string", + "null" +] - changed
Output schema / properties / manifestVersion / descriptionPrevious value: -"Charming manifest contract date (`YYYY-MM-DD`) the app is pinned to. The runtime branches on this value to pick contract-specific behaviour."New value: +"Deprecated public contract marker. Canonical apps return null; legacy apps return their stored contract date." - changed
Output schema / properties / manifestVersion / typePrevious value: -"string"New value: +[ + "string", + "null" +] - added
Output schema / properties / revisionAdded value: +{ + "description": "Server-owned app source revision. Historical null counters read as 0; new apps start at 1; each successful source write advances it once. Pass this value through `expected_revision` when guarding update_app.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - removed
Output schema / properties / source / properties / styles / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / source / properties / styles / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / source / properties / ui / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / source / properties / ui / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / versionRemoved value: -{ - "description": "Monotonic int version of the app. 0 on truly-legacy rows that predate versioning (still editable — pass `expected_version: 0`); otherwise a positive int bumped on every successful update_app/PUT. Pass back as `expected_version` on edits-aware update_app and as `If-Match: \"v<N>\"` on PATCH /app/:id/source.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" -} - changed
Output schema / requiredPrevious value: -[ - "ok", - "id", - "url", - "manifestId", - "sourceManifestId", - "displayName", - "description", - "version", - "appVersion", - "manifestVersion", - "capabilities", - "claimed", - "expiresAt", - "source" -]New value: +[ + "ok", + "id", + "url", + "manifestId", + "sourceManifestId", + "displayName", + "description", + "revision", + "appVersion", + "manifestVersion", + "capabilities", + "claimed", + "expiresAt", + "source" +]
- Changed
list_app_shares3 fields changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +} - removed
Output schema / properties / shares / items / properties / accepted_at / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / shares / items / properties / accepted_at / typeAdded value: +[ + "string", + "null" +]
- Changed
list_apps9 fields changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +} - removed
Output schema / properties / apps / items / properties / appName / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / apps / items / properties / appName / typeAdded value: +[ + "string", + "null" +] - changed
Output schema / properties / apps / items / properties / capabilities / properties / exports / descriptionPrevious value: -"Operations this app exposes to other apps via `window.buildy.api(<id>).<export>()`. Use to plan cross-app integrations without round-tripping through get_app_source."New value: +"Operations this app exposes to other apps via `window.charming.api(<id>).<export>()`. Use to plan cross-app integrations without round-tripping through get_app_source." - changed
Output schema / properties / apps / items / properties / capabilities / properties / imports / descriptionPrevious value: -"Capability tokens the app depends on (e.g. \"buildy:storage/kv@1.0\", \"buildy:browser/microphone@1.0\")."New value: +"Capability tokens the app depends on (e.g. \"charming:storage/kv@1.0\", \"charming:browser/microphone@1.0\")." - removed
Output schema / properties / apps / items / properties / description / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / apps / items / properties / description / typeAdded value: +[ + "string", + "null" +] - added
Output schema / properties / apps / items / properties / revisionAdded value: +{ + "description": "Server-owned app source revision. Historical null counters read as 0; new apps start at 1; each successful source write advances it once. Pass this value through `expected_revision` when guarding update_app.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - changed
Output schema / properties / apps / items / requiredPrevious value: -[ - "id", - "role", - "displayName", - "description", - "url", - "shareUrl", - "lastUpdatedAt", - "claimed", - "capabilities" -]New value: +[ + "id", + "role", + "displayName", + "revision", + "description", + "url", + "shareUrl", + "lastUpdatedAt", + "claimed", + "capabilities" +]
- Changed
list_feedback8 fields changed- changed
Input schema / properties / since / patternPrevious value: -"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"New value: +"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$" - added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +} - removed
Output schema / properties / cursor / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / cursor / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / items / items / properties / text / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / items / items / properties / text / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / items / items / properties / user_id / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / items / items / properties / user_id / typeAdded value: +[ + "string", + "null" +]
- Added
list_feedback_responses - Added
list_routines - Changed
mutate_app1 field changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +}
- Changed
query_app1 field changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +}
- Added
read_docs - Changed
rename_app1 field changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +}
- Added
search_templates - Changed
set_handle1 field changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +}
- Changed
set_public3 fields changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +} - changed
Output schema / properties / message / descriptionPrevious value: -"Consequence copy the agent should surface verbatim to the user. WARNS that anyone with the URL can read and overwrite (or wipe) the shared data, with no login and no per-visitor isolation."New value: +"Consequence copy the agent should surface verbatim to the user. It states that anyone with the URL can view the live App with no login but cannot change its data or source." - changed
Output schema / properties / public_url / descriptionPrevious value: -"Public URL for the now-public app. Friendly `/<handle>/<app-name>` form when available, `/app/<uuid>` otherwise. Anyone can open this URL with no login and read AND write the app's SHARED data. Free of write-capable `?t=` tokens."New value: +"Public URL for the now-Public App. Friendly `/<handle>/<app-name>` form when available, `/app/<uuid>` otherwise. Anyone can open this URL with no login and view the live App and its data, but cannot change data or source. Free of write-capable `?t=` tokens."
- Changed
set_remixable2 fields changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +} - changed
Output schema / properties / public_url / descriptionPrevious value: -"Public share URL for the now-remixable app. Friendly `/<handle>/<app-name>` form when available, `/app/<uuid>` otherwise. Visitors opening this URL get their own brand-new editable copy; the original is never mutated. Free of write-capable `?t=` tokens."New value: +"Canonical `/templates/<handle>/<app-name>` Template page when the owner and App have public names. A person who can already read the source App can choose to create an editable copy after signing in; the original is never mutated. Free of write-capable `?t=` tokens."
- Changed
set_starter_prompt3 fields changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +} - removed
Output schema / properties / starter_prompt / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / starter_prompt / typeAdded value: +[ + "string", + "null" +]
- Added
set_template - Changed
share_app3 fields changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +} - changed
Output schema / properties / message / descriptionPrevious value: -"Consequence copy the agent should surface verbatim: the invitee has no access until they accept, and what they will be able to do once they do."New value: +"Consequence copy the agent should surface verbatim: the invitee has no access until they choose Open app, and what their role permits after the durable grant succeeds." - changed
Output schema / properties / status / descriptionPrevious value: -"Sharing creates an invitation, not access. `pending` = the grantee has a Charming account and must accept (dashboard or invite email). `invited` = the email has no account yet; it becomes a pending share once they register and verify that address. `updated` = the grantee already had a share and re-sharing with a `role` changed it in place (no new invitation, no second email)."New value: +"Sharing creates an invitation, not access. `pending` = the grantee has a Charming account and gets access only when that account chooses Open app. `invited` = the email has no account yet; it becomes a pending share once they register and verify that address. `updated` = the grantee already had a share and re-sharing with a `role` changed it in place (no new invitation, no second email)."
- Changed
submit_feedback1 field changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +}
- Changed
unset_public1 field changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +}
- Changed
unset_remixable1 field changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +}
- Added
unset_template - Changed
unshare_app1 field changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +}
- Changed
update_app42 fields changed- changed
Input schema / properties / edits / descriptionPrevious value: -"Edits path. Array of exact-string find/replace operations applied atomically. Pass `expected_version` alongside to gate optimistic-concurrency. Mutually exclusive with full-source fields (`module` / `ui` / `styles`)."New value: +"Edits path. Array of exact-string find/replace operations applied atomically. Pass the last-read revision through `expected_revision` to gate optimistic concurrency. Mutually exclusive with full-source fields (`module` / `ui` / `styles`)." - added
Input schema / properties / expected_revisionAdded value: +{ + "description": "Revision precondition. Required for every ESM save and for existing-contract edits[]. Pass revision from your last get_app_source response. A new request against a stale desired revision fails with revision_mismatch; an identical idempotent retry returns its original build.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +} - changed
Input schema / properties / expected_version / descriptionPrevious value: -"Required when `edits[]` is present. Pass the `version` from your last get_app_source or update_app response. If the stored version has advanced, the edit is rejected with `version_mismatch` and the current version is surfaced so you can re-read and retry."New value: +"Deprecated compatibility input. Use `expected_revision`; while legacy hosts remain active, expected_version maps to the same revision precondition." - added
Input schema / properties / idempotency_keyAdded value: +{ + "description": "Required for ESM saves. Retry the exact request with the same key; use a new key and current expected_revision for a new save.", + "type": "string" +} - added
Input schema / properties / migrate_contractAdded value: +{ + "description": "Set true only when replacing a legacy app with a complete valid dated manifest and routes contract. Source submissions do not roll back contracts. History can explicitly restore a retained validated existing-contract revision.", + "type": "boolean" +} - changed
Input schema / properties / module / descriptionPrevious value: -"Full-source path only. Optional replacement ES module source. Same contract as create_app: must export a `manifest` with capabilities.imports including \"buildy:storage/kv@1.0\" for any app that persists state, plus route handlers that use env.storage.get/put. default.fetch is optional; Charming supplies a generic 404 handler when it is absent. Keep all persisted state in env.storage — do not move it into localStorage/sessionStorage/IndexedDB, which are empty inside Claude/ChatGPT and lose the data. env.storage stores JSON-compatible values directly; do not JSON.stringify before put or JSON.parse after get. Mutually exclusive with `edits`."New value: +"Full-source path only. Optional replacement ES module source. Same selected contract as create_app. For the existing contract, declare capabilities.imports including \"charming:storage/kv@1.0\" for persistence. For ESM, keep the exact ESM schema and import { kv } from \"charming:storage/kv@1.0\"; omit capabilities. A route handler in `export const routes = [...]` receives `(input, { env, ctx, request })` and returns exactly the value declared by `outputSchema`; for an array schema, use `handler: async (_input, { env }) => (await env.storage.get(\"key\")) ?? []`. Charming creates the transport envelope. Do not add a `{ ok, value }` or `{ value }` envelope unless those fields belong to `outputSchema` itself. A named context reads storage as `context.env.storage`. The optional unmatched-request fallback has the separate signature `export default { fetch(request, env, ctx) { ... } }`, where the second argument is the environment itself; Charming supplies a generic 404 handler when it is absent. `env.user` is always present, not gated by any import: the caller's public identity (`{ id, handle?, name?, image? }`) or `null`. It lives only on `env` — read `env.user` (or `context.env.user`); there is no `ctx.user`. Full reference: call read_docs with path `llms-full.txt` (https://charm.ing/docs/llms-full.txt). Keep persisted state in backend storage (env.storage for the existing contract, imported kv for ESM); do not move it into localStorage/sessionStorage/IndexedDB, which are empty inside Claude/ChatGPT and lose the data. env.storage stores JSON-compatible values directly; do not JSON.stringify before put or JSON.parse after get. Mutually exclusive with `edits`. Migrating a legacy app to the existing dated contract requires the complete canonical manifest with the exact dated `$schema: \"https://charm.ing/schema/app-manifest/2026-07-31.json\"` plus `migrate_contract: true`; on this full-source path only, an omitted `$schema` is inserted automatically, but a wrong one still fails. ESM migration instead selects `https://charm.ing/schema/app-manifest/2026-09-05.json` through full-source or edits and requires `migrate_contract: true`, `expected_revision`, and `idempotency_key`." - changed
Input schema / properties / ui / descriptionPrevious value: -"Full-source path only. Optional replacement frontend JavaScript. Same contract as create_app: must (a) populate #app innerHTML BEFORE attaching event listeners, and (b) call operations as `window.buildy.api('<manifest-id>').<opName>(input)` (operation name = method name, strips the { ok, value } envelope — you receive the value only, never ok) — NOT `api.operation(name, params)` and NOT raw fetch(). The signed-in caller is exposed synchronously as `window.buildy.user` (`{ id, handle?, name?, image? }`, or `null` for an anonymous visitor) — read `user.name` to greet, attribute, or personalize, instead of asking the user to type their name. PUBLIC fields only (never email); it is a convenience signal, not enforcement. The outermost container MUST fill the viewport — use `<main class=\"min-h-screen\">` (or a grid/flex layout that spans width) as the root shell. Do NOT wrap the root in `max-w-md`, `max-w-2xl`, or `container mx-auto`: those cap the entire app to a narrow central column with wide empty margins on 2K+ monitors. Cap the reading measure on an INNER wrapper only for text-heavy views, e.g. `<main class=\"min-h-screen\"><div class=\"mx-auto max-w-2xl\">…</div></main>`. Mutually exclusive with `edits`."New value: +"Full-source path only. Optional replacement frontend JavaScript. Follow the create_app rules for the selected contract. Existing-contract UI must (a) populate #app innerHTML BEFORE attaching event listeners, and (b) call operations as `window.charming.api('<manifest-id>').<opName>(input)` (operation name = method name, strips the { ok, value } envelope — you receive the value only, never ok) — NOT `api.operation(name, params)` and NOT raw fetch(). The signed-in caller is exposed synchronously as `window.charming.user` (`{ id, handle?, name?, image? }`, or `null` for an anonymous visitor) — read `user.name` to greet, attribute, or personalize, instead of asking the user to type their name. PUBLIC fields only (never email); it is a convenience signal, not enforcement. The outermost container MUST fill the viewport — use `<main class=\"min-h-screen\">` (or a grid/flex layout that spans width) as the root shell. Do NOT wrap the root in `max-w-md`, `max-w-2xl`, or `container mx-auto`: those cap the entire app to a narrow central column with wide empty margins on 2K+ monitors. Cap the reading measure on an INNER wrapper only for text-heavy views, e.g. `<main class=\"min-h-screen\"><div class=\"mx-auto max-w-2xl\">…</div></main>`. Mutually exclusive with `edits`." - added
Output schema / anyOfAdded value: +[ + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "advisories": { + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" + }, + "appName": { + "description": "URL-safe slug for the app, unique per owner. Distinct from manifestId and displayName, and stable across edits — use rename_app to change it (the title changing does NOT change the slug).", + "type": [ + "string", + "null" + ] + }, + "icon": { + "additionalProperties": false, + "description": "The effective home-screen icon stored for the app. Canonical source declares it at `manifest.meta.icon`.", + "properties": { + "bg": { + "description": "The icon background as a hex color (e.g. \"#1d8a4e\").", + "type": "string" + }, + "emoji": { + "description": "The single emoji rendered on the icon.", + "type": "string" + } + }, + "required": [ + "emoji", + "bg" + ], + "type": "object" + }, + "id": { + "description": "UUID of the created or updated app.", + "type": "string" + }, + "ok": { + "const": true, + "description": "Indicates success. Errors arrive as content with isError:true.", + "type": "boolean" + }, + "revision": { + "description": "Server-owned app source revision. Historical null counters read as 0; new apps start at 1; each successful source write advances it once. Pass this value through `expected_revision` when guarding update_app.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "shareUrl": { + "description": "The link to give humans whenever the user wants to open or share the app. Friendly /<handle>/<app-name> form when the OWNER has a live handle and the app a slug, /app/<uuid> otherwise. Always token-free — safe to show, paste, and send. Not an API base: appending /api/<op> to the friendly form 404s; use `url` for machine calls.", + "type": "string" + }, + "url": { + "description": "Machine/API URL for the app (stable /app/<uuid> form). Embeds a write-capable ?t= access token — NEVER show, paste, or send it to the user. Append /api/<op> to its path for out-of-band operation calls. For anything user-facing, use shareUrl instead.", + "type": "string" + }, + "warnings": { + "description": "Non-blocking publish feedback (#1126): present when static validation found UI/backend contract mismatches or legacy icon input needed a fallback. The write succeeded; fix the named source field.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "ok", + "id", + "url", + "shareUrl", + "revision", + "icon" + ], + "type": "object" + }, + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "acceptedAt": { + "type": "string" + }, + "activeRevision": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "appId": { + "description": "Existing target app ID, or the created app ID after publication.", + "type": "string" + }, + "attempts": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "buildId": { + "description": "Durable build ID. Use get_app_build to inspect progress or source.", + "type": "string" + }, + "deadline": { + "type": "string" + }, + "desiredRevision": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "elapsedMs": { + "minimum": 0, + "type": "number" + }, + "error": { + "additionalProperties": false, + "properties": { + "column": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "kind": { + "type": "string" + }, + "line": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "message": { + "type": "string" + }, + "retryable": { + "type": "boolean" + }, + "specifier": { + "type": "string" + }, + "target": { + "type": "string" + } + }, + "required": [ + "kind", + "message", + "retryable" + ], + "type": "object" + }, + "finishedAt": { + "type": [ + "string", + "null" + ] + }, + "idempotencyExpiresAt": { + "type": [ + "string", + "null" + ] + }, + "inputDigest": { + "type": "string" + }, + "inspectionExpiresAt": { + "type": [ + "string", + "null" + ] + }, + "intent": { + "description": "The accepted operation: create, update, migrate, restore, or copy.", + "type": "string" + }, + "lockDigest": { + "type": "string" + }, + "lockState": { + "enum": [ + "locked", + "unresolved" + ], + "type": "string" + }, + "ok": { + "const": true, + "type": "boolean" + }, + "queueDeadline": { + "type": "string" + }, + "resolvedDependencies": { + "additionalProperties": false, + "properties": { + "client": { + "additionalProperties": { + "type": "string" + }, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "server": { + "additionalProperties": { + "type": "string" + }, + "propertyNames": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "server", + "client" + ], + "type": "object" + }, + "retryAfterSeconds": { + "description": "Wait at least this many seconds before polling again.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "revision": { + "description": "Published app source revision.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "source": { + "additionalProperties": false, + "description": "Exact accepted source, returned only when include_source is true.", + "properties": { + "description": { + "type": [ + "string", + "null" + ] + }, + "module": { + "type": "string" + }, + "styles": { + "type": [ + "string", + "null" + ] + }, + "ui": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "module", + "ui", + "styles", + "description" + ], + "type": "object" + }, + "sourceEtag": { + "description": "ETag identifying the immutable input accepted for this build.", + "type": "string" + }, + "state": { + "enum": [ + "queued", + "resolving", + "building", + "validating", + "published", + "failed", + "superseded", + "canceled", + "expired" + ], + "type": "string" + }, + "statusUrl": { + "description": "Authenticated HTTP status URL for this build.", + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "url": { + "description": "App URL. Present only after successful publication.", + "type": "string" + } + }, + "required": [ + "ok", + "buildId", + "intent", + "state", + "sourceEtag", + "statusUrl", + "attempts", + "acceptedAt", + "updatedAt", + "finishedAt", + "queueDeadline", + "deadline", + "inspectionExpiresAt", + "idempotencyExpiresAt", + "elapsedMs", + "lockState", + "inputDigest" + ], + "type": "object" + } +] - added
Output schema / properties / acceptedAtAdded value: +{ + "type": "string" +} - added
Output schema / properties / activeRevisionAdded value: +{ + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - changed
Output schema / properties / advisories / descriptionPrevious value: -"Structured advisories attached when the tool detected a non-fatal authoring issue (e.g. legacy bridge usage). Each advisory.summary is also appended to the text content for the LLM path."New value: +"Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path." - added
Output schema / properties / appIdAdded value: +{ + "description": "Existing target app ID, or the created app ID after publication.", + "type": "string" +} - removed
Output schema / properties / appName / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / appName / typeAdded value: +[ + "string", + "null" +] - added
Output schema / properties / attemptsAdded value: +{ + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / buildIdAdded value: +{ + "description": "Durable build ID. Use get_app_build to inspect progress or source.", + "type": "string" +} - added
Output schema / properties / deadlineAdded value: +{ + "type": "string" +} - added
Output schema / properties / desiredRevisionAdded value: +{ + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / elapsedMsAdded value: +{ + "minimum": 0, + "type": "number" +} - added
Output schema / properties / errorAdded value: +{ + "additionalProperties": false, + "properties": { + "column": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "kind": { + "type": "string" + }, + "line": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "message": { + "type": "string" + }, + "retryable": { + "type": "boolean" + }, + "specifier": { + "type": "string" + }, + "target": { + "type": "string" + } + }, + "required": [ + "kind", + "message", + "retryable" + ], + "type": "object" +} - added
Output schema / properties / finishedAtAdded value: +{ + "type": [ + "string", + "null" + ] +} - changed
Output schema / properties / icon / descriptionPrevious value: -"The effective home-screen icon stored for the app, after normalization. Read this back to confirm what stuck: if you sent a `manifest.icon` and this is the default `{ emoji: \"🧱\", bg: \"#3b82f6\" }`, your icon was invalid and rejected — see `warnings`."New value: +"The effective home-screen icon stored for the app. Canonical source declares it at `manifest.meta.icon`." - added
Output schema / properties / idempotencyExpiresAtAdded value: +{ + "type": [ + "string", + "null" + ] +} - added
Output schema / properties / inputDigestAdded value: +{ + "type": "string" +} - added
Output schema / properties / inspectionExpiresAtAdded value: +{ + "type": [ + "string", + "null" + ] +} - added
Output schema / properties / intentAdded value: +{ + "description": "The accepted operation: create, update, migrate, restore, or copy.", + "type": "string" +} - added
Output schema / properties / lockDigestAdded value: +{ + "type": "string" +} - added
Output schema / properties / lockStateAdded value: +{ + "enum": [ + "locked", + "unresolved" + ], + "type": "string" +} - removed
Output schema / properties / ok / descriptionRemoved value: -"Indicates success. Errors arrive as content with isError:true." - added
Output schema / properties / queueDeadlineAdded value: +{ + "type": "string" +} - added
Output schema / properties / resolvedDependenciesAdded value: +{ + "additionalProperties": false, + "properties": { + "client": { + "additionalProperties": { + "type": "string" + }, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "server": { + "additionalProperties": { + "type": "string" + }, + "propertyNames": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "server", + "client" + ], + "type": "object" +} - added
Output schema / properties / retryAfterSecondsAdded value: +{ + "description": "Wait at least this many seconds before polling again.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" +} - added
Output schema / properties / revisionAdded value: +{ + "description": "Server-owned app source revision. Historical null counters read as 0; new apps start at 1; each successful source write advances it once. Pass this value through `expected_revision` when guarding update_app.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / sourceAdded value: +{ + "additionalProperties": false, + "description": "Exact accepted source, returned only when include_source is true.", + "properties": { + "description": { + "type": [ + "string", + "null" + ] + }, + "module": { + "type": "string" + }, + "styles": { + "type": [ + "string", + "null" + ] + }, + "ui": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "module", + "ui", + "styles", + "description" + ], + "type": "object" +} - added
Output schema / properties / sourceEtagAdded value: +{ + "description": "ETag identifying the immutable input accepted for this build.", + "type": "string" +} - added
Output schema / properties / stateAdded value: +{ + "enum": [ + "queued", + "resolving", + "building", + "validating", + "published", + "failed", + "superseded", + "canceled", + "expired" + ], + "type": "string" +} - added
Output schema / properties / statusUrlAdded value: +{ + "description": "Authenticated HTTP status URL for this build.", + "type": "string" +} - added
Output schema / properties / updatedAtAdded value: +{ + "type": "string" +} - changed
Output schema / properties / url / descriptionPrevious value: -"Machine/API URL for the app (stable /app/<uuid> form). Embeds a write-capable ?t= access token — NEVER show, paste, or send it to the user. Append /api/<op> to its path for out-of-band operation calls. For anything user-facing, use shareUrl instead."New value: +"App URL. Present only after successful publication." - removed
Output schema / properties / versionRemoved value: -{ - "description": "Monotonic int version of the app. 0 on truly-legacy rows that predate versioning (still editable — pass `expected_version: 0`); otherwise a positive int bumped on every successful update_app/PUT. Pass back as `expected_version` on edits-aware update_app and as `If-Match: \"v<N>\"` on PATCH /app/:id/source.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" -} - changed
Output schema / properties / warnings / descriptionPrevious value: -"Non-blocking publish feedback (#1126): present when static validation found UI/backend contract mismatches, or when a provided `manifest.icon` was invalid and coerced to the default. The write succeeded; fix by adding the backend op, renaming the UI call, or correcting the icon `{ emoji, bg }`."New value: +"Non-blocking publish feedback (#1126): present when static validation found UI/backend contract mismatches or legacy icon input needed a fallback. The write succeeded; fix the named source field." - changed
Output schema / requiredPrevious value: -[ - "ok", - "id", - "url", - "shareUrl", - "version", - "icon" -]New value: +[ + "ok" +]
- Added
update_routine - Changed
upload_asset2 fields changed- added
Output schema / properties / advisoriesAdded value: +{ + "description": "Structured non-fatal advisories, including authoring issues and unread staff feedback responses. Each advisory.summary is also appended to the text content for the LLM path.", + "items": { + "additionalProperties": false, + "properties": { + "data": { + "additionalProperties": {}, + "description": "Kind-specific structured payload. Shape varies per advisory kind.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "doc_url": { + "description": "Optional docs pointer for this advisory kind.", + "type": "string" + }, + "kind": { + "description": "Stable advisory identifier (e.g. \"legacy-bridge\").", + "type": "string" + }, + "severity": { + "description": "Severity; omitted advisories are treated as 'info'.", + "enum": [ + "info", + "warn" + ], + "type": "string" + }, + "summary": { + "description": "Agent-facing summary. Self-sufficient; no extra context required.", + "type": "string" + }, + "userSummary": { + "description": "End-user-facing summary. Set when the advisory should render in-app.", + "type": "string" + } + }, + "required": [ + "kind", + "summary" + ], + "type": "object" + }, + "type": "array" +} - changed
Output schema / properties / url / descriptionPrevious value: -"Same-origin URL serving the asset. Use in <img src>/<a href>/fetch, or read in the backend via env.assets.get(key). Equivalent to window.buildy.assets.getUrl(key)."New value: +"Same-origin URL serving the asset. Use in <img src>/<a href>/fetch, or read in the backend via env.assets.get(key). Equivalent to window.charming.assets.getUrl(key)."
1 tool update
- Changed
update_app1 field changed- changed
Input schema / properties / module / descriptionPrevious value: -"Full-source path only. Optional replacement ES module source. Same contract as create_app: must export a `manifest` with capabilities.imports including \"buildy:storage/kv@1.0\" for any app that persists state, plus a default.fetch handler that routes /api/<opName> requests using env.storage.get/put. Keep all persisted state in env.storage — do not move it into localStorage/sessionStorage/IndexedDB, which are empty inside Claude/ChatGPT and lose the data. env.storage stores JSON-compatible values directly; do not JSON.stringify before put or JSON.parse after get. Mutually exclusive with `edits`."New value: +"Full-source path only. Optional replacement ES module source. Same contract as create_app: must export a `manifest` with capabilities.imports including \"buildy:storage/kv@1.0\" for any app that persists state, plus route handlers that use env.storage.get/put. default.fetch is optional; Charming supplies a generic 404 handler when it is absent. Keep all persisted state in env.storage — do not move it into localStorage/sessionStorage/IndexedDB, which are empty inside Claude/ChatGPT and lose the data. env.storage stores JSON-compatible values directly; do not JSON.stringify before put or JSON.parse after get. Mutually exclusive with `edits`."
1 tool update
- Changed
create_app1 field changed- changed
Input schema / properties / ui / descriptionPrevious value: -"Frontend JavaScript rendered into #app. The #app mount point starts empty, so (a) populate its innerHTML before attaching event listeners, and (b) call operations as `window.buildy.api('<manifest-id>').<opName>(input)` (operation name = method name, strips the { ok, value } envelope — you receive the value only, never ok) — not `api.operation(name, params)` and not raw fetch(), which fails in the null-origin srcdoc iframe. The signed-in caller is exposed synchronously as `window.buildy.user` (`{ id, handle?, name?, image? }`, or `null` for an anonymous visitor) — read `user.name` to greet, attribute, or personalize, instead of asking the user to type their name. PUBLIC fields only (never email); it is a convenience signal, not enforcement. The outermost container MUST fill the viewport — use `<main class=\"min-h-screen\">` (or a grid/flex layout that spans width) as the root shell. Do NOT wrap the root in `max-w-md`, `max-w-2xl`, or `container mx-auto`: those cap the entire app to a narrow central column with wide empty margins on 2K+ monitors — the loudest \"AI-generated app\" tell. If the view is text-heavy (a note, an article, a form with long prose), cap the reading measure on an INNER wrapper only, e.g. `<main class=\"min-h-screen\"><div class=\"mx-auto max-w-2xl\">…</div></main>`. Dashboards, kanban, tables, canvases, galleries, and split views should use the full width. For live updates when an agent mutates state from another session, register `window.buildy.onStateChange((e) => { ... })` and update the DOM surgically rather than wiping #app. See the buildy:app-guide prompt for a canonical example."New value: +"Frontend JavaScript rendered into #app. The #app mount point starts empty, so (a) populate its innerHTML before attaching event listeners, and (b) call operations as `window.buildy.api('<manifest-id>').<opName>(input)` (operation name = method name, strips the { ok, value } envelope — you receive the value only, never ok) — not `api.operation(name, params)` and not raw fetch(), which fails in the null-origin srcdoc iframe. The signed-in caller is exposed synchronously as `window.buildy.user` (`{ id, handle?, name?, image? }`, or `null` for an anonymous visitor) — read `user.name` to greet, attribute, or personalize, instead of asking the user to type their name. PUBLIC fields only (never email); it is a convenience signal, not enforcement. The outermost container MUST fill the viewport — use `<main class=\"min-h-screen\">` (or a grid/flex layout that spans width) as the root shell. Do NOT wrap the root in `max-w-md`, `max-w-2xl`, or `container mx-auto`: those cap the entire app to a narrow central column with wide empty margins on 2K+ monitors — the loudest \"AI-generated app\" tell. If the view is text-heavy (a note, an article, a form with long prose), cap the reading measure on an INNER wrapper only, e.g. `<main class=\"min-h-screen\"><div class=\"mx-auto max-w-2xl\">…</div></main>`. Dashboards, kanban, tables, canvases, galleries, and split views should use the full width. For live updates when an agent mutates state from another session, register `window.buildy.onStateChange((e) => { ... })` and update the DOM surgically rather than wiping #app. See the charming:app-guide prompt for a canonical example."
21 tool updates
- First observed
create_app - First observed
delete_app - First observed
get_app - First observed
get_app_source - First observed
list_app_shares - First observed
list_apps - First observed
list_feedback - First observed
mutate_app - First observed
query_app - First observed
rename_app - First observed
set_handle - First observed
set_public - First observed
set_remixable - First observed
set_starter_prompt - First observed
share_app - First observed
submit_feedback - First observed
unset_public - First observed
unset_remixable - First observed
unshare_app - First observed
update_app - First observed
upload_asset
Related MCP Connectors
Publish AND operate micro-apps from your AI. No account, no API key. Stable URL, EU-hosted.
Host apps built with AI: deploy to a live HTTPS URL, custom domains, secrets and backups.
Host AI-generated single-file HTML apps with JSON persistence. No signup.
Deploy and host the apps your AI assistant builds — a real server, database, and shareable URL.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceInstant secure Full Stack Apps and AI Agents1,190MIT
- AlicenseAqualityCmaintenancePublish live web pages from AI coding agents. Instant shareable URLs for dashboards, landing pages, and reports with password protection.41MIT
- AlicenseAqualityBmaintenanceEnables building and shipping real apps directly from Claude or ChatGPT by scaffolding projects, editing code, running live previews with public share URLs, storing data, sending email and push notifications, deploying to hosting, and exporting finished repos.22MIT
- AlicenseBqualityCmaintenanceEnables AI assistants to scaffold, edit, live-preview, and deploy full-stack apps with built-in database, email, and push notification capabilities through natural language.19MIT
Glama MCP Gateway
Add one secure layer between your agents and this server.