# Shared MCP agent contract Read this resource once through `hackemist://docs/agent-contract`; tool descriptions contain operation-specific requirements. Discover static documentation with `resources/list` and read exact listed URIs with `resources/read`. `resources/templates/list` returns an empty `resourceTemplates` array: this service offers static allowlisted resources, not arbitrary file or URI expansion. Read `hosting_info` and `capabilities_get` for current limits and versioned effective availability; pass `project_id` for project-specific state. These metadata reads do not run browser checks or grant permissions. `initialize` and `tools/list` describe the current running server and scope-filtered catalog. ## Authentication and account requirements Creating a new named/manual credential through `POST /api/tokens` requires explicit `project_id`: an owned project ID, or `null` for deliberately selected all-owned access including future projects. Omission returns `PROJECT_SELECTION_REQUIRED`; existing credential boundaries stay unchanged. OAuth selects its boundary through consent. Studio uses its browser session and CSRF token. MCP uses a separately authorized scoped bearer or OAuth connection. Signing into Studio does not authorize an MCP client, and a connector manages its bearer privately. Never request or export a connector-managed bearer to upload files. `get_profile` exposes `email_verified` and `account_requirements`; `hosting_info` includes current account requirements, plan access and upload limits. `publish_allowed` describes the current account and optional credential gates only. It does not establish source, media, moderation, browser, gameplay or save readiness. Recheck the release checklist immediately before publication. OAuth supports exact administrator-registered IDs, dynamically registered IDs returned by `/oauth/register`, and verifiable HTTPS client metadata documents. A client's default name or arbitrary default ID is not a registration. Discovery advertises registration and metadata support. `invalid_client` now includes a safe `error_code`: `client_not_registered`, `invalid_redirect_uri`, `redirect_uri_mismatch`, or `client_metadata_unverifiable`. Configure the correct ID and callback or use supported dynamic registration; do not substitute another application's identity or weaken validation. HTTPS metadata must identify its exact document URL; TLS, public-address checks, no redirects, size/deadline limits, S256 PKCE, exact callbacks, resource audience and explicit consent remain enforced. Native loopback clients may vary only the callback port. The cause of the recorded external default-client rejection remains unconfirmed without that client's request/metadata; a local unregistered-ID rejection is reproducible and intentional. ## Connector-safe bounded uploads These additive native MCP tools work through the existing connector authorization. They return no bearer, upload capability secret or credential-bearing URL. Legacy authenticated HTTP upload endpoints remain supported for clients that already manage their own private credential. 1. Read `hosting_info`; build static output outside the server. For a bundle call `bundle_upload_create` with ZIP/HTML filename, exact byte length, full-file SHA-256 and current `base_version_id`. For listing media call `media_upload_create` with kind, exact byte length, full-file SHA-256, current listing `base_revision`, and honest caption/alt text/description. Use a stable media idempotency key for create retries. 2. Split bytes into chunks of at most **262,144 decoded bytes**. Call `bundle_upload_chunk` or `media_upload_chunk` with the ticket's `upload_id`, `offset_bytes` and strict `data_base64`. Read the returned `received_bytes`. Never concatenate base64 encodings of separate chunks; encode each byte chunk separately. 3. After interruption or an uncertain response, call the matching status tool. Retry the identical chunk at its original offset, or continue at the acknowledged offset. Already received identical bytes are accepted; conflicting or overlapping bytes are rejected. No partial request is committed. Tickets and offsets persist across worker restart while the staging volume is retained. 4. Call `bundle_upload_finalize` or `media_upload_finalize`. Full length/hash, file/media type, size/quota, expiry, current authorization, ownership, project scope and draft/listing revision are checked. Media decoding runs outside the write transaction and authorization/consent is rechecked at attachment. Successful finalize retries return the existing version or attachment. A stale draft or listing requires a fresh ticket against the current revision. 5. Abort an unused ticket with the matching abort tool. Media tickets expire after one hour; bundle tickets after 24 hours. Successful finalization/abort removes staged bytes. Creation also sweeps expired staging in bounded batches. Account deletion removes native media staging through the existing cleanup hook. A deleted project becomes inaccessible; its unfinished tickets expire. Expired or missing staging requires a new ticket; no account data is automatically merged or overwritten. Bundle tickets retain their existing account/project ownership rules. Media tickets remain bound to the exact creating credential. Both paths reject revoked/expired credentials and changed scopes/entitlements. Replacing existing media also requires `media:write` and creator authorization; scope consent alone is not a request to regenerate media. Uploads stage drafts and never publish automatically. ## Shared creation and release constraints Read `hackemist://docs/autonomous` for the complete publishing sequence, `hackemist://docs/save-sdk` before implementing account progress, and the creator guide before responsive layout work. The server hosts built static assets and never executes build hooks or arbitrary application server code. Use current source-version, listing-revision and runtime-policy guards. Runtime options do not integrate application code automatically; reviewed external connections retain their separate review requirement. Preserve player data, account/slot boundaries, migration originals and recovery warnings. Preview/local-only progress is not an acknowledged account save. Edit the existing project for updates. Preserve current thumbnail, screenshots and demo unless the creator requests replacement; fill missing initial-release media. Retained media consumes storage and can be recovered through media history. Permanent history deletion requires the creator's explicit request, `confirmed=true`, correct scopes and detached current references. Capture supports bundled assets, silent MP4, 0–60 seconds (30 by default), 15 encoded fps, up to 60 combined setup/timed actions and timed inputs through 60,000 ms. A zero-second capture creates a thumbnail. Positive default capture attaches thumbnail, eight screenshots across advertised device families and demo; select `media_kinds` to change only requested kinds. Use five to eight strictly increasing screenshot times within the duration to show actual distinct features/scenes. The draft screenshot gallery is replaced by default; `replace_screenshots=false` appends within eight. Supply accurate captions, alt text and descriptions; use `key_down`/`key_up` for held movement. Allow 300 seconds. Accepted capture jobs return a durable ID, phase and safe failure/retry receipt, including failures after reservation. After transport timeout retry exactly the same key/arguments, then query `capture_status`. The stdio bridge allows 310 seconds for capture calls. Declare honest device/orientation targets, author responsive source and resolve overflow in real viewports. Browser startup, changed pixels, screenshots and a responsive declaration do not prove gameplay, full feature coverage, authenticated saves or external backend correctness. Inspect actual output. `media_policy=preserve` permits retaining actual existing assets; it does not establish that absent media exists. `require_current` additionally checks capture provenance and never regenerates media. Read `project_release_checklist`, resolve factual blockers, then explicitly release the saved current draft. A release checklist is not completed publication. Honor external client approval controls; a client approval rejection is not permission to bypass them. Checklist contract v2 keeps compatibility `ready` as preflight eligibility (`ready_scope=preflight_only`). Inspect `media_inventory`, `legacy_media_exemptions`, `waivers`, `account_requirements`, `static_preflight`, `browser_checks` and `pending_checks`. `complete_release_ready` requires the current complete requirements and valid exact browser evidence; `ready_to_publish` separately accounts for allowed legacy exemptions. `publication` describes the saved/live state. No field claims authenticated save or gameplay verification merely from page loading. ## Operations, recovery and rollback The durable accepted-job phase/status contract currently applies to media capture. Browser checks and complete release still run synchronously: browser-check rows have durable IDs, but callers do not receive a new accepted operation ID/phase/polling contract before those calls finish. Upload finalization is also synchronous; its already-issued ticket remains the durable status/retry handle and successful retries return the existing asset/version. It has no distinct processing queue or intermediate processing phase. Export remains synchronous. These contracts were audited; this change does not claim every long-running operation has become asynchronous. Following an uncertain release response, inspect the current project/live version and listing revision before retrying publication. Operators can correlate upload IDs with existing `mcp.upload.*` / `mcp.media_upload.*` audit events; inspect safe capture receipts and operator diagnostics by capture ID. Monitor expired/open ticket counts, staged disk usage, checksum failures, offset/revision conflicts and repeated authorization rejection. Retrying after a lost response should not create another version or asset. Do not log base64 payloads, bearer tokens, consent nonce/code, raw upstream responses or private account data. Deploy the additive media-ticket columns with the normal schema initializer; no existing player save, project, asset or token rewrite is required. Keep the durable data volume. Rollback may hide new native chunk/finalize tools while preserving existing HTTP endpoints and immutable published data; staged tickets can be retried after re-upgrade or allowed to expire. Do not downgrade by restoring an old database over current user data. Offline or external-client errors remain distinct from confirmed server failures. Local protocol tests do not certify every external connector/vendor login or production deployment. ## Local regression evidence A separate confirmed storage boundary defect affected explicitly read-only MCP/checker previews: their native origin was the same account/project/default-slot origin as an actual player, even though their account bridge was disabled. Authored preview scripts could therefore change the player's browser-only cache. Read-only previews now add a versioned preview partition to the origin HMAC input; runtime host verification enforces that partition. All historical real-player default/named-slot HMAC inputs remain unchanged. Ordinary authenticated Studio previews keep their intended player bridge and current selected slot. Reopen old standalone preview URLs after upgrade; no saved data is moved, merged or deleted. This finding does not establish that production account saves were deleted. `tests/test_readonly_preview_origins.py` tests actual MCP preview issuance, account/default/named-slot/guest separation, immutable draft updates, ordinary Studio preview continuity, successful runtime HTML responses and rejection of crossed runtime hosts. Optional `HACKEMIST_READONLY_ORIGINS_OUTPUT` exports only those synthetic test origins; point `HACKEMIST_READONLY_ORIGINS_FIXTURE` at the result and run `node --test tests/browser_readonly_preview_origins.spec.mjs` to verify real browser localStorage writes/reloads remain separated. Set `HACKEMIST_BROWSER_PATH` if a system Chromium/Edge is required. No launch capability is exported in that fixture. `tests/test_mcp_connector_protocol.py` exercises OAuth registration/consent/token exchange, resource/template discovery, native bundle and decoded PNG media transfers, restart recovery on the retained volume, interrupted/truncated requests, identical retry, conflicting offsets, 256 KiB boundaries, wrong hashes, stale versions/revisions, expiry, other-account/credential rejection and authorization/consent changes during decoding. It includes the official Python MCP 1.29.0 Streamable HTTP client and an actual loopback HTTP server behind `scripts/mcp_stdio.py`. Existing media/publishing/OAuth suites continue to test the legacy binary HTTP transport. These are isolated fixture data, not production account checks. Run with the pinned requirements-dev dependencies (or the already prepared `.local/mcp-sdk` added to PYTHONPATH) and an isolated data directory: ```text python -m unittest discover -s tests -p test_mcp_connector_protocol.py -v ``` Historical native-chunk upgrade measurement, using Unicode character counts: the recorded external connector had 42 exposed tools and 241,510 description characters, including a roughly 5,300-character common prefix. The pre-upgrade local source had 44 tools, 7,439 operation-description characters and 5,298 initialize-instruction characters; that release had 47 tools, fewer than 7,500 operation-description characters (longest 497) and 737 initialize-instruction characters. The earlier source-derived repetition estimate was approximately 240,551 to 42,100 characters (82.5% less), not an external post-deployment measurement. These historical counts do not describe the current catalog: use `initialize` and `tools/list` for the running server's instructions and permitted tools.