godot-mcp-runtime 2.3.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -132
- package/dist/dispatch.d.ts +1 -11
- package/dist/dispatch.d.ts.map +1 -1
- package/dist/dispatch.js +32 -33
- package/dist/dispatch.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -10
- package/dist/index.js.map +1 -1
- package/dist/scripts/godot_operations.gd +268 -382
- package/dist/scripts/mcp_bridge.gd +206 -44
- package/dist/tools/autoload-tools.d.ts +51 -0
- package/dist/tools/autoload-tools.d.ts.map +1 -0
- package/dist/tools/autoload-tools.js +191 -0
- package/dist/tools/autoload-tools.js.map +1 -0
- package/dist/tools/node-tools.d.ts +9 -78
- package/dist/tools/node-tools.d.ts.map +1 -1
- package/dist/tools/node-tools.js +188 -312
- package/dist/tools/node-tools.js.map +1 -1
- package/dist/tools/project-tools.d.ts +0 -168
- package/dist/tools/project-tools.d.ts.map +1 -1
- package/dist/tools/project-tools.js +191 -1240
- package/dist/tools/project-tools.js.map +1 -1
- package/dist/tools/runtime-tools.d.ts +108 -0
- package/dist/tools/runtime-tools.d.ts.map +1 -0
- package/dist/tools/runtime-tools.js +994 -0
- package/dist/tools/runtime-tools.js.map +1 -0
- package/dist/tools/scene-tools.d.ts +6 -48
- package/dist/tools/scene-tools.d.ts.map +1 -1
- package/dist/tools/scene-tools.js +76 -212
- package/dist/tools/scene-tools.js.map +1 -1
- package/dist/tools/validate-tools.d.ts.map +1 -1
- package/dist/tools/validate-tools.js +115 -51
- package/dist/tools/validate-tools.js.map +1 -1
- package/dist/utils/autoload-ini.d.ts +38 -0
- package/dist/utils/autoload-ini.d.ts.map +1 -0
- package/dist/utils/autoload-ini.js +124 -0
- package/dist/utils/autoload-ini.js.map +1 -0
- package/dist/utils/bridge-manager.d.ts +46 -0
- package/dist/utils/bridge-manager.d.ts.map +1 -0
- package/dist/utils/bridge-manager.js +186 -0
- package/dist/utils/bridge-manager.js.map +1 -0
- package/dist/utils/bridge-protocol.d.ts +37 -0
- package/dist/utils/bridge-protocol.d.ts.map +1 -0
- package/dist/utils/bridge-protocol.js +78 -0
- package/dist/utils/bridge-protocol.js.map +1 -0
- package/dist/utils/godot-runner.d.ts +102 -16
- package/dist/utils/godot-runner.d.ts.map +1 -1
- package/dist/utils/godot-runner.js +497 -284
- package/dist/utils/godot-runner.js.map +1 -1
- package/dist/utils/handler-helpers.d.ts +34 -0
- package/dist/utils/handler-helpers.d.ts.map +1 -0
- package/dist/utils/handler-helpers.js +55 -0
- package/dist/utils/handler-helpers.js.map +1 -0
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +11 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +8 -4
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync } from 'fs';
|
|
3
|
+
import { logDebug } from './logger.js';
|
|
4
|
+
import { addAutoloadEntry, parseAutoloads, removeAutoloadEntry } from './autoload-ini.js';
|
|
5
|
+
const BRIDGE_AUTOLOAD_NAME = 'McpBridge';
|
|
6
|
+
const BRIDGE_SCRIPT_FILENAME = 'mcp_bridge.gd';
|
|
7
|
+
const MCP_GITIGNORE_ENTRY = '.mcp/';
|
|
8
|
+
// Matches the baked-port marker line inserted in src/scripts/mcp_bridge.gd —
|
|
9
|
+
// `const PORT := <int>` — so inject() can rewrite the integer per project.
|
|
10
|
+
const BAKED_PORT_REGEX = /const PORT := \d+/;
|
|
11
|
+
/**
|
|
12
|
+
* Owns the McpBridge autoload artifact: the script copy in the target project,
|
|
13
|
+
* the `[autoload]` entry in project.godot, the `.mcp/.gdignore` marker, and the
|
|
14
|
+
* `.gitignore` augmentation. GodotRunner delegates to this for inject/cleanup
|
|
15
|
+
* during run_project / attach_project / stop_project flows.
|
|
16
|
+
*
|
|
17
|
+
* The project-root bridge script is runtime-owned and refreshed on first
|
|
18
|
+
* injection for a manager session so a rebuilt server cannot talk to stale
|
|
19
|
+
* GDScript from an earlier run. Idempotent within a session via
|
|
20
|
+
* `injectedProjects`: a second `inject()` call for the same path short-circuits
|
|
21
|
+
* without rewriting project.godot.
|
|
22
|
+
*/
|
|
23
|
+
export class BridgeManager {
|
|
24
|
+
bridgeScriptPath;
|
|
25
|
+
injectedProjects = new Set();
|
|
26
|
+
repairedProjects = new Set();
|
|
27
|
+
/**
|
|
28
|
+
* Last port baked into the on-disk script per project. Used by the
|
|
29
|
+
* race-detection helper in runtime-tools to spot a concurrent re-inject
|
|
30
|
+
* after a bridge-wait timeout.
|
|
31
|
+
*/
|
|
32
|
+
lastInjectedPort = new Map();
|
|
33
|
+
constructor(bridgeScriptPath) {
|
|
34
|
+
this.bridgeScriptPath = bridgeScriptPath;
|
|
35
|
+
}
|
|
36
|
+
inject(projectPath, port) {
|
|
37
|
+
// Always rewrite the destination — the per-project bridge script may
|
|
38
|
+
// differ from the template by exactly the baked integer, so a size/mtime
|
|
39
|
+
// shortcut no longer maps to "up-to-date." Bake the resolved port into
|
|
40
|
+
// the const PORT line so the running game listens on the exact port the
|
|
41
|
+
// Node side will connect to.
|
|
42
|
+
const template = readFileSync(this.bridgeScriptPath, 'utf8');
|
|
43
|
+
if (!BAKED_PORT_REGEX.test(template)) {
|
|
44
|
+
throw new Error(`Bridge script template at ${this.bridgeScriptPath} is missing the 'const PORT := <int>' marker`);
|
|
45
|
+
}
|
|
46
|
+
const baked = template.replace(BAKED_PORT_REGEX, `const PORT := ${port}`);
|
|
47
|
+
const destScript = join(projectPath, BRIDGE_SCRIPT_FILENAME);
|
|
48
|
+
writeFileSync(destScript, baked, 'utf8');
|
|
49
|
+
this.lastInjectedPort.set(projectPath, port);
|
|
50
|
+
logDebug(`Wrote bridge autoload at ${destScript} (baked port ${port})`);
|
|
51
|
+
if (this.injectedProjects.has(projectPath)) {
|
|
52
|
+
logDebug('Bridge already injected for this project; refreshed script only.');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this.ensureMcpGdignore(projectPath);
|
|
56
|
+
this.ensureGitignored(projectPath);
|
|
57
|
+
const projectFile = join(projectPath, 'project.godot');
|
|
58
|
+
const existing = parseAutoloads(projectFile);
|
|
59
|
+
const alreadyRegistered = existing.some((a) => a.name === BRIDGE_AUTOLOAD_NAME);
|
|
60
|
+
if (alreadyRegistered) {
|
|
61
|
+
logDebug('Bridge autoload already present, skipping injection');
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
addAutoloadEntry(projectFile, BRIDGE_AUTOLOAD_NAME, BRIDGE_SCRIPT_FILENAME, true);
|
|
65
|
+
logDebug('Injected bridge autoload into project.godot');
|
|
66
|
+
}
|
|
67
|
+
this.injectedProjects.add(projectPath);
|
|
68
|
+
}
|
|
69
|
+
cleanup(projectPath) {
|
|
70
|
+
this.removeBridgeArtifacts(projectPath);
|
|
71
|
+
this.injectedProjects.delete(projectPath);
|
|
72
|
+
this.repairedProjects.delete(projectPath);
|
|
73
|
+
this.lastInjectedPort.delete(projectPath);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Read the port currently baked into the project's bridge script. Returns
|
|
77
|
+
* the integer on success, or null if the file is missing, unreadable, or
|
|
78
|
+
* lacks the marker. Used to detect concurrent re-inject after a bridge-
|
|
79
|
+
* wait timeout (another MCP client may have rewritten the port).
|
|
80
|
+
*/
|
|
81
|
+
readBakedPort(projectPath) {
|
|
82
|
+
const destScript = join(projectPath, BRIDGE_SCRIPT_FILENAME);
|
|
83
|
+
if (!existsSync(destScript))
|
|
84
|
+
return null;
|
|
85
|
+
try {
|
|
86
|
+
const content = readFileSync(destScript, 'utf8');
|
|
87
|
+
const match = content.match(BAKED_PORT_REGEX);
|
|
88
|
+
if (!match)
|
|
89
|
+
return null;
|
|
90
|
+
const parsed = Number.parseInt(match[0].replace(/^const PORT := /, ''), 10);
|
|
91
|
+
if (!Number.isFinite(parsed) || parsed <= 0 || parsed > 65535)
|
|
92
|
+
return null;
|
|
93
|
+
return parsed;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* If project.godot still has an `McpBridge=` line but the script file is
|
|
101
|
+
* missing, the autoload would crash every subsequent headless op. Detect and
|
|
102
|
+
* clean the orphan before running an operation.
|
|
103
|
+
*
|
|
104
|
+
* Cached per project: once a path has been checked clean, skip the file
|
|
105
|
+
* reads on subsequent ops in the same session.
|
|
106
|
+
*/
|
|
107
|
+
repairOrphaned(projectPath) {
|
|
108
|
+
if (this.repairedProjects.has(projectPath))
|
|
109
|
+
return;
|
|
110
|
+
const projectFile = join(projectPath, 'project.godot');
|
|
111
|
+
const bridgeScript = join(projectPath, BRIDGE_SCRIPT_FILENAME);
|
|
112
|
+
if (!existsSync(projectFile))
|
|
113
|
+
return;
|
|
114
|
+
if (existsSync(bridgeScript)) {
|
|
115
|
+
this.repairedProjects.add(projectPath);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
const content = readFileSync(projectFile, 'utf8');
|
|
120
|
+
if (content.includes(`${BRIDGE_AUTOLOAD_NAME}=`)) {
|
|
121
|
+
this.removeBridgeArtifacts(projectPath);
|
|
122
|
+
logDebug('Cleaned up orphaned McpBridge autoload entry');
|
|
123
|
+
}
|
|
124
|
+
this.repairedProjects.add(projectPath);
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
logDebug(`Non-fatal: Failed to check/repair orphaned bridge: ${err}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
removeBridgeArtifacts(projectPath) {
|
|
131
|
+
try {
|
|
132
|
+
const projectFile = join(projectPath, 'project.godot');
|
|
133
|
+
if (existsSync(projectFile)) {
|
|
134
|
+
const removed = removeAutoloadEntry(projectFile, BRIDGE_AUTOLOAD_NAME);
|
|
135
|
+
if (removed) {
|
|
136
|
+
logDebug(`Removed ${BRIDGE_AUTOLOAD_NAME} autoload from project.godot`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
logDebug(`Non-fatal: Failed to clean ${BRIDGE_AUTOLOAD_NAME} from project.godot: ${err}`);
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const scriptFile = join(projectPath, BRIDGE_SCRIPT_FILENAME);
|
|
145
|
+
if (existsSync(scriptFile)) {
|
|
146
|
+
unlinkSync(scriptFile);
|
|
147
|
+
logDebug(`Removed ${BRIDGE_SCRIPT_FILENAME} from project`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
logDebug(`Non-fatal: Failed to remove ${BRIDGE_SCRIPT_FILENAME}: ${err}`);
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const uidFile = join(projectPath, `${BRIDGE_SCRIPT_FILENAME}.uid`);
|
|
155
|
+
if (existsSync(uidFile)) {
|
|
156
|
+
unlinkSync(uidFile);
|
|
157
|
+
logDebug(`Removed ${BRIDGE_SCRIPT_FILENAME}.uid from project`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
logDebug(`Non-fatal: Failed to remove ${BRIDGE_SCRIPT_FILENAME}.uid: ${err}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
ensureMcpGdignore(projectPath) {
|
|
165
|
+
const mcpDir = join(projectPath, '.mcp');
|
|
166
|
+
mkdirSync(mcpDir, { recursive: true });
|
|
167
|
+
writeFileSync(join(mcpDir, '.gdignore'), '', 'utf8');
|
|
168
|
+
logDebug('Created .mcp/.gdignore');
|
|
169
|
+
}
|
|
170
|
+
ensureGitignored(projectPath) {
|
|
171
|
+
const gitignorePath = join(projectPath, '.gitignore');
|
|
172
|
+
if (existsSync(gitignorePath)) {
|
|
173
|
+
const gitignoreContent = readFileSync(gitignorePath, 'utf8');
|
|
174
|
+
if (!gitignoreContent.includes(MCP_GITIGNORE_ENTRY)) {
|
|
175
|
+
const newline = gitignoreContent.endsWith('\n') ? '' : '\n';
|
|
176
|
+
writeFileSync(gitignorePath, gitignoreContent + newline + MCP_GITIGNORE_ENTRY + '\n', 'utf8');
|
|
177
|
+
logDebug('Added .mcp/ to existing .gitignore');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
writeFileSync(gitignorePath, MCP_GITIGNORE_ENTRY + '\n', 'utf8');
|
|
182
|
+
logDebug('Created .gitignore with .mcp/ entry');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=bridge-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-manager.js","sourceRoot":"","sources":["../../src/utils/bridge-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire format shared between the Node-side `GodotRunner.sendCommand` and the
|
|
3
|
+
* GDScript-side `McpBridge` autoload.
|
|
4
|
+
*
|
|
5
|
+
* KEEP IN SYNC: src/scripts/mcp_bridge.gd implements the same framing on the
|
|
6
|
+
* Godot side. Any change here MUST be mirrored there (and vice versa).
|
|
7
|
+
*
|
|
8
|
+
* Frame: 4-byte big-endian length prefix + UTF-8 JSON payload.
|
|
9
|
+
* Max frame size is 16 MiB; oversize frames are rejected on receive.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEFAULT_BRIDGE_PORT = 9900;
|
|
12
|
+
export declare const MAX_FRAME_BYTES: number;
|
|
13
|
+
export declare const FRAME_HEADER_BYTES = 4;
|
|
14
|
+
/**
|
|
15
|
+
* Find an available TCP port by binding to port 0 (OS-assigned ephemeral port),
|
|
16
|
+
* reading the assigned port, and closing the listener. The brief TOCTOU window
|
|
17
|
+
* between close and the consumer's listen is acceptable — if a collision occurs,
|
|
18
|
+
* the bridge readiness check will surface the failure.
|
|
19
|
+
*/
|
|
20
|
+
export declare function findFreePort(): Promise<number>;
|
|
21
|
+
/**
|
|
22
|
+
* Encode a JSON string as a length-prefixed frame.
|
|
23
|
+
*/
|
|
24
|
+
export declare function encodeFrame(payload: string): Buffer;
|
|
25
|
+
export interface ParseFramesResult {
|
|
26
|
+
frames: Buffer[];
|
|
27
|
+
remainder: Buffer;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Pull as many complete frames as possible from a streaming buffer. Any
|
|
31
|
+
* partial frame at the tail is returned as `remainder` for the next call.
|
|
32
|
+
*
|
|
33
|
+
* Throws if a header advertises a payload larger than {@link MAX_FRAME_BYTES} —
|
|
34
|
+
* the caller should treat this as a fatal protocol error and close the socket.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseFrames(buffer: Buffer): ParseFramesResult;
|
|
37
|
+
//# sourceMappingURL=bridge-protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-protocol.d.ts","sourceRoot":"","sources":["../../src/utils/bridge-protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,eAAO,MAAM,mBAAmB,OAAO,CAAC;AACxC,eAAO,MAAM,eAAe,QAAmB,CAAC;AAChD,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAkB9C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CASnD;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,CAoB7D"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire format shared between the Node-side `GodotRunner.sendCommand` and the
|
|
3
|
+
* GDScript-side `McpBridge` autoload.
|
|
4
|
+
*
|
|
5
|
+
* KEEP IN SYNC: src/scripts/mcp_bridge.gd implements the same framing on the
|
|
6
|
+
* Godot side. Any change here MUST be mirrored there (and vice versa).
|
|
7
|
+
*
|
|
8
|
+
* Frame: 4-byte big-endian length prefix + UTF-8 JSON payload.
|
|
9
|
+
* Max frame size is 16 MiB; oversize frames are rejected on receive.
|
|
10
|
+
*/
|
|
11
|
+
import * as net from 'net';
|
|
12
|
+
export const DEFAULT_BRIDGE_PORT = 9900;
|
|
13
|
+
export const MAX_FRAME_BYTES = 16 * 1024 * 1024;
|
|
14
|
+
export const FRAME_HEADER_BYTES = 4;
|
|
15
|
+
/**
|
|
16
|
+
* Find an available TCP port by binding to port 0 (OS-assigned ephemeral port),
|
|
17
|
+
* reading the assigned port, and closing the listener. The brief TOCTOU window
|
|
18
|
+
* between close and the consumer's listen is acceptable — if a collision occurs,
|
|
19
|
+
* the bridge readiness check will surface the failure.
|
|
20
|
+
*/
|
|
21
|
+
export function findFreePort() {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const srv = net.createServer();
|
|
24
|
+
srv.listen(0, '127.0.0.1', () => {
|
|
25
|
+
const addr = srv.address();
|
|
26
|
+
if (!addr || typeof addr === 'string') {
|
|
27
|
+
srv.close();
|
|
28
|
+
reject(new Error('Failed to determine assigned port'));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const port = addr.port;
|
|
32
|
+
srv.close(() => resolve(port));
|
|
33
|
+
});
|
|
34
|
+
// One-shot: only fires before listen() succeeds. If listen succeeded,
|
|
35
|
+
// we proceed to srv.close() in the listening callback — a later error
|
|
36
|
+
// is not possible from this server, so the listener stays safely dormant.
|
|
37
|
+
srv.on('error', reject);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Encode a JSON string as a length-prefixed frame.
|
|
42
|
+
*/
|
|
43
|
+
export function encodeFrame(payload) {
|
|
44
|
+
const body = Buffer.from(payload, 'utf8');
|
|
45
|
+
if (body.length > MAX_FRAME_BYTES) {
|
|
46
|
+
throw new Error(`Bridge frame too large: ${body.length} bytes (limit ${MAX_FRAME_BYTES})`);
|
|
47
|
+
}
|
|
48
|
+
const frame = Buffer.allocUnsafe(FRAME_HEADER_BYTES + body.length);
|
|
49
|
+
frame.writeUInt32BE(body.length, 0);
|
|
50
|
+
body.copy(frame, FRAME_HEADER_BYTES);
|
|
51
|
+
return frame;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Pull as many complete frames as possible from a streaming buffer. Any
|
|
55
|
+
* partial frame at the tail is returned as `remainder` for the next call.
|
|
56
|
+
*
|
|
57
|
+
* Throws if a header advertises a payload larger than {@link MAX_FRAME_BYTES} —
|
|
58
|
+
* the caller should treat this as a fatal protocol error and close the socket.
|
|
59
|
+
*/
|
|
60
|
+
export function parseFrames(buffer) {
|
|
61
|
+
const frames = [];
|
|
62
|
+
let offset = 0;
|
|
63
|
+
while (buffer.length - offset >= FRAME_HEADER_BYTES) {
|
|
64
|
+
const len = buffer.readUInt32BE(offset);
|
|
65
|
+
if (len > MAX_FRAME_BYTES) {
|
|
66
|
+
throw new Error(`Bridge frame header advertises ${len} bytes, exceeds limit ${MAX_FRAME_BYTES}`);
|
|
67
|
+
}
|
|
68
|
+
const frameStart = offset + FRAME_HEADER_BYTES;
|
|
69
|
+
const frameEnd = frameStart + len;
|
|
70
|
+
if (buffer.length < frameEnd)
|
|
71
|
+
break;
|
|
72
|
+
frames.push(buffer.subarray(frameStart, frameEnd));
|
|
73
|
+
offset = frameEnd;
|
|
74
|
+
}
|
|
75
|
+
const remainder = offset === 0 ? buffer : buffer.subarray(offset);
|
|
76
|
+
return { frames, remainder };
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=bridge-protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-protocol.js","sourceRoot":"","sources":["../../src/utils/bridge-protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAE3B,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC;AACxC,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAChD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAEpC;;;;;GAKG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC;QAC/B,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE;YAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;YAC3B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACtC,GAAG,CAAC,KAAK,EAAE,CAAC;gBACZ,MAAM,CAAC,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC,CAAC;gBACvD,OAAO;YACT,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YACvB,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QACH,sEAAsE;QACtE,sEAAsE;QACtE,0EAA0E;QAC1E,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1C,IAAI,IAAI,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,CAAC,MAAM,iBAAiB,eAAe,GAAG,CAAC,CAAC;IAC7F,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,kBAAkB,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IACnE,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACpC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAC;IACrC,OAAO,KAAK,CAAC;AACf,CAAC;AAOD;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,OAAO,MAAM,CAAC,MAAM,GAAG,MAAM,IAAI,kBAAkB,EAAE,CAAC;QACpD,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,GAAG,GAAG,eAAe,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,kCAAkC,GAAG,yBAAyB,eAAe,EAAE,CAChF,CAAC;QACJ,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,GAAG,kBAAkB,CAAC;QAC/C,MAAM,QAAQ,GAAG,UAAU,GAAG,GAAG,CAAC;QAClC,IAAI,MAAM,CAAC,MAAM,GAAG,QAAQ;YAAE,MAAM;QACpC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;QACnD,MAAM,GAAG,QAAQ,CAAC;IACpB,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import type { ChildProcess } from 'child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when the bridge socket closes (Godot exited, port closed, or peer
|
|
4
|
+
* dropped the connection mid-flight). Lets callers distinguish
|
|
5
|
+
* "session ended" from generic transport errors.
|
|
6
|
+
*/
|
|
7
|
+
export declare class BridgeDisconnectedError extends Error {
|
|
8
|
+
constructor(message: string);
|
|
9
|
+
}
|
|
10
|
+
export declare const BRIDGE_WAIT_SPAWNED_TIMEOUT_MS = 8000;
|
|
2
11
|
/**
|
|
3
12
|
* Normalize a path for cross-platform comparison.
|
|
4
13
|
* Folds Windows backslashes to forward slashes and strips trailing slashes,
|
|
@@ -14,6 +23,7 @@ export declare function extractJson(output: string): string;
|
|
|
14
23
|
* Strip Godot banner and debug lines from output, keeping only meaningful content.
|
|
15
24
|
*/
|
|
16
25
|
export declare function cleanOutput(output: string): string;
|
|
26
|
+
export declare function cleanStdout(stdout: string): string;
|
|
17
27
|
export interface GodotProcess {
|
|
18
28
|
process: ChildProcess;
|
|
19
29
|
output: string[];
|
|
@@ -33,7 +43,6 @@ export interface RuntimeStopResult {
|
|
|
33
43
|
export interface GodotServerConfig {
|
|
34
44
|
godotPath?: string;
|
|
35
45
|
debugMode?: boolean;
|
|
36
|
-
strictPathValidation?: boolean;
|
|
37
46
|
}
|
|
38
47
|
export interface OperationParams {
|
|
39
48
|
[key: string]: unknown;
|
|
@@ -64,11 +73,63 @@ export interface ToolDefinition {
|
|
|
64
73
|
};
|
|
65
74
|
annotations?: ToolAnnotations;
|
|
66
75
|
}
|
|
67
|
-
export
|
|
68
|
-
|
|
76
|
+
export interface ToolResponse {
|
|
77
|
+
content: Array<{
|
|
78
|
+
type: string;
|
|
79
|
+
text?: string;
|
|
80
|
+
[k: string]: unknown;
|
|
81
|
+
}>;
|
|
82
|
+
isError?: boolean;
|
|
83
|
+
[k: string]: unknown;
|
|
84
|
+
}
|
|
85
|
+
export type ToolHandler = (runner: GodotRunner, args: OperationParams) => Promise<ToolResponse> | ToolResponse;
|
|
69
86
|
export declare function normalizeParameters(params: OperationParams): OperationParams;
|
|
70
87
|
export declare function convertCamelToSnakeCase(params: OperationParams): OperationParams;
|
|
88
|
+
/**
|
|
89
|
+
* Check whether a display server (X11 / Wayland) is available on the current
|
|
90
|
+
* platform. On macOS and Windows the display subsystem is always present;
|
|
91
|
+
* on Linux we probe the standard environment variables.
|
|
92
|
+
*/
|
|
93
|
+
export declare function checkDisplayAvailable(): boolean;
|
|
71
94
|
export declare function validatePath(path: string): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Stricter check for paths that must stay inside `projectPath`. Rejects `..`
|
|
97
|
+
* (via `validatePath`) and absolute paths that escape the project root.
|
|
98
|
+
* `path.join('/project', '/etc/passwd')` resolves to `/etc/passwd`, so the
|
|
99
|
+
* basic `..`-substring check alone permits absolute-path traversal.
|
|
100
|
+
*
|
|
101
|
+
* Tolerates a leading `res://` (Godot's project-root URI) by stripping it
|
|
102
|
+
* before resolving — autoload entries and resource paths use this prefix.
|
|
103
|
+
*/
|
|
104
|
+
export declare function validateSubPath(projectPath: string, userPath: string): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Validate a Godot scene-tree path (NodePath). Scene-tree paths are a
|
|
107
|
+
* separate namespace from filesystem paths — they address nodes inside
|
|
108
|
+
* a scene, not files on disk, so the project-root containment check
|
|
109
|
+
* in `validateSubPath` does not apply.
|
|
110
|
+
*
|
|
111
|
+
* Rejects empty strings and `..` segments. Accepts both relative
|
|
112
|
+
* (`root/Player`) and absolute (`/root/Player`) Godot forms; the
|
|
113
|
+
* codebase convention is the relative form.
|
|
114
|
+
*/
|
|
115
|
+
export declare function validateNodePath(path: string): boolean;
|
|
116
|
+
/**
|
|
117
|
+
* True when `child` resolves to `parent` or a path beneath it. Used by
|
|
118
|
+
* defense-in-depth checks on bridge-returned paths (e.g. screenshot files
|
|
119
|
+
* that must live under `.mcp/screenshots/`).
|
|
120
|
+
*/
|
|
121
|
+
export declare function isUnderDir(parent: string, child: string): boolean;
|
|
122
|
+
/**
|
|
123
|
+
* Return `error.message` when `error` is an `Error`, otherwise `'Unknown error'`.
|
|
124
|
+
* Centralizes the catch-block boilerplate so handlers can build error responses
|
|
125
|
+
* without repeating the `instanceof Error` ternary.
|
|
126
|
+
*/
|
|
127
|
+
export declare function getErrorMessage(error: unknown): string;
|
|
128
|
+
/**
|
|
129
|
+
* Build the absolute path to a project's `project.godot` manifest. Use this
|
|
130
|
+
* instead of `join(dir, 'project.godot')` ad hoc.
|
|
131
|
+
*/
|
|
132
|
+
export declare function projectGodotPath(projectDir: string): string;
|
|
72
133
|
/**
|
|
73
134
|
* Extract the first [ERROR] message from GDScript stderr output.
|
|
74
135
|
* Falls back to a generic message if no [ERROR] line is found.
|
|
@@ -96,44 +157,69 @@ export declare function validateSceneArgs(args: OperationParams, opts?: {
|
|
|
96
157
|
export declare class GodotRunner {
|
|
97
158
|
private godotPath;
|
|
98
159
|
private operationsScriptPath;
|
|
99
|
-
private
|
|
160
|
+
private bridge;
|
|
100
161
|
private validatedPaths;
|
|
101
|
-
private
|
|
102
|
-
private strictPathValidation;
|
|
162
|
+
private cachedVersion;
|
|
103
163
|
activeProcess: GodotProcess | null;
|
|
104
164
|
activeProjectPath: string | null;
|
|
105
165
|
activeSessionMode: RuntimeSessionMode | null;
|
|
166
|
+
activeBridgePort: number | null;
|
|
167
|
+
private socket;
|
|
168
|
+
private rxChunks;
|
|
169
|
+
private rxTotal;
|
|
170
|
+
private inFlight;
|
|
106
171
|
constructor(config?: GodotServerConfig);
|
|
107
172
|
private isValidGodotPathSync;
|
|
108
173
|
private spawnAsync;
|
|
109
174
|
private isValidGodotPath;
|
|
110
175
|
detectGodotPath(): Promise<void>;
|
|
111
176
|
getGodotPath(): string | null;
|
|
177
|
+
/**
|
|
178
|
+
* Read the port currently baked into the project's bridge script. Returns
|
|
179
|
+
* null if the file is missing or malformed. Thin pass-through to
|
|
180
|
+
* BridgeManager — used by bridge-wait-timeout race detection.
|
|
181
|
+
*/
|
|
182
|
+
readBakedBridgePort(projectPath: string): number | null;
|
|
112
183
|
getVersion(): Promise<string>;
|
|
113
|
-
isGodot44OrLater(version: string): boolean;
|
|
114
184
|
executeOperation(operation: string, params: OperationParams, projectPath: string, timeoutMs?: number): Promise<OperationResult>;
|
|
115
185
|
launchEditor(projectPath: string): ChildProcess;
|
|
116
|
-
runProject(projectPath: string, scene?: string, background?: boolean): GodotProcess
|
|
117
|
-
attachProject(projectPath: string): void
|
|
118
|
-
stopProject(): RuntimeStopResult | null
|
|
186
|
+
runProject(projectPath: string, scene?: string, background?: boolean, bridgePort?: number): Promise<GodotProcess>;
|
|
187
|
+
attachProject(projectPath: string, bridgePort?: number): Promise<void>;
|
|
188
|
+
stopProject(): Promise<RuntimeStopResult | null>;
|
|
119
189
|
hasActiveRuntimeSession(): boolean;
|
|
120
|
-
private removeAutoloadEntry;
|
|
121
190
|
/**
|
|
122
|
-
*
|
|
123
|
-
*
|
|
191
|
+
* Send a JSON command to the McpBridge over a long-lived TCP connection.
|
|
192
|
+
*
|
|
193
|
+
* MCP serializes tool calls so we hold one in-flight command at a time. The
|
|
194
|
+
* socket is lazy-connected on first call and persists across commands until
|
|
195
|
+
* `closeConnection` (or a peer-side close). A close mid-flight rejects with
|
|
196
|
+
* `BridgeDisconnectedError`; a per-command timeout rejects but does NOT
|
|
197
|
+
* close the socket — a slow command does not invalidate the session.
|
|
124
198
|
*/
|
|
125
|
-
injectBridgeAutoload(projectPath: string): void;
|
|
126
|
-
cleanupBridgeAutoload(projectPath: string): void;
|
|
127
|
-
private repairOrphanedBridge;
|
|
128
199
|
sendCommand(command: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<string>;
|
|
200
|
+
/**
|
|
201
|
+
* Tear down the bridge socket. Idempotent. Any in-flight command is
|
|
202
|
+
* rejected with a session-ended error.
|
|
203
|
+
*/
|
|
204
|
+
closeConnection(): void;
|
|
205
|
+
private resetRxBuffer;
|
|
129
206
|
getErrorCount(): number;
|
|
130
207
|
getErrorsSince(marker: number): string[];
|
|
131
208
|
private static readonly SCRIPT_ERROR_PATTERNS;
|
|
209
|
+
private static readonly RETRYABLE_BRIDGE_COMMANDS;
|
|
132
210
|
extractRuntimeErrors(lines: string[]): string[];
|
|
211
|
+
private sendCommandWithReconnect;
|
|
133
212
|
sendCommandWithErrors(command: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<{
|
|
134
213
|
response: string;
|
|
135
214
|
runtimeErrors: string[];
|
|
136
215
|
}>;
|
|
216
|
+
/**
|
|
217
|
+
* Shared poll loop for `waitForBridge` (spawned) and `waitForBridgeAttached`.
|
|
218
|
+
* Sends `ping` payloads until the bridge replies with a pong that
|
|
219
|
+
* `validatePong` accepts, the deadline passes, or `shouldAbort` reports
|
|
220
|
+
* the spawned process has exited.
|
|
221
|
+
*/
|
|
222
|
+
private pollBridge;
|
|
137
223
|
waitForBridgeAttached(timeoutMs?: number, intervalMs?: number): Promise<{
|
|
138
224
|
ready: boolean;
|
|
139
225
|
error?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"godot-runner.d.ts","sourceRoot":"","sources":["../../src/utils/godot-runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAgB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"godot-runner.d.ts","sourceRoot":"","sources":["../../src/utils/godot-runner.ts"],"names":[],"mappings":"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"}
|