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.