MCP server
taploka-mcp lets a coding agent publish games by talking to an arcade over the Model Context Protocol. It is a single
file with no dependencies and needs Node 20 or newer.
Install
taploka-mcp is a plain MCP stdio server, so any agent that speaks MCP can run it: Claude Code, Codex CLI, Cursor,
Windsurf, Gemini CLI, VS Code Copilot, and everything else that reads a mcpServers block. Claude Code writes its
own entry with one line:
claude mcp add taploka -- npx -y taploka-mcp@latest
Every other harness takes the same registration as JSON, wherever that tool keeps its config (~/.codex/config.toml
for Codex, .cursor/mcp.json for Cursor, .vscode/mcp.json for VS Code, ~/.gemini/settings.json for Gemini CLI):
{
"mcpServers": {
"taploka": {
"command": "npx",
"args": ["-y", "taploka-mcp@latest"]
}
}
}
Then connect it:
npx -y taploka-mcp@latest login
login asks the arcade for a short code, opens the approval page, and stores the key it is handed in
~/.taploka/credentials.json with owner-only permissions. See Harness API for what
happens on the wire.
| Command | Effect |
|---|---|
login [--url=…] [--profile=…] |
Connect this machine to an arcade: the official one, unless --url names a self-hosted one |
whoami [--profile=…] |
Show which arcade a profile points at |
logout [--profile=…] |
Forget a profile |
help |
Usage |
Without a command the script speaks MCP over stdio, which is how the client runs it.
Tools
push_game
Upload a bundle and release it.
| Argument | Meaning |
|---|---|
zip |
Path to a ZIP or a folder. A folder is zipped locally, skipping dotfiles and node_modules. |
title |
Name of the game. On a first push the slug is derived from it; later it is ignored. |
game |
Slug of an existing game. Use this on every push after the first. |
rollout |
Percentage of players served this version, 0-100. Default 100. |
changelog |
Short note stored with the version. |
draft |
Store and validate without releasing. Default false. |
Returns the version number, status, rollout, file count, play URL and the full list of validation checks.
update_game
Change what players see. Only the fields you pass are touched.
game (required), title, tagline, description, translations (the store pitch per language), cover (local
image path), cover_url, banner (local image
path), banner_url, screenshots (local
image paths, up to 6: they replace the whole set), screenshots_url (hosted URLs instead), categories (lowercase
slugs, max 5), features (offline, leaderboard, cloud_save), orientation, max_score,
max_score_per_second, cumulative_score, sort_order, coin_price, shop.
coin_price is what makes a game premium: a number keeps it browsable but starting a session requires a coin
purchase (a game_unlocks row mints on payment), null turns it back to free. The purchase credits the game's
owner and the unlock is permanent. See Coins and shop.
translations is the second exception: it replaces the whole map. Keep title, tagline and description in
Indonesian, the arcade's house language, and put the English pitch in translations.en. Only tagline and
description translate, and a code the arcade does not advertise is dropped rather than stored, so read the accepted
codes from game_status first.
update_game(game: 'rosok-garage', translations: {
en: { tagline: 'Fix junk, sell gold.', description: 'Run the yard, chase the record.' },
})
shop is the exception to "only the fields you pass": it replaces the game's whole coin price list, deactivating any
sku left out.
update_game(game: 'rosok-garage', shop: [
{ sku: 'engine_lv2', title: 'Mesin Lv2', coin_price: 800 },
{ sku: 'fuel', title: 'Bensin', coin_price: 150, type: 'consumable' },
])
See Coins and shop for what a game does with it.
A cover is uploaded as you have it (PNG, JPEG or WebP, up to 3 MB) and the arcade re-encodes it to WebP scaled to
fit 1200x800. Ship the source image; there is no need to convert or shrink it first.
A banner goes through the same upload and is the wide artwork (about 1200x500) used for the home hero and the header
of your detail page. Leave it unset and both slots fall back to the cover.
list_games
Every game with its status, version count, live version and play URL.
game_status
One game in detail: metadata, the version ladder with rollout and sizes, score totals, play and leaderboard URLs. Its
languages list is the set of codes translations accepts.
Profiles
One profile is one arcade. Log in again with a profile name to add another target:
npx -y taploka-mcp@latest login --profile=prod --url=https://your-arcade.example.com
claude mcp add taploka-prod -e TAPLOKA_PROFILE=prod -- npx -y taploka-mcp@latest
A profile never falls back to another profile's settings. If a profile has no key, the push stops with a message rather than quietly reaching a different server.
Environment
Environment variables win over stored credentials, which is what a build machine wants.
| Variable | Meaning |
|---|---|
TAPLOKA_URL |
Arcade to talk to. Only for a self-hosted arcade: the default is the official one |
TAPLOKA_KEY |
Harness token |
TAPLOKA_PROFILE |
Selects a profile: prod reads TAPLOKA_PROD_URL and TAPLOKA_PROD_KEY |
TAPLOKA_NO_BROWSER |
Set to anything to stop login opening a browser |
With no URL set, the default profile talks to the official Taploka arcade (or the app's own URL when run from this
repo). An environment key is still never paired with a URL left behind by login.
Protocol support
The server is dual-era. Clients on MCP 2026-07-28 get server/discover, per-request _meta versioning,
resultType, cache hints on tools/list and structuredContent on results. Clients still using the initialize
handshake (2025-11-25, 2025-06-18) are answered in the shape they expect. An unsupported version comes back as
error -32022 listing the versions on offer.