gopeak 2.3.3 → 2.3.4
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 +2 -0
- package/build/cli/setup.js +8 -1
- package/build/cli/utils.js +5 -0
- package/build/index.js +75 -76
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
**GoPeak is an MCP server for Godot that lets AI assistants run, inspect, modify, and debug real projects end-to-end.**
|
|
16
16
|
|
|
17
|
+
> Discord community chat is temporarily unavailable while the invite link is refreshed. Please use GitHub Discussions in the meantime: https://github.com/HaD0Yun/Gopeak-godot-mcp/discussions
|
|
18
|
+
|
|
17
19
|
---
|
|
18
20
|
|
|
19
21
|
## Quick Start (3 Minutes)
|
package/build/cli/setup.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* with a precheck function that displays cached GoPeak update notifications.
|
|
6
6
|
*/
|
|
7
7
|
import { existsSync, readFileSync, writeFileSync, appendFileSync } from 'fs';
|
|
8
|
-
import { getShellRcFile, getShellName, getLocalVersion, ensureGopeakDir, ONBOARDING_SHOWN_FILE, STAR_PROMPTED_FILE, } from './utils.js';
|
|
8
|
+
import { getShellRcFile, getShellName, getLocalVersion, ensureGopeakDir, ONBOARDING_SHOWN_FILE, STAR_PROMPTED_FILE, supportsShellHooks, } from './utils.js';
|
|
9
9
|
const MARKER_START = '# >>> GoPeak shell hooks >>>';
|
|
10
10
|
const MARKER_END = '# <<< GoPeak shell hooks <<<';
|
|
11
11
|
/** The shell hook block that gets appended to the RC file. */
|
|
@@ -55,6 +55,13 @@ function generateHookBlock() {
|
|
|
55
55
|
}
|
|
56
56
|
export async function setupShellHooks(args = []) {
|
|
57
57
|
const silent = args.includes('--silent');
|
|
58
|
+
if (!supportsShellHooks()) {
|
|
59
|
+
if (!silent) {
|
|
60
|
+
console.log('ℹ️ GoPeak shell hooks are only installed for bash/zsh on Unix-like systems.');
|
|
61
|
+
console.log(' Skipping shell hook setup on this platform/shell.');
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
58
65
|
const rcFile = getShellRcFile();
|
|
59
66
|
const shellName = getShellName();
|
|
60
67
|
const log = silent ? (..._args) => { } : console.log.bind(console);
|
package/build/cli/utils.js
CHANGED
|
@@ -122,6 +122,11 @@ export function getShellName() {
|
|
|
122
122
|
return 'zsh';
|
|
123
123
|
return 'bash';
|
|
124
124
|
}
|
|
125
|
+
export function supportsShellHooks(platform = process.platform, shell = process.env.SHELL ?? '') {
|
|
126
|
+
if (platform === 'win32')
|
|
127
|
+
return false;
|
|
128
|
+
return shell.includes('bash') || shell.includes('zsh');
|
|
129
|
+
}
|
|
125
130
|
/* ------------------------------------------------------------------ */
|
|
126
131
|
/* Command helpers */
|
|
127
132
|
/* ------------------------------------------------------------------ */
|
package/build/index.js
CHANGED
|
@@ -694,7 +694,7 @@ class GodotServer {
|
|
|
694
694
|
resolve({
|
|
695
695
|
content: [
|
|
696
696
|
{ type: 'text', text: `Screenshot captured: ${parsed.width}x${parsed.height} ${parsed.format}` },
|
|
697
|
-
{ type: 'image',
|
|
697
|
+
{ type: 'image', data: parsed.data, mimeType: 'image/png' },
|
|
698
698
|
],
|
|
699
699
|
});
|
|
700
700
|
return;
|
|
@@ -793,8 +793,41 @@ class GodotServer {
|
|
|
793
793
|
}
|
|
794
794
|
return handleDAPTool(this.dapClient, toolName, args);
|
|
795
795
|
}
|
|
796
|
+
sanitizeExportedToolName(toolName) {
|
|
797
|
+
const sanitized = toolName
|
|
798
|
+
.normalize('NFKD')
|
|
799
|
+
.replace(/[^\x00-\x7F]/g, '')
|
|
800
|
+
.replace(/[^a-zA-Z0-9-]+/g, '-')
|
|
801
|
+
.replace(/-+/g, '-')
|
|
802
|
+
.replace(/^-+|-+$/g, '')
|
|
803
|
+
.slice(0, 128);
|
|
804
|
+
return sanitized.length > 0 ? sanitized : 'tool';
|
|
805
|
+
}
|
|
806
|
+
buildToolNameResolutionMap(allTools) {
|
|
807
|
+
const resolutionMap = new Map();
|
|
808
|
+
const register = (candidateName, resolvedName) => {
|
|
809
|
+
const existing = resolutionMap.get(candidateName);
|
|
810
|
+
if (existing && existing !== resolvedName) {
|
|
811
|
+
throw new Error(`Sanitized tool name collision: "${candidateName}" maps to both "${existing}" and "${resolvedName}"`);
|
|
812
|
+
}
|
|
813
|
+
resolutionMap.set(candidateName, resolvedName);
|
|
814
|
+
};
|
|
815
|
+
for (const tool of allTools) {
|
|
816
|
+
register(tool.name, tool.name);
|
|
817
|
+
register(this.sanitizeExportedToolName(tool.name), tool.name);
|
|
818
|
+
}
|
|
819
|
+
for (const [compactName, legacyName] of Object.entries(this.compactAliasToLegacy)) {
|
|
820
|
+
register(compactName, legacyName);
|
|
821
|
+
register(this.sanitizeExportedToolName(compactName), legacyName);
|
|
822
|
+
}
|
|
823
|
+
return resolutionMap;
|
|
824
|
+
}
|
|
796
825
|
resolveToolAlias(requestedToolName) {
|
|
797
|
-
|
|
826
|
+
const allTools = this.getAllToolDefinitions();
|
|
827
|
+
const resolutionMap = this.buildToolNameResolutionMap(allTools);
|
|
828
|
+
return resolutionMap.get(requestedToolName)
|
|
829
|
+
|| resolutionMap.get(this.sanitizeExportedToolName(requestedToolName))
|
|
830
|
+
|| requestedToolName;
|
|
798
831
|
}
|
|
799
832
|
buildCompactTools(allTools) {
|
|
800
833
|
const compactTools = [];
|
|
@@ -811,6 +844,26 @@ class GodotServer {
|
|
|
811
844
|
}
|
|
812
845
|
return compactTools;
|
|
813
846
|
}
|
|
847
|
+
sanitizeToolsForList(tools) {
|
|
848
|
+
const seenNames = new Map();
|
|
849
|
+
return tools.map((tool) => {
|
|
850
|
+
const sanitizedName = this.sanitizeExportedToolName(tool.name);
|
|
851
|
+
const existing = seenNames.get(sanitizedName);
|
|
852
|
+
if (existing && existing !== tool.name) {
|
|
853
|
+
throw new Error(`Sanitized tool name collision in tools/list: "${sanitizedName}" from "${existing}" and "${tool.name}"`);
|
|
854
|
+
}
|
|
855
|
+
seenNames.set(sanitizedName, tool.name);
|
|
856
|
+
if (sanitizedName !== tool.name) {
|
|
857
|
+
this.logDebug(`Exporting tool "${tool.name}" as "${sanitizedName}" for OpenAI-compatible clients`);
|
|
858
|
+
}
|
|
859
|
+
return sanitizedName === tool.name
|
|
860
|
+
? tool
|
|
861
|
+
: {
|
|
862
|
+
...tool,
|
|
863
|
+
name: sanitizedName,
|
|
864
|
+
};
|
|
865
|
+
});
|
|
866
|
+
}
|
|
814
867
|
getExposedTools(allTools) {
|
|
815
868
|
if (this.toolExposureProfile === 'full' || this.toolExposureProfile === 'legacy') {
|
|
816
869
|
return allTools;
|
|
@@ -3626,7 +3679,7 @@ class GodotServer {
|
|
|
3626
3679
|
this.server.setRequestHandler(ListToolsRequestSchema, async (request) => {
|
|
3627
3680
|
const allTools = buildToolDefinitions();
|
|
3628
3681
|
this.cachedToolDefinitions = allTools;
|
|
3629
|
-
const exposedTools = this.getExposedTools(allTools);
|
|
3682
|
+
const exposedTools = this.sanitizeToolsForList(this.getExposedTools(allTools));
|
|
3630
3683
|
return this.paginateToolsForList(exposedTools, request.params?.cursor);
|
|
3631
3684
|
});
|
|
3632
3685
|
// Handle tool calls
|
|
@@ -6433,27 +6486,14 @@ class GodotServer {
|
|
|
6433
6486
|
return this.createErrorResponse('Project path is required', ['Provide a valid path to a Godot project directory']);
|
|
6434
6487
|
}
|
|
6435
6488
|
try {
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
return {
|
|
6442
|
-
content: [{
|
|
6443
|
-
type: 'text',
|
|
6444
|
-
text: JSON.stringify({
|
|
6445
|
-
status: 'running',
|
|
6446
|
-
nodePath: args.nodePath || '/',
|
|
6447
|
-
depth: args.depth || 3,
|
|
6448
|
-
note: 'Runtime tree inspection requires the godot_mcp_runtime addon. Current output shows debug logs.',
|
|
6449
|
-
recentOutput: this.activeProcess.output.slice(-20),
|
|
6450
|
-
recentErrors: this.activeProcess.errors.slice(-10),
|
|
6451
|
-
}, null, 2),
|
|
6452
|
-
}],
|
|
6453
|
-
};
|
|
6489
|
+
return await this.handleRuntimeCommand('get_tree', {
|
|
6490
|
+
root: args.nodePath || '/root',
|
|
6491
|
+
depth: args.depth || 3,
|
|
6492
|
+
include_properties: Boolean(args.includeProperties),
|
|
6493
|
+
});
|
|
6454
6494
|
}
|
|
6455
6495
|
catch (error) {
|
|
6456
|
-
return this.createErrorResponse(`Failed to inspect runtime tree: ${error?.message || 'Unknown error'}`, ['Ensure a Godot process is running']);
|
|
6496
|
+
return this.createErrorResponse(`Failed to inspect runtime tree: ${error?.message || 'Unknown error'}`, ['Ensure a Godot process is running with the runtime addon enabled']);
|
|
6457
6497
|
}
|
|
6458
6498
|
}
|
|
6459
6499
|
/**
|
|
@@ -6465,24 +6505,11 @@ class GodotServer {
|
|
|
6465
6505
|
return this.createErrorResponse('Missing required parameters', ['Provide projectPath, nodePath, property, and value']);
|
|
6466
6506
|
}
|
|
6467
6507
|
try {
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
content: [{
|
|
6474
|
-
type: 'text',
|
|
6475
|
-
text: JSON.stringify({
|
|
6476
|
-
status: 'not_implemented',
|
|
6477
|
-
note: 'Runtime property modification requires the godot_mcp_runtime addon installed in the running project.',
|
|
6478
|
-
requested: {
|
|
6479
|
-
nodePath: args.nodePath,
|
|
6480
|
-
property: args.property,
|
|
6481
|
-
value: args.value,
|
|
6482
|
-
},
|
|
6483
|
-
}, null, 2),
|
|
6484
|
-
}],
|
|
6485
|
-
};
|
|
6508
|
+
return await this.handleRuntimeCommand('set_property', {
|
|
6509
|
+
path: args.nodePath,
|
|
6510
|
+
property: args.property,
|
|
6511
|
+
value: args.value,
|
|
6512
|
+
});
|
|
6486
6513
|
}
|
|
6487
6514
|
catch (error) {
|
|
6488
6515
|
return this.createErrorResponse(`Failed to set runtime property: ${error?.message || 'Unknown error'}`, ['Ensure a Godot process is running with the runtime addon']);
|
|
@@ -6497,24 +6524,11 @@ class GodotServer {
|
|
|
6497
6524
|
return this.createErrorResponse('Missing required parameters', ['Provide projectPath, nodePath, and method']);
|
|
6498
6525
|
}
|
|
6499
6526
|
try {
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
content: [{
|
|
6506
|
-
type: 'text',
|
|
6507
|
-
text: JSON.stringify({
|
|
6508
|
-
status: 'not_implemented',
|
|
6509
|
-
note: 'Runtime method calling requires the godot_mcp_runtime addon installed in the running project.',
|
|
6510
|
-
requested: {
|
|
6511
|
-
nodePath: args.nodePath,
|
|
6512
|
-
method: args.method,
|
|
6513
|
-
args: args.args || [],
|
|
6514
|
-
},
|
|
6515
|
-
}, null, 2),
|
|
6516
|
-
}],
|
|
6517
|
-
};
|
|
6527
|
+
return await this.handleRuntimeCommand('call_method', {
|
|
6528
|
+
path: args.nodePath,
|
|
6529
|
+
method: args.method,
|
|
6530
|
+
args: Array.isArray(args.args) ? args.args : [],
|
|
6531
|
+
});
|
|
6518
6532
|
}
|
|
6519
6533
|
catch (error) {
|
|
6520
6534
|
return this.createErrorResponse(`Failed to call runtime method: ${error?.message || 'Unknown error'}`, ['Ensure a Godot process is running with the runtime addon']);
|
|
@@ -6529,24 +6543,9 @@ class GodotServer {
|
|
|
6529
6543
|
return this.createErrorResponse('Project path is required', ['Provide a valid path to a Godot project directory']);
|
|
6530
6544
|
}
|
|
6531
6545
|
try {
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
}
|
|
6535
|
-
// Basic metrics from process output
|
|
6536
|
-
return {
|
|
6537
|
-
content: [{
|
|
6538
|
-
type: 'text',
|
|
6539
|
-
text: JSON.stringify({
|
|
6540
|
-
status: 'running',
|
|
6541
|
-
metrics: {
|
|
6542
|
-
outputLines: this.activeProcess.output.length,
|
|
6543
|
-
errorLines: this.activeProcess.errors.length,
|
|
6544
|
-
note: 'Detailed metrics require the godot_mcp_runtime addon.',
|
|
6545
|
-
},
|
|
6546
|
-
requestedMetrics: args.metrics || 'all',
|
|
6547
|
-
}, null, 2),
|
|
6548
|
-
}],
|
|
6549
|
-
};
|
|
6546
|
+
return await this.handleRuntimeCommand('get_metrics', {
|
|
6547
|
+
metrics: Array.isArray(args.metrics) ? args.metrics : [],
|
|
6548
|
+
});
|
|
6550
6549
|
}
|
|
6551
6550
|
catch (error) {
|
|
6552
6551
|
return this.createErrorResponse(`Failed to get runtime metrics: ${error?.message || 'Unknown error'}`, ['Ensure a Godot process is running']);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gopeak",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.4",
|
|
4
4
|
"mcpName": "io.github.HaD0Yun/gopeak",
|
|
5
5
|
"description": "GoPeak — The most comprehensive MCP server for Godot Engine. 95+ tools: scene management, GDScript LSP diagnostics, DAP debugger, screenshot capture, input injection, ClassDB introspection, CC0 asset library. AI-assisted game development with Claude, Cursor, Cline, OpenCode.",
|
|
6
6
|
"type": "module",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"watch": "tsc --watch",
|
|
30
30
|
"inspector": "npx @modelcontextprotocol/inspector build/index.js",
|
|
31
31
|
"pack": "npm pack --dry-run",
|
|
32
|
-
"version:bump": "node scripts/bump-version.mjs"
|
|
32
|
+
"version:bump": "node scripts/bump-version.mjs",
|
|
33
|
+
"test:setup": "npm run build && node test-setup-hooks.mjs"
|
|
33
34
|
},
|
|
34
35
|
"engines": {
|
|
35
36
|
"node": ">=18"
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
},
|
|
49
50
|
"overrides": {
|
|
50
51
|
"@hono/node-server": "^1.19.11",
|
|
51
|
-
"hono": "
|
|
52
|
+
"hono": "4.12.7"
|
|
52
53
|
},
|
|
53
54
|
"license": "MIT",
|
|
54
55
|
"repository": {
|