# Prompt to paste into your AI tool

Copy everything below the line into Claude, ChatGPT, Cursor or any other assistant, then describe the app you want. Attach or link `docs/ai/README.md` (the full spec) when the tool can read files or URLs.

---

You are writing an app for a managed hosting platform. Follow these rules exactly; the platform rejects or breaks anything else.

Output: one JavaScript file `app.js`, zero or more SQL files in `migrations/` (`0001_init.sql`, `0002_...sql`), and a `platform.json`. Nothing else. No package.json, no npm packages, no imports.

The file shape:

```js
export default {
  async fetch(req, ctx) {           // HTTP: standard Request in, Response out. Route by hand on new URL(req.url).pathname and req.method.
    return new Response('Not found', { status: 404 });
  },
  jobs: {                           // background work: (ctx, input). Triggered by ctx.jobs.enqueue, a schedule, or an authenticated app route.
    async 'job-name'(ctx, input) {},
  },
  tools: {                          // MCP tools: actions an AI assistant may call. The platform serves them at /mcp.
    tool_name: {
      description: 'What it does, when to use it, what it returns. Written for the assistant.',
      input: { type: 'object', properties: { id: { type: 'string', description: '...' } }, required: ['id'] }, // JSON Schema
      async run(input, ctx) { return 'text, or an object, or { content: [...] }'; },   // throw new Error('...') for a tool error
    },
  },
};
```

Use `tools` when the user wants an "MCP server", "connector", or something "an AI assistant can use". Use `fetch` for pages, forms and webhooks. Both may live in one file. MCP-only servers need no `fetch`; a small page at `/` that explains how to connect is nice to have. Optional `resources: { name: { description, mimeType, async read(ctx) } }` and `prompts: { name: { description, args: [{ name, required }], async get(args, ctx) } }`.

MCP rules: tool names are snake_case verbs; describe every input property; check inputs in code; tools may be called twice, so writes must be safe to repeat; errors are thrown with a message the assistant can act on. With `mcp_access: "users"` the platform logs the caller in and `ctx.user` is set; call `ctx.auth.require()` in tools that touch personal data, `ctx.auth.require('admin')` for owner-only tools. With `mcp_access: "public"` `ctx.user` is null: never return private data without a matching email or order number given as input.

`ctx` is the only way to reach the outside world:
- `ctx.user` -> `{ id, email, roles }` or null. `ctx.auth.require()` forces a login; `ctx.auth.require('admin')` forces a role. Never write your own login, sessions, JWT or password handling.
- `ctx.db.query(sql, ...params)` -> rows; `ctx.db.exec(sql, ...params)` -> `{ changes }`. SQLite dialect, `?` placeholders only. Schema goes in migrations files; never edit an existing migration, add a new numbered one.
- `ctx.kv.get/put(key, stringValue, ttlSeconds?)/delete` -> small strings, caches, settings.
- `ctx.files.put(key, body, contentType)`, `ctx.files.get(key)` -> Response or null, `ctx.files.url(key)`, `ctx.files.delete(key)`. No filesystem exists.
- `ctx.jobs.enqueue(name, input, { delaySeconds?, idempotencyKey? })` -> runs `jobs[name]` later. Jobs may run more than once; make them safe to repeat.
- Standard `fetch(url, init)` calls outside APIs. Read credentials from app secrets such as `ctx.env.API_TOKEN`; never hardcode them. Managed Connections are not available.
- `ctx.env.NAME` -> configuration and secrets as strings.
- `ctx.log.info/warn/error(...)`.

Not available: Node.js modules (fs, path, process, Buffer, http, Node crypto), npm packages, Express/Koa/Fastify, ORMs, long-running processes, setInterval, WebSockets, in-memory state between requests, TypeScript, multiple source files. Use Web APIs: fetch, Request, Response, Headers, URL, FormData, crypto.subtle, TextEncoder, btoa/atob.

Patterns:
- Webhook from an outside system: list its exact POST path in `webhook_paths`, verify the provider signature, store the payload, and enqueue on every retry with a stable `idempotencyKey` even if the payload already exists. Return `new Response('accepted', { status: 202 })`. Do the slow work in the job.
- Login is invitation-only. Role changes and deletion invalidate previous credentials. File URLs use app-wide access, so store per-user private files under `private/` and serve them through an ownership-checked app route.
- Per-user data: store `ctx.user.email` or `ctx.user.id` in a column and filter on it. Admins (`user.roles.includes('admin')`) see all rows.
- Scheduled work: a job plus an entry in `platform.json`: `"schedules": [{ "job": "name", "cron": "0 3 * * *" }]` (five fields, UTC).
- HTML pages: return a string with `content-type: text/html; charset=utf-8`. Forms post to the same app; read them with `await req.formData()`.
- Money in integer cents, timestamps as ISO text, ids as `INTEGER PRIMARY KEY`.

`platform.json`:

```json
{ "name": "my-app", "entry": "app.js",
  "features": { "auth": true, "db": true, "files": false, "jobs": false, "kv": false, "mcp": false },
  "mcp_access": "users", "migrations": "migrations", "schedules": [], "webhook_paths": [] }
```

Set each feature to true only when the code uses it (`mcp` when there is a `tools` object; `auth` when `mcp_access` is `users`). Keep `app.js` under about 300 lines, with short comments in plain words. At the top of the file, list in a comment the secrets (`ctx.env.*`) the app needs.

Before answering, check: one `export default`, no imports, no `app.listen`, every route returns a Response, `?` placeholders in all SQL, every table in a migration, every job and tool idempotent, every tool has a JSON Schema input and an assistant-facing description, no credentials in code.

Now, the app I want:
