# Publish to Hackemist through MCP ## Transport and compatibility Read [SAVE_SDK.md](/docs/save-sdk.md) for SDK 1.5.0's `Hackemist.createProgressStore` and direct checkpoint APIs. For memory-only app state, implement validated JSON capture/restore, initial-state and recovery callbacks, await `ready` before changes, preserve originals and never turn a read failure or empty cloud slot into an unconditional local reset. Only acknowledged `saved` status means cloud saved; gated local cache is recovery. Legacy `createProgressSaver` remains supported. Use `save_integration_get` for bounded source hints; neither upload nor source inspection proves every progress field is saved. MCP exposes allowlisted resources at `hackemist://docs/hosting`, `hackemist://docs/save-sdk` and `hackemist://docs/publishing`. Standalone previews cannot test authenticated saves; use the player or Studio bridge. Copy the deployment's exact `${APP_ORIGIN}/mcp` endpoint from `/mcp`. Production uses HTTPS. By default, MCP requires an active subscription whose frozen plan includes MCP access, such as Pro. Administrator support grants can provide access without representing paid revenue. Manual browser creation, HTML/ZIP/folder import, editing, preview, and publishing remain free. Read [the shared capability and version contract](/docs/platform-contract.md) for stable public discovery, private effective availability and optional [app support declarations](/docs/app-support.md). Implemented transport is stateless **Streamable HTTP** with JSON responses. It supports initialize, initialized notifications, ping, tools/list and tools/call. There is no server-initiated SSE channel or stateful MCP session. Browser GET requests accepting HTML receive the public connection page; protocol GET and DELETE requests return 405. Initialize negotiates `2026-07-28`, `2025-11-25`, `2025-06-18` or `2025-03-26`. Later requests use the negotiated `MCP-Protocol-Version`. Every MCP POST requires a valid bearer credential and current account eligibility. Browser Origin is validated when present. Prefer OAuth so clients can manage consent, refresh, and revocation. Named credentials from Dashboard → Agent credentials are shown once, have a default 90-day expiry, and can be revoked. Keep credentials in the client's secret mechanism, never in an app, URL, package ZIP, or source control. New named/manual credentials created with `POST /api/tokens` must explicitly include `project_id`: one owned project ID, or `null` after deliberately selecting all owned projects, including future projects. Omitting the field returns `PROJECT_SELECTION_REQUIRED`. The Dashboard form requires the same choice; existing credentials retain their boundaries. OAuth selects its boundary through consent. Example remote HTTP configuration (adapt the exact keys to your client and complete OAuth): ```json { "mcpServers": { "hackemist": { "type": "http", "url": "https://YOUR_APP_HOST/mcp" } } } ``` See [client setup and generated packages](/mcp) for Codex/ChatGPT, Claude Code, Gemini CLI, Cursor, VS Code/Copilot, and Mistral/custom HTTP setup. Local tests and documented formats do not establish live third-party login behavior or public directory approval. After the owner's initial authorization, an agent can create the app, save its static build, fill all listing metadata, upload or capture a thumbnail/screenshots/real gameplay demo, and run checks plus publication without creator forms. Follow [the autonomous publishing workflow](/docs/autonomous.md) for the complete sequence and acceptance test. ## Scopes and the tool catalog | Scope | Tools | | --- | --- | | `projects:read` | `get_profile`, `projects_list`, `project_get`, `project_access_list`, `project_access_requests_list`, `listing_get`, `listing_media_history`, `hosting_info`, `capabilities_get`, `project_validate`, `project_preview`, `compatibility_get`, `runtime_options_get`, `save_integration_get`, `capture_status`, `project_release_checklist` | | `projects:write` | `project_create`, `project_update`, `project_access_grant`, `project_access_revoke`, `project_access_request_decide`, `listing_update`, `project_delete`, `compatibility_request`, `runtime_options_update`, `connections_request`, `media_upload_create`, `media_upload_chunk`, `media_upload_finalize`, `media_upload_status`, `media_upload_abort`, `listing_asset_remove`, `listing_asset_update`, `listing_gallery_reorder`, `project_capture_media` | | `projects:publish` | `project_publish`, `project_release` | | `files:read` | `files_list`, `file_read`, `project_export` | | `files:write` | `file_write`, `files_replace`, `file_delete`, `version_rollback`, `bundle_upload_create`, `bundle_upload_chunk`, `bundle_upload_status`, `bundle_upload_finalize`, `bundle_upload_abort` | | `media:write` | Explicit permission to replace/remove existing media, edit media text/order, or call `listing_asset_restore`/`listing_asset_delete`; also requires `projects:write`. Ordinary project-writing permission can fill missing initial media | Consent selects one owned project or all owned projects, including future projects. A project-bound credential cannot create another project. Every operation checks current credential validity, ownership, project boundary, scopes, and eligible Pro access. An administrator's MCP credential still cannot operate another user's source. tools/list exposes only tools permitted by scope. Capture creation additionally needs both `projects:read` and `projects:write`; a complete release needs project read, write and publish scopes together. Upload tickets and capture receipts remain bound to the credential that created them. Reading status does not grant another client access to an existing client's ticket or capture. `media:write` is a separate owner consent choice, unchecked by default. Updating source, app details or release notes does not grant it. A client can create missing first-release media with project-writing permission, but replacing existing thumbnail, screenshots or demo requires explicit media consent. Retain existing media when the owner only requests an app update. Revoking permission prevents subsequent mutations, including an unfinished capture's attachment. Build source projects in the coding client, then transfer their static output. Files use safe relative bundle paths. file_write and files_replace support UTF-8 or strict base64 contents; supply `base_version_id` to detect a stale draft. Bundle size, expanded size, file count, paths, and content checks apply to both MCP and manual imports. ZIP finalization also uses the app import selector (operator repository reference: `APP_IMPORTS.md`) to locate wrapped static output and report skipped ancillary files. The server does not run npm, build hooks, a shell, or arbitrary server-side application code. Read `hosting_info` and `capabilities_get` before choosing features. The latter returns the shared versioned effective capability/limit contract; include `project_id` for an owned project's current availability. It performs metadata discovery without running browser checks or granting permissions. Read `initialize` and `tools/list` for the running server's current instructions and scope-filtered catalog. `hosting_info` reports live limits, taxonomy, browser capabilities, room-service availability, `creator_program_enabled`, `plus_publishing_enabled` and `plus_content_enabled`. The creator program is off by default. An administrator can separately grant selected accounts Plus publishing: their current `tools/list` includes `premium` visibility while off, and their Plus apps require viewer access whose plan benefits include `premium_apps`. The grant does not enable MCP access or creator earnings. Without that grant, the off-state schema omits `premium`; stored Plus apps remain effectively public/free and ordinary source/listing updates can preserve their unchanged paused choices. Omit hidden program fields when editing. `monetization_enabled` stays hidden and cannot be enabled while the global program is off, including for grantees. Grant revocation takes effect in fresh server checks rather than waiting for credentials to expire. When globally enabled, existing eligibility and approved-creator rules apply. Ordinary building, manual publishing and Pro/MCP subscriptions remain independent. See the administrator policy (operator repository reference: `CREATOR_PROGRAM_OPERATIONS.md`). `runtime_options_get` returns creator cloud-save/room options, current `policy_revision`, and approved/pending external origins. `save_integration_get` inspects bounded source hints and the save declaration in an owned draft/version; it never executes uploaded code or reads player data. `runtime_options_update` accepts supported `auto_save`, `isolated`, and `multiplayer` booleans with `base_policy_revision`, preserving other permissions. `connections_request` requests exact external backend origins for administrator review, preserving approved/pending capabilities. Both follow the same ownership and operator controls as Studio. Read [creator connections](/docs/connections.md), exposed as `hackemist://docs/connections`, and integrate the SDK or external service in your source; enabling a permission does not add app logic. Pointer capture, immersive WebXR VR/AR, native workers and shared-memory WASM are controlled site-wide by Admin → Site settings, all enabled by default. They work automatically for supported apps and need no creator/MCP permission request. `hosting_info`, `compatibility_get` and `runtime_options_get` expose `graphics_settings` and the effective profile. WebXR and workers automatically use dedicated project/player origins when wildcard runtime hosting is available; `isolated_available` and `webxr_available` report that deployment prerequisite. Shared memory requires effective workers and retains cross-origin isolation with matching external-asset CORS/CORP headers. Disabling workers also suspends shared memory. Global disables override legacy project flags, and project requests cannot override them. Spatial tracking does not enable raw camera, microphone or unrelated sensor APIs. A secure context, compatible device/browser, player gestures/consent and authored session controls remain required. Managed service-worker caching, nested frames and external origins retain their separate controls. Validation warns when the site has disabled required APIs or WebXR hosting is unavailable; normal-screen fallbacks can still publish. Validate the saved draft with `project_validate`, then open its short-lived private `project_preview`. Validation includes publishing findings and the authenticated browser-check endpoint when available. Browser checks use a private Chromium service with a temporary in-memory SDK bridge, no account/database volume and no application credentials; they do not prove authenticated saves or external services work. `project_export` returns an authenticated ZIP download endpoint. Compatibility requests require administrator review and cannot grant browser permissions themselves. Example tool call: ```json { "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "file_write", "arguments": { "project_id": "YOUR_PROJECT_ID", "path": "index.html", "content": "

