UNPKG

run402-mcp

Version:

MCP server for Run402 — full-stack backend infrastructure for AI agents: Postgres, auth, storage, serverless functions and atomic deploys. Paid with x402/MPP. Includes $0.03 image generation.

1,182 lines (1,181 loc) • 31.5 kB
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://run402.com/schemas/release-spec.v1.json", "title": "Run402 ReleaseSpec v1", "description": "Authoring schema for Run402 deploy manifests. Use run402 up for complete application workflows. Project selection may be supplied by an explicit CLI target, app-local link or approved --name creation; the SDK requires a resolved project before gateway planning.", "type": "object", "additionalProperties": false, "properties": { "$schema": { "type": "string", "description": "Editor metadata only. Manifest adapters strip this before deploy planning." }, "x-run402-omitted_features": { "type": "array", "description": "App-kit evidence metadata for humans/agents. Manifest adapters preserve it in the loaded manifest and strip it before deploy planning." }, "project_id": { "type": "string", "description": "The target project id. The one spelling in a manifest, in a typed ReleaseSpec, and on the wire." }, "idempotency_key": { "type": "string" }, "idempotencyKey": { "type": "string" }, "base": { "$ref": "#/$defs/base" }, "database": { "$ref": "#/$defs/database" }, "secrets": { "$ref": "#/$defs/secrets" }, "functions": { "$ref": "#/$defs/functions" }, "site": { "$ref": "#/$defs/site" }, "assets": { "$ref": "#/$defs/assets" }, "subdomains": { "$ref": "#/$defs/subdomains" }, "routes": { "oneOf": [ { "type": "null" }, { "$ref": "#/$defs/routes" } ] }, "checks": { "type": "array", "items": { "$ref": "#/$defs/smokeCheck" } }, "i18n": { "oneOf": [ { "type": "null" }, { "$ref": "#/$defs/i18n" } ] }, "verify": { "$ref": "#/$defs/verify", "description": "Authoring-only post-apply HTTP verification, run by `run402 up` after a successful apply and rerunnable with `run402 up verify`. Stripped before deploy planning (never part of the wire ReleaseSpec)." } }, "$defs": { "base": { "oneOf": [ { "type": "object", "additionalProperties": false, "required": [ "release" ], "properties": { "release": { "enum": [ "current", "empty" ] } } }, { "type": "object", "additionalProperties": false, "required": [ "release_id" ], "properties": { "release_id": { "type": "string" } } } ] }, "database": { "type": "object", "additionalProperties": false, "properties": { "migrations": { "type": "array", "items": { "$ref": "#/$defs/migration" } }, "expose": { "type": "object", "description": "Authorization/expose manifest. See https://run402.com/schemas/manifest.v1.json for its schema." }, "zero_downtime": { "type": "boolean" } } }, "migration": { "type": "object", "additionalProperties": false, "properties": { "id": { "type": "string", "description": "Versioned immutable migration id, e.g. 001_init. Same id plus same checksum noops; same id plus different SQL is MIGRATION_CHECKSUM_MISMATCH. Use name instead for generated/idempotent SQL whose identity should track content changes." }, "name": { "type": "string", "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$", "description": "Content-tracked migration name for generated/idempotent SQL. The SDK compiles this to <name>_<sha256(sql)[0:16]>; changed content applies once under a new id and identical re-deploys noop. SQL declared with name MUST be idempotent because it re-runs whenever content changes against a database where prior versions may already exist." }, "checksum": { "type": "string" }, "sql": { "type": "string" }, "sql_ref": { "$ref": "#/$defs/contentRef" }, "sql_path": { "type": "string" }, "sql_file": { "type": "string" }, "transaction": { "enum": [ "required", "none" ] } }, "oneOf": [ { "required": [ "id" ], "not": { "required": [ "name" ] } }, { "required": [ "name" ], "not": { "required": [ "id" ] } } ] }, "secrets": { "type": "object", "additionalProperties": false, "properties": { "require": { "type": "array", "items": { "type": "string" } }, "delete": { "type": "array", "items": { "type": "string" } } } }, "functions": { "type": "object", "additionalProperties": false, "properties": { "replace": { "type": "object", "additionalProperties": { "$ref": "#/$defs/functionSpec" } }, "patch": { "type": "object", "additionalProperties": false, "properties": { "set": { "type": "object", "additionalProperties": { "$ref": "#/$defs/functionSpec" } }, "delete": { "type": "array", "items": { "type": "string" } } } } } }, "functionSpec": { "type": "object", "additionalProperties": false, "description": "FunctionSpec. schedule is a sibling of runtime/source/files/config, not config.schedule.", "properties": { "runtime": { "enum": [ "node22" ] }, "source": { "$ref": "#/$defs/fileEntry" }, "files": { "$ref": "#/$defs/fileSet" }, "entrypoint": { "type": "string" }, "config": { "type": "object", "additionalProperties": false, "properties": { "timeoutSeconds": { "type": "integer", "minimum": 1 }, "memoryMb": { "type": "integer", "minimum": 1 } } }, "schedule": { "oneOf": [ { "type": "string", "description": "5-field cron expression." }, { "type": "null", "description": "Remove an existing schedule in patch mode." } ] }, "deps": { "type": "array", "items": { "type": "string", "minLength": 1 } }, "triggers": { "type": "array", "items": { "$ref": "#/$defs/functionTrigger" } }, "requireAuth": { "type": "boolean" }, "require_auth": { "type": "boolean" }, "requireRole": { "oneOf": [ { "type": "null" }, { "$ref": "#/$defs/functionRequireRole" } ] }, "require_role": { "oneOf": [ { "type": "null" }, { "$ref": "#/$defs/functionRequireRole" } ] }, "class": { "enum": ["ssr", "standard"] }, "capabilities": { "type": "array", "items": { "type": "string", "minLength": 1 } } } }, "functionTrigger": { "type": "object", "additionalProperties": true, "required": [ "id", "type" ], "properties": { "id": { "type": "string", "minLength": 1 }, "type": { "type": "string", "minLength": 1 } } }, "functionRequireRole": { "type": "object", "additionalProperties": false, "required": [ "table", "id_column", "role_column", "allowed" ], "properties": { "table": { "type": "string", "minLength": 1 }, "id_column": { "type": "string", "minLength": 1 }, "role_column": { "type": "string", "minLength": 1 }, "allowed": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1 }, "cache_ttl": { "type": "integer", "minimum": 0 }, "on_deny": { "enum": [ "envelope", "redirect" ] }, "sign_in_path": { "type": "string", "minLength": 1 } } }, "site": { "oneOf": [ { "type": "object", "additionalProperties": false, "required": [ "replace" ], "properties": { "replace": { "oneOf": [ { "$ref": "#/$defs/fileSet" }, { "$ref": "#/$defs/localDirRef" } ] }, "public_paths": { "$ref": "#/$defs/sitePublicPaths" }, "embedding": { "$ref": "#/$defs/siteEmbedding" }, "sign_in_path": { "$ref": "#/$defs/siteSignInPath" }, "noindex": { "$ref": "#/$defs/siteNoindex" } } }, { "type": "object", "additionalProperties": false, "required": [ "patch" ], "properties": { "patch": { "type": "object", "additionalProperties": false, "properties": { "put": { "oneOf": [ { "$ref": "#/$defs/fileSet" }, { "$ref": "#/$defs/localDirRef" } ] }, "delete": { "type": "array", "items": { "type": "string" } } } }, "public_paths": { "$ref": "#/$defs/sitePublicPaths" }, "embedding": { "$ref": "#/$defs/siteEmbedding" }, "sign_in_path": { "$ref": "#/$defs/siteSignInPath" }, "noindex": { "$ref": "#/$defs/siteNoindex" } } }, { "type": "object", "additionalProperties": false, "properties": { "public_paths": { "$ref": "#/$defs/sitePublicPaths" }, "embedding": { "$ref": "#/$defs/siteEmbedding" }, "sign_in_path": { "$ref": "#/$defs/siteSignInPath" }, "noindex": { "$ref": "#/$defs/siteNoindex" } }, "anyOf": [ { "required": [ "public_paths" ] }, { "required": [ "embedding" ] }, { "required": [ "sign_in_path" ] }, { "required": [ "noindex" ] } ] } ] }, "sitePublicPaths": { "oneOf": [ { "type": "object", "additionalProperties": false, "required": [ "mode" ], "properties": { "mode": { "const": "implicit" } } }, { "type": "object", "additionalProperties": false, "required": [ "mode", "replace" ], "properties": { "mode": { "const": "explicit" }, "replace": { "type": "object", "additionalProperties": { "$ref": "#/$defs/publicStaticPath" } } } } ] }, "publicStaticPath": { "type": "object", "additionalProperties": false, "required": [ "asset" ], "properties": { "asset": { "type": "string", "minLength": 1, "description": "Release static asset path, not a public URL." }, "cache_class": { "$ref": "#/$defs/staticCacheClass" } } }, "staticCacheClass": { "type": "string", "description": "Known values: html, immutable_versioned, revalidating_asset. Unknown future strings may be returned by observability APIs and should be preserved." }, "subdomains": { "type": "object", "additionalProperties": false, "properties": { "set": { "type": "array", "items": { "type": "string" } }, "add": { "type": "array", "items": { "type": "string" } }, "remove": { "type": "array", "items": { "type": "string" } } } }, "routes": { "type": "object", "additionalProperties": false, "required": [ "replace" ], "properties": { "replace": { "type": "array", "items": { "$ref": "#/$defs/route" } } } }, "route": { "type": "object", "additionalProperties": false, "required": [ "pattern", "target" ], "properties": { "pattern": { "type": "string" }, "methods": { "type": "array", "minItems": 1, "items": { "enum": [ "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS" ] } }, "target": { "oneOf": [ { "$ref": "#/$defs/functionRouteTarget" }, { "$ref": "#/$defs/staticRouteTarget" } ] }, "pricing": { "$ref": "#/$defs/routePricing" } }, "allOf": [ { "if": { "properties": { "target": { "type": "object", "properties": { "type": { "const": "static" } }, "required": [ "type" ] } }, "required": [ "target" ] }, "then": { "not": { "required": [ "pricing" ] } } } ] }, "routePricingDiscovery": { "type": "object", "additionalProperties": false, "required": ["description"], "description": "Opt-in x402 Bazaar listing for this priced route. Exact route with exactly one method (GET may include HEAD); at most 4096 bytes of canonical JSON. The 402 challenge carries it plus the run402 tag and accepts[].extra.hosted_by.", "properties": { "description": { "type": "string", "minLength": 1, "maxLength": 500, "description": "What the endpoint does and when to call it; used verbatim." }, "mime_type": { "type": "string", "description": "Response media type. Default application/json." }, "service_name": { "type": "string", "minLength": 1, "maxLength": 32, "pattern": "^[\\x20-\\x7e]+$" }, "tags": { "type": "array", "maxItems": 4, "uniqueItems": true, "items": { "type": "string", "minLength": 1, "maxLength": 32, "pattern": "^[\\x20-\\x7e]+$" }, "description": "At most 4; the Bazaar keeps five and Run402 appends run402." }, "icon_url": { "type": "string", "format": "uri", "pattern": "^https://", "maxLength": 2048, "description": "Absolute https URL on a public host name, without credentials." }, "input": { "type": "object", "additionalProperties": false, "description": "GET/HEAD/DELETE: query_params_schema and an object example. POST/PUT/PATCH: body_type (required), body_schema, example. Schemas may reference only local # fragments.", "properties": { "query_params_schema": { "type": "object" }, "body_type": { "enum": ["json", "form-data", "text"] }, "body_schema": { "type": "object" }, "example": {} } }, "output": { "type": "object", "additionalProperties": false, "properties": { "example": {}, "schema": { "type": "object" } } } } }, "routePricing": { "type": "object", "additionalProperties": false, "required": [ "mode", "amount_usd_micros", "pay_to" ], "properties": { "discovery": { "$ref": "#/$defs/routePricingDiscovery" }, "mode": { "const": "always" }, "amount_usd_micros": { "type": "integer", "minimum": 1, "maximum": 9007199254740991 }, "amount_msat": { "type": "integer", "minimum": 1, "maximum": 9007199254740991, "description": "Pins the Lightning price in millisatoshis for `lightning` / `lightning-testnet` entries; needs a Lightning network. Absent, each challenge quotes `amount_usd_micros` into whole satoshis." }, "pay_to": { "const": "org_default_payout" }, "networks": { "type": "array", "minItems": 1, "uniqueItems": true, "default": [ "mainnet" ], "items": { "enum": [ "mainnet", "testnet", "lightning", "lightning-testnet", "tempo", "tempo-testnet" ] }, "description": "Omit to accept production mainnet only. Include testnet explicitly for testnet payments. `lightning` / `lightning-testnet` sell for sats over x402 `exact` on `lnbtc`, settled to the org's Lightning receiver (`run402 orgs lightning-receiver set`). `tempo` / `tempo-testnet` also take an MPP Tempo charge in pathUSD on Tempo mainnet (chain 4217) / Moderato (42431), paid to the org's payout address. `receipt` cannot be combined with Lightning or Tempo networks." } } }, "functionRouteTarget": { "type": "object", "additionalProperties": false, "required": [ "type", "name" ], "properties": { "type": { "const": "function" }, "name": { "type": "string" } } }, "staticRouteTarget": { "type": "object", "additionalProperties": false, "required": [ "type", "file" ], "properties": { "type": { "const": "static" }, "file": { "type": "string", "description": "Relative materialized release static asset path, not a public path." } } }, "smokeCheck": { "type": "object", "additionalProperties": true }, "i18n": { "type": "object", "additionalProperties": false, "required": [ "defaultLocale", "locales" ], "description": "Routed-locale-context release slice (v2.5+). Omit at the top level to carry forward from the base release; pass top-level i18n: null to clear; pass an object to replace. defaultLocale MUST be byte-identical to one entry in locales[].", "properties": { "defaultLocale": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$", "description": "Default locale tag. MUST be byte-identical to one entry in locales[]." }, "locales": { "type": "array", "minItems": 1, "maxItems": 50, "items": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" }, "description": "Supported locale tags. Non-empty, max 50 entries. Tags are opaque — only the safety regex is enforced (no BCP-47 semantic validation)." }, "detect": { "type": "array", "maxItems": 10, "items": { "type": "string", "pattern": "^(accept-language|cookie:[!#$%&'*+\\-.^_`|~0-9A-Za-z]+)$" }, "description": "Walked in order; first match wins. Defaults to ['accept-language'] when omitted, max 10 entries; [] is allowed and means 'always default'. Sources: 'accept-language' and 'cookie:<name>' (RFC 6265 cookie-name grammar)." }, "unknownLocalePolicy": { "type": "string", "enum": [ "reject", "pass-through" ], "description": "What to do when a detect-source signal does not match locales[]. 'reject' (default, backwards-compatible) falls through to the next detect source then to defaultLocale. 'pass-through' returns the lowercased, trimmed signal value verbatim — letting the consumer's app DB decide whether translations exist for the tag. Capability i18n-unknown-locale-policy (issue #413)." } } }, "fileSet": { "type": "object", "additionalProperties": { "$ref": "#/$defs/fileEntry" } }, "localDirRef": { "type": "object", "additionalProperties": false, "required": [ "__source", "path" ], "properties": { "__source": { "const": "local-dir" }, "path": { "type": "string", "description": "Local directory path resolved by the SDK/CLI from the manifest directory and stripped before the apply request." } }, "description": "CLI/MCP/Node SDK authoring form for static site directories. This is never sent to the gateway wire API." }, "fileEntry": { "oneOf": [ { "type": "string" }, { "$ref": "#/$defs/contentRef" }, { "type": "object", "additionalProperties": false, "required": [ "path" ], "properties": { "path": { "type": "string" }, "content_type": { "type": "string" } } }, { "type": "object", "additionalProperties": false, "required": [ "data" ], "properties": { "data": { "oneOf": [ { "type": "string" }, { "$ref": "#/$defs/contentRef" } ] }, "encoding": { "enum": [ "utf-8", "base64" ] }, "content_type": { "type": "string" } } } ] }, "contentRef": { "type": "object", "additionalProperties": false, "required": [ "sha256", "size" ], "properties": { "sha256": { "type": "string" }, "size": { "type": "integer", "minimum": 0 }, "content_type": { "type": "string" }, "contentType": { "type": "string", "description": "Legacy SDK ContentRef spelling accepted by the normalizer; prefer content_type in manifests." }, "integrity": { "type": "string" } } }, "assets": { "type": "object", "description": "v1.48 unified-apply asset slice. Per-key assets are committed inside the same activation transaction as functions/site/secrets so a release flips atomically.", "additionalProperties": false, "properties": { "put": { "type": "array", "items": { "$ref": "#/$defs/assetPutEntry" } }, "delete": { "type": "array", "items": { "type": "string" }, "description": "Asset keys to remove at activation." }, "sync": { "$ref": "#/$defs/assetSync" } } }, "assetPutEntry": { "type": "object", "additionalProperties": false, "required": [ "key" ], "properties": { "key": { "type": "string", "description": "Asset key under the project's asset namespace (no leading slash)." }, "source": { "$ref": "#/$defs/fileEntry", "description": "SDK-input form. Mutually exclusive with sha256/size_bytes; the SDK normalizer hashes + uploads via /content/v1/plans." }, "sha256": { "type": "string", "description": "Pre-uploaded CAS reference (wire form). Mutually exclusive with source." }, "size_bytes": { "type": "integer", "minimum": 0, "description": "Required when using the wire form (sha256 set)." }, "content_type": { "type": "string" }, "visibility": { "enum": [ "public", "private" ] }, "immutable": { "type": "boolean" } } }, "assetSync": { "type": "object", "additionalProperties": false, "required": [ "prefix", "prune" ], "properties": { "prefix": { "type": "string", "description": "Prefix under which destructive sync operates." }, "prune": { "const": true }, "confirm": { "type": "object", "additionalProperties": false, "required": [ "base_revision", "delete_set_digest", "expected_delete_count" ], "properties": { "base_revision": { "type": "string" }, "delete_set_digest": { "type": "string" }, "expected_delete_count": { "type": "integer", "minimum": 0 } }, "description": "Confirmation token echoed back from a prior plan; required to commit a destructive sync." } } }, "verify": { "type": "object", "additionalProperties": false, "properties": { "http": { "type": "array", "items": { "$ref": "#/$defs/verifyHttpCheck" } } } }, "verifyHttpCheck": { "type": "object", "additionalProperties": false, "required": [ "id" ], "properties": { "id": { "type": "string", "minLength": 1 }, "path": { "type": "string", "minLength": 1, "description": "Request path resolved against the project public origin." }, "url": { "type": "string", "minLength": 1 }, "expect": { "type": "object", "additionalProperties": false, "required": [ "status" ], "properties": { "status": { "type": "integer", "minimum": 100, "maximum": 599 } } }, "expected_status": { "type": "integer", "minimum": 100, "maximum": 599, "description": "Snake-case alias for expect.status." }, "retries": { "type": "integer", "minimum": 1 } }, "allOf": [ { "anyOf": [ { "required": [ "path" ] }, { "required": [ "url" ] } ] }, { "anyOf": [ { "required": [ "expect" ] }, { "required": [ "expected_status" ] } ] } ] }, "siteSignInPath": { "description": "The app's own same-origin sign-in page (e.g. \"/join\") that MCP authorization sends a signed-out user to, with returnTo appended, instead of the hosted /auth/sign-in. Not under /_run402/ or /auth/; no query or fragment. Remote validation is authoritative. Null resets to the hosted page; omission carries prior state.", "oneOf": [ { "type": "null" }, { "type": "string", "maxLength": 512, "pattern": "^/(?!/)(?!_run402(?:/|$))(?!auth/)(?!.*://)[^?#\\\\\\s\\u0000-\\u001f\\u007f]*$" } ] }, "siteNoindex": { "description": "true adds X-Robots-Tag: noindex, nofollow, nosnippet to every response of the host. false or null resets to indexable (the default); omission carries prior state.", "type": [ "boolean", "null" ] }, "siteEmbedding": { "description": "Framing opt-in using platform catalog keys, never origins. Remote validation checks the current catalog. Null resets to deny; omission carries prior state.", "oneOf": [ { "type": "null" }, { "type": "object", "additionalProperties": false, "required": [ "frame_ancestors" ], "properties": { "frame_ancestors": { "type": "array", "uniqueItems": true, "items": { "type": "string", "pattern": "^[a-z][a-z0-9_-]*$" } } } } ] } } }