← CharmingCONTENT HISTORY

Update to Charming

Snapshot Sep 30, 2026 · 22:53 UTC · version 1.1.0

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "name": "build-a-charming-app",
  "description": "Create, host, and iterate on a personal interactive web app with Charming — a real URL, persistent storage, and an MCP or HTTP API — from any agent.",
  "included_files": [],
  "skill_md_contents": "---\nname: build-a-charming-app\ndescription: Create, host, and iterate on a personal interactive web app with Charming — a real URL, persistent storage, and an MCP or HTTP API — from any agent.\n---\n\n# Build a Charming app\n\nCharming turns a natural-language description into a hosted web app with a real URL and a database. Use it from any MCP-capable client or over plain HTTP; the HTTP path builds a first app with no signup.\n\n## When to use this\n\nReach for Charming when the user wants a small app that outlives the chat: a tracker, a dashboard, a form, a personal tool. Charming gives it a persistent URL, storage, and an API your agent can keep using and updating across sessions.\n\n## Two surfaces\n\n- **MCP** (Claude, Cursor, Claude Code, and more): add `https://charm.ing/mcp` as an MCP server, then call the tools. ChatGPT uses its own endpoint, `https://charm.ing/mcp/chatgpt`, which carries the inline-widget metadata. Per-client setup strings: https://charm.ing/docs/clients.txt\n- **HTTP**: `POST https://charm.ing/app` with the app source. Full contract: https://charm.ing/.well-known/openapi.json\n\n## The app contract\n\nAn app is one ES module with two named exports, `manifest` and `routes`. Every new app uses this shape on both surfaces.\n\n```js\nexport const manifest = {\n  $schema: 'https://charm.ing/schema/app-manifest/2026-07-31.json',\n  id: 'counter',\n  meta: { name: 'Counter', icon: { emoji: '➕', bg: '#e8611c' } },\n  capabilities: { imports: ['charming:storage/kv@1.0'] },\n};\n\nexport const routes = [\n  {\n    op: 'increment',\n    method: 'POST',\n    path: '/api/increment',\n    title: 'Increment counter',\n    description: 'Add one and return the new value.',\n    inputSchema: { type: 'object', properties: {}, additionalProperties: false },\n    outputSchema: { type: 'object', properties: { count: { type: 'integer' } } },\n    annotations: { readOnlyHint: false },\n    public: true,\n    examples: [{ input: {}, output: { count: 1 } }],\n    handler: async (input, { env, ctx, request }) => {\n      const count = ((await env.storage.get('count')) ?? 0) + 1;\n      await env.storage.put('count', count);\n      return { count };\n    },\n  },\n];\n```\n\n`manifest` is parsed statically, so keep it a plain literal with no computed values. `capabilities` accepts `imports` and nothing else: `charming:storage/kv@1.0` gives `env.storage`, `charming:storage/blob@1.0` gives `env.assets`, `charming:logging/emit@1.0` gives `env.log`, `charming:network/fetch@1.0` gives plain outbound `fetch` and `charming:secrets/fetch@1.0` gives the sealed `env.fetch` that substitutes `{{secret:NAME}}` into headers. Both are claimed-apps-only, and `permissions.server.fetch` gates every backend egress path, plain and sealed alike: list each allowed origin there, because an import without origins leaves egress blocked and origins without the import do too. Remote image origins go in `permissions.browser[\"img-src\"]`. Each origin is a concrete `https://` host. `$schema` may be omitted on create; Charming inserts the dated URL.\n\n`routes` is an array, never an object keyed by operation name. Only `op` and `handler` are required; the rest of the metadata is what Charming reads to route `query_app` versus `mutate_app`, to answer `get_app`, and to build the app's OpenAPI document, so declare a read with `annotations: { readOnlyHint: true }`. A default export is optional: Charming serves the unmatched-path 404 itself.\n\n## Build flow (MCP)\n\n1. `search_templates` — look for an existing template before building from scratch. A hit's `copyUrl` starts browser navigation where the user chooses to copy and signs in before Charming creates the owned App. It is not an MCP or REST write. After creation, use `list_apps` or `GET /app` to find the copy and `get_app_source` or `GET /app/:id/source` to read it.\n2. `create_app` — author a new app from a name plus source (an ES module for logic + UI/styles). The response carries the live URL.\n3. `update_app` — iterate on the source.\n4. `query_app` / `mutate_app` — read and write the app's stored state.\n5. `share_app` — share with specific people.\n6. `set_template` — enable copies for people who can already read the source App. Pass `listed: true` plus a nonblank listing title and summary to let anyone discover and copy it without access to the live App or its data.\n\nGenerated app HTML talks to its backend through the injected `window.charming` API, never raw `fetch`: `const api = window.charming.api('counter'); await api.increment({});`.\n\n## Learn more\n\nOver MCP, read these with `read_docs` rather than fetching them: `read_docs({})` returns the docs index and `read_docs({ path: 'prompts/build-your-first-app.md' })` reads one page. For any `https://charm.ing/docs/...` link below, pass the part after `/docs/` as `path`. A long page returns `next_offset`; pass it back as `offset` with the same path until it is null. The tool comes over the existing MCP connection, so no browser or second connector is needed.\n\n- Start here: https://charm.ing/docs/prompts/build-your-first-app.md\n- Full build manual: https://charm.ing/docs/llms-full.txt\n- Design rules (avoid the generic AI-app look): https://charm.ing/docs/prompts/design-an-app.md\n\n## Auth\n\nMCP always needs a bearer token: a request without one gets a 401 whose `WWW-Authenticate` header points at `https://charm.ing/.well-known/oauth-protected-resource`, and a client that speaks OAuth Dynamic Client Registration bootstraps the token itself through a one-time consent screen. Anonymous creation is the HTTP path only: `POST /app` with no `Authorization` header returns the app plus a one-time `chrm_app_*` token, and an unclaimed app expires 7 days after creation. For an account-scoped `chrm_user_*`, run device pairing: `POST https://charm.ing/api/pair/start`, show the returned `user_code` to the user, then poll `POST https://charm.ing/api/pair/poll`. See https://charm.ing/docs/technical-reference/authentication.md.\n"
}

SHA-256: c2310ba02fa2cea59d575da66335fe17e498b6f2e532ffdcd8cdf82685b887c6