Hello Hackemist

", "base_version_id": "YOUR_CURRENT_DRAFT_VERSION" } } } ``` File writes create immutable draft versions. App-details, listing and media edits also start a new numbered draft when editing an already published version; further metadata saves reuse that unpublished draft, and unchanged saves create no version. Publication is separate and requires publishing scope. `project_publish` defaults to the complete-release workflow: require complete listing/media, pass the current `listing_revision`, run fresh browser checks and snapshot the saved app details, rich listing and gallery with the chosen build. `project_release` exposes this workflow directly. Both complete operations require project read, write and publish scopes. Explicit `release_mode: "basic"` on `project_publish` selects the operator-configured gate for intentionally limited releases; it preserves required operator checks and does not claim complete readiness. Manual studio publishing keeps its existing configured behavior. Changed build/permissions invalidate old browser receipts. `version_rollback` restores to a new draft, without changing public publication. `project_delete` soft-deletes the app and revokes active launches and project-bound credentials. ## Private account access A published `private` app stays out of public discovery, Launches, creator profiles and SEO listings. Its `/app/{project_id}` URL is account gated; copying it does not authorize another visitor. The owner can grant registered accounts access through Studio's **Private access** dialog or these project-scoped MCP tools: - `project_access_list` (`projects:read`) accepts `project_id` and optional `page`/`limit` (up to 100). It returns bounded `grants`, including `user_id`, username, expiry and active state, plus pagination and `max_grants`. - `project_access_grant` (`projects:write`) accepts `project_id`, a registered `username` and optional `expires_at` as a future Unix timestamp in seconds. Omit expiry for ongoing access. Regranting the same account updates its expiry; the current project limit is 200 retained grants. - `project_access_revoke` (`projects:write`) accepts `project_id` and the grant's `user_id`. It revokes that account's grant and existing launches while retaining saved player data. - `project_access_requests_list` (`projects:read`) accepts `project_id` and optional `page`/`limit` to list pending access requests, with pagination. - `project_access_request_decide` (`projects:write`) accepts `project_id`, `request_id`, `decision` (`approve` or `deny`) and optional `expires_at` as a future Unix timestamp in seconds. Approval creates the usual account grant; omit expiry for ongoing access. Only the project owner can manage these grants. They can be prepared before publication, but authorize only the current published private release and its published listing/media. Invitees must sign in to the granted account and retain normal account play eligibility. The grant does not authorize drafts, previews, source/file APIs, Studio, analytics, historical releases or the owner's MCP credentials. No email invitation or bearer access link is sent. Stored grants survive visibility changes but are effective only while the app has a published private release. Expired or revoked grants fail fresh server access checks; changing or republishing source does not turn a grant into draft access. The copied `/app/{project_id}` share URL and private listing/media endpoints require the signed-in authorized account. The underlying short-lived `/run/...` runtime capability retains its bearer semantics; it is not an invitation or cryptographically bound to one browser. Treat raw runtime URLs as private while the issuing session/grant is live. Private asset requests recheck current authorization, and revocation, expiry, unpublication or a changed published version invalidates the capability. This preserves the existing opaque runtime sandbox. The owner-only HTTP equivalents are `GET /api/projects/{project_id}/access`, `POST` to the same route with `username` and optional `expires_at` (timezone-qualified ISO date or `null` for ongoing access), and `DELETE /api/projects/{project_id}/access/{user_id}`. Private sharing is separate from administrator Plus publishing grants and subscription-plan assignment. Signed-in visitors who cannot open a shared app link can submit `POST /api/projects/{project_id}/access-requests` with optional `message` (up to 500 characters). It returns a generic 202 `{ok: true, message: ...}`; this confirms neither that the app exists nor that access was approved. Unknown, public, unpublished, self/admin and already-authorized targets return the same acceptance without creating a request. A request never grants app, listing or media access by itself. The owner reads pending requests with `GET /api/projects/{project_id}/access-requests`, using `page` and `limit` (default 25, at most 100), and submits `POST /api/projects/{project_id}/access-requests/{request_id}/decision` with `decision: "approve"` or `"deny"`. Approval accepts optional `expires_at` as a future Unix timestamp, timezone-qualified ISO date or `null`; it creates the normal account grant and requires a current published private release. The response reports `status: "approved"` or `"denied"` and includes `grant` on approval. Targeted in-app notifications use generic text without the private app's title: the creator's notice opens Studio, an approved requester's notice opens the app, and a denied requester's notice opens Notifications. Delivery follows the existing site notification switch, account notification capability and inbox/daily limits; pending requests remain reviewable in Studio. There is no email or follower broadcast. One request row is retained per requester/project, including handled requests. Identical decision retries return the recorded status without granting access again. Limits are 200 pending requests per project, 20 pending per requester and 10 submissions per account per hour; handled rows do not consume pending capacity. A full pending queue returns the same generic acceptance without recording another request. A denied requester can receive a later direct owner grant; resubmitting does not reopen the handled request. These endpoints and owner review tools preserve MCP ownership and credential boundaries; there is no requester MCP tool for another creator's app. ## Rich listing metadata Call `listing_get` with `project_id` to read the draft, published snapshot and current `revision`. Call `listing_update` with `project_id`, that integer `base_revision`, and a `metadata` object. Title, description, visibility, category, subcategory and tags use `project_create`/`project_update`; pass the same current `base_revision` to `project_update`. Both forms stage their saved fields for the next release and share the listing revision guard. Owner reads show draft details; public visitors keep the prior release until explicit publication. Unknown fields, stale revisions and cross-project access are rejected. When the creator program is enabled, `monetization_enabled` still requires the existing approved-creator authorization; an agent cannot approve its owner or grant itself access. Setting visibility to private immediately withdraws public access; restoring an allowed public visibility requires publication. ```json { "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "listing_update", "arguments": { "project_id": "YOUR_PROJECT_ID", "base_revision": 0, "metadata": { "input_controls": ["touch", "keyboard"], "device_targets": ["phone", "desktop"], "languages": ["en"], "orientation": "any", "accessibility": ["reduced-motion"], "engine": "Godot", "licence": "MIT", "release_notes": "Added touch controls." } } } } ``` Supported fields are controls, languages, device targets, orientation, accessibility descriptors, engine/framework, licence, credits, requirements and release notes. See rich-listings.md (operator repository reference: `rich-listings.md`) for enums and bounds. `listing_update` does not accept screenshot IDs, remote clip URLs or authentication fields. Dedicated media upload and capture tools attach owner-scoped gallery assets. Tool responses omit account credentials and internal media paths. Keep bearer/OAuth credentials in the client's secret mechanism, outside model-facing tool arguments and app code. Publish separately with `project_publish`; pass the saved `listing_revision` returned as `revision` to reject a concurrent listing change. `version_id` selects the saved build, and both identifiers remain project scoped. Listing changes do not become public merely because a coding client saves them. For a complete automated release, `project_release_checklist` reports missing build/listing/media requirements. Initial publication and new/replaced galleries need at least five screenshots of different real features or scenes. The gate requires five distinct trusted stored content checksums; different asset IDs or captions are insufficient, and byte variation alone does not prove semantic differences. Inspect the images. An initial or new/replaced demo must have a validated duration from 30 through 60 seconds. A thumbnail, every screenshot and demo each need accurate nonempty caption, alt text and description. Existing same-project media is preserved by default when details or source change, with original IDs, text and truthful capture provenance. `project_release_checklist`, `project_release` and complete `project_publish` accept optional `media_policy: "preserve"` (default) or `"require_current"`. The latter requires captured media to match the intended source/policy but never generates media. Both policies still run fresh static and desktop/mobile browser checks for the current app. Pass the intended `version_id` and current `listing_revision`. Basic and manual publishing retain their existing media requirements. The private checker must be available; a failed or stale release remains unpublished. Checklist contract v2 reports actual media inventory and explicit legacy count/duration exemptions. Empty media is absent under every policy and never reported retained. `ready`/`preflight_ready` mean preflight eligibility only; inspect `browser_checks`/`pending_checks`, `current_requirements`, `complete_release_ready`, `account_requirements` and `publication.completed` separately. A preserved legacy exemption does not meet the current complete-release requirements. Existing exact browser evidence still does not skip the fresh check in `project_release`. Startup/layout evidence does not verify gameplay or authenticated saves. Studio's Publishing checks uses this same checklist; manual publishing continues to use the operator-configured gate. `hosting_info.responsive_authoring` exposes the source layout choices, attributes, events, supported resolution bounds and browser viewport matrix. Implement responsive CSS or engine resizing before declaring `data-hackemist-layout="responsive"` or `"adaptive"`; complete releases advertising phone/tablet require an authored `responsive_contract` and an active declaration observed in the browser. Fullscreen fluid games use `data-hackemist-viewport="fit"`; adaptive also means viewport fit. Optional `data-hackemist-resolutions`/`hackemist:layout` resolution profiles apply only to adaptive apps and preserve shape through uniform contain scaling. Read [CREATOR_GUIDE.md](/docs/creator-guide.md) and test actual menus/gameplay/HUD/controls in each supported shape. Source layout choices are edited through `file_write`/`files_replace`; they do not require an undiscoverable server setting. `project_validate` and `project_release_checklist` return `browser_targets` derived from current listing `device_targets`/`orientation`. Checker report version 2 records each target's `layout_mode`, `horizontal_overflow`, `vertical_overflow`, `viewport_fit_required` and `content_outside_viewport`. Explicit responsive horizontal overflow blocks; viewport-fit games must also fit vertically and keep required stage/controls within bounds. Responsive documents can scroll vertically. Undeclared/fixed overflow remains a warning for legacy uniform-fit apps. Reports and media policy fingerprints include advertised targets, so changing devices/orientation requires fresh evidence. The matrix includes desktop 1440 × 900, phone 390 × 844/844 × 390, tablet 768 × 1024/1024 × 768, TV 1920 × 1080 as advertised, bounded to six loads. This is startup layout evidence, not proof of every gameplay state or real-device performance. Browser checking has a bounded budget based on the requested matrix: 12 seconds for startup plus eight seconds per target, at least 25 and at most 60 seconds. Its private HTTP call allows a further seven seconds for the response. Capture uses a separate server-derived budget of at most 240 seconds, with a ten-second HTTP margin for bounded cleanup and response delivery; media validation follows afterward. Compatible MCP clients should allow up to 300 seconds for capture and at least 90 seconds for release/publish calls. A checker `checker_deadline` signal marks the job unavailable and returns an actionable retry/startup-inspection hint; it does not create a passing receipt or assert a layout defect. ## Media uploads and real browser capture `media_upload_create` accepts `project_id`, `kind` (`thumbnail`, `screenshot`, `demo`), `byte_length`, `sha256`, current `base_revision`, `caption` (240 characters), `alt_text` (500), `description` (2000) and optional idempotency key. Descriptive fields are optional for staging but required for a complete release. Their values are included in ticket idempotency checks and attached immutable asset metadata. Connector clients use `media_upload_chunk` and `media_upload_finalize` without exporting their bearer. Clients that already manage a private bearer can use authenticated binary PUT to the returned upload URL. Length/checksum, current Pro access, ownership, scopes and revision are checked before sanitized bytes attach to the draft. Query `media_upload_status` or cancel an unfinished ticket with `media_upload_abort`. Repeating a completed PUT returns the same attachment. For connector-managed authorization, use `media_upload_chunk` with at most 262,144 decoded bytes of strict base64 per call, then `media_upload_finalize`. Read status after uncertain responses and retry identical chunks at the original offset. Legacy authenticated binary PUT remains supported; it is not required for connector clients. Use `listing_asset_update` with `project_id`, a retained draft `asset_id`, current `base_revision` and `metadata` containing `caption`, `alt_text` and/or `description` to edit asset text without reuploading bytes. Per-listing snapshot overrides preserve published and historical text. Unknown fields, stale revisions, foreign and detached assets are rejected. Studio has the same caption/alt-text/description controls and staged thumbnail uploads. Refresh `listing_get` after every edit. `project_capture_media` asks the private Chromium service to capture the saved browser app. Supply `project_id`, optional `version_id`, `base_listing_revision` and required `idempotency_key`. Bounded dimensions and integer `seconds` (zero to 60) are optional; the default is 30 seconds at 15 frames per second. Plan 30–60-second public demos around the app's actual menu, representative interactions and a visible outcome, using controls and scenes you have inspected. Allowlisted pre-capture `actions` and timed `demo_actions` can click accessible controls or simple selectors, send keys, or wait briefly before recording. At most 60 actions are accepted across both lists, and sorted `demo_actions.at_ms` cannot exceed the chosen duration or 60000 milliseconds. Explicit shorter diagnostics and thumbnail-only captures remain supported. This interface cannot execute arbitrary JavaScript, navigate to external backends, grant runtime permissions or automatically choose gameplay highlights. Inspect the result before describing scenes or outcomes. A capture with video enabled stages a thumbnail, up to eight screenshots and a real silent MP4 of running browser output in one listing revision; `seconds: 0` captures only a thumbnail. Fresh captures use contract v3. Without a screenshot selector, the server chooses eight timed stills across advertised device families: desktop, phone and tablet together produce three desktop, three phone and two tablet images. While device metadata is unset, the existing desktop/phone diagnostic fallback applies. Each image is captured at its actual viewport after resizing the running app, then the fixed video viewport is restored. This records the app's own responsive layout; it does not stretch a desktop image into a phone image. Optional `screenshot_plan` specifies one to eight entries `{width, height, at_ms, scene}`. Width and height use the same even 320–1920 and 180–1080 bounds as capture dimensions. Scheduled times must be sorted, unique integers from 1 through `seconds * 1000`, at most 60000. Each required scene label is 1–120 plain characters without control characters and describes the caller's intent, not verified gameplay. One to four entries support diagnostics; a new complete gallery still needs five to eight distinct real scenes. `screenshot_plan` and legacy `screenshot_at_ms` are mutually exclusive. The legacy array still accepts five to eight times at the selected video dimensions. Supply matching `screenshot_captions`, `screenshot_alt_texts` and `screenshot_descriptions`, up to eight entries each. See [the mixed-device example](/docs/autonomous.md#capture-the-app-itself). Thumbnails are captured natively at 1280 × 800 (16:10), matching the listing-card frame. Uploaded artwork and other image shapes fit uniformly inside their frame so the whole image remains visible. The thumbnail has its own viewport; `width`/`height` select the video viewport. Screenshot provenance includes `capture_version`, `capture_width`, `capture_height`, `capture_at_ms`, `capture_elapsed_ms` and `capture_scene`, alongside the original source version and policy fingerprint. Scheduled `capture_at_ms` and actual elapsed `capture_elapsed_ms` are separate because browser work can delay a capture. The receipt echoes the normalized `screenshot_plan`; retained older captures keep their real original provenance. Each still is freshly captured; the 15 fps encoded video can repeat frames on slower software rendering. `replace_screenshots: true` is the default and replaces only the draft gallery while preserving live/history; false appends within the eight-shot limit. Supply `thumbnail_caption`/`thumbnail_alt_text`/`thumbnail_description` and equivalent `demo_` fields. Omitted capture text receives factual generic descriptions; explicit empty text prevents a complete release. Receipts report `frame_count`, `distinct_frames` and `motion_detected`, which establish visual change without claiming gameplay or external-service correctness. Capture supports bundled assets only. It exposes asset metadata, not binary base64 or private filesystem paths. `capture_status` takes the returned `capture_id`; identical idempotent retries reuse its result. Capture creation needs both project read and write scopes. Media is private until the chosen version and listing are explicitly published. Use `listing_asset_remove` and `listing_gallery_reorder` with the current revision to maintain draft media. See [AUTONOMOUS_PUBLISHING.md](/docs/autonomous.md) for the workflow and limits. For continuous movement, pair safe `key_down` and `key_up` actions for an allowlisted key at the intended start/end times. Both count toward the 60-action total. Held keys are released during cleanup. Single `key` actions remain presses; pre-capture waits remain bounded to four seconds total. These controls cannot execute arbitrary JavaScript or provide an external backend. For an initial release, capture/upload missing required media as part of the requested publishing work. For an existing app update, retain its thumbnail, screenshots and demo unless the creator requests a refresh. Source/details saves do not automatically recreate media. Explicit `project_capture_media` calls accept `media_kinds`, a nonempty unique subset of `thumbnail`, `screenshot` and `demo`, defaulting to all three for video capture. Zero seconds defaults to thumbnail only. Unselected media stays unchanged; screenshot replacement/append applies only when screenshot is selected. Use the returned draft version ID when an explicit media refresh forks a published source. Preserved kinds are identified by unchanged asset IDs in an earlier published listing. Legacy media can retain eligible existing count/duration exemptions during updates; absent media remains missing under every policy and is never treated as retained. Initial releases and newly added/replaced kinds must meet the current five-distinct-image and 30–60-second gate. Other required text, app metadata, source, authorization and fresh browser checks remain enforced. Checklist output includes `retained_media_kinds` (gallery label `screenshots`); capture input remains singular `screenshot`. Replaced or detached assets remain available to their owner through `listing_media_history`, including assets that were never published. `listing_asset_restore` restores a retained asset into the draft with its original capture provenance, using `project_id`, `asset_id` and current `base_revision`. This does not change the public release. `listing_asset_remove` only detaches a draft reference. `listing_asset_delete` is a separate permanent erasure with those identifiers and `confirmed: true`; it refuses assets still selected by the draft or current live release. Recovery and deletion require both project-writing permission and explicit `media:write` consent. Keep all retained assets unless the creator specifically asks to delete them; the existing 40-asset limit and storage quota apply, with errors rather than automatic eviction. ## Resumable binary uploads For larger HTML/ZIP bundles, call `bundle_upload_create` with filename, byte length, SHA-256, project, and current draft version. Prefer `bundle_upload_chunk` through the connector authorization, with `upload_id`, `offset_bytes` and strict `data_base64` for at most 262,144 decoded bytes. Legacy binary PATCH remains supported for clients already managing their private credential, using the returned URL and `Upload-Offset` header. After an interruption, query `bundle_upload_status` and resume at the exact offset. Finalization verifies complete length, checksum, current draft, and bundle rules before creating one version. Repeating a successful finalization returns that version. Abort unwanted transfers. Upload URLs contain no credential; transfers remain owner/project scoped and expire. Connector clients do not need to export a bearer or fall back to inline files: use the native chunk/status/finalize tools. The browser also supports HTML/ZIP/folder import. ## OAuth 2.1-style authorization-code/PKCE Discovery endpoints: - `/.well-known/oauth-protected-resource` - `/.well-known/oauth-authorization-server` The resource audience is the exact `${APP_ORIGIN}/mcp`. An unauthenticated challenge points to protected-resource metadata. Clients can use public-client dynamic registration at `/oauth/register`, a supported HTTPS Client Identifier Metadata Document (CIMD), or an administrator's pre-registered client identity and redirects. Public clients use token-endpoint authentication `none`, without a client secret. HTTPS redirects must match exactly. Clients explicitly registered as `application_type: native` can use loopback HTTP callbacks with an ephemeral port: only the port may differ, while scheme, raw host, path and query remain exact. Literal `localhost` is accepted only for native clients; loopback IP literals are preferred. The issued code remains bound to the exact callback used in the authorization request, including its port. Wildcards, non-loopback HTTP, credentials and port zero are rejected. Registration and CIMD retrieval remain bounded and validated. 1. Generate a cryptographically random verifier and S256 challenge. 2. Open `/oauth/authorize` with client_id, exact redirect_uri, response_type=code, code_challenge, code_challenge_method=S256, requested space-separated scopes, state, and resource. 3. The owner signs in, selects permissions, and explicitly chooses a project boundary. Consent is bound to that browser session, nonce, and CSRF token. 4. Exchange the one-time code at `/oauth/token` with grant_type=authorization_code, code, client_id, redirect_uri, code_verifier and resource. 5. Use the returned 15-minute access token only on this MCP resource. Rotating refresh tokens require both client refresh support and the user's explicit approval of requested `offline_access`; a renewable grant lasts at most 90 days while the account remains eligible. Deselecting or omitting that permission produces only a short access token. If a refresh request narrows its scope to exclude `offline_access`, it returns a final short access token without a new refresh token; renewal requires fresh consent. 6. Revoke at `/oauth/revoke` or Dashboard → Agent credentials. Authorization-code and refresh-token replay revoke the affected grant family. On upgrade, previously renewable OAuth connections whose approved scopes lack `offline_access` are revoked and must reconnect through consent. Correctly approved renewable connections, ordinary short OAuth connections, and named Dashboard credentials keep their existing behavior. The token endpoint also rejects these unconsented legacy refresh credentials independently of the startup migration. OAuth callbacks identify the issuer. Client metadata changes, expired access, account deletion, token revocation, disabled MCP, and loss of eligible plan access are checked during the relevant request. Tokens are audience-bound to the exact MCP resource; no upstream provider token is passed through. ## Stdio-only clients and verification `scripts/mcp_stdio.py` bridges newline-delimited stdio JSON-RPC to the authenticated HTTP endpoint. Configure `HACKEMIST_MCP_URL` and `HACKEMIST_MCP_TOKEN` through the client's private environment, then launch the script with Python. It uses the standard library, avoids credential logging and redirects, and preserves protocol negotiation. The bridge still requires eligible Pro access and scoped credentials; it does not create a separate authorization path. The independent tests exercise scoped token issuance, ownership, draft conflicts, consent/PKCE/replay, live limits, and restored-capability revocation. The official Python MCP SDK interoperability test is optional and runs only when that SDK is installed. Client configuration checks and browser tests are separate from live vendor acceptance. ## Security expectations - Prefer one token per agent/task and the smallest scope/project boundary. - Never paste the token into user-authored browser code. - Treat uploaded code and external prompt content as untrusted instructions. - Unpublishing/quarantine/deletion and token revocation take effect on subsequent requests. - Review tool output `isError`; HTTP 200 can carry a correctly encoded MCP tool error. - The server provides structuredContent and a readable text content block on success. ## Primary protocol references - [MCP Streamable HTTP transports](https://modelcontextprotocol.io/specification/2026-07-28/basic/transports) - [MCP authorization](https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization) - [OAuth protected-resource metadata, RFC 9728](https://www.rfc-editor.org/rfc/rfc9728) - [PKCE, RFC 7636](https://www.rfc-editor.org/rfc/rfc7636) Verify current client compatibility and the deployment's TLS/discovery behavior during launch acceptance. Local tests do not establish every third-party connector's behavior.