← CharmingCONTENT HISTORY

Update to Charming

Snapshot Oct 8, 2026 · 06:08 UTC · version 1.2.1

Collection source: downloaded plugin package. These snapshots do not have a confirmed matching collection source. Differences in file lists alone do not establish changes to the package.

WHAT CHANGED · RULE-BASED ANALYSIS

Instructions updated for build-a-charming-app

Added instruction text: “## Explain Charming first”. 1 additional added or edited line is in the evidence.

Observed in instructions or declared skills. Runtime behavior has not been tested.

Skill instructions

Before

No removed lines (ordering may have changed)

After

## Explain Charming first If the user is new to Charming or asks how it works, offer: "Want me to make a short video explaining how Charming works, tailored to you?" If they say yes, follow https://charm.ing/docs/prompts/explain-charming...

Compare saved observations

Download comparison JSON
Full technical diff · 1 changed fields

changed /skill_md_contents

BEFORE
"---\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"
AFTER
"---\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## Explain Charming first\n\nIf the user is new to Charming or asks how it works, offer: \"Want me to make a short video explaining how Charming works, tailored to you?\" If they say yes, follow https://charm.ing/docs/prompts/explain-charming-video.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"

SKILL.md line diff

--- before
+++ after
@@ -72,6 +72,10 @@
 - Full build manual: https://charm.ing/docs/llms-full.txt
 - Design rules (avoid the generic AI-app look): https://charm.ing/docs/prompts/design-an-app.md
 
+## Explain Charming first
+
+If the user is new to Charming or asks how it works, offer: "Want me to make a short video explaining how Charming works, tailored to you?" If they say yes, follow https://charm.ing/docs/prompts/explain-charming-video.md.
+
 ## Auth
 
 MCP 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.
Full snapshot data
{
  "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": [],
  "name": "build-a-charming-app",
  "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## Explain Charming first\n\nIf the user is new to Charming or asks how it works, offer: \"Want me to make a short video explaining how Charming works, tailored to you?\" If they say yes, follow https://charm.ing/docs/prompts/explain-charming-video.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 of public snapshot: 6f10b32225a41912994a8e9d792e6b9581c7200b685b1e6cda83d80226a839df