game-bridge-mcp
game-bridge-mcp
Lassen Sie einen KI-Agenten Ihr Spiel starten, es steuern und lesen, was passiert ist – über HTTP, ein Port pro Instanz.
Ihr Spiel weiß bereits alles über sich selbst: was auf dem Bildschirm ist, wo sich jede Entität befindet, welche Befehle es akzeptiert. game-bridge-mcp ist ein MCP-Server, der das einem Agenten übergibt – als Werkzeuge, die der Agent aufrufen kann, entdeckt aus dem laufenden Spiel und nicht hier fest codiert.
agent ──MCP(stdio)──▶ game-bridge-mcp ──HTTP──▶ 127.0.0.1:7820 ← it launched this one
├───────▶ 127.0.0.1:7801 ← your IDE started this one
└───────▶ 127.0.0.1:7802 ← a colleague's sessionDrei Dinge machen es mehr als ein Debug-Bridge-Skript, das Sie an einem Nachmittag schreiben würden:
Es startet Instanzen und wählt ihre Ports. Kein Aufrufer wählt jemals einen Port oder tippt einen Build-Befehl, sodass zwei Agenten nicht kollidieren können, und die Brücke erntet, was sie gestartet hat – keine verwaisten Spielfenster nach einer Sitzung.
Jedes Werkzeug nimmt einen
port. Eine Brücke steuert jede Instanz, die Sie laufen haben: mehrere Agenten auf einer Maschine oder ein Agent, der zwei Builds nebeneinander vergleicht.Die Werkzeugliste kommt vom Spiel. Die Brücke ruft
GET /toolsvon jeder Instanz zur Laufzeit ab, sodass ein Debug-Befehl, den Sie heute Morgen hinzugefügt haben, heute Nachmittag aufrufbar ist – keine Veröffentlichung dieses Pakets, keine erneute Verbindung und keine Abweichung zwischen dem, was der Agent denkt, dass das Spiel akzeptiert, und dem, was es akzeptiert.
Es ist engine-agnostisch. Jede Sprache, alles, was vier kleine HTTP-Endpunkte auf localhost bedienen kann. Der Vertrag ist absichtlich kurz.
Schnellstart
npx @wildware/game-bridge-mcp --helpRegistrieren Sie es bei einem MCP-Client – für Claude Code, aus Ihrem Projektverzeichnis:
claude mcp add game-bridge -- npx -y @wildware/game-bridge-mcpDann, von der Seite des Agenten:
launch_instance {} // start a game; the bridge picks the port
list_instances {} // ...or find one already running
list_toolsets { "port": 7820 } // what can this instance do?
describe_toolset { "port": 7820, "name": "play" } // exact schemas
call_tool { "port": 7820, "name": "drop", "arguments": { "x": 1.2 } }
stop_instance { "port": 7820 } // clean shutdown, not a killlaunch_instance benötigt eine Startdeklaration. Alles andere funktioniert mit jedem Spiel, das die HTTP-Oberfläche implementiert, unabhängig davon, ob diese Brücke es gestartet hat oder nicht.
Related MCP server: minecraft-mcp
Der Vertrag
Implementieren Sie dies und Ihr Spiel ist durch diese Brücke von jedem Agenten steuerbar, ohne dass hier Code vorhanden ist, der etwas darüber weiß. Es hat drei Teile, und jeder Teil bringt etwas Bestimmtes:
Teil | Was es bringt |
Lesen und Steuern einer laufenden Instanz. | |
Instanzen finden, ohne Ports zu erraten. | |
Instanzen starten, ohne dass ein Mensch einen Port wählt. |
Nur Teil 1 ist erforderlich. Teil 2 macht die Erkennung zuverlässig; Teil 3 macht das Ganze angenehm.
1. Die HTTP-Oberfläche
Stellen Sie diese auf 127.0.0.1:<port> bereit, wobei der Port von einem expliziten Debug-Flag stammt. Binden Sie nur an Loopback und halten Sie die gesamte Oberfläche ausgeschaltet, es sei denn, das Spiel wurde mit diesem Flag gestartet: Dies ist eine Debug-Oberfläche, kein Netzwerkdienst.
GET /health – erforderlich
Der Lebendigkeits- und Identitätscheck. Die Erkennung ruft ihn für jeden Port in einem Bereich auf, also muss er billig sein.
GET /health{ "ok": true, "frame": 91422 }ok: true ist das, was den Port als einen von uns markiert. Ein Port, der mit etwas anderem auf HTTP antwortet, wird dem Agenten als „etwas anderes hat diesen Port belegt“ gemeldet – ein anderes Problem mit einer anderen Lösung als „das Spiel läuft nicht“.
frame ist ein Zähler, der für die Lebensdauer des Prozesses ansteigt. Die Brücke beobachtet ihn: Ein frame, das rückwärts geht, bedeutet, dass ein neuer Prozess auf diesem Port antwortet, und das zwischengespeicherte Tool-Manifest wird automatisch verworfen. Das macht Wiederaufbau und Neustart für den Agenten unsichtbar.
GET /state – erforderlich
Der vollständige Schnappschuss: alles, was ein Agent wissen möchte, als JSON. Es gibt kein erforderliches Schema – es ist Ihr Spiel – aber ein paar konventionelle Felder schalten Brückenfunktionen frei:
{
"frame": 91422,
"simFrame": 48110,
"completedCommandId": 17,
"paused": false,
"ui": {
"screen": "GameScreen",
"elements": [ { "label": "Restart", "visible": true } ]
},
"events": [ { "m": "merge:cherry" }, { "m": "click:Restart" } ],
"game": { "score": 1280, "state": "RUNNING" }
}Feld | Warum die Brücke sich darum kümmert |
| Neustarterkennung; die Fallback-Bestätigung, dass ein Befehl ausgeführt wurde. |
| Die starke Bestätigung, dass ein Befehl ausgeführt wurde – siehe unten. |
| Wird von |
| Im kompakten |
| Aktuelle Ereignisse, die nach jedem Befehl zurückgegeben werden, damit der Agent die Konsequenz sieht. Auch einfache Zeichenketten werden akzeptiert. |
| Skalare Felder sind im Digest enthalten. Verschachtelte Objekte und Arrays nicht – dort leben die megabytegroßen Entitätslisten. |
Alles andere, was Sie hier einfügen, wird von get_state unverändert durchgereicht.
GET /command – erforderlich
GET /command?cmd=spawn&type=cherry&x=-1.5{ "accepted": true, "commandId": 18, "frame": 91430 }Der Befehlsname ist als cmd verschlüsselt, nicht als name – Befehle nehmen routinemäßig ein eigenes name-Argument an, und ein doppelter Abfrageschlüssel würde den aufgerufenen Befehl stillschweigend überschreiben. Jeder andere Abfrageparameter ist ein Argument.
Dieser Endpunkt ist Fire-and-Forget, und das ist das Wichtigste, was man über den Vertrag verstehen muss. Er antwortet vom HTTP-Thread in dem Moment, in dem der Befehl in die Warteschlange gestellt wird; der Befehl selbst läuft später im Spiel-Thread. Ein Client, der sofort danach /state liest, liest die Welt von vor dem Befehl. Der Test sieht instabil aus; das Spiel ist in Ordnung.
Die Brücke handhabt das, und die Art und Weise, wie sie es tut, sollte Ihr Spiel unterstützen:
GET /command?...→ notieren Sie die zurückgegebenecommandId.Pollen Sie
GET /state, biscompletedCommandId >= commandId.Geben Sie diesen Zustand zurück – wirklich nachdem der Befehl ausgeführt wurde.
Wenn Ihr Spiel completedCommandId nicht veröffentlicht, degradiert die Brücke dazu, zu warten, bis frame um zwei vorrückt, und kennzeichnet das Ergebnis als "confirmation": "frames-advanced", damit der Agent weiß, dass er die schwächere Garantie erhalten hat. completedCommandId zu veröffentlichen ist eine Handvoll Zeilen und lohnt sich:
// game thread, once per frame
while (true) {
val cmd = queue.poll() ?: break
apply(cmd)
completedCommandId = cmd.id // published in the next /state snapshot
}Ein close-Befehl, der das Spiel über sein normales Herunterfahren beendet, wird dringend empfohlen: Er ermöglicht es einem Agenten, eine Instanz zu beenden, ohne einen Prozess zu töten. Die Brücke behandelt close besonders – sie wartet nie auf eine Vervollständigung, die nicht eintreffen kann, sondern wartet darauf, dass der Port ruhig wird.
GET /tools – optional, aber das ist der gute Teil
Das Manifest: was Ihrem Spiel gesagt werden kann, in seinen eigenen Worten.
{
"game": { "name": "Orbital Freight", "version": "0.9.2", "protocol": 1 },
"toolsets": [
{
"name": "play",
"description": "Drive the game the way a player does.",
"tools": [
{
"name": "drop",
"description": "Release the held crate, aiming first if x is given.",
"args": [
{ "name": "x", "type": "number", "description": "World x, -2..2", "required": true, "default": null },
{ "name": "settle", "type": "boolean", "description": "Wait for the stack to settle", "default": "true" }
]
}
]
}
],
"passthrough": {
"description": "Any command the debug bridge accepts, passed straight through.",
"examples": ["set_seed { seed }", "set_gravity { x, y }"]
}
}Feld | Bedeutung |
| Identität. Wird von |
| Versioniert das Dokument, nicht den Befehlssatz. Einen Befehl hinzuzufügen ändert hier nichts; das Manifest umzustrukturieren schon. Es ermöglicht einer Brücke, „Ich kann das nicht lesen“ von „Dieses Spiel kennt andere Befehle als beim letzten Mal“ zu unterscheiden. Aktuelle Version: |
| Gruppen, benannt nach dem, was ein Aufrufer zu tun versucht, nicht nach der Anordnung Ihres Codes. Halten Sie sie wenige und offensichtlich. |
| Was der Agent aufruft. |
| Für den Agenten geschrieben. Sagen Sie, was es tut und wann man es verwenden sollte – das ist der Text, über den das Modell nachdenkt. |
|
|
| Der zu sendende |
|
|
| Wenn Sie bereits JSON-Schema haben, senden Sie es anstelle von |
| Freitext plus Beispiele, die Befehle beschreiben, die Sie nicht formal veröffentlicht haben. |
Standardwerte können Zeichenketten sein ("true", "0.05") – ein Manifest, das aus einer typisierten Sprache serialisiert wurde, rendert sie normalerweise so. Die Brücke fügt sie in die Beschreibung ein, anstatt default im Schema auszugeben, weil default: "false" bei einer boolean-Eigenschaft von einem strengen Client abgelehnt werden könnte. null bedeutet „kein Standardwert“.
Der Parser ist bewusst tolerant, weil ein Manifest in dem Serialisierer geschrieben wird, der gerade zur Hand war:
toolsetskann ein Array von Objekten oder eine Map vonname → toolsetsein.Argumente können unter
args,argumentsoderparamsliegen, als Array von Objekten, als Array von bloßen Namen oder als Map vonname → { type, description }.Ein fehlerhaftes Werkzeug wird verworfen, nicht fatal. Ein schlechter Eintrag darf nicht eine ganze Instanz offline nehmen.
Wenn /tools 404 liefert, bricht nichts. Die Brücke fällt auf ein eingebautes Manifest zurück, das nur die Vertragswerkzeuge enthält, und teilt dem Agenten mit, dass das Spiel keine Befehlsliste veröffentlicht, also arbeiten Sie über raw_command und lesen Sie /state. Instanzen werden entsprechend als live oder live-no-manifest gemeldet, und --manifest ./my-game.json liefert eines aus einer Datei für ein Spiel, das Sie nicht ändern können.
2. Selbstregistrierung
Port-Scanning ist die schwache Form der Erkennung: begrenzt durch einen Bereich, den jemand erraten hat, still über die Identität eines Spiels, bis es antwortet, und anfällig für falsch negative Ergebnisse während des Starts – genau der Moment, in dem ein Agent am wahrscheinlichsten sucht.
Ein Spiel, das seinen Debug-Port erfolgreich gebunden hat, schreibt also eine kleine JSON-Datei, die sich selbst benennt:
~/.game-bridge/instances/<pid>.json{
"name": "Orbital Freight",
"version": "0.9.2",
"protocol": 1,
"port": 7820,
"pid": 12345,
"host": "127.0.0.1",
"started": "2026-08-20T22:27:19.774Z",
"cwd": "/home/dev/checkouts/main"
}cwd ist beabsichtigt: Mehrere Checkouts desselben Spiels laufen gleichzeitig, und „Welcher Build ist das?“ ist sonst von außerhalb des Prozesses nicht zu beantworten.
Die Regeln, die ein Schreiber befolgen muss:
Schreibe den Eintrag nach dem Binden des Ports, niemals davor. Ein Eintrag für einen Port, der nie beansprucht wurde, ist schlimmer als gar kein Eintrag.
Lösche ihn beim sauberen Herunterfahren.
Lass niemals zu, dass ein Registry-Fehler das Spiel zerstört. Ein nicht beschreibbares Verzeichnis, ein schreibgeschütztes Home-Verzeichnis, eine Sandbox – das Spiel muss trotzdem starten und seine Endpunkte bedienen. Das ist Werbung, keine Infrastruktur.
Beachte
GAME_BRIDGE_INSTANCES(das Einträge-Verzeichnis) oderGAME_BRIDGE_HOME(dessen Elternverzeichnis), falls eines gesetzt ist.
Die Regeln, die ein Leser befolgen muss – und diese sind wichtiger:
Einträge sind beratend, niemals maßgeblich. Ein Absturz oder ein erzwungenes Beenden hinterlässt die Datei. Das passiert in der Praxis ständig.
Überprüfe jeden Eintrag mit
GET /health, bevor du ihm glaubst. Ein Eintrag, dessen Port nicht antwortet, ist eine veraltete Datei, kein laufendes Spiel, und muss als solche gemeldet werden, nicht als Instanz.Vertraue niemals einem Eintrag über dem Live-Spiel. Die Bridge übernimmt Name und Version von
/tools, wenn das Spiel antwortet, und verwendet den Eintrag nur für das, was die Leitung nicht sagen kann: PID, Arbeitsverzeichnis, Startzeit.Lösche standardmäßig keine Dateien anderer Prozesse. Ein Spiel, das seinen Port noch bindet, ist für ein oder zwei Sekunden nicht von einem abgestürzten zu unterscheiden. Die Bridge bereinigt nur bei explizitem
prune: trueund nur, nachdem sie verifiziert hat, dass der Port tot ist.Scanne weiterhin. Es gibt Spiele, die älter als die Registry sind; die Bridge führt Registry-Einträge und einen Port-Scan zusammen und dedupliziert nach Port, wobei sie die
discoveryjeder Instanz alsregistry,scanoderbothmeldet.
3. Die Startdeklaration
Ein Projekt deklariert einmal, wie es gestartet wird, damit kein Aufrufer jemals einen Build-Befehl eingibt oder einen Port wählt. Lege gamebridge.json im Projektstamm ab – die Bridge geht von ihrem Arbeitsverzeichnis nach oben, um es zu finden, so wie jedes andere JS-Tool seine Konfiguration findet, und --config <file> überschreibt das.
{
"name": "Orbital Freight",
"launch": {
"command": "./gradlew lwjgl3:run -PdebugPort={port} --console=plain",
"cwd": ".",
"portRange": "7820-7839",
"readyTimeoutMs": 180000,
"env": { "ORBITAL_DEV": "1" }
}
}Feld | Bedeutung |
| Shell-Befehlszeile. |
| Alternative zu |
| Arbeitsverzeichnis, relativ zu dieser Datei aufgelöst – nicht zu dem Ort, an dem der MCP-Client die Bridge zufällig gestartet hat, was fast nie das Projekt ist. |
| Ports, die der Launcher beanspruchen darf. Standard |
| Wie lange auf |
| Zusätzliche Umgebung und nachgestellte Argumente. |
Was der Launcher dann garantiert:
Der Port wird zweimal als frei verifiziert – nichts ist daran gebunden und nichts beantwortet einen Health-Check darauf –, weil ein Spiel, das gerade startet, den Port auf die Weise beansprucht hat, die zählt, während es Millisekunden zuvor einen Bind-Test noch fehlschlug. Wenn der deklarierte Bereich voll ist, fällt es auf einen vom Betriebssystem zugewiesenen Port zurück.
launch_instancegibt erst zurück, wenn/healthantwortet, sodass ein Aufrufer nie eine Wiederholungsschleife schreiben muss.Ein Startfehler schlägt laut fehl, mit der eigenen Ausgabe des Kindprozesses. Wenn ein Spiel nicht startet, ist der Stack-Trace die ganze Antwort:
BridgeUsageError: Launch failed on port 7820: the process exited with code 1. Command: ./gradlew lwjgl3:run -PdebugPort=7820 --console=plain Working directory: /home/dev/orbital Full log: /tmp/game-bridge-logs/instance-7820-1787264781573.log Last output: 'gradlew' is not recognized as an internal or external command, operable program or batch file.stdout und stderr werden für die Lebensdauer der Instanz in diese Logdatei erfasst, und die letzten 200 Zeilen werden für
instance_logim Speicher gehalten.Kinder werden eingesammelt. Bei
stop_instanceund beim Herunterfahren des Servers, SIGINT, SIGTERM oder wenn der Client die Verbindung trennt, wird jede gestartete Instanz geschlossen – zuerst der eigeneclose-Befehl des Spiels, dann die Beendigung des Prozessbaums, falls es nicht gehen will. Eine geschlossene Sitzung hinterlässt niemals Spielfenster auf dem Desktop.
Die Eskalation über ein sauberes Schließen hinaus gilt nur für Prozesse, die diese Bridge gestartet hat und noch verfolgt. stop_instance auf einem anderen Port verweigert und sagt dir, das Spiel zu bitten, sich selbst zu schließen.
Werkzeuge
Werkzeug | Argumente | Was es tut |
|
| Startet ein Spiel, wählt einen freien Port, wartet auf |
|
| Registry plus Port-Scan. Namen, Versionen, PIDs, Arbeitsverzeichnisse, Bildschirme. Schreibgeschützt. |
|
| Sauberes Schließen, dann Eskalation – nur für Instanzen, die diese Bridge gestartet hat. |
|
| Erfasste stdout/stderr einer gestarteten Instanz. |
|
| Die Toolsets dieser Instanz, wie sie sich selbst beschreibt. |
|
| Vollständige JSON-Schemas. |
|
| Führt ein Werkzeug aus, wartet auf die Bestätigung des Spiels, gibt den resultierenden Digest zurück. |
Nur diese sieben werden beworben. Die eigenen Werkzeuge des Spiels werden über call_tool erreicht, weil ein MCP-Client die Werkzeugliste nur einmal erhält, wenn er sich verbindet, und nie wieder fragt – eine feste Liste wäre für ein Spiel, das noch geschrieben wird, veraltet und schlicht falsch, wenn eine Sitzung zwei verschiedene Builds steuert. (--eager flacht alles im Voraus ab für Clients, die keinen Discovery-Pfad gehen können.)
Das eigene Toolset der Bridge
Für jedes konforme Spiel bereitgestellt, was auch immer es ist:
Werkzeug | Was es tut |
| Die vollständige |
| Lebendigkeit und Frame-Zähler. |
| Jeder Befehl nach Namen, veröffentlicht oder nicht. |
| Pollt |
| Sauberes Herunterfahren; der Port, der still wird, ist die Bestätigung. |
Wie call_tool einen Namen auflöst
Die Composites der Bridge, einschließlich aller, die eine Host-Anwendung registriert hat. Ein Composite überschattet einen Spielbefehl mit demselben Namen, was immer eine Verbesserung und keine Überraschung ist: Ein Composite trägt genau deshalb diesen Namen, weil der rohe Befehl zurückkehrt, bevor das, worum er gebeten wurde, geschehen ist.
Das Manifest des Spiels – mit einem erneuten Abruf bei einem Fehlschlag, sodass ein neu gebautes Spiel mit neuen Befehlen mitten in der Sitzung erkannt wird.
Passthrough – alles andere wird als roher Befehl gesendet. Ein Befehl, der im Spiel existiert, aber nicht in seinem Manifest, funktioniert heute noch. Wenn das Spiel ihn als unbekannt ablehnt, erhältst du die Liste dessen, was es akzeptiert.
Wie port aufgelöst wird
Jedes Werkzeug akzeptiert einen optionalen port (auf oberster Ebene oder innerhalb von arguments). Er wird in dieser Reihenfolge aufgelöst:
der explizite
portim Aufruf,--portauf der Befehlszeile,GAME_BRIDGE_PORTin der Umgebung,7777.
Ein Einzelinstanz-Setup muss sich also nie um Ports kümmern, und eine Mehrinstanz-Sitzung braucht nie einen zweiten Server.
Zwei Instanzen gleichzeitig steuern
Das Szenario, für das dies gebaut wurde: zwei Builds desselben Spiels nebeneinander, ein Agent, eine Sitzung.
// 1. What is already running?
list_instances {}{
"registryDir": "/home/dev/.game-bridge/instances",
"live": [
{ "port": 7801, "discovery": "scan", "status": "live-no-manifest",
"frame": 2453, "manifest": "fallback", "screen": "GameScreen" },
{ "port": 7820, "discovery": "both", "status": "live", "game": "Orbital Freight",
"version": "0.9.2", "protocol": 1, "manifest": "game", "pid": 87488,
"cwd": "/home/dev/checkouts/main", "screen": "MenuScreen",
"toolsets": ["play", "build", "flow", "bridge"], "launchedByThisBridge": true }
],
"stale": [],
"notAGame": [],
"free": [7777, 7802, 7803]
}Port 7801 ist ein älterer Build ohne /tools: vollständig steuerbar, nur nicht selbstbeschreibend. Port 7820 ist einer, den diese Bridge gestartet hat.
// 2. Start a second one. You do not choose the port.
launch_instance {}{ "port": 7821, "pid": 90114, "name": "Orbital Freight", "version": "0.9.3-rc1",
"cwd": "/home/dev/checkouts/rc", "readyInMs": 4080,
"logFile": "/tmp/game-bridge-logs/instance-7821-1787264835950.log" }// 3. Same seed, same move, both runs.
call_tool { "port": 7820, "name": "set_seed", "arguments": { "seed": 12345 } }
call_tool { "port": 7821, "name": "set_seed", "arguments": { "seed": 12345 } }
call_tool { "port": 7820, "name": "drop", "arguments": { "x": 1.2 } }
call_tool { "port": 7821, "name": "drop", "arguments": { "x": 1.2 } }Jeder gibt den Zustand nach der Anwendung des Befehls zurück, sodass die beiden direkt vergleichbar sind:
{
"port": 7821, "tool": "drop", "via": "manifest", "command": "drop",
"applied": true, "commandId": 18, "confirmation": "completedCommandId",
"frame": 948, "screen": "GameScreen",
"game": { "score": 1280, "state": "RUNNING" },
"events": ["merge:cherry", "score:+40"]
}// 4. Wait for something the command could not report.
call_tool { "port": 7821, "name": "wait_for",
"arguments": { "path": "game.pendingMerges", "equals": 0, "timeoutMs": 5000 } }
// 5. Clean up what you started. 7801 is not yours - leave it alone.
stop_instance { "port": 7821 }{ "port": 7821, "stopped": true, "how": "closed cleanly" }Wenn etwas falsch ist
Die Bridge unterscheidet die Fehler, die von außen identisch aussehen:
GameOffline: No game is answering on http://127.0.0.1:7809.
Start one with:
./gradlew lwjgl3:run -PdebugPort=7809
NotAGameSurface: Something is listening on http://127.0.0.1:7802, but it is not a
debuggable game: GET /health returned HTTP 404.
A drivable game must answer GET /health with {"ok":true,"frame":N}. Check whether
another process has taken this port.
CommandTimeout: Command 'restart' was queued on port 7801 but was not applied
within 5000ms. The game accepted it, so it is probably blocked, frozen, or on a
screen that ignores this command.Setze den Befehl, der in der ersten Nachricht genannt wird, mit --launch-hint "make run PORT={port}" (oder lass launch_instance das Starten übernehmen).
CLI
npx @wildware/game-bridge-mcp [options]
-p, --port <n> Default port for tools that do not name one (default 7777)
--scan-range <spec> Ports list_instances sweeps (default 7777,7800-7810)
--no-scan Discover only via the instance registry
--no-registry Discover only by scanning ports
--registry-dir <dir> Where instance entries live (default ~/.game-bridge/instances)
--config <file> Project launch declaration (default: nearest gamebridge.json)
--launch-hint <cmd> Command shown when a port is dead; {port} is substituted
--manifest <file> Tool manifest for games that do not serve GET /tools
--eager Advertise every tool flatly, for clients that cannot discover
--timeout <ms> HTTP and command timeout (default 5000)
-h, --help
-v, --versionUmgebung: GAME_BRIDGE_PORT, GAME_BRIDGE_SCAN_RANGE (oder GAME_BRIDGE_SCAN), GAME_BRIDGE_LAUNCH_HINT (oder GAME_BRIDGE_LAUNCH), GAME_BRIDGE_MANIFEST, GAME_BRIDGE_CONFIG, GAME_BRIDGE_INSTANCES, GAME_BRIDGE_HOME.
Alles, was die Bridge protokolliert, geht an stderr; stdout ist der MCP-Transport, und eine verirrte Zeile darauf beschädigt den Protokollstrom.
Verwendung in deinem eigenen Projekt
Die Teile werden sowohl als CLI ausgeliefert als auch exportiert. Wenn dein Spiel Werkzeuge benötigt, die mehrere Befehle verketten – „ablegen, dann warten, bis sich das Brett beruhigt, dann das Punktedelta melden“ – registriere sie als Composites und erbe das Protokoll, den Launcher, die Discovery und die Fehlermeldungen, anstatt eine zweite Kopie davon zu pflegen.
#!/usr/bin/env node
import { parseCli, applyProjectConfig, startStdioServer } from "@wildware/game-bridge-mcp";
const { config } = parseCli(process.argv.slice(2), process.env);
await applyProjectConfig(config);
await startStdioServer(config, {
composites: [
{
name: "drop_and_settle",
description: "Drop at world x and wait until nothing is moving. The main way to play.",
only: "Orbital Freight", // never offered to a game that has no crates
args: [{ name: "x", type: "number", required: true, description: "World x" }],
async run(ctx) {
const before = await ctx.state();
await ctx.commandAndSync("drop", { x: ctx.args.x });
const settled = await ctx.call("wait_for", { path: "game.moving", equals: 0, timeoutMs: 10000 });
const after = await ctx.state();
return { settled: settled.matched, scoreDelta: after.game.score - before.game.score };
},
},
],
});Ein Composite erhält einen Kontext, der auf eine Instanz beschränkt ist – state, health, command, commandAndSync, call (jedes andere Werkzeug), manifest, summarise, sleep –, sodass es sich nie um Ports kümmern muss. only benennt die Spiele, für die es geeignet ist, abgeglichen mit dem game.name des Manifests; eine Bridge, die vorgibt, generisch zu sein, darf einem Flugsimulator kein drop_and_settle anbieten.
Die Regel für das, was in ein Composite gehört: Es verkettet entweder mehrere Befehle oder wartet auf etwas, das /command nicht melden kann. Alles, was ein Befehl mit einem Satz Argumente ist, gehört in das eigene Manifest des Spiels, wo es mit dem Code, der es implementiert, Schritt hält.
Niedrigere Ebenen – Bridge, GameClient, Launcher, readRegistry, normaliseManifest – werden ebenfalls exportiert. Bridge und GameClient akzeptieren ein optionales fetchImpl, was die Testsuite nutzt, um das Ganze ohne Spiel oder Socket zu steuern.
Entwicklung
npm install
npm run build # TypeScript -> dist/
npm test # builds, then runs node --test81 Tests, von denen keiner ein laufendes Spiel benötigt: Portauflösungsreihenfolge, Manifest-Caching und seine drei Invalidierungspfade, der /tools-404-Fallback, der command/poll/confirm-Zyklus und seine frames-advanced-Degradierung, Registry-Lesen mit veralteten und fehlerhaften Einträgen, Launcher-Portauswahl, Boot-Fehler und Child-Reaping sowie die MCP-Oberfläche selbst, die über einen In-Memory-Transport betrieben wird.
Veröffentlichung
Noch nicht auf npm veröffentlicht. Wenn es so weit ist:
npm version minor # keep SERVER_VERSION in src/server.ts in step
npm test # prepublishOnly runs build + test again
npm pack --dry-run # confirm dist/, README.md and LICENSE are the payload
npm publish # publishConfig.access is already "public"files in package.json begrenzt das Tarball auf dist/, README.md und LICENSE; prepare baut bei der Installation aus git, sodass eine per git installierte Abhängigkeit ohne ein eingechecktes dist/ funktioniert.
Lizenz
MIT — siehe LICENSE.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAn educational MCP server that exposes system tools (like IP, hostname, file operations, ping) for AI agents to execute via HTTP.381MIT
- AlicenseNot gradedqualityDmaintenanceA set of MCP servers that allow AI assistants to control a Minecraft server and client, including running commands, managing plugins, taking screenshots, and calling arbitrary API methods via reflection.10MIT
- AlicenseNot gradedqualityCmaintenanceLocal MCP server that gives AI agents 44 engine tools to build, run, and debug real 2D and 3D games through conversation.MIT
- AlicenseAqualityAmaintenanceAn MCP server that empowers AI coding agents to work effectively with Minecraft mod development, providing static analysis of decompiled source code and runtime interaction with a running Minecraft instance.313913MIT
Related MCP Connectors
A MCP server built for developers enabling Git based project management with project and personal…
An MCP server that gives your AI access to the source code and docs of all public github repos
MCP server exposing the Backtest360 engine API as tools for AI agents.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/wildware-uk/game-bridge-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server