Old School Ops

Client Automation API

Letting an AI agent or your own program observe and play a character through the GoMMO client, over MCP or HTTP.

Every game · updated 2026-11-06
On this page

The GoMMO client can be driven by a program outside it: Claude Code, Claude Desktop, any other Model Context Protocol client, a Python loop, or curl. The program sees what the character sees and acts through the same actions a client script uses: walk, fight, loot, wear, drink, talk.

Nothing listens unless you start the client with -mcp or -serve.

The API arrived in GoMMO Client 0.1.0. A section that arrived later names its version, and -version prints the version you have.

Starting it

The client is GoMMO Client, a download of its own from the GoMMO Client page: GoMMO_Client.exe for Windows, and a Linux build with no window for a server. GoMMO Client covers installing it and pointing it at your game. The examples below assume it is unzipped to C:\GoMMO and plays RF Online from C:\RF Online.

You wantRun
Claude Code to playclaude mcp add gommo -- "C:\GoMMO\GoMMO_Client.exe" -game rf2232 -rf "C:\RF Online" -headless -mcp
Your own program over HTTPGoMMO_Client.exe -game rf2232 -rf "C:\RF Online" -headless -serve
To watch while it playsleave out -headless: the window opens and the agent drives the character in it

-headless needs no graphics driver, which is what makes the Linux build useful on a server.

-game picks the game, and the game folder comes with it: -rf <folder> for RF Online, -wokf <folder> for World of Kung Fu, or -data <folder>/data for Gate to Heavens. The client reads monster and item names from that folder. -host and -port pick the server.

The client logs in with the account in the [Account] section of gommo.ini, which it makes on its first run. -login and -password name another.

Claude Desktop

Add the client to claude_desktop_config.json:

{
  "mcpServers": {
    "gommo": {
      "command": "C:\\GoMMO\\GoMMO_Client.exe",
      "args": ["-game", "rf2232", "-rf", "C:\\RF Online", "-headless", "-mcp"]
    }
  }
}

What the agent can do

Ask the client for the list: MCP tools/list or GET /tools, both generated from the build you are running. GoMMO_Client.exe -verbs lists the script verbs the single-action tools come from.

ToolWhat it does
observeEverything at once: position, health, map, the nearest entities with their distances, bag, equipment, skills, chat channels, the party and any invitations waiting on an answer (0.1.3 and later), buffs and debuffs, the cast in progress, the animus and MAU lists (0.2.1 and later), the guild, the race election and the chip war where the game has them, capabilities, standing orders, and the event cursor.
eventsWhat happened after a cursor: combat, chat, deaths, respawns, level-ups, players arriving and leaving, map changes and party changes (0.1.3 and later), buffs gained and lost and animus and MAU answers (0.2.1 and later), guild, election, chip war and module notices, standing-order notices. Pass back next as the next since. wait_seconds blocks until something happens.
walk_to, fight, loot_nearby, equip_bestThe common actions, each run to completion. fight walks to the target first.
attack, use_skill, use_item, pick_up, say, whisper, equip, step_toward, stop, ...One action each, the same as the script verb of that name.
run_luaRuns Lua in the client's script sandbox and returns what it returns. No file or network access.
start_orders, stop_orders, orders_statusStanding orders, below.

An action that returns without an error was sent. Whether it worked is in the next observe or in events: the server decides whether a swing hits or an item can be picked up.

say sends a line starting with % as a GM command in RF Online (0.2.0 and later), as the original client does, so say "%lv 20" works where the server allows player commands.

Health, experience and effects

GoMMO Client 0.2.1 and later.

In RF Online, vitals in observe carries:

Field
hp, max_hphealth
fp, max_fpforce points. mp and max_mp carry the same numbers, for scripts written before 0.2.1.
sp, max_spstamina
exp_percenthow far through the current level the character is, 0 to 100
levelthe character's level

The server sends experience only as a share of the level, so the client cannot give a raw count or the amount still needed.

