Publish games from your AI app with PlayDrop MCP
How AI agents publish to PlayDrop through MCP: the simple and package paths, private testing, publish_game_version, updates, limits and recovery.
PlayDrop's MCP server lets an AI app publish a game to its human's PlayDrop account and return a link that keeps working through updates. This guide is the order of operations. Each step links to the guide with the details.
Before you build
- Call
get_documentation({topic:"index"})and read the guides for the features the game will use, before writing code. Planning full-screen layout, saves and controls from the start avoids rewriting the game later. For exact method signatures read the topicssdk-types,server-sdk-typesandshared-types. - Call
list_games. If the game already exists, you will update it and keep itsname, so it keeps its link and players. - Choose the path. One self-contained HTML file without the PlayDrop SDK uses the simple path. Everything else, including any SDK feature, listing media or a server, uses a package.
Keep the creator's source project yourself. PlayDrop receives built output only: it is not a source backup and cannot return source code.
Simple path: upload_game
Send requestId, the complete HTML (at most 10 MiB, 10,485,760 UTF-8 bytes) and a mini catalogue. Pass gameId to update an existing game. Versions increment automatically. See simple HTML games for the fields.
Package path: prepare, upload, complete
Call prepare_catalogue_upload with requestId, expectedCurrentVersion and one input form:
- Folder:
catalogue(the content of catalogue.json) plusfiles, one descriptor per file with its relative path. A descriptor carries inlinetext, a signed-upload description, an attachment binding orreuseof a file from a retained version. - Local ZIP or .play: unpack it locally, then send the root catalogue and folder descriptors. Never put base64 archive bytes in a tool call.
- ChatGPT attachment: pass the ZIP or .play the host resolved as
archive, with its realdownload_urlandfile_id. It may be up to 20 MiB compressed and needs catalogue.json at the root.
For signed uploads, each descriptor supplies path, contentType, sizeBytes, sha256 (64 lowercase hex characters) and md5Base64, computed locally from the exact bytes. PUT those bytes to each returned URL with every returned header, then call complete_catalogue_upload with the same requestId.
In ChatGPT, folder files can come from attachments bound by position:
attachments: [htmlFile, iconFile]
files: [{path:"index.html", attachmentIndex:0}, {path:"listing/icon.png", attachmentIndex:1, role:"LISTING", app:"my-game"}]Every app needs a declared version, a visibility (on the request or the app) and an entry in expectedCurrentVersion keyed by its name: null to create it, or the current published version from list_games to update it. A stale value is refused, so another upload is never overwritten by accident.
Test privately, then release
Upload with visibility: "private" when the game uses the SDK or a server, or when your human wants to try it first. Open the receipt's url to play that exact version inside PlayDrop with the real SDK host. Hosted server functions only run on uploaded versions, so test them this way.
When it works, release that exact version with publish_game_version, passing gameId, version, expectedCurrentVersion and, if your human chose one, visibility (public or unlisted). It reuses the tested files, listing and review. Do not upload the game again to release it.
To try a local build in the PlayDrop frame before uploading, open https://www.playdrop.ai/@<username>/<slug>/dev?url=<URL-encoded local address> in a normal browser on the machine that runs the local server.
Choose who can play
visibility accepts public, unlisted and private. For a new game without a choice, use the creator's saved setting: upload_game applies it when you leave visibility out, and get_account shows it as publishing.defaultVisibility. For an update, keep the game's gameVisibility from list_games unless your human changes it; upload_game does this when you leave visibility out, and a package states it. Unlisted means anyone with the link can play; it is not a friends list. Never make a game more visible than your human intended. Details: who can play.
Hand back the right link
Receipts for public and unlisted games include shareUrl, the game's lasting page: give that one to your human. A private version returns url, its exact-version link for the owner. Package receipts list each item; completed items stay published even if a later item fails.
Update a game
Read list_games and the current version, keep the name, and send a complete snapshot: every intended file and all intended metadata, because omitted fields are absent from the new version. Omitted catalogue items are never deleted. Use get_game_version to read the exact catalogue of the version you are changing, including its Tweaks state.
Limits
- Simple path: one HTML file of at most 10 MiB.
- Package: 512 folder file descriptors or 512 archive entries (including catalogue.json and directories), 50 MiB per file, 200 MiB unpacked in total and 1 MiB of catalogue JSON.
- Archives: safe relative paths only; duplicate paths, symlinks, encrypted, multipart and ZIP64 archives are refused.
- Prepared uploads expire after 30 minutes.
Errors and retries
Errors carry error, message, retryable, nextAction, docsUrl and documentationTopic. Follow nextAction. After a lost response, retry identical content with the same requestId; changed content needs a new one. See errors and recovery.
Use send_feedback to report a reproducible problem or a confusing step, without tokens or signed URLs.