godot-daedalus_backend 1.0.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.
Files changed (67) hide show
  1. package/README.md +101 -0
  2. package/bin/godot-daedalus-backend.js +4 -0
  3. package/bin/godot-daedalus-mcp.js +4 -0
  4. package/bin/godot-daedalus-terminal-mcp.js +4 -0
  5. package/bin/run-tsx-entry.js +26 -0
  6. package/package.json +54 -0
  7. package/scripts/deepseek-tokenizer-server.py +54 -0
  8. package/src/app-paths.ts +36 -0
  9. package/src/main.ts +21 -0
  10. package/src/mcp/content-length-protocol.ts +68 -0
  11. package/src/mcp/custom-mcp-config-store.ts +397 -0
  12. package/src/mcp/godot-diagnostics-bridge.ts +1298 -0
  13. package/src/mcp/godot-editor-bridge.ts +307 -0
  14. package/src/mcp/godot-mcp-server.ts +3484 -0
  15. package/src/mcp/godot-paths.ts +151 -0
  16. package/src/mcp/godot-project-settings.ts +233 -0
  17. package/src/mcp/godot-tool-registration.ts +46 -0
  18. package/src/mcp/mcp-config.ts +48 -0
  19. package/src/mcp/mcp-host.ts +393 -0
  20. package/src/mcp/mcp-session.ts +81 -0
  21. package/src/mcp/terminal-mcp-server.ts +576 -0
  22. package/src/mcp/tscn-tools.ts +302 -0
  23. package/src/mcp/types.ts +12 -0
  24. package/src/ping-client.ts +24 -0
  25. package/src/prompts/registry.ts +97 -0
  26. package/src/prompts/templates/backend-helper.md +25 -0
  27. package/src/prompts/templates/gdscript-reviewer.md +19 -0
  28. package/src/prompts/templates/godot-assistant.md +225 -0
  29. package/src/prompts/templates/scene-architect.md +15 -0
  30. package/src/prompts/templates/session-compressor.md +33 -0
  31. package/src/protocol/schema.ts +486 -0
  32. package/src/protocol/types.ts +77 -0
  33. package/src/providers/deepseek-agent.ts +1014 -0
  34. package/src/providers/deepseek-client.ts +114 -0
  35. package/src/providers/deepseek-dsml-tools.ts +90 -0
  36. package/src/providers/deepseek-loose-tools.ts +450 -0
  37. package/src/providers/provider-config-store.ts +164 -0
  38. package/src/server/client-session.ts +93 -0
  39. package/src/server/request-dispatcher.ts +74 -0
  40. package/src/server/response-helpers.ts +33 -0
  41. package/src/server/send-json.ts +8 -0
  42. package/src/server/websocket-server.ts +3997 -0
  43. package/src/session/session-compressor.ts +68 -0
  44. package/src/session/session-store.ts +669 -0
  45. package/src/skills/registry.ts +180 -0
  46. package/src/skills/templates/backend-helper.md +12 -0
  47. package/src/skills/templates/file-creator.md +14 -0
  48. package/src/skills/templates/gdscript-review.md +12 -0
  49. package/src/skills/templates/godot-project-init.md +29 -0
  50. package/src/skills/templates/scene-builder.md +12 -0
  51. package/src/tokens/deepseek-tokenizer-counter.ts +233 -0
  52. package/src/tokens/model-profiles.ts +38 -0
  53. package/src/tokens/token-counter-factory.ts +52 -0
  54. package/src/tokens/token-counter.ts +22 -0
  55. package/src/tools/approval-gateway.ts +111 -0
  56. package/src/tools/llm-tools.ts +1415 -0
  57. package/src/tools/tool-dispatcher.ts +147 -0
  58. package/src/tools/tool-event-describer.ts +387 -0
  59. package/src/tools/tool-idempotency.ts +373 -0
  60. package/src/tools/tool-policy-table.ts +61 -0
  61. package/src/tools/tool-policy.ts +73 -0
  62. package/src/workflow/llm-planner.ts +407 -0
  63. package/src/workflow/planner.ts +201 -0
  64. package/src/workflow/runner.ts +141 -0
  65. package/src/workflow/types.ts +69 -0
  66. package/src/workspace/registry.ts +104 -0
  67. package/src/workspace/types.ts +7 -0