effects lists the character's buffs and debuffs, each with its name, whether it is harmful, and seconds_left when the server gave a duration. events reports effect_gained and effect_lost.

casting names the skill or force whose result the server has not answered yet, and is absent the rest of the time.

A monster's level comes from your RF install. The server sends a monster's health only as a percentage, so for a monster hp is out of 100 and hp_percent says so.

Skills and forces

GoMMO Client 0.2.1 and later.

skills in observe lists the force window first, then the weapon skills a character trains (Slash, Multi Shot and the rest), then its class skills. use_skill takes the slot shown. Each entry has:

Field
namefrom your RF install, in its language
kindforce, skill or class_skill
costFP per cast
rangea force's reach, or what a skill adds to the weapon's reach
cooldown_seconds, ready_inthe delay between casts, and how long until this one can be cast again
level, masterythe level it casts at (1 to 7) and the use count that level grows from

An attack skill needs a target. A support skill, or any skill or force given no target, is cast on the character. The names, costs, ranges and cooldowns are read from your RF install; without one, forces are listed as Force <n> and skills and class skills are not listed.

Class skills are the ones a character's class and the classes it came from grant, so a Berserker keeps the class skills of the classes it was before. They always cast at level 1. An attack class skill given no target goes at the selected one, and with nothing selected it is not sent. A curse goes on its target, and anything else on the character. events reports a refused class skill as refused with the reason, and one that went off without effect as no_effect.

Class changes

GoMMO Client 0.2.1 and later. RF only.

ToolWhat it does
rf_classthe character's class: its row and code, the classes it came from, and how many class skills it may cast
rf_class_optionsthe classes it can promote into, from your RF install, with any reward items and whether one must be chosen
rf_class_probeask the server whether the character may change class now
rf_class_changeclass (a code such as BWF1, or a row) and an optional reward slot: promote
rf_class_ofanother player's class, once the server has reported a change

A promotion takes effect as soon as the server accepts it, with no relog: observe carries the new class, and the class skills the new class grants join skills behind the ones the character keeps, so no slot it already had moves. events reports offered when the server prompts a promotion, probe with its answer, changed, refused with the reason, no_answer when the server stays silent for 15 seconds, and other_changed when another player promotes. The server asks for level 30 to leave a base class and 40 to leave a first class. A class whose reward must be chosen needs a reward slot from rf_class_options, and -1 sends no choice. On RF 1.5 the reward items are not listed.

A character's forces come from the force items it has learned. To give an existing character every skill and force, type %allskills while player GM commands are on.

Animus

GoMMO Client 0.2.1 and later. Cora only.

ToolWhat it does
petsthe animus window: each with its slot, name, level, experience, HP and FP, and whether it is out
pet_refreshask the server for the list again
pet_summonslot: summon one
pet_dismisssend it home
pet_commandattack (it picks its own fights) or follow (it stays with you)
pet_attackset it on a target

observe carries the list as pets, and events reports each answer: summoned, returned, level_up, died, or refused with the reason. The server refuses an animus under 30% of its health, a summoner under 60 FP, and a second summon within 30 seconds.

There is no feeding. A resting animus heals over time, and pet_refresh shows how far it has recovered.

MAU

GoMMO Client 0.2.1 and later. Bellato only.

ToolWhat it does
vehiclesthe MAU list: each with its slot, frame name, hull, booster, fitted parts, magazines and spare packs (named, with a full magazine's size), how many spare packs its back carries, the fee for calling it out, and where it is parked
vehicle_framesthe frames you can buy, with each name and its price before tax
vehicle_buyframe: buy a new MAU; outside RF 1.5 its key goes into your bag
vehicle_fill_sparesslot: fill the spare packs (for dalant, except on RF 1.5), topping up each pack that is not full with what it holds, or with the type ids given, one per pack. Without the client's data only empty packs are topped up, since a part-used pack cannot be told from a full one
vehicle_refreshask the server for the list again
vehicle_callslot: have one delivered beside you, for its fee
vehicle_board, vehicle_leaveclimb in, or climb out where you stand
vehicle_returnsend the parked MAU back to storage
vehicle_repairslot: restore its hull for dalant
vehicle_reloadslot: refill both magazines for dalant, with the ammunition they hold, or with the type ids given

observe carries the list as vehicles, and events reports delivered, boarded, left, returned, repaired, reloaded, bought, spares_filled, towed and destroyed, or refused with the reason. Repair, reload and spare filling are refused while riding. A new MAU carries no spare packs, so filling them waits until a back that holds them is fitted. A MAU is the only thing RF Online repairs: weapons and armour do not wear out.

Frame, part and ammunition names are read from your RF install, in its language; without one they show as MAU frame <n>, MAU <part> <n> and MAU ammunition <n>. vehicle_buy takes a frame from vehicle_frames, and the price is the one before tax: bought reports what was charged. On RF 1.5 only the classic frames can be bought.

Parties

GoMMO Client 0.1.3 and later.

In a game with parties (every RF version the client plays), the agent can invite, join and lead:

ToolWhat it does
party_inviteinvite a player in sight; forms a party if you have none
party_applyask a party leader in sight to take you in
party_accept, party_declineanswer an invitation or application, by player id or the newest
party_leave, party_kick, party_disband, party_promotethe rest; the last three are the leader's
rf_party_lock, rf_party_lootRF only: close the party to newcomers, and pick how drops are shared

The party section of observe lists the members, leader first, each with health and mana as a fraction of full, level, map, and where they are. Its requests are the invitations and applications waiting on you. events reports invited, applied, joined, left, member_joined, member_left and leader_changed.

A refused invitation looks the same as an ignored one: the server says nothing unless the player accepts. RF has no "no" message either, so party_decline only forgets the request.

Stores and money

GoMMO Client 0.1.3 and later.

observe carries money (dalant and gold on RF) and the ten nearest stores, each with the NPC standing at it.

ToolWhat it does
buystore, slot, count: buy from a store. slot is the goods' place in the store's own list, from 0, in the order its window shows them.
sellstore, inventory_slot, count: sell what is in a bag cell
goodsstore: what it sells, each with the slot that buy takes and its name
rf_exchangeRF only: change dalant into gold or back

Both wait for the server and return ok, a reason when refused (the bag is full, not enough dalant, the store does not sell that), what was spent or earned, and how many bag cells filled. A refusal is an answer, not an error. The bag in observe is refreshed after each one.

No server says what a store sells, so goods reads it from your own RF install: point the client at it with -rf, or run it from inside the RF folder. In a script, lib.buy("potion", 5) finds the nearest store that sells something with that name, walks there and buys it. RF 4.15 does not list goods yet, because the 4.15 client in circulation and the 4.15 server disagree about what some stores stock.

Moving between maps

GoMMO Client 0.1.3 and later.

ToolWhat it does
portalsthis map's portals, each with its index, name and distance (needs the RF install, as goods does)
use_portalindex: step through one
recallgo home to your town

observe lists the portals too. When the map changes, map_name changes, events reports map_changed, and everything the character could see is forgotten and sent again for the new map: players, monsters, NPCs and stores.

Trading with a player

GoMMO Client 0.1.3 and later.

ToolWhat it does
trade_ask, trade_acceptoffer a trade to a player standing close by, or accept one
trade_offer, trade_withdrawput a bag cell on the table, or take it back
trade_moneyput dalant or gold on the table
trade_lock, trade_confirmboth sides lock, then both confirm, and the trade completes
trade_cancelwalk away

The trade section of observe shows both sides of the table. events reports offered, opened, their_offer_changed, they_locked, they_confirmed and closed; closed says whether it completed. The server closes a trade the moment either side does something it refuses, such as offering an item that cannot be traded, and the bag is refreshed when a trade completes.

What a game offers

capabilities in observe says which of the sections below this game has, once the client has connected: guild, election, chip_war, race_admin and modules. A script asks the same with has_guilds(), has_elections(), has_chip_war(), has_race_admin() and has_modules().

SectionGames
Guilds, election, chip warRF 4.55, 4.15, 1.5 and 2.2.3.2
Race offices and taxRF 4.55 and 2.2.3.2
Server module messagesRF 4.55 and 2.2.3.2

Guilds

ToolWhat it does
guild, guild_membersyour guild as the client last heard it, and its members
guild_createname: found a guild
guild_apply, guild_cancel_applyname: apply to a guild, or withdraw the application
guild_accept, guild_rejectanswer an application; give applicant, or leave it out for the newest
guild_leaveleave your guild
guild_refreshask the server for the roster again
guild_query, guild_infoguild_id: ask about another guild, and read what the server said

RF has no guild invitation: a player applies by guild name and the master or a committee member accepts. A guild is founded by a party, through its leader. Guild war events are not offered, because guild battle is not built.

observe carries the section as guild. events reports kind guild: created, joined, applied, application_cancelled, applicant, member_joined, member_left, left, expelled, disbanded, info, or refused with request and reason.

Race election

ToolWhat it does
electionthe election as the client last heard it: leader, council, candidates, ballot, tax_rate, next_tax_rate, tax_shares, and offer, the seat a patriarch offered you
election_candidatesask the server for the candidates
election_registerstand as a candidate
election_votename: vote for a candidate
election_officesask the server who holds the appointed seats

events reports kind election: candidates, voting_opened, score, result, council, leader_changed, offices, registered, voted, refunded, or refused.

Race offices and tax

ToolWhat it does
election_appointseat, name: the patriarch offers a seat
election_answeraccept: answer the seat you were offered
election_dischargeseat, and optionally name: empty a seat
race_tax_refreshask the server for the rate and the distribution
race_tax_setpercent: propose the next rate
race_tax_distributeshares, a list of {guild, rate}: set how the tax is shared among guilds

The seats are vice_archon_b, attack_officer_b, defence_officer_b and support_officer_b. The rules, and who may do what, are on Race Offices and Tax.

events reports these under kind election too: appoint_ok, appoint_offered, appointed, appointment, tax_rate, tax_change_accepted, tax_income, tax_shares, tax_shares_set.

Chip war

chip_war is the war as the client last heard it: its phase, and seconds_left, stone_hp, the master_race and the last result when the server has given them. observe carries the same section.

events reports kind chipwar: phase, started, stone_hp (on a change), stone_destroyed, master, ended, pvp_points, next.

Server module messages

A server's Lua modules can declare messages of their own. The client asks for the list after it enters the world.

ToolWhat it does
module_catalogthe declared messages: module, name, direction, fields
module_lastmodule, name: the newest values of a message, nil before one arrives
module_sendmodule, name, fields: send a message declared in or both; a wrong field is refused before it is sent
module_refreshask for the list again, after an operator reloads a module

events reports kind module: declared, message (with module, name and fields), and dropped, a message that did not match its declaration.

Standing orders

A model takes seconds to answer and a fight moves faster than that. start_orders hands the fast loop to a script that runs in the client, and the agent is told only when it has something to decide.

The default script, default/standing, hunts an area. Its parameters:

ParameterDefault
x, ywhere the character standscentre of the area
radius300how far from the centre it hunts
drink_below0.5health fraction at which it drinks
potionany item named like a potionpart of the name of the item to drink
rest_below0.35health fraction at which it stops and rests
loottruepick up drops after each kill
avoid_playersfalsewalk back to the centre when a player comes into the area
minutes0stop after this long; 0 runs until stopped

It raises these notices in events:

NoticeWhen
diedthe character died; it has respawned
player_neara player entered the area (once per player)
area_emptyno monster in the area for 30 seconds
out_of_potionsit needed to drink and had nothing
doneminutes ran out
reconsultevery reconsult_seconds (default 60), with health and position

Pass script to run another script as orders, or source for inline Lua. Your script reads its parameters with params() and raises a notice with notify("reason", details). Starting new orders replaces running ones; stop_orders stops them and halts the character.

A hunting buddy

GoMMO Client 0.1.3 and later.

default/buddy waits for a party invitation, accepts it, then follows the leader and fights beside them: it attacks what the leader attacks and whatever attacks the party, drinks, loots and respawns. It raises invited, joined, left, died, out_of_potions and done.

ParameterDefault
accept_fromanyonea name, or a list of names, whose invitations it accepts
distance60how close it stays to the leader when nothing needs fighting
assisttruefight the leader's target and the party's attackers
leash250ignore a fight further than this from the leader
loottruepick up drops after a kill
drink_below, potion0.5, any potionas for default/standing
greetnonea line to say on the party channel on joining
leave_alonefalseleave once every other member has been out of sight for a minute
minutes0stop after this long; 0 runs until stopped

Many characters at once

-headless -clients N plays N characters from one process, each with its own throwaway account. -script picks what they do. From GoMMO Client 0.1.3, -params passes the script its parameters as JSON and a script tells the copies apart with client_index().

One API for several characters

GoMMO Client 0.2.1 and later.

-headless -clients N with -serve or -mcp puts all N characters behind one API:

HTTPGET /clients lists them by index, with name, map and whether each is in the world. /c/<index>/ in front of any request drives that one: /c/2/observe, POST /c/2/act. A request without it drives character 0.
MCPevery tool takes an optional client, the index, and the clients tool lists them

A character still logging in answers 503.

Choosing the character a new account makes

GoMMO Client 0.2.1 and later. RF Online.

When an account has no character yet, the client makes one. -race and -class choose it:

FlagTakes
-racebellato, bellato_female, cora, cora_female, accretia, or the race/sex code 0 to 4
-classwarrior, ranger, spiritualist or specialist (Accretia have no spiritualists), or a class code such as BWB0. Warrior by default.

They change nothing for an account that already has a character. A population group's class takes the same names.

default/wander (0.1.3 and later) makes passers-by: each character strolls around an area, rests, and strolls again, on its own timing. With accept_party it accepts the first invitation it gets and turns into default/buddy for the rest of the run, so a town full of wanderers is also a pool of hunting partners:

GoMMO_Client.exe -game rf2232 -headless -clients 50 -script default/wander -params "{\"radius\":300,\"accept_party\":true,\"accept_from\":\"YourName\"}"
ParameterDefault
x, ywhere the character enteredcentre of the area
radius250how far from the centre it strolls
pause_min, pause_max2, 10seconds it stands between walks
runfalserun rather than walk
fightfalseswing at a monster that comes within reach
lines, say_everynone, 120things to say now and then, and roughly how often in seconds
accept_partyfalseaccept an invitation and become a buddy
minutes0stop after this long; 0 runs until stopped

Keeping a crowd online

GoMMO Client 0.1.3 and later.

-population crowd.json runs a standing crowd until you stop it, restarting any character that drops (sooner after one that ran a while, more slowly after one that keeps failing at once). Each group is one kind of character:

{
  "groups": [
    { "name": "townsfolk", "count": 50, "accounts": "town", "script": "default/wander",
      "params": { "radius": 300, "accept_party": true } },
    { "name": "field", "count": 20, "accounts": "field", "script": "default/wander",
      "route": [3], "params": { "radius": 250, "fight": true } },
    { "name": "cora", "count": 30, "accounts": "cora", "script": "default/wander",
      "race_sex": 2, "params": { "radius": 300 } },
    { "name": "guards", "count": 10, "accounts": "guard", "script": "default/standing",
      "params": { "radius": 200 }, "class": "Warrior", "race_sex": 0 }
  ],
  "restart_after": "15s",
  "stagger": "200ms",
  "status_every": "1m"
}

Every group needs its own accounts prefix; its characters are town000, town001 and so on, so the same crowd comes back each time. One process runs at most 2000 characters.

Where a group lives. A character starts in its race's town: race_sex 0 or 1 is Bellato, 2 or 3 Cora, 4 Accretia. route is a list of portal indexes it takes first, in order, so [3] from the Bellato town is the field next to it; portals lists a map's indexes. default/wander and default/standing then settle where they arrive.

Controlling a crowd while it runs

GoMMO Client 0.1.3 and later.

Add -serve and the population is controlled over HTTP on 127.0.0.1:47800, with the token in automation.token beside the client, as for one character:

Request
GET /populationevery group: how many are wanted, running and in the world, restarts and the last error, and each character's name, map and state (?bots=0 leaves the characters out)
POST /population/groupsadd a group: the same object as one entry of groups
POST /population/groups/{name}/scale{"count": 80}: new characters log in, the highest-numbered leave
POST /population/groups/{name}/paramsreplace the group's script settings; each character picks them up when it next starts
POST /population/groups/{name}/restartlog the whole group out and back in, which applies new settings now
DELETE /population/groups/{name}log the group out and forget it
curl -H "Authorization: Bearer $(cat automation.token)" -d '{"count":80}' http://127.0.0.1:47800/population/groups/townsfolk/scale

Running a large crowd

GoMMO Client 0.1.3 and later.

Flag
-bot-accounts townevery character gets a stable account and name, town000, town001 and so on, so the same crowd comes back each run with its levels and its bags. Without it each run registers throwaway accounts.
-stagger 100msthe gap between one login and the next, 300ms by default. A busy server wants more.
-memprofile crowd.pprofrewrites a heap profile every 10 seconds, for go tool pprof

All the characters in one process share a handful of timers rather than running one each, and share one compiled copy of every script. A server running these clients also sends a character that declared itself scripted other players' movement at most once a second each, rather than every step (THIN_SCRIPTED_MOVEMENT in settings.ini, on by default); real players see everything as before. On a desktop, 200 wanderers in one town used about 110 MB and a quarter of one core in the client, and the server's own load fell by over a third.

HTTP

-serve listens on 127.0.0.1:47800; -serve-addr picks another loopback address. It refuses any address that is not on this machine.

Each start writes a new token beside the client. Every request carries it. From GoMMO Client 0.2.1:

  • the file is automation.token on port 47800 and automation-<port>.token on any other port, so instances on different ports never overwrite each other's; -token-file names it yourself;
  • -serve-addr 127.0.0.1:0 takes any free port, and the client prints the address it took as AUTOMATION url=... token_file=...;
  • a port already in use stops the client with an error instead of starting it without an API.

Before 0.2.1 every instance wrote automation.token, so start each from its own folder.

curl -H "Authorization: Bearer $(cat automation.token)" http://127.0.0.1:47800/observe
Request
GET /observethe observe snapshot
GET /toolsevery tool with its JSON Schema
GET /events?since=N&wait_seconds=Sthe events tool
GET /stream?since=Nthe same events as a Server-Sent Events stream; reconnect with Last-Event-ID
POST /act{"tool": "fight", "args": {"seconds": 20}} runs any tool
POST /lua{"source": "return vitals()"}
GET / POST / DELETE /ordersorders status, start, stop

An action that cannot finish because the game connection has stopped answering gets 504 at its time limit (0.2.1 and later) instead of waiting forever.

A request with a wrong or missing token gets 401. A request whose Host is not localhost or 127.0.0.1 gets 403, so a web page cannot reach the API through a rebound DNS name.

For server operators

A client under -mcp or -serve declares itself as scripted when it logs in, the same as one running a script. ALLOW_SCRIPTED_CLIENTS in settings.ini decides whether your server accepts it; see Refusing scripted clients. A modified client can leave the declaration out, so the setting is a stated policy, not a detection.

Something here wrong?

Out of date, incomplete, or assuming something it should not. Documentation errors are bugs, and this one arrives tagged and pointed at this page.

Top xp sources

How xp works
Loading…

Open the full search page