> For the complete documentation index, see [llms.txt](https://wiki.playgama.com/playgama/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wiki.playgama.com/playgama/mcp/tools.md).

# Tools reference

The Playgama MCP server exposes **20 tools**. Your client discovers them automatically once connected — this page is for knowing what to ask for, and what each tool will and will not do.

A few rules hold everywhere:

* Every tool works on **your own games** only — the ones you see in the cabinet. An id that is not yours, or a deleted game, is refused the same way an unknown one is.
* Schemas are **strict**: an argument the tool does not declare is refused by name rather than silently dropped.
* Reads are marked read-only; **every write is annotated as destructive**, so your client asks for confirmation before running it.
* Business refusals (validation, ownership, a stale checksum) come back as readable results the agent can act on, not as protocol errors.

## Games

### list\_applications

Every named game on your account, newest first — `id`, `title`, `status` and `updatedAt`. Takes paging only: no status filter and no search, so the agent matches on the rows it gets back. The order is fixed and does not change when you edit a game, so paging visits every game exactly once.

Deleted games never appear here, and neither does a game whose title was never filled in — the cabinet does not list those either.

| Parameter | Type    | Required | Description           |
| --------- | ------- | -------- | --------------------- |
| `limit`   | integer | No       | 1–100. Default 20     |
| `skip`    | integer | No       | How many rows to skip |

### create\_application

Creates a new game as a draft, the way the cabinet's **New Game** page does, and answers its `id` — the id every other tool takes.

Titles are not unique, so after a call that failed without a clear answer the agent should check `list_applications` before creating again; a duplicate draft is deleted by a human in the cabinet.

| Parameter | Type   | Required | Description                                                                                                                        |
| --------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `title`   | string | Yes      | In English — Latin letters, digits, spaces and punctuation, up to 120 characters. Players and moderators see it as-is              |
| `engine`  | enum   | Yes      | `unity`, `construct3`, `cocos-creator`, `defold`, `godot`, `gdevelop`, `game_maker`, `scratch`, `js` (plain JavaScript, no engine) |

### get\_application

The full saved form of one game, together with the metadata of its archives and media. File contents and download links are not part of this surface.

| Parameter       | Type   | Required | Description                                 |
| --------------- | ------ | -------- | ------------------------------------------- |
| `applicationId` | string | Yes      | Game id, as returned by `list_applications` |

### update\_application\_form

Saves form fields of a game. This is a **partial save**: a field left out keeps its stored value, so the agent sends only what it means to change. Enum arrays are replaced whole. Saving does not submit anything to moderation.

| Parameter              | Type    | Required | Cabinet field                                   |
| ---------------------- | ------- | -------- | ----------------------------------------------- |
| `applicationId`        | string  | Yes      | —                                               |
| `title`                | string  | No       | Title                                           |
| `description`          | string  | No       | Description                                     |
| `howToPlayText`        | string  | No       | How to play                                     |
| `engine`               | string  | No       | Game Engine                                     |
| `link`                 | string  | No       | Link to the game if it is published elsewhere   |
| `isHorizontal`         | boolean | No       | Screen Orientation — landscape                  |
| `isVertical`           | boolean | No       | Screen Orientation — portrait                   |
| `leaderboards`         | boolean | No       | Game Features — leaderboards                    |
| `multiplayer`          | boolean | No       | Game Features — multiplayer                     |
| `social`               | boolean | No       | Game Features — social                          |
| `distributeEverywhere` | boolean | No       | Distribution — publish on all partner platforms |
| `excludedPlatforms`    | array   | No       | Distribution — platforms to exclude             |
| `supportedLanguages`   | array   | No       | Game Languages                                  |
| `supportedDevices`     | array   | No       | Supported Devices — `IOS`, `ANDROID`, `DESKTOP` |

The accepted values of the enum arrays are published to your client in the tool schema, so the agent sees the current list without guessing. For what each field means to a moderator, see [Submitting a game](/playgama/submitting-a-game.md).

Covers, screenshots and archives are **not** form fields here: covers have their own tools, and other assets are not on this surface at all.

### get\_submission\_state

Whether the game can be submitted to moderation right now and, when it cannot, why: missing certification, a cooldown after a failed moderation, or nothing changed since the last submit.

Submitting itself is not available through MCP — a human does it in the cabinet.

| Parameter       | Type   | Required | Description |
| --------------- | ------ | -------- | ----------- |
| `applicationId` | string | Yes      | Game id     |

### list\_moderation\_comments

The moderation correspondence on a game, newest first, grouped by moderation task. Read-only: replying to moderation is not part of this surface.

| Parameter       | Type   | Required | Description |
| --------------- | ------ | -------- | ----------- |
| `applicationId` | string | Yes      | Game id     |

## Game builds

Uploading a build is three steps, and the bytes never travel through MCP: the agent gets a presigned URL, performs the upload itself, then confirms.

{% hint style="info" %}
**start → PUT → confirm → poll.** The upload URL is valid for one hour and is signed for exactly the byte length and content type you declared, so the storage refuses anything else. Nothing is in the game's form until the confirm.
{% endhint %}

### start\_archive\_upload

Step 1 of 3. Creates an archive record for the game and answers `uploadUrl` and `headers` — PUT the zip to it with exactly those headers:

```bash
curl -T game.zip -H "Content-Type: application/zip" "<uploadUrl>"
```

The zip must contain `index.html` at its root (or inside a single top-level folder) with the Playgama Bridge SDK integrated.

| Parameter       | Type    | Required | Description                                                                                                                                                                             |
| --------------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `applicationId` | string  | Yes      | Game id                                                                                                                                                                                 |
| `name`          | string  | Yes      | Up to 200 characters. Put the build version in it, e.g. `coin-town-1.4.2` — the form keeps every archive, and the version is how a human tells them apart. A trailing `.zip` is dropped |
| `contentSize`   | integer | Yes      | Exact size of the zip in bytes (`wc -c < game.zip`). At most 300 MB                                                                                                                     |

### confirm\_archive\_upload

Step 3 of 3: tells the cabinet the PUT has finished. The archive is added to the game's form and queued for unpacking and Bridge SDK analysis. Archives already in the form stay there — removing one is a human action in the cabinet.

Refused with 409 while nothing is stored at the upload URL. Safe to repeat: once processing has run, a repeat only answers the current state.

| Parameter       | Type   | Required | Description                           |
| --------------- | ------ | -------- | ------------------------------------- |
| `applicationId` | string | Yes      | Game id                               |
| `archiveId`     | string | Yes      | As answered by `start_archive_upload` |

### get\_archive\_status

The state of one archive. Unpacking takes a minute or more, so the agent polls this until `processing` is `DONE` or `FAILED`.

| Field          | Meaning                                                                                                                   |
| -------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `processing`   | `PENDING` while the upload is unconfirmed or being unpacked, `DONE` once published, `FAILED` if the zip could not be used |
| `errorMessage` | Why it failed                                                                                                             |
| `inForm`       | Whether the archive is in the game's form                                                                                 |
| `analysis`     | The Bridge SDK analysis (version, engine) once it ran; `null` before that                                                 |

| Parameter       | Type   | Required | Description |
| --------------- | ------ | -------- | ----------- |
| `applicationId` | string | Yes      | Game id     |
| `archiveId`     | string | Yes      | Archive id  |

## Covers

The form has three cover slots, and each takes an image of exactly that size — resize before uploading:

| Slot        | Size        |
| ----------- | ----------- |
| `square`    | 800 × 800   |
| `portrait`  | 1080 × 1920 |
| `landscape` | 1920 × 1080 |

### start\_cover\_upload

Step 1 of 3, one slot per call. Answers `mediaId`, `uploadUrl` and `headers`:

```bash
curl -T cover.png -H "Content-Type: image/png" "<uploadUrl>"
```

| Parameter       | Type    | Required | Description                                                          |
| --------------- | ------- | -------- | -------------------------------------------------------------------- |
| `applicationId` | string  | Yes      | Game id                                                              |
| `cover`         | enum    | Yes      | `square`, `portrait`, `landscape`                                    |
| `format`        | enum    | Yes      | `png` or `jpeg` — checked against the bytes on confirm               |
| `contentSize`   | integer | Yes      | Exact size of the file in bytes (`wc -c < cover.png`). At most 10 MB |

### confirm\_cover\_upload

Step 3 of 3. The file is checked — it must be the format the upload was started as, exactly the size of its slot, and a complete, decodable image — and then put into the form **in place of** the cover that was there; `replaced` names what it took out.

Refused with 409 while nothing is stored at the upload URL, and with 422 when the file does not fit (with the reason — upload a corrected file with a new `start_cover_upload`). Safe to repeat: a confirmed cover only answers its state.

| Parameter       | Type   | Required | Description                         |
| --------------- | ------ | -------- | ----------------------------------- |
| `applicationId` | string | Yes      | Game id                             |
| `mediaId`       | string | Yes      | As answered by `start_cover_upload` |

## In-app catalog

{% hint style="warning" %}
`replace_in_app_products` is a **whole-state write**, not a patch: a product left out is deleted, and an empty list clears the catalog. The agent must call `list_in_app_products` first and pass its `checksum` back — if someone edited the catalog in the browser meanwhile, the call is refused instead of deleting their work.
{% endhint %}

### list\_in\_app\_products

The catalog of a game as currently saved, plus the `checksum` naming that exact state. Each product comes back in exactly the fields a replace accepts, so the answer can be sent straight back with one field changed.

| Parameter       | Type   | Required | Description |
| --------------- | ------ | -------- | ----------- |
| `applicationId` | string | Yes      | Game id     |

### replace\_in\_app\_products

Replaces the whole catalog with the list passed in. The form's "has in-game purchases" flag travels with it: leave `hasInGamePurchases` out and it follows whether the list is empty. Turning purchases on with an empty catalog is refused.

| Parameter            | Type    | Required | Description                                                               |
| -------------------- | ------- | -------- | ------------------------------------------------------------------------- |
| `applicationId`      | string  | Yes      | Game id                                                                   |
| `products`           | array   | Yes      | The complete catalog after the change                                     |
| `expectedChecksum`   | string  | Yes      | The `checksum` `list_in_app_products` answered — the state being replaced |
| `hasInGamePurchases` | boolean | No       | Defaults to whether `products` is non-empty                               |

Each product:

| Field         | Type   | Required | Description                      |
| ------------- | ------ | -------- | -------------------------------- |
| `productId`   | string | Yes      | Stable id, unique in the catalog |
| `title`       | string | Yes      | —                                |
| `description` | string | No       | —                                |
| `type`        | enum   | Yes      | `consumable` or `non_consumable` |
| `priceUsd`    | number | Yes      | Price in USD, at most 2 decimals |

## Leaderboards

Deleting a leaderboard is not available through MCP — it takes its scores with it and cannot be undone, so it stays a human action in the cabinet.

### list\_leaderboards

The leaderboards configured for a game.

| Parameter       | Type   | Required | Description |
| --------------- | ------ | -------- | ----------- |
| `applicationId` | string | Yes      | Game id     |

### create\_leaderboard

Adds a leaderboard to a game. `id` is the tag the game passes to the [Playgama Bridge SDK](/playgama/bridge-sdk/api/leaderboards.md).

| Parameter       | Type    | Required | Description                         |
| --------------- | ------- | -------- | ----------------------------------- |
| `applicationId` | string  | Yes      | Game id                             |
| `id`            | string  | Yes      | Leaderboard tag, e.g. `top_players` |
| `name`          | string  | Yes      | —                                   |
| `type`          | enum    | Yes      | `numeric` or `time`                 |
| `scoreOrder`    | enum    | No       | `desc` or `asc`                     |
| `decimals`      | integer | No       | —                                   |

### update\_leaderboard

Changes the name, type or score order of an existing leaderboard. At least one of them is required.

| Parameter       | Type   | Required | Description         |
| --------------- | ------ | -------- | ------------------- |
| `applicationId` | string | Yes      | Game id             |
| `leaderboardId` | string | Yes      | The leaderboard tag |
| `name`          | string | No       | —                   |
| `type`          | enum   | No       | `numeric` or `time` |
| `scoreOrder`    | enum   | No       | `desc` or `asc`     |

## QA Tool

Both tools hand out a link for you to open in a browser. Ask for the link every time rather than building it by hand — the cabinet's address depends on the deployment.

### get\_archive\_qa\_tool\_link

The link that opens an uploaded archive in the Playgama QA Tool, where you play the build inside the Bridge SDK harness and submit it to moderation. The page has something to show only once `processing` is `DONE`.

| Parameter       | Type   | Required | Description |
| --------------- | ------ | -------- | ----------- |
| `applicationId` | string | Yes      | Game id     |
| `archiveId`     | string | Yes      | Archive id  |

### get\_local\_game\_qa\_tool\_link

A link that opens the QA Tool on a game you are serving yourself — typically a dev server on `localhost`. Use it to test before uploading anything.

| Parameter   | Type   | Required | Description                                                                                                                                                                    |
| ----------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `url`       | string | Yes      | Absolute http(s) URL of the game's index page, e.g. `http://localhost:5503/`                                                                                                   |
| `sessionId` | string | No       | Keys the QA Tool's `localStorage`: letters, digits, `-` and `_`, up to 64. Leave it out for a fresh environment every time; pass your own key to keep saved state between runs |

## Sandbox

A **sandbox** is a public page where anyone with the link plays the build you uploaded — no submit, no certification, no moderation, no partner platform. One sandbox per game, and its address never changes, so re-publishing keeps the players' saved progress.

{% hint style="danger" %}
`publish_sandbox` puts the game in front of anyone with the link **immediately**. It is the only write on this surface whose effect is visible outside the cabinet, which is why it has a consent line of its own on the token page. Tell your agent to ask before calling it.
{% endhint %}

Publishing is refused when the archive failed to unpack, when it has not finished unpacking, when the game has no title, when the sandbox was blocked, and after **3 publications in a rolling hour**. `get_sandbox_state` gives the same verdict in advance.

### get\_sandbox\_state

What is on the game's sandbox right now: the link, the status (`ACTIVE` or `BLOCKED`) and the live revision — its archive, the title it was published under, and when. `site` is `null` when the game has never been published to a sandbox.

Pass `archiveId` and the answer also carries a `publish` verdict for that archive:

| Field            | Meaning                                                                                                                                   |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `allowed`        | Whether `publish_sandbox` would go through                                                                                                |
| `reason`         | When it would not: `ARCHIVE_FAILED`, `ARCHIVE_PENDING`, `NO_TITLE`, `BLOCKED`, `RATE_LIMITED`                                             |
| `changeStatus`   | `CHANGED` — publishing writes a new revision; `UNCHANGED` — the same thing is already live; `UNKNOWN` — cannot be told, and never refuses |
| `archiveChanged` | Whether that archive differs from the one on the page                                                                                     |

This is also the check-up read after a publish whose answer never arrived: `UNCHANGED` means it landed, `CHANGED` means it did not.

| Parameter       | Type   | Required | Description                                                                      |
| --------------- | ------ | -------- | -------------------------------------------------------------------------------- |
| `applicationId` | string | Yes      | Game id                                                                          |
| `archiveId`     | string | No       | An archive of this game. When given, the answer also carries the publish verdict |

### publish\_sandbox

Publishes an archive to the game's sandbox page. The archive must be in the game's form and finished unpacking (`get_archive_status` says `DONE`), and the game must have a title.

Answers `outcome`: `PUBLISHED` — a new revision is live; `UNCHANGED` — the same archive and the same form were already published, nothing was written and the link is the same one. A repeat after a failed or lost answer is therefore safe: it never creates a second revision of the same thing.

Take the link from `url` in the answer and hand it to the developer — never build it by hand.

| Parameter       | Type   | Required | Description                                                 |
| --------------- | ------ | -------- | ----------------------------------------------------------- |
| `applicationId` | string | Yes      | Game id                                                     |
| `archiveId`     | string | Yes      | An archive of this game, in its form and finished unpacking |
