An open API service indexing awesome lists of open source software.

https://github.com/kychee-com/run402

MCP server for Run402 — AI-native Postgres + REST + auth + storage + static sites. Pay with x402 USDC on Base. No signups.
https://github.com/kychee-com/run402

ai-agent backend-as-a-service database mcp micropayments model-context-protocol openclaw postgres supabase-alternative x402

Last synced: 27 days ago
JSON representation

MCP server for Run402 — AI-native Postgres + REST + auth + storage + static sites. Pay with x402 USDC on Base. No signups.

Awesome Lists containing this project

README

          


run402 logo

run402 — Postgres, storage & deploys for AI agents

[![Tests](https://github.com/kychee-com/run402/actions/workflows/test.yml/badge.svg)](https://github.com/kychee-com/run402/actions/workflows/test.yml)
[![CodeQL](https://github.com/kychee-com/run402/actions/workflows/codeql.yml/badge.svg)](https://github.com/kychee-com/run402/actions/workflows/codeql.yml)
[![npm: @run402/sdk](https://img.shields.io/npm/v/@run402/sdk?label=%40run402%2Fsdk)](https://www.npmjs.com/package/@run402/sdk)
[![npm: run402](https://img.shields.io/npm/v/run402?label=run402)](https://www.npmjs.com/package/run402)
[![npm: run402-mcp](https://img.shields.io/npm/v/run402-mcp?label=run402-mcp)](https://www.npmjs.com/package/run402-mcp)
[![npm: @run402/functions](https://img.shields.io/npm/v/@run402/functions?label=%40run402%2Ffunctions)](https://www.npmjs.com/package/@run402/functions)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)

[Run402](https://run402.com) gives an agent a full Postgres database, REST API, user auth, content-addressed file storage, static site hosting, serverless functions, and image generation — provisioned with one call, paid with x402 USDC on Base (or Stripe credits). The prototype tier is free on testnet.

This monorepo ships every surface an agent can pick up:

| Package | Use when… |
|---------|-----------|
| [`@run402/sdk`](./sdk/) | Calling Run402 from TypeScript — typed kernel, isomorphic (Node 22 / Deno / Bun / V8 isolates) with a Node entry that auto-loads the local keystore + allowance + x402 fetch |
| [`run402` CLI](./cli/) | Terminal, scripts, CI, agent-controlled shells — JSON in, JSON out, exit code on failure |
| [`run402-mcp`](./src/) | Claude Desktop, Cursor, Cline, Claude Code — core Run402 operations as MCP tools |
| [OpenClaw skill](./openclaw/) | OpenClaw agents (no MCP server required) |
| [`@run402/functions`](https://www.npmjs.com/package/@run402/functions) | Imported _inside_ deployed functions (`db(req?)`, `adminDb()`, `auth.user()`, `email`, `ai`, `assets`) and for TypeScript autocomplete in your editor. Source lives in the private gateway monorepo (it's bundled into your function zip at deploy time, so it co-evolves with the gateway). |

All five interfaces share a single typed kernel where appropriate: `@run402/sdk`. MCP tools, CLI subcommands, and OpenClaw scripts are thin shims over SDK calls. `@run402/functions` is the in-function helper that runs inside deployed code; the npm package on the registry stays in lockstep with what the gateway bundles, even though its source ships from the private monorepo. Pick whichever interface fits your runtime.

## 30-second start

```bash
npm install -g run402
run402 init # creates allowance, requests testnet faucet
run402 tier set prototype # free on testnet (verifies x402 setup)
run402 projects provision --name my-app # → anon_key, service_key, project_id
run402 sites deploy-dir ./dist # incremental deploy of a directory → live URL
run402 subdomains claim my-app # → https://my-app.run402.com
```

That's a real Postgres database + a deployed static site, paid for autonomously with testnet USDC.

## The patterns

### Paste-and-go assets — content-addressed URLs with SRI

`assets.put()` returns an `AssetRef` whose `scriptTag()` / `linkTag()` / `imgTag()` emitters produce HTML with the URL, the SRI integrity hash, and modern best-practice attributes (`defer`, `loading="lazy"`, `decoding="async"`, `crossorigin`) already wired. The URL is content-addressed (`pr-.run402.com/_blob/-<8hex>.`), served through the v1.33 CDN, and never needs invalidation:

```ts
import { run402 } from "@run402/sdk/node";
const r = run402();
const p = await r.project(projectId);

const logo = await p.assets.put("logo.png", { bytes: pngBytes });
const app = await p.assets.put("app.js", { content: jsSource });
const style = await p.assets.put("app.css", { content: css });

const html = `

${style.linkTag()}${app.scriptTag({ type: "module" })}
${logo.imgTag("Company logo")}

`;
```

`immutable: true` is the default — the SDK computes the SHA-256 client-side, the gateway returns a content-hashed URL, and the browser refuses execution on byte mismatch. No cache-invalidation choreography, no waiting, no integrity-attribute construction.

### Dark-by-default tables + the expose manifest

Tables you create are unreachable via `/rest/v1/*` until you declare them in a manifest. That closes the "agent created a table, forgot to set RLS, data leaked" footgun. The manifest is convergent — applying it twice is a no-op; items removed between applies have their policies, grants, triggers, and views dropped.

```bash
cat > manifest.json <<'EOF'
{
"$schema": "https://run402.com/schemas/manifest.v1.json",
"version": "1",
"tables": [
{ "name": "items", "expose": true, "policy": "user_owns_rows",
"owner_column": "user_id", "force_owner_on_insert": true },
{ "name": "audit", "expose": false }
],
"views": [
{ "name": "leaderboard", "base": "items", "select": ["user_id", "score"], "expose": true }
],
"rpcs": [
{ "name": "compute_streak", "signature": "(user_id uuid)", "grant_to": ["authenticated"] }
]
}
EOF

run402 projects validate-expose --file manifest.json
run402 projects apply-expose --file manifest.json
run402 projects get-expose
```

Built-in policies: `user_owns_rows` (rows where `owner_column = auth.uid()`; with `force_owner_on_insert: true` a BEFORE INSERT trigger sets it), `public_read_authenticated_write` (anyone reads, any authenticated user writes), `public_read_write_UNRESTRICTED` (fully open; requires `i_understand_this_is_unrestricted: true`), and `custom` (escape hatch — your own `CREATE POLICY` SQL).

Use `run402 projects validate-expose` or the MCP `validate_manifest` tool for a non-mutating feedback loop before applying. Optional migration SQL is used only to check manifest references; it is not executed as a PostgreSQL dry run, and this does not validate deploy manifests.

**Auth-as-SDLC:** put the same JSON under `database.expose` in your v2 `ReleaseSpec`. The gateway validates it against your migration SQL during deploy and rejects mismatches with a structured `errors` array listing every violation.

### Slick deploys — `deployDir` + plan/commit + progress

`deployDir` walks a local directory, hashes every file client-side, asks the gateway _which_ bytes it doesn't already have, and PUTs only those. Re-deploying an unchanged tree returns immediately with `bytes_uploaded: 0`.

```ts
import { run402 } from "@run402/sdk/node";

const r = run402();
const { url, bytes_uploaded, bytes_total } = await r.sites.deployDir({
project: projectId,
dir: "./dist",
onEvent: (e) => process.stderr.write(JSON.stringify(e) + "\n"),
});
```

Progress events stream over `onEvent` (or stderr from the CLI) as unified
`DeployEvent` JSON objects from the v2 deploy primitive.

CLI:

```bash
run402 sites deploy-dir ./dist --project prj_… > result.json 2> events.log
```

### Same-origin web routes — static site + function ingress

Deploy-v2 routes and static public paths are release resources: they activate atomically with the site, functions, migrations, secrets, and subdomains in the same `deploy apply`. Release static asset paths such as `events.html` are distinct from browser-visible public static paths such as `/events`. Use `site.public_paths` for ordinary clean static URLs; keep routes for function ingress and exact, method-aware static aliases.

```json
{
"project_id": "prj_...",
"site": {
"replace": {
"index.html": { "data": "fetch('/api/hello')" },
"events.html": { "data": "

Events

" }
},
"public_paths": {
"mode": "explicit",
"replace": {
"/events": { "asset": "events.html", "cache_class": "html" }
}
}
},
"functions": {
"replace": {
"api": {
"runtime": "node22",
"source": {
"data": "export default async function handler(req) { const url = new URL(req.url); return Response.json({ ok: true, path: url.pathname }); }"
}
},
"login": {
"runtime": "node22",
"source": { "data": "export default async function handler(req) { return Response.json({ ok: true }); }" }
}
}
},
"routes": {
"replace": [
{ "pattern": "/api/*", "methods": ["GET", "POST", "OPTIONS"], "target": { "type": "function", "name": "api" } },
{ "pattern": "/login", "methods": ["POST"], "target": { "type": "function", "name": "login" } }
]
}
}
```

`site.public_paths.mode: "explicit"` means only the complete `public_paths.replace` table is directly reachable as static URLs. In the example, `/events` serves the release asset `events.html`, while `/events.html` is not public unless separately declared. `mode: "implicit"` restores filename-derived public reachability and can widen access, so review gateway warnings before confirming it.

Omit `routes` or pass `routes: null` to carry forward base routes. Use `routes: { "replace": [] }` to clear the route table. Route entries are an ordered `replace` list, not a path-keyed map. Function targets use `{ "type": "function", "name": "" }`. Static route targets use exact patterns only, methods `["GET"]` or `["GET","HEAD"]`, and `{ "pattern": "/events", "methods": ["GET","HEAD"], "target": { "type": "static", "file": "events.html" } }` where `file` is a release static asset path, not a public path, URL, CAS hash, rewrite, or redirect. Use static route targets for method-aware aliases such as static `GET /login` plus function `POST /login`; in explicit public path mode the backing asset can stay private by filename. Direct `/functions/v1/:name` calls remain API-key protected; browser-routed paths are public same-origin ingress.

Matching is exact or final-prefix-wildcard only. `/admin` and `/admin/` are exact trailing-slash equivalents; `/admin/*` matches children but not `/admin`, `/admin/`, `/admin.css`, or `/administrator`, so deploy both `/admin` and `/admin/*` for a routed section root. Query strings are ignored for matching and preserved in the handler's full public `req.url`. Exact routes beat prefix routes; longest prefix wins; method-compatible dynamic routes beat static assets. A `POST /login` route can coexist with static `GET /login` HTML. Unsafe method mismatch returns `405`, and matched dynamic route failures fail closed instead of falling back to static files.

Routed functions use the Node 22 Fetch Request -> Response contract: `export default async function handler(req) { ... }`. `req.method` is the browser method, and `req.url` is the full public URL on managed subdomains, deployment hosts, and verified custom domains. Derive OAuth callbacks from it, for example `new URL("/admin/oauth/google/callback", new URL(req.url).origin)`. Append multiple cookies with `headers.append("Set-Cookie", value)`; redirects, cookies, and query strings are preserved. The raw `run402.routed_http.v1` envelope is internal; do not write route handlers against it.

Avoid routing every static file, broad method lists by default, wildcard static route targets, leading-slash static files, directory shorthand, and one-static-route-target-per-page tables that exhaust route limits. Also watch wildcard function routes that shadow direct public static paths. Warning codes to handle include `STATIC_ALIAS_SHADOWS_STATIC_PATH`, `STATIC_ALIAS_RELATIVE_ASSET_RISK`, `STATIC_ALIAS_DUPLICATE_CANONICAL_URL`, `STATIC_ALIAS_EXTENSIONLESS_NON_HTML`, and `STATIC_ALIAS_TABLE_NEAR_LIMIT`; inspect active routes, `static_public_paths`, and resolve diagnostics to distinguish the route pattern from the backing `asset_path`.

Diagnose public URLs with the URL-first CLI or MCP/SDK equivalents:

```bash
run402 deploy diagnose --project prj_123 https://example.com/events --method GET
run402 deploy resolve --project prj_123 --url https://example.com/events?utm=x#hero --method GET
run402 deploy resolve --project prj_123 --host example.com --path /events --method GET
```

`deploy_diagnose_url` and `r.project(id).deploy.resolve({ url, method: "GET" })` return `would_serve`, `diagnostic_status`, `match`, normalized request data, warnings, full resolution JSON, and next steps. When returned, `asset_path`, `reachability_authority`, and `direct` explain which release asset backs the public URL and whether reachability came from implicit file-path mode, explicit `site.public_paths`, or a route-only static alias. Stable-host diagnostics may also include `authorization_result`, `cas_object` (`sha256`, `exists`, `expected_size`, `actual_size`), hostname-specific `response_variant`, and route/static fields such as `allow`, `route_pattern`, `target_type`, `target_name`, and `target_file`. Known `match` literals are `host_missing`, `manifest_missing`, `active_release_missing`, `unsupported_manifest_version`, `path_error`, `none`, `static_exact`, `static_index`, `spa_fallback`, `spa_fallback_missing`, `route_function`, `route_static_alias`, and `route_method_miss`; preserve unknown future strings. Known `authorization_result` values include `authorized`, `not_public`, `not_applicable`, `manifest_missing`, `target_missing`, `active_release_missing`, `unsupported_manifest_version`, `path_error`, `missing_cas_object`, `unfinalized_or_deleting_cas_object`, `size_mismatch`, and `unauthorized_cas_object`. Known `fallback_state` values include `active_release_missing`, `unsupported_manifest_version`, and `negative_cache_hit`; preserve unknown future strings. `result` is the diagnostic body status, not the HTTP status of the SDK call, so host misses can still be successful CLI/MCP/SDK calls with `would_serve: false`. Do not treat resolve/diagnose as a fetch, cache purge, or cache-policy oracle; route method misses should inspect `allow`, and CAS authorization/health failures should inspect or redeploy the affected static asset. Branch on structured JSON fields such as `cache_class` and preserve unknown cache classes.

Release observability exposes stable asset identity and public reachability. Inventories include `release_generation`, `static_manifest_sha256`, nullable `static_manifest_metadata` (`file_count`, `total_bytes`, `cache_classes`, `cache_class_sources`, `spa_fallback`), and `static_public_paths[]` when returned. `site.paths` lists release static assets; `static_public_paths[]` lists browser-visible public paths with `public_path`, `asset_path`, `reachability_authority`, `direct`, cache class, and content type. Plan and release diffs expose `static_assets` counters: unchanged/changed/added/removed, `newly_uploaded_cas_bytes`, `reused_cas_bytes`, `deployment_copy_bytes_eliminated`, `legacy_immutable_warnings`, `previous_immutable_failures`, and `cas_authorization_failures`.

Runtime route failure codes to branch on: `ROUTE_MANIFEST_LOAD_FAILED` (manifest/propagation), `ROUTED_INVOKE_WORKER_SECRET_MISSING` (custom-domain Worker secret), `ROUTED_INVOKE_AUTH_FAILED` (internal invoke signature), `ROUTED_ROUTE_STALE` (selected route failed release revalidation), `ROUTE_METHOD_NOT_ALLOWED` (method mismatch), and `ROUTED_RESPONSE_TOO_LARGE` (body over 6 MiB).

### GitHub Actions OIDC deploys — link once, deploy with the same CLI

For repo-driven deploys, Run402 does not need service keys or allowance files in GitHub secrets. Run a local link command once:

```bash
run402 ci link github --project prj_... --manifest run402.deploy.json
# Optional route authority for CI route declarations:
run402 ci link github --project prj_... --manifest run402.deploy.json --route-scope /admin --route-scope /api/*
```

That creates a deploy-scoped `/ci/v1/*` binding and writes a workflow that grants `id-token: write`, checks out the repo, and runs the existing deploy primitive:

```yaml
permissions:
contents: read
id-token: write

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to run402
run: npx --yes run402@1.60.0 deploy apply --manifest 'run402.deploy.json' --project 'prj_...'
```

CI deploys are intentionally narrow: `site`, `functions`, `database`, absent/current `base`, and route declarations only when the binding has covering `--route-scope` patterns. Without route scopes, CI cannot ship `routes`. Keep secrets, domains, subdomains, checks, non-current base, and broader trust changes in a local allowance-backed deploy. If the gateway returns `CI_ROUTE_SCOPE_DENIED`, re-link with exact scopes like `/admin` or final-wildcard scopes like `/api/*`, or deploy locally. Manage bindings with `run402 ci list` and `run402 ci revoke`.

### In-function helpers — caller-context vs BYPASSRLS

Inside a deployed function, import from `@run402/functions`. Two distinct DB clients keep RLS clean:

```ts
import { db, adminDb, auth, email, ai } from "@run402/functions";

export default async (req: Request) => {
const user = await auth.requireUser();

// Caller-context — db() mints a 60s actor JWT so run402.current_user_id() resolves in RLS.
// No .eq("user_id", user.id) needed — RLS already binds the visitor's rows; the redundant
// filter is a deploy-fail (R402_AUTH_REDUNDANT_USER_FILTER) under @run402/functions v3.0+.
const mine = await db().from("items").select("*");

// BYPASSRLS — for platform-authored writes (audit logs, cron cleanup, webhook handlers).
await adminDb().from("audit").insert({ event: "items_read", user_id: user.id });

// Send mail from the project's mailbox — discovers it automatically.
if (mine.length === 0) {
await email.send({ to: user.email, subject: "Welcome", html: "

hi

" });
}

return Response.json(mine);
};
```

`adminDb().sql(query, params?)` runs raw parameterized SQL and always bypasses RLS. It returns a flat `Promise[]>` (just the rows — no envelope):

```ts
import { adminDb, auth } from "@run402/functions";

export default async (req: Request) => {
const user = await auth.requireUser();

const rows = await adminDb().sql(
"SELECT count(*)::int AS n FROM items WHERE user_id = $1",
[user.id],
);
const n = (rows[0]?.n as number | undefined) ?? 0;
return Response.json({ count: n });
};
```

`@run402/functions` is auto-bundled into deployed code; install it in your editor for full TypeScript autocomplete (also works at build time for static-site generation with `RUN402_SERVICE_KEY` + `RUN402_PROJECT_ID` set).

`ai.generateImage({ prompt, aspect? })` is available inside deployed functions for live app flows such as generated avatars or OG images. It calls the project runtime image endpoint with `RUN402_SERVICE_KEY`, so deployed functions do not need allowance wallets or x402 signing code. Aspects are `square`, `landscape`, and `portrait`; the result is `{ image, content_type, aspect }` with base64 image bytes. Runtime image generation is billed, rate-limited, and spend-capped against the project billing account; public routed functions should authenticate/rate-limit their users before calling it.

`assets.put(key, source, opts?)` uploads bytes from inside a deployed function through the same CAS-backed apply substrate as deploy-time assets. It uses `RUN402_SERVICE_KEY`, accepts a string, `Uint8Array`, or `{ content | bytes }`, and returns an SDK-compatible `AssetRef` with mutable and immutable URLs.

**Calling from outside a function entirely** (raw `curl`/`fetch` from CI scripts, bash bootstrappers, non-TS runtimes) — service-key writes go to `/admin/v1/rest/`, not `/rest/v1/*`. The gateway 403s service-role tokens on `/rest/v1/*` so a leaked key can't silently bypass RLS, which means `curl ... > /dev/null` against the wrong path looks like success but writes nothing. SQL-shaped admin work uses `POST /projects/v1/admin/:id/sql` (or `run402 projects sql`).

```bash
curl -X POST https://api.run402.com/admin/v1/rest/audit \
-H "Authorization: Bearer $RUN402_SERVICE_KEY" \
-H "Content-Type: application/json" \
-d '{"event":"seed","ts":"2026-04-30"}'
```

## SDK — `@run402/sdk`

```bash
npm install @run402/sdk
```

Two entry points:

- **`@run402/sdk`** — isomorphic. Bring your own `CredentialsProvider` (a session-token shim, a remote vault, anything that resolves project keys + auth headers). Works in Node 22, Deno, Bun, V8 isolates.
- **`@run402/sdk/node`** — Node-only convenience. Reads `~/.config/run402/projects.json`, signs x402 payments from the local allowance, exposes `sites.deployDir(...)`, `fileSetFromDir(...)`, and typed deploy-manifest helpers (`loadDeployManifest`, `normalizeDeployManifest`).

```ts
import { run402 } from "@run402/sdk/node";

const r = run402();
const project = await r.projects.provision({ tier: "prototype" });
const p = await r.project(project.project_id);
await p.assets.put("hello.txt", { content: "hi" });
```

The SDK is organised as 23 namespaces: `projects`, `assets`, `cache`, `ci`, `sites`, `functions`, `jobs`, `secrets`, `subdomains`, `domains`, `email` (+ `webhooks`), `senderDomain`, `auth`, `apps`, `tier`, `billing`, `contracts`, `ai`, `allowance`, `service`, `admin`, `operator` (the human/email operator session — browser-delegated `login` + `overview` across every wallet that verified your email), plus the `r.project(id).apply` hero for atomic mixed writes (release slices + assets slice via `/apply/v1/*`). Every operation throws a typed `Run402Error` subclass on failure: `PaymentRequired`, `ProjectNotFound`, `Unauthorized`, `ApiError`, `NetworkError`, `LocalError`, `Run402DeployError`. `apply()` automatically re-plans safe current-base `BASE_RELEASE_CONFLICT` races and emits `apply.retry` progress events. See [`sdk/README.md`](./sdk/README.md).

**Astro SSR + ISR cache (v1.52+).** For Astro apps, use `@run402/astro` 1.0+ — `export default run402();` in `astro.config.mjs` returns an `AstroUserConfig` composing the SSR adapter (Lambda + SnapStart + ISR cache + AsyncLocalStorage request-context), image integration, and build-time detectors. Functions opt into the SSR class via `FunctionSpec.class: "ssr"` in `ReleaseSpec`; the gateway provisions SnapStart and caches HTML responses keyed by `(host, path, search, method, locale, release_id)`. Cache is bypass-by-default (no-store unless `Cache-Control` explicitly allows it AND no `Set-Cookie` AND no auth-taint flag from `auth.*` helpers / payment primitives). Invalidate from in-function code or out-of-band: `r.cache.invalidate(url)` / `r.cache.invalidatePrefix({ host, prefix })` / `r.cache.invalidateAll({ host })` (SDK), `run402 cache invalidate ` (CLI). Inspect cached state with `r.cache.inspect(url)` / `run402 cache inspect `. Agent DX helpers also in the CLI: `run402 doctor` (5 health checks), `run402 dev` (Astro dev with `.env.local`), `run402 logs --request-id req_...` (correlate across functions). Full reference at [`astro/README.md`](./astro/README.md) and [`cli/llms-cli.txt`](./cli/llms-cli.txt) (R402_* SSR Runtime Error Codes section).

## CLI — `run402`

```bash
npm install -g run402
```

Every subcommand prints JSON to stdout, JSON errors to stderr, exits 0 on success and 1 on failure — designed for an agent shell, not a human. Full reference: [`cli/llms-cli.txt`](./cli/llms-cli.txt) (also at ).

```bash
run402 init # one-shot allowance + faucet + tier check
run402 status # account snapshot (allowance, balance, tier, projects)
run402 projects provision --name my-app
run402 projects sql "CREATE TABLE …"
run402 projects validate-expose --file manifest.json
run402 projects apply-expose --file manifest.json
run402 sites deploy-dir ./dist
run402 deploy release active --project # inspect current-live release inventory
run402 deploy diagnose --project https://example.com/events --method GET
run402 functions deploy --file fn.ts
run402 ci link github --project # GitHub Actions OIDC deploy binding (--route-scope for CI routes)
run402 assets put ./asset.png --immutable
run402 assets diagnose # inspect live CDN state for a public URL
run402 cdn wait-fresh --sha # poll until a mutable URL serves the new SHA
```

The active project is sticky: `run402 projects use ` makes `` the default for every subsequent ``-taking subcommand, so most commands work without it.

## MCP server — `run402-mcp`

```bash
npx -y run402-mcp # standalone test
```

### Claude Desktop

Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
"mcpServers": {
"run402": { "command": "npx", "args": ["-y", "run402-mcp"] }
}
}
```

### Cursor

Add to `.cursor/mcp.json`:

```json
{
"mcpServers": {
"run402": { "command": "npx", "args": ["-y", "run402-mcp"] }
}
}
```

### Cline

Add to your Cline MCP settings:

```json
{
"mcpServers": {
"run402": { "command": "npx", "args": ["-y", "run402-mcp"] }
}
}
```

### Claude Code

```bash
claude mcp add run402 -- npx -y run402-mcp
```

## OpenClaw skill

```bash
cp -r openclaw ~/.openclaw/skills/run402
cd ~/.openclaw/skills/run402/scripts && npm install
```

Each script re-exports from `cli/lib/*.mjs` — the OpenClaw command surface is identical to the CLI command surface by construction. See [`openclaw/README.md`](./openclaw/README.md).

## MCP tools

The full MCP surface — every tool is a thin shim over an SDK call.

### Database

| Tool | Description |
|------|-------------|
| `provision_postgres_project` | Provision a new database. Auto-handles x402 payment. |
| `run_sql` | Execute SQL (DDL or queries). Returns a markdown table. |
| `rest_query` | Query/mutate via PostgREST. |
| `apply_expose` | Apply the declarative authorization manifest (tables, views, RPCs). Convergent — drops items removed between applies. |
| `validate_manifest` | Validate the auth/expose manifest without applying it. Accepts manifest object/string, optional `migration_sql`, optional `project_id`. |
| `get_expose` | Return the current manifest. `source` is either `applied` (from the tracking table) or `introspected` (regenerated from live DB state). |
| `get_schema` | Introspect tables, columns, types, constraints, RLS policies. |
| `get_usage` | Per-project usage report (API calls, storage, lease expiry). |
| `promote_user` / `demote_user` | Manage `project_admin` role on a project user. |
| `delete_project` | Cascade purge — schema, Lambdas, S3 site files, deployments, secrets, published versions. Irreversible. |

### Asset storage (content-addressed CDN)

| Tool | Description |
|------|-------------|
| `assets_put` | Upload an asset (any size, up to 5 TiB) via direct-to-S3 presigned URLs. Returns an `AssetRef` with `scriptTag()` / `linkTag()` / `imgTag()` emitters. |
| `assets_get` | Download an asset to a local file. |
| `assets_ls` | Keyset-paginated list with prefix filter. |
| `assets_rm` | Delete an asset. |
| `assets_sign` | Time-boxed presigned GET URL for a private asset. |
| `diagnose_public_url` | Live CDN state for a public URL — expected vs observed SHA, cache headers, invalidation status. |
| `wait_for_cdn_freshness` | Poll a mutable URL until it serves the expected SHA-256. |

### Sites & subdomains

| Tool | Description |
|------|-------------|
| `deploy_site` | Deploy a static site from inline file bytes. |
| `deploy_site_dir` | Deploy a static site from a local directory. Routes through the unified deploy primitive (CAS-backed) — only uploads bytes the gateway doesn't have. |
| `claim_subdomain` | Claim `.run402.com` (idempotent; reassigns to latest deployment on subsequent deploys). |
| `list_subdomains` / `delete_subdomain` | Manage subdomains. |
| `add_custom_domain` / `list_custom_domains` / `check_domain_status` / `remove_custom_domain` | Point your own domain at a Run402 subdomain. |
| `deploy` / `deploy_resume` / `deploy_list` / `deploy_events` | Apply, resume, list, and inspect deploy operations. |
| `deploy_release_get` / `deploy_release_active` / `deploy_release_diff` | Inspect release inventory and release-to-release diffs without starting a new deploy mutation. |
| `deploy_diagnose_url` | URL-first deploy resolver diagnostics. Params: `project_id`, either `url` or `host`/`path`, optional `method`; returns `would_serve`, `diagnostic_status`, `match`, warnings, next steps, and fenced JSON. |

### CI/OIDC bindings

| Tool | Description |
|------|-------------|
| `ci_create_binding` | Create a GitHub Actions CI deploy binding from a locally signed delegation. Optional `route_scopes` delegate exact paths like `/admin` or final wildcards like `/api/*`; omitted means no CI route authority. |
| `ci_list_bindings` / `ci_get_binding` / `ci_revoke_binding` | Inspect and revoke CI bindings, including returned `route_scopes`. |

### Functions & secrets

| Tool | Description |
|------|-------------|
| `deploy_function` | Deploy a Node 22 serverless function. Cron-schedulable. |
| `invoke_function` | Invoke a deployed function over the direct API-key-protected test path. |
| `get_function_logs` | Recent logs (CloudWatch), filterable by `since` and routed `request_id`. |
| `update_function` | Update schedule / timeout / memory without redeploying code. |
| `list_functions` / `delete_function` | List / remove functions. |
| `set_secret` / `list_secrets` / `delete_secret` | Manage `process.env` secrets injected into all functions. Values are write-only; list returns keys and timestamps only. |
| `jobs_submit` / `jobs_get` / `jobs_logs` / `jobs_cancel` | Submit and inspect fixed platform-managed jobs. Requests use the gateway jobs shape; the SDK supplies the required idempotency header. |

### Auth & email

| Tool | Description |
|------|-------------|
| `request_magic_link` | Send a passwordless login email. |
| `verify_magic_link` | Exchange the magic link token for `access_token` + `refresh_token`. |
| `create_auth_user` / `invite_auth_user` | Create/update auth users and send trusted service-key invites. |
| `set_user_password` | Change, reset, or set a user's password. |
| `auth_settings` | Configure password set, preferred sign-in method, public signup policy, and project-admin passkey enforcement. |
| `passkey_register_options` / `passkey_register_verify` | Create and verify WebAuthn passkey registration ceremonies. |
| `passkey_login_options` / `passkey_login_verify` | Create and verify WebAuthn passkey login ceremonies. |
| `list_passkeys` / `delete_passkey` | List or delete the authenticated user's passkeys. |
| `create_mailbox` / `get_mailbox` / `delete_mailbox` | Per-project mailbox at `@mail.run402.com`. |
| `send_email` | Template (`project_invite`, `magic_link`, `notification`) or raw HTML. Single recipient. |
| `list_emails` / `get_email` / `get_email_raw` | Read messages. `get_email_raw` returns RFC-822 bytes for DKIM / zk-email verification. |
| `register_mailbox_webhook` / `list_mailbox_webhooks` / `get_mailbox_webhook` / `update_mailbox_webhook` / `delete_mailbox_webhook` | Email-event webhooks (delivery, bounced, complained, reply_received). |
| `register_sender_domain` / `sender_domain_status` / `remove_sender_domain` | Send from your own domain (DKIM verified). |
| `enable_sender_domain_inbound` / `disable_sender_domain_inbound` | Receive replies on your custom sender domain. |

### AI helpers

| Tool | Description |
|------|-------------|
| `generate_image` | Text-to-PNG via x402 ($0.03 / image). |
| `ai_translate` | Translate text. Metered per project. |
| `ai_moderate` | Moderate text (free). |
| `ai_usage` | Translation quota (used / included / remaining). |

### Apps marketplace

| Tool | Description |
|------|-------------|
| `browse_apps` | Browse public forkable apps. |
| `get_app` | Inspect an app, including expected `bootstrap_variables`. |
| `fork_app` | Clone schema + site + functions into a new project. Runs the app's `bootstrap` function with provided variables. |
| `publish_app` | Publish a project as a forkable app. |
| `list_versions` / `update_version` / `delete_version` | Manage published versions. |

### Tier & billing

| Tool | Description |
|------|-------------|
| `set_tier` | Subscribe / renew / upgrade a tier (auto-detects action). x402 payment. |
| `tier_status` | Current tier, lease expiry, usage, and function authoring caps when returned. |
| `get_quote` | Tier pricing (free, no auth). |
| `tier_checkout` | Stripe checkout for a tier (alternative to x402). |
| `create_email_billing_account` / `link_wallet_to_account` | Email-based billing accounts; hybrid Stripe + x402. |
| `billing_history` | Ledger history. |
| `buy_email_pack` | $5 for 10,000 emails (never expire). |
| `set_auto_recharge` | Auto-buy email packs when credits run low. |

### KMS contract wallets (on-chain signing)

| Tool | Description |
|------|-------------|
| `provision_contract_wallet` | AWS KMS-backed Ethereum wallet. $0.04/day rental + $0.000005 per call. Private keys never leave KMS. |
| `get_contract_wallet` / `list_contract_wallets` | Metadata + live native balance. |
| `set_recovery_address` / `set_low_balance_alert` | Optional safety nets. |
| `contract_call` | Submit a write call (chain gas at-cost + KMS sign fee). |
| `contract_read` | Read-only call (free). |
| `get_contract_call_status` | Lifecycle, gas, receipt. |
| `drain_contract_wallet` | Drain native balance (works on suspended wallets — the safety valve). |
| `delete_contract_wallet` | Schedule KMS key deletion (refused if balance ≥ dust). |

### Allowance & account

| Tool | Description |
|------|-------------|
| `init` | One-shot setup: allowance + faucet + tier check + project list. |
| `status` | Full account snapshot (allowance, balance, tier, projects). |
| `allowance_status` / `allowance_create` / `allowance_export` | Local allowance management. |
| `request_faucet` | Request testnet USDC. |
| `check_balance` | USDC balance for an allowance address. |
| `list_projects` | Active projects for a wallet. |
| `pin_project` | Pin a project (admin only — uses the configured admin allowance wallet). |
| `project_info` / `project_keys` / `project_use` | Inspect / set the active project. |
| `create_checkout` | Stripe checkout to add cash credit. |
| `send_message` | Send feedback to the Run402 team. |
| `set_agent_contact` / `get_agent_contact_status` / `verify_agent_contact_email` | Register agent contact info, read assurance status, and start the operator email reply challenge. |
| `start_operator_passkey_enrollment` | Email a Run402 operator passkey enrollment link to the verified contact email. |
| `get_operator_status` | Compact operator-health snapshot — contact assurance state, critical items, skipped notifications, billing accounts, projects, active thresholds. Read via `run402 doctor` or directly. |
| `get_notification_preferences` / `set_notification_preferences` | Read/update operator notification preferences (cadence, channels, per-class toggles, locale, timezone). Cross-wallet effects require `email_verified`; webhook URL changes require `operator_passkey`. |
| `list_notifications` | Per-delivery-attempt audit log. Paginated, filterable by event_type / since. |
| `test_notification` | Fire a real test notification through the full worker pipeline. Audit row marked `is_test=true`. Rate-limited per wallet at 1/min. |
| `rotate_webhook_secret` | Generate a new HMAC signing secret for the operator webhook (returned exactly once). Previous secret remains valid for 24h. Requires `operator_passkey`. |

### Service status (no auth)

| Tool | Description |
|------|-------------|
| `service_status` | Public availability report — 24h/7d/30d uptime per capability, operator, deployment topology. |
| `service_health` | Liveness probe with per-dependency results. |

## Configuration

| Variable | Default | Purpose |
|----------|---------|---------|
| `RUN402_API_BASE` | `https://api.run402.com` | API base URL (override for staging) |
| `RUN402_CONFIG_DIR` | `~/.config/run402` | Local credential storage base directory (named wallets live under `profiles//`) |
| `RUN402_WALLET` | `default` | Active named wallet (profile). Overridden by `--wallet ` and per-directory `.run402.json`; `RUN402_PROFILE` is an alias. See `run402 wallets`. |
| `RUN402_ALLOWANCE_PATH` | `{config_dir}/allowance.json` | Custom allowance file path |

Local state lives at:

- `~/.config/run402/projects.json` (`0600`) — `{ projects: { : { anon_key, service_key, tier, lease_expires_at } } }`
- `~/.config/run402/allowance.json` (`0600`) — wallet for x402 signing

`anon_key` and `service_key` have no expiry — lease enforcement happens server-side. Rotate them by deleting the project and re-provisioning.

## Development

```bash
npm run build # builds core/, sdk/, then the MCP server
npm test # SKILL + sync + unit tests
npm run test:e2e # 47 CLI end-to-end tests
npm run test:sync # checks MCP/CLI/OpenClaw/SDK stay in sync
npm run test:skill # validates SKILL.md frontmatter + body
```

Architecture: every tool / subcommand / skill script is a thin shim over an `@run402/sdk` call. `core/` holds Node-only filesystem primitives (keystore, allowance, SIWE signing) wrapped by the SDK's Node provider. See [`CLAUDE.md`](./CLAUDE.md) for the full layout.

## Links

- Web:
- API docs (HTTP): ·
- CLI docs:
- Status:
- Health:

[简体中文](./README.zh-CN.md)

## License

MIT