@@ -0,0 +1,1415 @@
1
+ import { createHash } from "node:crypto";
2
+ import type { ChatCompletionTool } from "openai/resources/chat/completions";
3
+
4
+ export const DEFAULT_TOOL_STEPS: number = 10;
5
+ export const CUSTOM_MCP_TOOLS_SENTINEL: string = "__custom_mcp_tools__";
6
+ export const CUSTOM_MCP_TOOL_PREFIX: string = "mcp_custom_";
7
+ const MAX_DYNAMIC_TOOLS_TOTAL: number = 96;
8
+ const MAX_DYNAMIC_TOOLS_PER_SERVER: number = 32;
9
+ const MAX_DYNAMIC_SCHEMA_CHARS: number = 8000;
10
+
11
+ export type ToolBudgetLevel = "simple" | "normal" | "codegen" | "project_edit";
12
+
13
+ const TOOL_BUDGET_MAP: Record<ToolBudgetLevel, number> = {
14
+ simple: 4,
15
+ normal: 10,
16
+ codegen: 20,
17
+ project_edit: 30
18
+ };
19
+
20
+ const SKILL_BUDGET_MAP: Record<string, number> = {
21
+ "gdscript.review": 8,
22
+ "godot.project_init": 12,
23
+ "file.creator": 16,
24
+ "scene.builder": 20,
25
+ "backend.helper": 10
26
+ };
27
+
28
+ export function resolveToolBudget(
29
+ budgetLevel?: ToolBudgetLevel | string,
30
+ skillId?: string
31
+ ): number {
32
+ if (budgetLevel && TOOL_BUDGET_MAP[budgetLevel as ToolBudgetLevel]) {
33
+ return TOOL_BUDGET_MAP[budgetLevel as ToolBudgetLevel];
34
+ }
35
+
36
+ if (skillId && SKILL_BUDGET_MAP[skillId]) {
37
+ return SKILL_BUDGET_MAP[skillId];
38
+ }
39
+
40
+ return DEFAULT_TOOL_STEPS;
41
+ }
42
+
43
+ export const MAX_TOOL_RESULT_CHARS: number = 12000;
44
+ export const MAX_TOTAL_TOOL_RESULT_CHARS: number = 48000;
45
+
46
+ type ToolMapping = {
47
+ serverId: string;
48
+ toolName: string;
49
+ };
50
+
51
+ export type DynamicMcpToolSource = {
52
+ serverId: string;
53
+ serverName: string;
54
+ toolName: string;
55
+ description?: string | undefined;
56
+ inputSchema?: unknown;
57
+ };
58
+
59
+ export type DynamicMcpToolMetadata = DynamicMcpToolSource & {
60
+ llmToolName: string;
61
+ };
62
+
63
+ const dynamicToolDefinitions: ChatCompletionTool[] = [];
64
+ const dynamicToolMap: Map<string, ToolMapping> = new Map();
65
+ const dynamicToolMetadata: Map<string, DynamicMcpToolMetadata> = new Map();
66
+
67
+ function slugifyToolPart(value: string, maxLength: number): string {
68
+ const slug: string = value
69
+ .toLowerCase()
70
+ .replace(/[^a-z0-9]+/g, "_")
71
+ .replace(/^_+|_+$/g, "")
72
+ .slice(0, maxLength);
73
+ return slug.length > 0 ? slug : "tool";
74
+ }
75
+
76
+ function createDynamicToolName(source: DynamicMcpToolSource): string {
77
+ const serverSlug: string = slugifyToolPart(source.serverName || source.serverId, 16);
78
+ const toolSlug: string = slugifyToolPart(source.toolName, 27);
79
+ const hash: string = createHash("sha1")
80
+ .update(`${source.serverId}\n${source.toolName}`)
81
+ .digest("hex")
82
+ .slice(0, 8);
83
+ return `${CUSTOM_MCP_TOOL_PREFIX}${serverSlug}_${toolSlug}_${hash}`;
84
+ }
85
+
86
+ function sanitizeDynamicInputSchema(inputSchema: unknown): Record<string, unknown> {
87
+ if (inputSchema === null || typeof inputSchema !== "object" || Array.isArray(inputSchema)) {
88
+ return {
89
+ type: "object",
90
+ properties: {},
91
+ additionalProperties: true
92
+ };
93
+ }
94
+
95
+ const schema: Record<string, unknown> = { ...(inputSchema as Record<string, unknown>) };
96
+ delete schema.$schema;
97
+ if (schema.type !== "object") {
98
+ schema.type = "object";
99
+ }
100
+ if (JSON.stringify(schema).length > MAX_DYNAMIC_SCHEMA_CHARS) {
101
+ return {
102
+ type: "object",
103
+ properties: {},
104
+ additionalProperties: true
105
+ };
106
+ }
107
+
108
+ return schema;
109
+ }
110
+
111
+ function createDynamicToolDefinition(source: DynamicMcpToolSource, llmToolName: string): ChatCompletionTool {
112
+ const descriptionParts: string[] = [
113
+ `自定义 MCP 工具,来自 server "${source.serverName}" 的工具 "${source.toolName}"。`,
114
+ "该工具由用户配置的外部 MCP server 提供,默认按写风险处理,调用前会走审批。"
115
+ ];
116
+ if (source.description !== undefined && source.description.trim().length > 0) {
117
+ descriptionParts.push(source.description.trim());
118
+ }
119
+
120
+ return {
121
+ type: "function",
122
+ function: {
123
+ name: llmToolName,
124
+ description: descriptionParts.join(" ").slice(0, 1024),
125
+ parameters: sanitizeDynamicInputSchema(source.inputSchema)
126
+ }
127
+ };
128
+ }
129
+
130
+ export function replaceDynamicMcpTools(sources: readonly DynamicMcpToolSource[]): void {
131
+ dynamicToolDefinitions.length = 0;
132
+ dynamicToolMap.clear();
133
+ dynamicToolMetadata.clear();
134
+
135
+ const perServerCounts: Map<string, number> = new Map();
136
+ for (const source of sources) {
137
+ if (dynamicToolDefinitions.length >= MAX_DYNAMIC_TOOLS_TOTAL) {
138
+ break;
139
+ }
140
+
141
+ const nextServerCount: number = (perServerCounts.get(source.serverId) ?? 0) + 1;
142
+ if (nextServerCount > MAX_DYNAMIC_TOOLS_PER_SERVER) {
143
+ continue;
144
+ }
145
+ perServerCounts.set(source.serverId, nextServerCount);
146
+
147
+ const llmToolName: string = createDynamicToolName(source);
148
+ const definition: ChatCompletionTool = createDynamicToolDefinition(source, llmToolName);
149
+ dynamicToolDefinitions.push(definition);
150
+ dynamicToolMap.set(llmToolName, {
151
+ serverId: source.serverId,
152
+ toolName: source.toolName
153
+ });
154
+ dynamicToolMetadata.set(llmToolName, {
155
+ ...source,
156
+ llmToolName
157
+ });
158
+ }
159
+ }
160
+
161
+ export function getDynamicMcpToolNames(): string[] {
162
+ return Array.from(dynamicToolMap.keys());
163
+ }
164
+
165
+ export function isDynamicMcpToolName(toolName: string): boolean {
166
+ return toolName.startsWith(CUSTOM_MCP_TOOL_PREFIX);
167
+ }
168
+
169
+ export function getDynamicMcpToolMetadata(toolName: string): DynamicMcpToolMetadata | undefined {
170
+ return dynamicToolMetadata.get(toolName);
171
+ }
172
+
173
+ const TOOL_MAP: Record<string, ToolMapping> = {
174
+ "mcp_godot_get_project_summary": {
175
+ serverId: "godot",
176
+ toolName: "get_project_summary"
177
+ },
178
+ "mcp_godot_list_project_files": {
179
+ serverId: "godot",
180
+ toolName: "list_project_files"
181
+ },
182
+ "mcp_godot_list_scenes": {
183
+ serverId: "godot",
184
+ toolName: "list_scenes"
185
+ },
186
+ "mcp_godot_list_scripts": {
187
+ serverId: "godot",
188
+ toolName: "list_scripts"
189
+ },
190
+ "mcp_godot_read_text_file": {
191
+ serverId: "godot",
192
+ toolName: "read_text_file"
193
+ },
194
+ "mcp_godot_search_text": {
195
+ serverId: "godot",
196
+ toolName: "search_text"
197
+ },
198
+ "mcp_godot_get_project_log_config": {
199
+ serverId: "godot",
200
+ toolName: "get_project_log_config"
201
+ },
202
+ "mcp_godot_list_project_logs": {
203
+ serverId: "godot",
204
+ toolName: "list_project_logs"
205
+ },
206
+ "mcp_godot_read_project_log": {
207
+ serverId: "godot",
208
+ toolName: "read_project_log"
209
+ },
210
+ "mcp_godot_get_project_settings": {
211
+ serverId: "godot",
212
+ toolName: "get_project_settings"
213
+ },
214
+ "mcp_godot_get_editor_config_summary": {
215
+ serverId: "godot",
216
+ toolName: "get_editor_config_summary"
217
+ },
218
+ "mcp_godot_get_editor_settings": {
219
+ serverId: "godot",
220
+ toolName: "get_editor_settings"
221
+ },
222
+ "mcp_godot_list_editor_config_files": {
223
+ serverId: "godot",
224
+ toolName: "list_editor_config_files"
225
+ },
226
+ "mcp_godot_read_editor_config_file": {
227
+ serverId: "godot",
228
+ toolName: "read_editor_config_file"
229
+ },
230
+ "mcp_godot_get_editor_project_state": {
231
+ serverId: "godot",
232
+ toolName: "get_editor_project_state"
233
+ },
234
+ "mcp_godot_get_recent_projects": {
235
+ serverId: "godot",
236
+ toolName: "get_recent_projects"
237
+ },
238
+ "mcp_godot_propose_set_project_setting": {
239
+ serverId: "godot",
240
+ toolName: "propose_set_project_setting"
241
+ },
242
+ "mcp_godot_set_project_setting": {
243
+ serverId: "godot",
244
+ toolName: "set_project_setting"
245
+ },
246
+ "mcp_godot_propose_unset_project_setting": {
247
+ serverId: "godot",
248
+ toolName: "propose_unset_project_setting"
249
+ },
250
+ "mcp_godot_unset_project_setting": {
251
+ serverId: "godot",
252
+ toolName: "unset_project_setting"
253
+ },
254
+ "mcp_godot_propose_create_text_file": {
255
+ serverId: "godot",
256
+ toolName: "propose_create_text_file"
257
+ },
258
+ "mcp_godot_create_text_file": {
259
+ serverId: "godot",
260
+ toolName: "create_text_file"
261
+ },
262
+ "mcp_godot_propose_overwrite_text_file": {
263
+ serverId: "godot",
264
+ toolName: "propose_overwrite_text_file"
265
+ },
266
+ "mcp_godot_overwrite_text_file": {
267
+ serverId: "godot",
268
+ toolName: "overwrite_text_file"
269
+ },
270
+ "mcp_godot_propose_replace_text_in_file": {
271
+ serverId: "godot",
272
+ toolName: "propose_replace_text_in_file"
273
+ },
274
+ "mcp_godot_replace_text_in_file": {
275
+ serverId: "godot",
276
+ toolName: "replace_text_in_file"
277
+ },
278
+ "mcp_godot_delete_file": {
279
+ serverId: "godot",
280
+ toolName: "delete_file"
281
+ },
282
+ "mcp_terminal_run_safe_preset": {
283
+ serverId: "terminal",
284
+ toolName: "run_safe_preset"
285
+ },
286
+ "mcp_terminal_run_write_preset": {
287
+ serverId: "terminal",
288
+ toolName: "run_write_preset"
289
+ },
290
+ "mcp_terminal_get_capabilities": {
291
+ serverId: "terminal",
292
+ toolName: "get_terminal_capabilities"
293
+ },
294
+ "mcp_godot_inspect_scene_tree": {
295
+ serverId: "godot",
296
+ toolName: "inspect_scene_tree"
297
+ },
298
+ "mcp_godot_propose_create_scene": {
299
+ serverId: "godot",
300
+ toolName: "propose_create_scene"
301
+ },
302
+ "mcp_godot_create_scene": {
303
+ serverId: "godot",
304
+ toolName: "create_scene"
305
+ },
306
+ "mcp_godot_propose_add_node_to_scene": {
307
+ serverId: "godot",
308
+ toolName: "propose_add_node_to_scene"
309
+ },
310
+ "mcp_godot_add_node_to_scene": {
311
+ serverId: "godot",
312
+ toolName: "add_node_to_scene"
313
+ },
314
+ "mcp_godot_propose_attach_script_to_node": {
315
+ serverId: "godot",
316
+ toolName: "propose_attach_script_to_node"
317
+ },
318
+ "mcp_godot_attach_script_to_node": {
319
+ serverId: "godot",
320
+ toolName: "attach_script_to_node"
321
+ },
322
+ "mcp_godot_propose_connect_signal_in_scene": {
323
+ serverId: "godot",
324
+ toolName: "propose_connect_signal_in_scene"
325
+ },
326
+ "mcp_godot_connect_signal_in_scene": {
327
+ serverId: "godot",
328
+ toolName: "connect_signal_in_scene"
329
+ },
330
+ "mcp_godot_propose_apply_scene_patch": {
331
+ serverId: "godot",
332
+ toolName: "propose_apply_scene_patch"
333
+ },
334
+ "mcp_godot_apply_scene_patch": {
335
+ serverId: "godot",
336
+ toolName: "apply_scene_patch"
337
+ },
338
+ "mcp_godot_editor_get_context": {
339
+ serverId: "godot_editor",
340
+ toolName: "get_context"
341
+ },
342
+ "mcp_godot_editor_get_selected_nodes": {
343
+ serverId: "godot_editor",
344
+ toolName: "get_selected_nodes"
345
+ },
346
+ "mcp_godot_editor_inspect_node": {
347
+ serverId: "godot_editor",
348
+ toolName: "inspect_node"
349
+ },
350
+ "mcp_godot_editor_apply_scene_patch": {
351
+ serverId: "godot_editor",
352
+ toolName: "apply_scene_patch"
353
+ },
354
+ "mcp_godot_lsp_get_status": {
355
+ serverId: "godot_diagnostics",
356
+ toolName: "lsp_get_status"
357
+ },
358
+ "mcp_godot_lsp_get_file_diagnostics": {
359
+ serverId: "godot_diagnostics",
360
+ toolName: "lsp_get_file_diagnostics"
361
+ },
362
+ "mcp_godot_lsp_get_document_symbols": {
363
+ serverId: "godot_diagnostics",
364
+ toolName: "lsp_get_document_symbols"
365
+ },
366
+ "mcp_godot_lsp_hover": {
367
+ serverId: "godot_diagnostics",
368
+ toolName: "lsp_hover"
369
+ },
370
+ "mcp_godot_lsp_goto_definition": {
371
+ serverId: "godot_diagnostics",
372
+ toolName: "lsp_goto_definition"
373
+ },
374
+ "mcp_godot_dap_get_status": {
375
+ serverId: "godot_diagnostics",
376
+ toolName: "dap_get_status"
377
+ },
378
+ "mcp_godot_dap_get_last_error": {
379
+ serverId: "godot_diagnostics",
380
+ toolName: "dap_get_last_error"
381
+ },
382
+ "mcp_godot_dap_get_stack_trace": {
383
+ serverId: "godot_diagnostics",
384
+ toolName: "dap_get_stack_trace"
385
+ },
386
+ "mcp_godot_dap_get_variables": {
387
+ serverId: "godot_diagnostics",
388
+ toolName: "dap_get_variables"
389
+ },
390
+ "mcp_terminal_run_godot_scene_script": {
391
+ serverId: "terminal",
392
+ toolName: "run_godot_scene_script"
393
+ }
394
+ };
395
+
396
+ const TOOL_DEFINITIONS: ChatCompletionTool[] = [
397
+ {
398
+ type: "function",
399
+ function: {
400
+ name: "mcp_godot_get_project_summary",
401
+ description: "获取 Godot 项目的摘要信息,包括项目名称、主场景、场景和脚本数量、插件列表等",
402
+ parameters: {
403
+ type: "object",
404
+ properties: {},
405
+ required: []
406
+ }
407
+ }
408
+ },
409
+ {
410
+ type: "function",
411
+ function: {
412
+ name: "mcp_godot_list_project_files",
413
+ description: "递归列出 Godot 项目文件,可按子目录和扩展名过滤",
414
+ parameters: {
415
+ type: "object",
416
+ properties: {
417
+ subdir: {
418
+ type: "string",
419
+ description: "相对于项目根目录的子目录路径"
420
+ },
421
+ extensions: {
422
+ type: "array",
423
+ items: { type: "string" },
424
+ description: "扩展名过滤,例如 ['.gd', '.tscn']"
425
+ },
426
+ includeAddons: {
427
+ type: "boolean",
428
+ description: "是否包含 addons 目录"
429
+ }
430
+ },
431
+ required: []
432
+ }
433
+ }
434
+ },
435
+ {
436
+ type: "function",
437
+ function: {
438
+ name: "mcp_godot_list_scenes",
439
+ description: "列出 Godot 项目中所有 .tscn 场景文件",
440
+ parameters: {
441
+ type: "object",
442
+ properties: {
443
+ includeAddons: {
444
+ type: "boolean",
445
+ description: "是否包含 addons 目录"
446
+ }
447
+ },
448
+ required: []
449
+ }
450
+ }
451
+ },
452
+ {
453
+ type: "function",
454
+ function: {
455
+ name: "mcp_godot_list_scripts",
456
+ description: "列出 Godot 项目中所有 .gd 脚本文件",
457
+ parameters: {
458
+ type: "object",
459
+ properties: {
460
+ includeAddons: {
461
+ type: "boolean",
462
+ description: "是否包含 addons 目录"
463
+ }
464
+ },
465
+ required: []
466
+ }
467
+ }
468
+ },
469
+ {
470
+ type: "function",
471
+ function: {
472
+ name: "mcp_godot_read_text_file",
473
+ description: "读取 Godot 项目中的文本文件内容",
474
+ parameters: {
475
+ type: "object",
476
+ properties: {
477
+ relativePath: {
478
+ type: "string",
479
+ description: "相对于项目根目录的文件路径"
480
+ }
481
+ },
482
+ required: ["relativePath"]
483
+ }
484
+ }
485
+ },
486
+ {
487
+ type: "function",
488
+ function: {
489
+ name: "mcp_godot_search_text",
490
+ description: "在项目文本文件中搜索关键词,返回匹配的文件路径和行号",
491
+ parameters: {
492
+ type: "object",
493
+ properties: {
494
+ query: {
495
+ type: "string",
496
+ description: "要搜索的文本关键词"
497
+ },
498
+ extensions: {
499
+ type: "array",
500
+ items: { type: "string" },
501
+ description: "扩展名过滤,例如 ['.gd']"
502
+ },
503
+ limit: {
504
+ type: "integer",
505
+ description: "最多返回多少条匹配,默认 50"
506
+ }
507
+ },
508
+ required: ["query"]
509
+ }
510
+ }
511
+ },
512
+ {
513
+ type: "function",
514
+ function: {
515
+ name: "mcp_godot_get_project_log_config",
516
+ description: "读取 Godot 项目日志配置并解析 user://。当用户询问日志位置、运行报错或 user://logs/godot.log 时,先用本工具获取真实路径,不要自己猜 user://。",
517
+ parameters: {
518
+ type: "object",
519
+ properties: {},
520
+ required: []
521
+ }
522
+ }
523
+ },
524
+ {
525
+ type: "function",
526
+ function: {
527
+ name: "mcp_godot_list_project_logs",
528
+ description: "列出当前 Godot 项目日志目录中的 godot.log 和轮转日志,包含大小和修改时间。排查运行错误时先列出日志再读取最新日志。",
529
+ parameters: {
530
+ type: "object",
531
+ properties: {},
532
+ required: []
533
+ }
534
+ }
535
+ },
536
+ {
537
+ type: "function",
538
+ function: {
539
+ name: "mcp_godot_read_project_log",
540
+ description: "读取 Godot 项目日志尾部。默认读取 godot.log;如果不存在则读取最新轮转日志。只读取日志目录内文件,返回 user:// 解析说明。",
541
+ parameters: {
542
+ type: "object",
543
+ properties: {
544
+ fileName: {
545
+ type: "string",
546
+ description: "可选,来自 mcp_godot_list_project_logs 的纯文件名,例如 godot.log"
547
+ },
548
+ lines: {
549
+ type: "integer",
550
+ description: "读取尾部行数,默认 200,最多 1000"
551
+ }
552
+ },
553
+ required: []
554
+ }
555
+ }
556
+ },
557
+ {
558
+ type: "function",
559
+ function: {
560
+ name: "mcp_godot_get_project_settings",
561
+ description: "结构化读取 project.godot 中显式写出的项目设置。key 使用 Godot 完整路径,例如 application/config/name 或 debug/file_logging/log_path。",
562
+ parameters: {
563
+ type: "object",
564
+ properties: {
565
+ keys: {
566
+ type: "array",
567
+ items: { type: "string" },
568
+ description: "按完整 key 精确读取,例如 ['debug/file_logging/log_path']"
569
+ },
570
+ prefix: {
571
+ type: "string",
572
+ description: "按完整 key 前缀过滤,例如 debug/file_logging/"
573
+ }
574
+ },
575
+ required: []
576
+ }
577
+ }
578
+ },
579
+ {
580
+ type: "function",
581
+ function: {
582
+ name: "mcp_godot_get_editor_config_summary",
583
+ description: "读取 Godot 编辑器全局设置和当前项目 .godot/editor 状态摘要,包括主题、字体、打开场景/脚本、最近项目数量等。默认脱敏本机路径;只有用户明确要求原始配置/路径时才设置 raw=true。",
584
+ parameters: {
585
+ type: "object",
586
+ properties: {
587
+ raw: {
588
+ type: "boolean",
589
+ description: "是否返回原始本机路径。默认 false,会脱敏用户名和非当前项目绝对路径。"
590
+ }
591
+ },
592
+ required: []
593
+ }
594
+ }
595
+ },
596
+ {
597
+ type: "function",
598
+ function: {
599
+ name: "mcp_godot_get_editor_settings",
600
+ description: "按 key 或 prefix 读取 editor_settings-*.tres 中的 Godot 编辑器设置,例如 interface/theme/、interface/editor/fonts/、text_editor/。默认脱敏路径值。",
601
+ parameters: {
602
+ type: "object",
603
+ properties: {
604
+ keys: {
605
+ type: "array",
606
+ items: { type: "string" },
607
+ description: "按完整 EditorSettings key 精确读取,例如 ['interface/theme/style']"
608
+ },
609
+ prefix: {
610
+ type: "string",
611
+ description: "按 key 前缀过滤,例如 interface/theme/"
612
+ },
613
+ raw: {
614
+ type: "boolean",
615
+ description: "是否返回原始路径值。默认 false。"
616
+ }
617
+ },
618
+ required: []
619
+ }
620
+ }
621
+ },
622
+ {
623
+ type: "function",
624
+ function: {
625
+ name: "mcp_godot_list_editor_config_files",
626
+ description: "列出只读白名单中的 Godot 编辑器配置文件,包括 editor_settings、projects.cfg、recent_dirs、text_editor_themes、script_templates 和当前项目 .godot/editor/*.cfg。读取原文前先用本工具拿 fileId。",
627
+ parameters: {
628
+ type: "object",
629
+ properties: {
630
+ raw: {
631
+ type: "boolean",
632
+ description: "是否返回原始绝对路径。默认 false。"
633
+ }
634
+ },
635
+ required: []
636
+ }
637
+ }
638
+ },
639
+ {
640
+ type: "function",
641
+ function: {
642
+ name: "mcp_godot_read_editor_config_file",
643
+ description: "读取 mcp_godot_list_editor_config_files 返回的白名单编辑器配置文件。默认脱敏内容中的本机路径;只有用户明确要求原始内容时才设置 raw=true。",
644
+ parameters: {
645
+ type: "object",
646
+ properties: {
647
+ fileId: {
648
+ type: "string",
649
+ description: "来自 list_editor_config_files 的 fileId,例如 global_config:editor_settings-4.7.tres"
650
+ },
651
+ filePath: {
652
+ type: "string",
653
+ description: "可选路径写法;推荐优先使用 fileId"
654
+ },
655
+ raw: {
656
+ type: "boolean",
657
+ description: "是否返回原始内容。默认 false。"
658
+ }
659
+ },
660
+ required: []
661
+ }
662
+ }
663
+ },
664
+ {
665
+ type: "function",
666
+ function: {
667
+ name: "mcp_godot_get_editor_project_state",
668
+ description: "结构化读取当前项目 .godot/editor/editor_layout.cfg 与 script_editor_cache.cfg,返回打开场景、当前场景、FileSystem Dock 选中项、打开脚本、当前脚本和光标行列。默认脱敏路径。",
669
+ parameters: {
670
+ type: "object",
671
+ properties: {
672
+ raw: {
673
+ type: "boolean",
674
+ description: "是否返回原始路径。默认 false。"
675
+ }
676
+ },
677
+ required: []
678
+ }
679
+ }
680
+ },
681
+ {
682
+ type: "function",
683
+ function: {
684
+ name: "mcp_godot_get_recent_projects",
685
+ description: "读取 Godot projects.cfg 和 recent_dirs,返回最近项目与最近目录。默认脱敏非当前项目路径;只有用户明确要求原始路径时才设置 raw=true。",
686
+ parameters: {
687
+ type: "object",
688
+ properties: {
689
+ raw: {
690
+ type: "boolean",
691
+ description: "是否返回原始路径。默认 false。"
692
+ }
693
+ },
694
+ required: []
695
+ }
696
+ }
697
+ },
698
+ {
699
+ type: "function",
700
+ function: {
701
+ name: "mcp_godot_propose_set_project_setting",
702
+ description: "预览设置 project.godot 中的某个项目设置,不写入磁盘。修改项目设置前优先先读取当前值,再调用本工具预览。valueExpression 是 project.godot 右侧原始表达式,例如 '\"Daedalus\"'、true、PackedStringArray(...)。",
703
+ parameters: {
704
+ type: "object",
705
+ properties: {
706
+ key: {
707
+ type: "string",
708
+ description: "完整项目设置 key,例如 debug/file_logging/log_path"
709
+ },
710
+ valueExpression: {
711
+ type: "string",
712
+ description: "project.godot 右侧原始表达式,例如 '\"user://logs/godot.log\"'"
713
+ }
714
+ },
715
+ required: ["key", "valueExpression"]
716
+ }
717
+ }
718
+ },
719
+ {
720
+ type: "function",
721
+ function: {
722
+ name: "mcp_godot_set_project_setting",
723
+ description: "实际修改 project.godot 中的某个项目设置,会触发用户审批。修改前应读取当前值并用 mcp_godot_propose_set_project_setting 预览。",
724
+ parameters: {
725
+ type: "object",
726
+ properties: {
727
+ key: {
728
+ type: "string",
729
+ description: "完整项目设置 key,例如 debug/file_logging/log_path"
730
+ },
731
+ valueExpression: {
732
+ type: "string",
733
+ description: "project.godot 右侧原始表达式,例如 '\"user://logs/godot.log\"'"
734
+ }
735
+ },
736
+ required: ["key", "valueExpression"]
737
+ }
738
+ }
739
+ },
740
+ {
741
+ type: "function",
742
+ function: {
743
+ name: "mcp_godot_propose_unset_project_setting",
744
+ description: "预览移除 project.godot 中的某个显式项目设置,不写入磁盘。移除后 Godot 会回退默认值。",
745
+ parameters: {
746
+ type: "object",
747
+ properties: {
748
+ key: {
749
+ type: "string",
750
+ description: "完整项目设置 key,例如 debug/file_logging/log_path"
751
+ }
752
+ },
753
+ required: ["key"]
754
+ }
755
+ }
756
+ },
757
+ {
758
+ type: "function",
759
+ function: {
760
+ name: "mcp_godot_unset_project_setting",
761
+ description: "实际移除 project.godot 中的某个显式项目设置,会触发用户审批。移除后 Godot 会回退默认值。",
762
+ parameters: {
763
+ type: "object",
764
+ properties: {
765
+ key: {
766
+ type: "string",
767
+ description: "完整项目设置 key,例如 debug/file_logging/log_path"
768
+ }
769
+ },
770
+ required: ["key"]
771
+ }
772
+ }
773
+ },
774
+ {
775
+ type: "function",
776
+ function: {
777
+ name: "mcp_godot_inspect_scene_tree",
778
+ description: "解析并检查 Godot .tscn 场景树,返回节点、脚本和连接等结构信息",
779
+ parameters: {
780
+ type: "object",
781
+ properties: {
782
+ relativePath: {
783
+ type: "string",
784
+ description: "场景文件的相对路径,例如 'scenes/main.tscn'"
785
+ }
786
+ },
787
+ required: ["relativePath"]
788
+ }
789
+ }
790
+ },
791
+ {
792
+ type: "function",
793
+ function: {
794
+ name: "mcp_godot_editor_get_context",
795
+ description: "读取在线 Godot 编辑器上下文,包括在线状态、当前打开场景、选中节点和上下文新鲜度。若编辑器离线会返回 editor_unavailable,可回退到离线 Godot MCP 工具。",
796
+ parameters: {
797
+ type: "object",
798
+ properties: {},
799
+ required: []
800
+ }
801
+ }
802
+ },
803
+ {
804
+ type: "function",
805
+ function: {
806
+ name: "mcp_godot_lsp_get_status",
807
+ description: "探测 Godot GDScript LSP 是否可用,返回 host/port、编辑器设置来源和最近错误。LSP 默认端口通常是 6005。",
808
+ parameters: {
809
+ type: "object",
810
+ properties: {},
811
+ required: []
812
+ }
813
+ }
814
+ },
815
+ {
816
+ type: "function",
817
+ function: {
818
+ name: "mcp_godot_lsp_get_file_diagnostics",
819
+ description: "读取指定 GDScript 文件的 Godot LSP 诊断,返回 1-based 行列、severity、message 和 code。修改 .gd 后应优先调用该工具,再运行 Godot check-only。",
820
+ parameters: {
821
+ type: "object",
822
+ properties: {
823
+ resourcePath: {
824
+ type: "string",
825
+ description: "脚本路径,可用 res://、项目相对路径或项目内绝对路径,例如 'res://scripts/player.gd' 或 'scripts/player.gd'。"
826
+ }
827
+ },
828
+ required: ["resourcePath"]
829
+ }
830
+ }
831
+ },
832
+ {
833
+ type: "function",
834
+ function: {
835
+ name: "mcp_godot_lsp_get_document_symbols",
836
+ description: "读取指定 GDScript 文件的 document symbols 摘要,用于理解类、函数、变量和枚举结构。",
837
+ parameters: {
838
+ type: "object",
839
+ properties: {
840
+ resourcePath: {
841
+ type: "string",
842
+ description: "脚本路径,可用 res://、项目相对路径或项目内绝对路径。"
843
+ }
844
+ },
845
+ required: ["resourcePath"]
846
+ }
847
+ }
848
+ },
849
+ {
850
+ type: "function",
851
+ function: {
852
+ name: "mcp_godot_lsp_hover",
853
+ description: "读取指定 GDScript 文件某个位置的 hover 信息,用于确认 API、变量或类型含义。line/column 使用 1-based。",
854
+ parameters: {
855
+ type: "object",
856
+ properties: {
857
+ resourcePath: {
858
+ type: "string",
859
+ description: "脚本路径,可用 res://、项目相对路径或项目内绝对路径。"
860
+ },
861
+ line: {
862
+ type: "integer",
863
+ description: "1-based 行号。"
864
+ },
865
+ column: {
866
+ type: "integer",
867
+ description: "1-based 列号。"
868
+ }
869
+ },
870
+ required: ["resourcePath", "line", "column"]
871
+ }
872
+ }
873
+ },
874
+ {
875
+ type: "function",
876
+ function: {
877
+ name: "mcp_godot_lsp_goto_definition",
878
+ description: "读取指定 GDScript 文件某个位置的 definition 位置,用于追踪函数、变量、类或资源定义。line/column 使用 1-based。",
879
+ parameters: {
880
+ type: "object",
881
+ properties: {
882
+ resourcePath: {
883
+ type: "string",
884
+ description: "脚本路径,可用 res://、项目相对路径或项目内绝对路径。"
885
+ },
886
+ line: {
887
+ type: "integer",
888
+ description: "1-based 行号。"
889
+ },
890
+ column: {
891
+ type: "integer",
892
+ description: "1-based 列号。"
893
+ }
894
+ },
895
+ required: ["resourcePath", "line", "column"]
896
+ }
897
+ }
898
+ },
899
+ {
900
+ type: "function",
901
+ function: {
902
+ name: "mcp_godot_dap_get_status",
903
+ description: "探测 Godot DAP 是否可用,并只读检查当前是否可 attach 到正在运行的调试会话。DAP 默认端口通常是 6006。",
904
+ parameters: {
905
+ type: "object",
906
+ properties: {},
907
+ required: []
908
+ }
909
+ }
910
+ },
911
+ {
912
+ type: "function",
913
+ function: {
914
+ name: "mcp_godot_dap_get_last_error",
915
+ description: "只读读取当前 Godot DAP stopped/output 事件和顶部调用栈摘要。遇到运行时报错时优先调用;DAP 不可用时回退到项目日志。",
916
+ parameters: {
917
+ type: "object",
918
+ properties: {},
919
+ required: []
920
+ }
921
+ }
922
+ },
923
+ {
924
+ type: "function",
925
+ function: {
926
+ name: "mcp_godot_dap_get_stack_trace",
927
+ description: "只读读取当前 Godot 调试会话调用栈和 frame scopes。不会 pause/continue/step,也不会 evaluate。",
928
+ parameters: {
929
+ type: "object",
930
+ properties: {},
931
+ required: []
932
+ }
933
+ }
934
+ },
935
+ {
936
+ type: "function",
937
+ function: {
938
+ name: "mcp_godot_dap_get_variables",
939
+ description: "只读读取 DAP variablesReference 对应的变量摘要。variablesReference 来自 mcp_godot_dap_get_stack_trace 的 scopes 或变量结果。",
940
+ parameters: {
941
+ type: "object",
942
+ properties: {
943
+ variablesReference: {
944
+ type: "integer",
945
+ description: "来自 DAP scopes 或变量结果的 variablesReference。"
946
+ }
947
+ },
948
+ required: ["variablesReference"]
949
+ }
950
+ }
951
+ },
952
+ {
953
+ type: "function",
954
+ function: {
955
+ name: "mcp_godot_editor_get_selected_nodes",
956
+ description: "读取当前 Godot 编辑器中多个选中节点的路径、类型、脚本、owner 和关键属性摘要。适合用户说“这些节点/当前选中按钮”等实时编辑器上下文。",
957
+ parameters: {
958
+ type: "object",
959
+ properties: {},
960
+ required: []
961
+ }
962
+ }
963
+ },
964
+ {
965
+ type: "function",
966
+ function: {
967
+ name: "mcp_godot_editor_inspect_node",
968
+ description: "检查在线 Godot 编辑器中指定节点的实时结构,能看到尚未保存到 .tscn 的当前状态。若编辑器离线或上下文不匹配,应回退到 mcp_godot_inspect_scene_tree。",
969
+ parameters: {
970
+ type: "object",
971
+ properties: {
972
+ scenePath: {
973
+ type: "string",
974
+ description: "可选场景路径;为空时使用当前打开场景。"
975
+ },
976
+ nodePath: {
977
+ type: "string",
978
+ description: "相对当前场景根节点的 NodePath,例如 '.'、'CanvasLayer/Button'。"
979
+ }
980
+ },
981
+ required: ["nodePath"]
982
+ }
983
+ }
984
+ },
985
+ {
986
+ type: "function",
987
+ function: {
988
+ name: "mcp_godot_editor_apply_scene_patch",
989
+ description: "在在线 Godot 编辑器中应用场景 patch,会使用 EditorUndoRedoManager 合并为一个可撤销动作,并默认保存当前场景。该工具会实际修改场景,必须经过用户审批;编辑器离线时回退到离线 mcp_godot_apply_scene_patch 或 headless 工具。",
990
+ parameters: {
991
+ type: "object",
992
+ properties: {
993
+ title: {
994
+ type: "string",
995
+ description: "UndoRedo 动作标题,例如 'Daedalus: 调整按钮文本'。"
996
+ },
997
+ scenePath: {
998
+ type: "string",
999
+ description: "可选场景路径;为空时使用当前打开场景。"
1000
+ },
1001
+ saveAfter: {
1002
+ type: "boolean",
1003
+ description: "提交 UndoRedo 动作后是否保存当前场景,默认 true。"
1004
+ },
1005
+ operations: {
1006
+ type: "array",
1007
+ description: "按顺序执行的在线场景操作。第一版支持 set_property、add_node、rename_node、attach_script、connect_signal。",
1008
+ items: {
1009
+ oneOf: [
1010
+ {
1011
+ type: "object",
1012
+ properties: {
1013
+ type: { const: "set_property" },
1014
+ nodePath: { type: "string" },
1015
+ property: { type: "string" },
1016
+ value: {}
1017
+ },
1018
+ required: ["type", "nodePath", "property", "value"]
1019
+ },
1020
+ {
1021
+ type: "object",
1022
+ properties: {
1023
+ type: { const: "add_node" },
1024
+ parentPath: { type: "string" },
1025
+ nodeType: { type: "string" },
1026
+ nodeName: { type: "string" },
1027
+ properties: {
1028
+ type: "object",
1029
+ additionalProperties: true
1030
+ }
1031
+ },
1032
+ required: ["type", "parentPath", "nodeType", "nodeName"]
1033
+ },
1034
+ {
1035
+ type: "object",
1036
+ properties: {
1037
+ type: { const: "rename_node" },
1038
+ nodePath: { type: "string" },
1039
+ name: { type: "string" }
1040
+ },
1041
+ required: ["type", "nodePath", "name"]
1042
+ },
1043
+ {
1044
+ type: "object",
1045
+ properties: {
1046
+ type: { const: "attach_script" },
1047
+ nodePath: { type: "string" },
1048
+ scriptPath: { type: "string" }
1049
+ },
1050
+ required: ["type", "nodePath", "scriptPath"]
1051
+ },
1052
+ {
1053
+ type: "object",
1054
+ properties: {
1055
+ type: { const: "connect_signal" },
1056
+ fromNode: { type: "string" },
1057
+ signal: { type: "string" },
1058
+ toNode: { type: "string" },
1059
+ method: { type: "string" },
1060
+ flags: { type: "integer" }
1061
+ },
1062
+ required: ["type", "fromNode", "signal", "toNode", "method"]
1063
+ }
1064
+ ]
1065
+ },
1066
+ minItems: 1,
1067
+ maxItems: 50
1068
+ }
1069
+ },
1070
+ required: ["operations"]
1071
+ }
1072
+ }
1073
+ },
1074
+ {
1075
+ type: "function",
1076
+ function: {
1077
+ name: "mcp_godot_propose_create_text_file",
1078
+ description: "仅预览新建文本文件方案,不会实际写入磁盘,也不会创建审批。只能创建 .gd/.tres/.tscn/.json/.md/.txt 文件。.tscn 文件必须包含 [gd_scene ...] 头部和至少一个 [node ...] 根节点。不允许覆盖已有文件。需要真正写入时,必须改用 mcp_godot_create_text_file。",
1079
+ parameters: {
1080
+ type: "object",
1081
+ properties: {
1082
+ relativePath: {
1083
+ type: "string",
1084
+ description: "相对于项目根目录的新文件路径,例如 'scripts/enemy.gd'"
1085
+ },
1086
+ content: {
1087
+ type: "string",
1088
+ description: "文件的完整内容"
1089
+ }
1090
+ },
1091
+ required: ["relativePath", "content"]
1092
+ }
1093
+ }
1094
+ },
1095
+ {
1096
+ type: "function",
1097
+ function: {
1098
+ name: "mcp_godot_create_text_file",
1099
+ description: "创建一个新的 Godot 项目文本文件。该工具会实际写入磁盘,默认需要用户在 Godot 客户端审批。支持创建 .gd/.tres/.tscn/.json/.md/.txt 文件。.tscn 文件必须包含 [gd_scene ...] 头部和至少一个 [node ...] 根节点。不允许覆盖已有文件,不允许写入 .godot/ 或 addons/。写入后建议运行 godot.check_only 验证。",
1100
+ parameters: {
1101
+ type: "object",
1102
+ properties: {
1103
+ relativePath: {
1104
+ type: "string",
1105
+ description: "相对于项目根目录的新文件路径,例如 'scripts/enemy.gd'"
1106
+ },
1107
+ content: {
1108
+ type: "string",
1109
+ description: "文件的完整内容"
1110
+ }
1111
+ },
1112
+ required: ["relativePath", "content"]
1113
+ }
1114
+ }
1115
+ },
1116
+ {
1117
+ type: "function",
1118
+ function: {
1119
+ name: "mcp_godot_propose_overwrite_text_file",
1120
+ description: "仅预览覆盖已有文件方案,不会实际写入,也不会创建审批。支持 .gd/.tres/.tscn/.json/.md/.txt 文件。.tscn 文件必须包含 [gd_scene ...] 头部和至少一个 [node ...] 根节点。文件必须已存在,会返回新旧内容对比。需要真正覆盖时,必须改用 mcp_godot_overwrite_text_file。",
1121
+ parameters: {
1122
+ type: "object",
1123
+ properties: {
1124
+ relativePath: { type: "string", description: "要覆盖的已有文件路径" },
1125
+ content: { type: "string", description: "新的完整文件内容" }
1126
+ },
1127
+ required: ["relativePath", "content"]
1128
+ }
1129
+ }
1130
+ },
1131
+ {
1132
+ type: "function",
1133
+ function: {
1134
+ name: "mcp_godot_overwrite_text_file",
1135
+ description: "覆盖已有文本文件,会实际写入磁盘,默认需要用户在 Godot 客户端审批。支持写入 .gd/.tres/.tscn/.json/.md/.txt 文件。.tscn 文件必须包含 [gd_scene ...] 头部和至少一个 [node ...] 根节点。不允许写入 .godot/、addons/ 或隐藏目录。写入后建议运行 godot.check_only 验证。",
1136
+ parameters: {
1137
+ type: "object",
1138
+ properties: {
1139
+ relativePath: { type: "string", description: "要覆盖的已有文件路径" },
1140
+ content: { type: "string", description: "新的完整文件内容" }
1141
+ },
1142
+ required: ["relativePath", "content"]
1143
+ }
1144
+ }
1145
+ },
1146
+ {
1147
+ type: "function",
1148
+ function: {
1149
+ name: "mcp_godot_propose_replace_text_in_file",
1150
+ description: "仅预览替换文件中指定文本的方案,不会实际写入,也不会创建审批。oldText 必须精确匹配(含空白和缩进),只替换首次出现。需要真正替换时,必须改用 mcp_godot_replace_text_in_file。",
1151
+ parameters: {
1152
+ type: "object",
1153
+ properties: {
1154
+ relativePath: { type: "string", description: "已有文件路径" },
1155
+ oldText: { type: "string", description: "要被替换的原文本,必须精确匹配" },
1156
+ newText: { type: "string", description: "替换后的新文本" }
1157
+ },
1158
+ required: ["relativePath", "oldText", "newText"]
1159
+ }
1160
+ }
1161
+ },
1162
+ {
1163
+ type: "function",
1164
+ function: {
1165
+ name: "mcp_godot_replace_text_in_file",
1166
+ description: "替换已有文本文件中首次出现的指定文本,会实际写入磁盘,默认需要用户在 Godot 客户端审批。oldText 必须精确匹配。",
1167
+ parameters: {
1168
+ type: "object",
1169
+ properties: {
1170
+ relativePath: { type: "string", description: "已有文件路径" },
1171
+ oldText: { type: "string", description: "要被替换的原文本,必须精确匹配" },
1172
+ newText: { type: "string", description: "替换后的新文本" }
1173
+ },
1174
+ required: ["relativePath", "oldText", "newText"]
1175
+ }
1176
+ }
1177
+ },
1178
+ {
1179
+ type: "function",
1180
+ function: {
1181
+ name: "mcp_godot_delete_file",
1182
+ description: "删除项目中的文件。此操作不可逆,需要用户确认。不能删除 .godot/ 中的文件。",
1183
+ parameters: {
1184
+ type: "object",
1185
+ properties: {
1186
+ relativePath: { type: "string", description: "要删除的文件路径" }
1187
+ },
1188
+ required: ["relativePath"]
1189
+ }
1190
+ }
1191
+ },
1192
+ {
1193
+ type: "function",
1194
+ function: {
1195
+ name: "mcp_terminal_get_capabilities",
1196
+ description: "获取终端 MCP 支持的所有预设命令列表及其风险等级。首次使用终端工具前应先调用此工具了解可用命令。",
1197
+ parameters: {
1198
+ type: "object",
1199
+ properties: {},
1200
+ required: []
1201
+ }
1202
+ }
1203
+ },
1204
+ {
1205
+ type: "function",
1206
+ function: {
1207
+ name: "mcp_terminal_run_safe_preset",
1208
+ description: "执行安全的(read/verify 风险)终端预设命令,自动允许。包括:backend.typecheck(TypeScript 类型检查)、git.status(Git 工作区状态)、git.diff(Git 差异)、godot.check_only(Godot 语法检查)、godot.validate_scene(Godot 场景加载验证)。Godot 预设建议传 resourcePath 精确检查目标 .gd 或 .tscn,工具结果会返回实际执行命令和 cwd。",
1209
+ parameters: {
1210
+ type: "object",
1211
+ properties: {
1212
+ presetName: {
1213
+ type: "string",
1214
+ description: "安全预设名称,如 'backend.typecheck'、'git.status'、'git.diff'、'godot.check_only'、'godot.validate_scene'"
1215
+ },
1216
+ resourcePath: {
1217
+ type: "string",
1218
+ description: "Godot 资源路径,仅 Godot 预设需要。可用 res://、项目相对路径或项目内绝对路径,例如 scripts/main.gd、scenes/main.tscn。检查脚本用 godot.check_only + .gd;验证场景用 godot.validate_scene + .tscn。"
1219
+ }
1220
+ },
1221
+ required: ["presetName"]
1222
+ }
1223
+ }
1224
+ },
1225
+ {
1226
+ type: "function",
1227
+ function: {
1228
+ name: "mcp_terminal_run_godot_scene_script",
1229
+ description: "通过 Godot headless 模式调用 scene_operator.gd 执行场景创建/编辑操作。支持 create_scene(创建场景)、add_node(添加节点)、attach_script(挂载脚本)、connect_signal(连接信号)、inspect(查看场景树)。传入 JSON 格式的 operationJson 参数。此工具需要用户审批。",
1230
+ parameters: {
1231
+ type: "object",
1232
+ properties: {
1233
+ operationJson: {
1234
+ type: "string",
1235
+ description: "JSON 格式的场景操作。create_scene: {\"operation\":\"create_scene\",\"path\":\"scenes/foo.tscn\",\"root_type\":\"Node2D\",\"root_name\":\"Main\"}。add_node: {\"operation\":\"add_node\",\"scene_path\":\"...\",\"parent_path\":\".\",\"node_type\":\"Label\",\"node_name\":\"Hello\",\"properties\":{}}。attach_script: {\"operation\":\"attach_script\",\"scene_path\":\"...\",\"node_path\":\"Main\",\"script_path\":\"res://scripts/main.gd\"}。connect_signal: {\"operation\":\"connect_signal\",\"scene_path\":\"...\",\"signal\":\"pressed\",\"from\":\"Button\",\"to\":\".\",\"method\":\"_on_pressed\"}。inspect: {\"operation\":\"inspect\",\"scene_path\":\"...\"}"
1236
+ }
1237
+ },
1238
+ required: ["operationJson"]
1239
+ }
1240
+ }
1241
+ },
1242
+ {
1243
+ type: "function",
1244
+ function: {
1245
+ name: "mcp_terminal_run_write_preset",
1246
+ description: "执行写操作(write 风险)终端预设命令,需要通过审批系统批准。可用的写预设:git.init。此工具调用后不会立即执行,需要用户在 Godot 客户端批准。",
1247
+ parameters: {
1248
+ type: "object",
1249
+ properties: {
1250
+ presetName: {
1251
+ type: "string",
1252
+ description: "写操作预设名称,如 'git.init'"
1253
+ }
1254
+ },
1255
+ required: ["presetName"]
1256
+ }
1257
+ }
1258
+ },
1259
+ {
1260
+ type: "function",
1261
+ function: {
1262
+ name: "mcp_godot_propose_apply_scene_patch",
1263
+ description: "仅预览批量修改已有 Godot .tscn 场景的方案,不会实际写入,也不会创建审批。支持一次添加多个节点、挂载脚本、连接信号。需要真正修改场景时,必须改用 mcp_godot_apply_scene_patch。",
1264
+ parameters: {
1265
+ type: "object",
1266
+ properties: {
1267
+ scenePath: {
1268
+ type: "string",
1269
+ description: "已有场景文件路径,例如 'scenes/guess_number.tscn'"
1270
+ },
1271
+ operations: {
1272
+ type: "array",
1273
+ description: "按顺序执行的场景操作列表。节点属性值必须是 .tscn 表达式字符串,例如 text 写成 '\"Hello\"',数值可写成 '15'。",
1274
+ items: {
1275
+ oneOf: [
1276
+ {
1277
+ type: "object",
1278
+ properties: {
1279
+ type: { const: "add_node" },
1280
+ parentPath: { type: "string" },
1281
+ nodeType: { type: "string" },
1282
+ nodeName: { type: "string" },
1283
+ properties: {
1284
+ type: "object",
1285
+ additionalProperties: { type: "string" }
1286
+ }
1287
+ },
1288
+ required: ["type", "parentPath", "nodeType", "nodeName"]
1289
+ },
1290
+ {
1291
+ type: "object",
1292
+ properties: {
1293
+ type: { const: "attach_script" },
1294
+ nodePath: { type: "string" },
1295
+ scriptPath: { type: "string" }
1296
+ },
1297
+ required: ["type", "nodePath", "scriptPath"]
1298
+ },
1299
+ {
1300
+ type: "object",
1301
+ properties: {
1302
+ type: { const: "connect_signal" },
1303
+ signal: { type: "string" },
1304
+ fromNode: { type: "string" },
1305
+ toNode: { type: "string" },
1306
+ method: { type: "string" },
1307
+ flags: { type: "integer" },
1308
+ binds: { type: "string" }
1309
+ },
1310
+ required: ["type", "signal", "fromNode", "toNode", "method"]
1311
+ }
1312
+ ]
1313
+ },
1314
+ minItems: 1,
1315
+ maxItems: 50
1316
+ }
1317
+ },
1318
+ required: ["scenePath", "operations"]
1319
+ }
1320
+ }
1321
+ },
1322
+ {
1323
+ type: "function",
1324
+ function: {
1325
+ name: "mcp_godot_apply_scene_patch",
1326
+ description: "批量修改已有 Godot .tscn 场景,会实际写入磁盘并触发用户审批。支持一次添加多个节点、挂载脚本、连接信号。创建复杂 UI 或小游戏场景时,应优先使用本工具,不要逐个调用 add_node_to_scene。",
1327
+ parameters: {
1328
+ type: "object",
1329
+ properties: {
1330
+ scenePath: {
1331
+ type: "string",
1332
+ description: "已有场景文件路径,例如 'scenes/guess_number.tscn'"
1333
+ },
1334
+ operations: {
1335
+ type: "array",
1336
+ description: "按顺序执行的场景操作列表。节点属性值必须是 .tscn 表达式字符串,例如 text 写成 '\"Hello\"',数值可写成 '15'。",
1337
+ items: {
1338
+ oneOf: [
1339
+ {
1340
+ type: "object",
1341
+ properties: {
1342
+ type: { const: "add_node" },
1343
+ parentPath: { type: "string" },
1344
+ nodeType: { type: "string" },
1345
+ nodeName: { type: "string" },
1346
+ properties: {
1347
+ type: "object",
1348
+ additionalProperties: { type: "string" }
1349
+ }
1350
+ },
1351
+ required: ["type", "parentPath", "nodeType", "nodeName"]
1352
+ },
1353
+ {
1354
+ type: "object",
1355
+ properties: {
1356
+ type: { const: "attach_script" },
1357
+ nodePath: { type: "string" },
1358
+ scriptPath: { type: "string" }
1359
+ },
1360
+ required: ["type", "nodePath", "scriptPath"]
1361
+ },
1362
+ {
1363
+ type: "object",
1364
+ properties: {
1365
+ type: { const: "connect_signal" },
1366
+ signal: { type: "string" },
1367
+ fromNode: { type: "string" },
1368
+ toNode: { type: "string" },
1369
+ method: { type: "string" },
1370
+ flags: { type: "integer" },
1371
+ binds: { type: "string" }
1372
+ },
1373
+ required: ["type", "signal", "fromNode", "toNode", "method"]
1374
+ }
1375
+ ]
1376
+ },
1377
+ minItems: 1,
1378
+ maxItems: 50
1379
+ }
1380
+ },
1381
+ required: ["scenePath", "operations"]
1382
+ }
1383
+ }
1384
+ }
1385
+ ];
1386
+
1387
+ export function getToolDefinitions(): ChatCompletionTool[] {
1388
+ return [...TOOL_DEFINITIONS, ...dynamicToolDefinitions];
1389
+ }
1390
+
1391
+ export function getToolDefinitionsForNames(toolNames: readonly string[]): ChatCompletionTool[] {
1392
+ const allowedNames: Set<string> = new Set(toolNames);
1393
+ const includeDynamicTools: boolean = allowedNames.has(CUSTOM_MCP_TOOLS_SENTINEL);
1394
+ return getToolDefinitions().filter((tool: ChatCompletionTool): boolean => {
1395
+ if (tool.type !== "function" || !("function" in tool)) {
1396
+ return false;
1397
+ }
1398
+
1399
+ if (includeDynamicTools && isDynamicMcpToolName(tool.function.name)) {
1400
+ return true;
1401
+ }
1402
+
1403
+ return allowedNames.has(tool.function.name);
1404
+ });
1405
+ }
1406
+
1407
+ export function resolveToolMapping(llmToolName: string): ToolMapping {
1408
+ const mapping: ToolMapping | undefined = TOOL_MAP[llmToolName] ?? dynamicToolMap.get(llmToolName);
1409
+
1410
+ if (!mapping) {
1411
+ throw new Error(`Unknown tool: ${llmToolName}`);
1412
+ }
1413
+
1414
+ return mapping;
1415
+ }