This guide shows you how to add games to GABS and what the important config fields mean.
For most users, this is the only command you need:
gabs games add factoryGABS will ask a few questions and save the result to your local config file.
When you run gabs games add <game-id>, GABS asks for:
A friendly label such as Example Game or AdventureGame.
How GABS should start the game:
- DirectPath: a local executable or script
- SteamManaged: a Steam App ID resolved to the installed game executable
- SteamAppId: a legacy Steam launcher URL for compatibility
- EpicAppId: Use Epic Games Store ID
- CustomCommand: Use a custom command with arguments
The executable path, App ID, or command for the selected launch mode:
- For DirectPath:
/path/to/game.exe - For Steam:
123456(the App ID) - For Epic: The Epic App ID
- For Custom: Your complete command
Where the game should run from. Leave blank to use the game's default location.
The real game process name used by games.stop and games.kill.
This is required for Steam and Epic launch modes. Without it, GABS can launch the game but cannot stop the actual game process reliably.
Examples:
- For AdventureGame:
GameName.exe(Windows) orAdventureGame(Linux/macOS) - For FactorySim with Java:
java - For Unity games: often the game name with
.exeextension
After setup, verify the saved config:
gabs games list
gabs games show factory- GABS stores your games in
~/.gabs/config.json - GABS starts games using the launch data you entered above
- If the game also has a GABP-compatible bridge, GABS can connect to that bridge and mirror game-specific tools into MCP
Most users can skip this section.
GABS uses local-only GABP communication for security and simplicity.
When you start a game, GABS passes GABP connection data to the game-side bridge through:
- Environment variables:
GABP_SERVER_PORT,GABP_TOKEN,GABS_GAME_ID
The bridge.json file is GABS' endpoint cache/debug artifact. Game-side bridge
code should not read it as runtime configuration or use it as a fallback for
missing GABP_* environment values.
In the GABP layer, your game-side bridge listens for the connection and GABS connects to it.
GABS also keeps an internal ownership record at ~/.gabs/{gameId}/runtime.json
so separate GABS sessions do not accidentally launch or drive the same game at
the same time. Ownership is an expiring active-use lease, so you can hop
between live AI sessions by using games_connect after the previous session
goes idle. Game integrations should ignore runtime.json; it is for GABS
itself.
gabs games listShows all configured games and their current status.
gabs games show factoryShows complete configuration for one game.
gabs games remove factoryRemoves the game from your configuration.
Your games are saved in ~/.gabs/config.json.
Example:
The top-level "version" field below is the GABS config schema version, not
the GABP wire version.
{
"version": "1.0",
"toolNormalization": {
"enableOpenAINormalization": false,
"maxToolNameLength": 64,
"preserveOriginalName": true
},
"stripOutputSchema": false,
"timeouts": {
"startup": {
"processStartSeconds": 10,
"gabpConnectSeconds": 60
}
},
"games": {
"factory": {
"id": "factory",
"name": "Example Game",
"launchMode": "DirectPath",
"target": "/opt/factory/start.sh",
"workingDir": "/opt/factory",
"stopProcessName": "java",
"description": "Main FactorySim server"
},
"adventure": {
"id": "adventure",
"name": "AdventureGame",
"launchMode": "SteamManaged",
"target": "123456"
}
}
}Best for custom game installs, scripts, and local test setups.
{
"launchMode": "DirectPath",
"target": "/home/user/games/factory/start.sh",
"workingDir": "/home/user/games/factory"
}Best for Steam games with GABP bridges.
{
"launchMode": "SteamManaged",
"target": "123456"
}You can find the App ID in the game's Steam store URL. GABS locates the Steam
library, reads the app manifest, starts the Steam client if needed, launches the
resolved executable with GABP environment variables, and prepares
steam_appid.txt when direct Steamworks startup requires it. Configured args
are passed to the game in this mode. If Steam or the platform relaunches the
final game process without inheriting those variables, games_status reports
process-bridge-environment-missing; use DirectPath or CustomCommand for a
wrapper that the final process actually inherits from.
Use gabs games doctor <id> to inspect the resolved executable and
gabs games repair <id> to switch an older SteamAppId config to this mode.
Legacy Steam launcher URL mode.
{
"launchMode": "SteamAppId",
"target": "123456",
"stopProcessName": "GameName.exe"
}You can find the App ID in the game's Steam store URL. stopProcessName is
required for Steam games.
GABS starts Steam games through the platform launcher URL. Configured args
are not passed to the game in this mode. Put launch options such as
-savedatafolder=... in Steam's own launch options, or use SteamManaged,
DirectPath, or CustomCommand when GABS must control process arguments and
bridge environment directly.
In launcher-driven setups, an already-running platform launcher process can
prevent GABS from proving that new environment variables reached the real game
process. games_status reports whether the real game process environment is
readable. Prefer SteamManaged over launcher URL mode, but still verify the
final process environment. If it reports process-bridge-environment-missing,
use DirectPath or CustomCommand. For an existing Steam launcher config, run
gabs games repair <id>.
Best for games installed through Epic Games Store.
{
"launchMode": "EpicAppId",
"target": "your-epic-app-id",
"stopProcessName": "GameName.exe"
}stopProcessName is required for Epic games.
As with Steam, configured args are not passed to the game in this mode. Use
the game launcher's own launch options, DirectPath, or CustomCommand for
process arguments.
Best for complex launch setups or special requirements.
{
"launchMode": "CustomCommand",
"target": "java -Xmx4G -jar server.jar --nogui",
"workingDir": "/opt/factory"
}This section is mainly useful if you are writing or debugging a game-side bridge.
GABS uses local-only GABP communication.
- GABS connects to game integrations on localhost (
127.0.0.1) only - Each game gets a unique port and token
- GABS may write
~/.gabs/{gameId}/bridge.jsonas an endpoint cache/debug artifact
When you start a game, GABS sends GABP configuration through environment variables:
GABP_SERVER_PORTGABP_TOKENGABS_GAME_ID
Game integrations should read these environment variables directly.
When a game is already starting or running, GABS writes a per-game
runtime.json file so other live GABS sessions can see whether the game
currently has an active owner.
- A second
games.startreturns immediately with "already starting" or "already running" instead of launching a second copy games.connecttakes ownership naturally once the previous owner's lease is idlegames.connectreturns immediately with an active-owner result when another session is still inside its lease- game-bound tool calls also check the lease before touching the bridge
games.statusreports the runtime owner, lease expiry, and bridge diagnostics
If you intentionally want a different GABS session to take over before the
active lease expires, use games.connect with forceTakeover: true.
If games.start reports endpoint_cache_in_use, the cached port is already
listening. Use games.connect if an already-running bridge owns that endpoint.
Use games.start with resetEndpoint: true only after confirming the cached
endpoint should be rotated for a new process.
GABS exposes strict-safe MCP tool names by default. This keeps tools/list
accepted by clients that reject dotted names, including Claude variants seen in
the field. The configuration key keeps its historical name for compatibility.
The toolNormalization section supports these options:
enableOpenAINormalization(boolean): Enable/disable strict-safe MCP name normalization (default:truewhentoolNormalizationis omitted)- Replaces dots, slashes, and other unsafe separators with underscores
- Enforces 64-character length limit
maxToolNameLength(integer): Maximum length for tool names (default:64)preserveOriginalName(boolean): Store original name in tool description/metadata (default:true)
Set enableOpenAINormalization to false only when you intentionally need the
old dotted MCP names in tools/list.
Example transformations:
games.call_tool->games_call_toolfactory.inventory.get->factory_inventory_get- GABP
core/pingfor gameadventure->adventure_core_ping
Call aliases remain backward compatible: games_call_tool, games.call_tool,
qualified slash names, qualified dotted names, and discovered strict-safe names
all resolve without underscore guessing.
{
"version": "1.0",
"toolNormalization": {
"enableOpenAINormalization": true,
"maxToolNameLength": 64,
"preserveOriginalName": true
},
"stripOutputSchema": false,
"games": {
// ... your game configurations
}
}For complete details about tool normalization, see the Tool Normalization Guide.
Some MCP clients reject tools/list responses when a public tool includes an
outputSchema field. Mirrored game tools are discovered through
games_tool_names, but GABS can still omit output schemas from the public tool
list:
{
"stripOutputSchema": true
}Default: false.
Use this for clients such as Claude Code if they disconnect with an
outputSchema.type validation error while reading tools/list.
This does not change tool execution and does not remove input schemas. Detailed
tool metadata, including output schema information, remains available through
games_tool_detail.
If your game takes longer to appear in the process list or longer for its GABP
game-side bridge to start listening, you can override the startup process wait
and connection budget in ~/.gabs/config.json.
The timeouts.startup section supports these options:
processStartSeconds(integer): How long GABS waits for the launched game process to become detectable in the OS process list (default:10)gabpConnectSeconds(integer): The total connection budget for the game GABP server to become available (default:60).games_startwaits only for a bounded initial slice of that budget, returns before MCP clients hit their own tool-call timeout, and continues connecting in the background. You can also pass a one-offtimeoutargument togames_startfor unusually slow bridge startup without changing the saved configuration.
The timeouts.session section supports:
ownerLeaseSeconds(integer): How long an idle GABS session remains the active runtime owner after a normal game-bound action (default:30). Long game-bound calls extend the lease to cover their requested timeout plus a small safety margin, so this value controls roaming between idle sessions, not the maximum duration of a running command.
games_start only waits for an initial GABP handshake window. If the game is
still loading, GABS keeps trying in the background for the remaining startup
budget. Mirroring the connected bridge's full tool list can continue briefly in
the background. The public tools/list response stays stable, and known startup
commands can be sent immediately through games_call_tool while discovery tools
refresh.
{
"version": "1.0",
"timeouts": {
"startup": {
"processStartSeconds": 20,
"gabpConnectSeconds": 120
},
"session": {
"ownerLeaseSeconds": 30
}
},
"games": {
// ... your game configurations
}
}GABS can stop games more reliably when stopProcessName is set correctly.
When stopProcessName is configured, GABS will:
- First try to find and stop processes with that name
- If no processes are found with that name, fall back to stopping the launched process (if any)
- Support both graceful termination (games.stop) and force killing (games.kill)
The process finding works across platforms:
- Windows: Uses
tasklistandtaskkillcommands - macOS: Uses
pgrepwith standard process signals - Linux: Uses
pgrepwith standard process signals
| Game | Platform | Process Name |
|---|---|---|
| AdventureGame | Windows | GameName.exe |
| AdventureGame | macOS/Linux | AdventureGame |
| FactorySim (Java) | All | java |
| Unity Games | Windows | GameName.exe |
| Steam Games | All | Check game's install directory |
{
"games": {
"adventure-steam": {
"launchMode": "SteamManaged",
"target": "123456"
},
"factory-server": {
"launchMode": "DirectPath",
"target": "/opt/factory/start.sh",
"stopProcessName": "java"
},
"epic-game": {
"launchMode": "EpicAppId",
"target": "epic-app-id",
"stopProcessName": "GameName.exe"
}
}
}For launcher-based games (SteamAppId and EpicAppId), stopProcessName is
mandatory. SteamManaged launches the resolved game executable directly, so
stopProcessName is optional.
- Check that your target path or ID is correct
- Make sure the game is installed
- Run
gabs games doctor <id> - Try running the launch command manually first
- Make sure your game-side bridge supports GABP
- Check that the game-side bridge is listening on the right port
- Verify the game-side bridge is using
GABP_SERVER_PORT,GABP_TOKEN, andGABS_GAME_IDfrom the environment - Run
games_statusand inspectdiagnostics.code,diagnostics.message, andnextActions; it can identify stale runtime state, runtime ownership, and whether the real game process environment is readable. - If
diagnostics.codeisprocess-bridge-environment-missing, the running process is visible but cannot be attached through its environment. For Steam launcher URL configs, rungabs games repair <id>first; if managed launch still loses the environment, useDirectPathorCustomCommand.
The config file is created automatically when you add your first game. If it's missing, run gabs games add to create a new one.