@tmhs/godot-correctness-mcp 0.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.
Files changed (100) hide show
  1. package/LICENSE +34 -0
  2. package/README.md +118 -0
  3. package/data/godot-4.4.symbols.json.gz +0 -0
  4. package/data/godot-4.5.symbols.json.gz +0 -0
  5. package/data/tree-sitter-gdscript.wasm +0 -0
  6. package/dist/capabilities.d.ts +21 -0
  7. package/dist/capabilities.js +24 -0
  8. package/dist/capabilities.js.map +1 -0
  9. package/dist/cli.d.ts +12 -0
  10. package/dist/cli.js +105 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/gdscript/ast.d.ts +25 -0
  13. package/dist/gdscript/ast.js +82 -0
  14. package/dist/gdscript/ast.js.map +1 -0
  15. package/dist/gdscript/parser.d.ts +7 -0
  16. package/dist/gdscript/parser.js +44 -0
  17. package/dist/gdscript/parser.js.map +1 -0
  18. package/dist/godot/text-format.d.ts +35 -0
  19. package/dist/godot/text-format.js +105 -0
  20. package/dist/godot/text-format.js.map +1 -0
  21. package/dist/index.d.ts +4 -0
  22. package/dist/index.js +38 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/lint/config.d.ts +83 -0
  25. package/dist/lint/config.js +55 -0
  26. package/dist/lint/config.js.map +1 -0
  27. package/dist/lint/engine.d.ts +7 -0
  28. package/dist/lint/engine.js +33 -0
  29. package/dist/lint/engine.js.map +1 -0
  30. package/dist/lint/project.d.ts +12 -0
  31. package/dist/lint/project.js +49 -0
  32. package/dist/lint/project.js.map +1 -0
  33. package/dist/lint/rules/await-misuse.d.ts +3 -0
  34. package/dist/lint/rules/await-misuse.js +34 -0
  35. package/dist/lint/rules/await-misuse.js.map +1 -0
  36. package/dist/lint/rules/connect-without-disconnect.d.ts +7 -0
  37. package/dist/lint/rules/connect-without-disconnect.js +38 -0
  38. package/dist/lint/rules/connect-without-disconnect.js.map +1 -0
  39. package/dist/lint/rules/delta-misuse.d.ts +7 -0
  40. package/dist/lint/rules/delta-misuse.js +78 -0
  41. package/dist/lint/rules/delta-misuse.js.map +1 -0
  42. package/dist/lint/rules/float-grid-equality.d.ts +6 -0
  43. package/dist/lint/rules/float-grid-equality.js +39 -0
  44. package/dist/lint/rules/float-grid-equality.js.map +1 -0
  45. package/dist/lint/rules/get-node-in-process.d.ts +3 -0
  46. package/dist/lint/rules/get-node-in-process.js +31 -0
  47. package/dist/lint/rules/get-node-in-process.js.map +1 -0
  48. package/dist/lint/rules/index.d.ts +4 -0
  49. package/dist/lint/rules/index.js +21 -0
  50. package/dist/lint/rules/index.js.map +1 -0
  51. package/dist/lint/rules/missing-onready.d.ts +3 -0
  52. package/dist/lint/rules/missing-onready.js +52 -0
  53. package/dist/lint/rules/missing-onready.js.map +1 -0
  54. package/dist/lint/rules/stringly-nodepath.d.ts +3 -0
  55. package/dist/lint/rules/stringly-nodepath.js +35 -0
  56. package/dist/lint/rules/stringly-nodepath.js.map +1 -0
  57. package/dist/lint/rules/untyped-declaration.d.ts +7 -0
  58. package/dist/lint/rules/untyped-declaration.js +25 -0
  59. package/dist/lint/rules/untyped-declaration.js.map +1 -0
  60. package/dist/lint/types.d.ts +36 -0
  61. package/dist/lint/types.js +2 -0
  62. package/dist/lint/types.js.map +1 -0
  63. package/dist/resources/skills.d.ts +12 -0
  64. package/dist/resources/skills.js +45 -0
  65. package/dist/resources/skills.js.map +1 -0
  66. package/dist/scenes/paths.d.ts +14 -0
  67. package/dist/scenes/paths.js +38 -0
  68. package/dist/scenes/paths.js.map +1 -0
  69. package/dist/scenes/report.d.ts +17 -0
  70. package/dist/scenes/report.js +25 -0
  71. package/dist/scenes/report.js.map +1 -0
  72. package/dist/scenes/validate.d.ts +16 -0
  73. package/dist/scenes/validate.js +185 -0
  74. package/dist/scenes/validate.js.map +1 -0
  75. package/dist/server-info.d.ts +13 -0
  76. package/dist/server-info.js +21 -0
  77. package/dist/server-info.js.map +1 -0
  78. package/dist/symbols/db.d.ts +11 -0
  79. package/dist/symbols/db.js +42 -0
  80. package/dist/symbols/db.js.map +1 -0
  81. package/dist/symbols/lookup.d.ts +89 -0
  82. package/dist/symbols/lookup.js +271 -0
  83. package/dist/symbols/lookup.js.map +1 -0
  84. package/dist/symbols/schema.d.ts +1419 -0
  85. package/dist/symbols/schema.js +109 -0
  86. package/dist/symbols/schema.js.map +1 -0
  87. package/dist/tools/api.d.ts +3 -0
  88. package/dist/tools/api.js +91 -0
  89. package/dist/tools/api.js.map +1 -0
  90. package/dist/tools/lint.d.ts +3 -0
  91. package/dist/tools/lint.js +45 -0
  92. package/dist/tools/lint.js.map +1 -0
  93. package/dist/tools/scenes.d.ts +3 -0
  94. package/dist/tools/scenes.js +43 -0
  95. package/dist/tools/scenes.js.map +1 -0
  96. package/package.json +57 -0
  97. package/skills/data-driven-resources.md +78 -0
  98. package/skills/resource-preload.md +43 -0
  99. package/skills/signal-hygiene.md +46 -0
  100. package/skills/typed-gdscript.md +47 -0
@@ -0,0 +1,109 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Schema for the compact Godot symbol database produced by
4
+ * scripts/generate-symbol-db.ts from `godot --dump-extension-api`. Only the
5
+ * fields needed for API lookup and cross-version diffing are kept; hashes,
6
+ * memory offsets, class sizes, and native structures are stripped.
7
+ *
8
+ * Bump SCHEMA_VERSION when the shape changes so stale artifacts fail validation.
9
+ */
10
+ export const SCHEMA_VERSION = 1;
11
+ const argSchema = z.object({
12
+ name: z.string(),
13
+ type: z.string(),
14
+ /** Default value as Godot renders it, e.g. "false", "0", "Vector2(0, 0)". */
15
+ default: z.string().optional(),
16
+ });
17
+ const methodSchema = z.object({
18
+ name: z.string(),
19
+ /** Return type name, or null for void. */
20
+ ret: z.string().nullable(),
21
+ args: z.array(argSchema),
22
+ isConst: z.boolean().optional(),
23
+ isStatic: z.boolean().optional(),
24
+ isVirtual: z.boolean().optional(),
25
+ isVararg: z.boolean().optional(),
26
+ });
27
+ const propertySchema = z.object({
28
+ name: z.string(),
29
+ type: z.string(),
30
+ getter: z.string().optional(),
31
+ setter: z.string().optional(),
32
+ });
33
+ const signalSchema = z.object({
34
+ name: z.string(),
35
+ args: z.array(argSchema),
36
+ });
37
+ const enumSchema = z.object({
38
+ name: z.string(),
39
+ isBitfield: z.boolean().optional(),
40
+ values: z.array(z.object({ name: z.string(), value: z.number() })),
41
+ });
42
+ const classConstantSchema = z.object({ name: z.string(), value: z.number() });
43
+ const classSchema = z.object({
44
+ inherits: z.string().nullable(),
45
+ apiType: z.string(),
46
+ isRefcounted: z.boolean().optional(),
47
+ isInstantiable: z.boolean().optional(),
48
+ methods: z.array(methodSchema),
49
+ properties: z.array(propertySchema),
50
+ signals: z.array(signalSchema),
51
+ enums: z.array(enumSchema),
52
+ constants: z.array(classConstantSchema),
53
+ });
54
+ const builtinOperatorSchema = z.object({
55
+ name: z.string(),
56
+ right: z.string().nullable(),
57
+ ret: z.string(),
58
+ });
59
+ const builtinConstantSchema = z.object({
60
+ name: z.string(),
61
+ type: z.string(),
62
+ value: z.string(),
63
+ });
64
+ const builtinClassSchema = z.object({
65
+ members: z.array(z.object({ name: z.string(), type: z.string() })),
66
+ constants: z.array(builtinConstantSchema),
67
+ enums: z.array(enumSchema),
68
+ operators: z.array(builtinOperatorSchema),
69
+ methods: z.array(methodSchema),
70
+ isKeyed: z.boolean().optional(),
71
+ });
72
+ const utilityFunctionSchema = z.object({
73
+ name: z.string(),
74
+ ret: z.string().nullable(),
75
+ category: z.string(),
76
+ args: z.array(argSchema),
77
+ isVararg: z.boolean().optional(),
78
+ });
79
+ const metaSchema = z.object({
80
+ /** Full patch version, e.g. "4.4.1". */
81
+ godotVersion: z.string(),
82
+ /** Minor-line label used to name the artifact, e.g. "4.4". */
83
+ versionLabel: z.string(),
84
+ /** Godot's own version string, e.g. "4.4.1.stable.official". */
85
+ versionFull: z.string(),
86
+ /** Release tag the binary came from, e.g. "4.4.1-stable". */
87
+ releaseTag: z.string().nullable(),
88
+ /** Downloaded archive filename, if the binary was fetched. */
89
+ archiveName: z.string().nullable(),
90
+ /** SHA-256 of the downloaded archive, if fetched. */
91
+ archiveSha256: z.string().nullable(),
92
+ /** Provenance of the binary, e.g. "godotengine/godot-builds" or "local". */
93
+ source: z.string(),
94
+ schemaVersion: z.literal(SCHEMA_VERSION),
95
+ generatedAt: z.string(),
96
+ });
97
+ export const symbolDbSchema = z.object({
98
+ meta: metaSchema,
99
+ classes: z.record(z.string(), classSchema),
100
+ builtinClasses: z.record(z.string(), builtinClassSchema),
101
+ utilityFunctions: z.array(utilityFunctionSchema),
102
+ globalEnums: z.array(enumSchema),
103
+ singletons: z.array(z.object({ name: z.string(), type: z.string() })),
104
+ });
105
+ /** Parse and validate an already-decompressed DB object. Throws on mismatch. */
106
+ export function parseSymbolDb(data) {
107
+ return symbolDbSchema.parse(data);
108
+ }
109
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/symbols/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ /** Register the API symbol lookup / summary / diff tools. */
3
+ export declare function registerApiTools(server: McpServer): void;
@@ -0,0 +1,91 @@
1
+ import { z } from "zod";
2
+ import { availableVersions, loadSymbolDb } from "../symbols/db.js";
3
+ import { classSummary, diffClass, diffSymbol, lookupSymbol, } from "../symbols/lookup.js";
4
+ const VERSION = z.enum(["4.4", "4.5"]);
5
+ const KIND = z.enum([
6
+ "class",
7
+ "method",
8
+ "property",
9
+ "signal",
10
+ "enum",
11
+ "constant",
12
+ "builtin_class",
13
+ "builtin_method",
14
+ "utility_function",
15
+ "singleton",
16
+ "global_enum",
17
+ ]);
18
+ function ok(data) {
19
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
20
+ }
21
+ function fail(message) {
22
+ return { content: [{ type: "text", text: JSON.stringify({ error: message }, null, 2) }] };
23
+ }
24
+ /** Register the API symbol lookup / summary / diff tools. */
25
+ export function registerApiTools(server) {
26
+ server.tool("api_symbol_lookup", "Look up a Godot API symbol (class, method, property, or signal) by name across " +
27
+ "the pinned Godot versions. Supports 'Owner.member' queries. Returns full " +
28
+ "signature data, or closest-match suggestions on a miss.", {
29
+ symbol: z.string().describe("Symbol name, e.g. 'Node', 'move_and_slide', or 'Node.get_child_count'"),
30
+ version: VERSION.optional().describe("Limit to one version; omit to search all available versions"),
31
+ kind: KIND.optional().describe("Restrict results to one symbol kind"),
32
+ }, async (args) => {
33
+ const { symbol, version, kind } = args;
34
+ const versions = version ? [version] : availableVersions();
35
+ if (versions.length === 0)
36
+ return fail("No symbol databases are available.");
37
+ try {
38
+ const byVersion = Object.fromEntries(versions.map((v) => [v, lookupSymbol(loadSymbolDb(v), symbol, kind ? { kind } : {})]));
39
+ return ok({ symbol, versions, results: byVersion });
40
+ }
41
+ catch (err) {
42
+ return fail(err instanceof Error ? err.message : String(err));
43
+ }
44
+ });
45
+ server.tool("api_class_summary", "Full member listing for one Godot class in one version: inheritance chain, " +
46
+ "methods, properties, signals, enums, and constants.", {
47
+ class: z.string().describe("Class name, e.g. 'CharacterBody2D'"),
48
+ version: VERSION.describe("Which Godot version to summarize"),
49
+ }, async (args) => {
50
+ const { class: className, version } = args;
51
+ try {
52
+ const db = loadSymbolDb(version);
53
+ const summary = classSummary(db, className);
54
+ if (!summary) {
55
+ const suggestions = lookupSymbol(db, className, { kind: "class" }).suggestions ?? [];
56
+ return fail(`Class '${className}' not found in Godot ${version}.` +
57
+ (suggestions.length ? ` Did you mean: ${suggestions.map((s) => s.name).join(", ")}?` : ""));
58
+ }
59
+ return ok({ version, ...summary });
60
+ }
61
+ catch (err) {
62
+ return fail(err instanceof Error ? err.message : String(err));
63
+ }
64
+ });
65
+ server.tool("api_diff", "Diff a Godot class or symbol between two versions (default 4.4 -> 4.5): what " +
66
+ "was added, removed, or had its signature changed, with old and new signatures " +
67
+ "side by side.", {
68
+ symbol: z.string().describe("Class name (e.g. 'TileMap') or symbol (e.g. 'move_and_slide')"),
69
+ from: VERSION.optional().describe("Base version (default 4.4)"),
70
+ to: VERSION.optional().describe("Target version (default 4.5)"),
71
+ }, async (args) => {
72
+ const { symbol, from = "4.4", to = "4.5" } = args;
73
+ if (from === to)
74
+ return fail("'from' and 'to' must be different versions.");
75
+ try {
76
+ const fromDb = loadSymbolDb(from);
77
+ const toDb = loadSymbolDb(to);
78
+ // A dotted query is always a member; otherwise try a class diff first.
79
+ if (!symbol.includes(".")) {
80
+ const cd = diffClass(fromDb, toDb, symbol);
81
+ if (cd.presence.inFrom || cd.presence.inTo)
82
+ return ok({ type: "class", diff: cd });
83
+ }
84
+ return ok({ type: "symbol", diff: diffSymbol(fromDb, toDb, symbol) });
85
+ }
86
+ catch (err) {
87
+ return fail(err instanceof Error ? err.message : String(err));
88
+ }
89
+ });
90
+ }
91
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/tools/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ /** Register the GDScript lint tools (lint_file, lint_project). */
3
+ export declare function registerLintTools(server: McpServer): void;
@@ -0,0 +1,45 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import { z } from "zod";
4
+ import { loadConfig } from "../lint/config.js";
5
+ import { lintSource } from "../lint/engine.js";
6
+ import { lintProject } from "../lint/project.js";
7
+ function ok(data) {
8
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
9
+ }
10
+ function fail(message) {
11
+ return { content: [{ type: "text", text: JSON.stringify({ error: message }, null, 2) }] };
12
+ }
13
+ /** Register the GDScript lint tools (lint_file, lint_project). */
14
+ export function registerLintTools(server) {
15
+ server.tool("lint_file", "Lint a single GDScript (.gd) file against the default ruleset. Provide either " +
16
+ "inline source or a path on disk. Config is loaded from the file's directory " +
17
+ "if a godot-correctness.config.json is present. Returns a findings array.", {
18
+ path: z.string().describe("Path to the .gd file (used for the reported location and config lookup)"),
19
+ source: z.string().optional().describe("Inline GDScript source; if omitted, the file at 'path' is read"),
20
+ }, async (args) => {
21
+ const { path, source } = args;
22
+ try {
23
+ const code = source ?? readFileSync(path, "utf-8");
24
+ const config = loadConfig(dirname(path));
25
+ const findings = await lintSource(path, code, config);
26
+ return ok({ file: path, findings, count: findings.length });
27
+ }
28
+ catch (err) {
29
+ return fail(err instanceof Error ? err.message : String(err));
30
+ }
31
+ });
32
+ server.tool("lint_project", "Lint every GDScript file under a project root, honoring include/exclude globs " +
33
+ "from godot-correctness.config.json. Returns aggregated findings and severity counts.", {
34
+ path: z.string().describe("Path to the Godot project root"),
35
+ }, async (args) => {
36
+ const { path } = args;
37
+ try {
38
+ return ok(await lintProject(path));
39
+ }
40
+ catch (err) {
41
+ return fail(err instanceof Error ? err.message : String(err));
42
+ }
43
+ });
44
+ }
45
+ //# sourceMappingURL=lint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.js","sourceRoot":"","sources":["../../src/tools/lint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAIjD,SAAS,EAAE,CAAC,IAAa;IACvB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC9E,CAAC;AAED,SAAS,IAAI,CAAC,OAAe;IAC3B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC5F,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IACjD,MAAM,CAAC,IAAI,CACT,WAAW,EACX,gFAAgF;QAC9E,8EAA8E;QAC9E,0EAA0E,EAC5E;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yEAAyE,CAAC;QACpG,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gEAAgE,CAAC;KACzG,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAyC,CAAC;QACnE,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACnD,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;YACzC,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YACtD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,cAAc,EACd,gFAAgF;QAC9E,sFAAsF,EACxF;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;KAC5D,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,EAAE,IAAI,EAAE,GAAG,IAAwB,CAAC;QAC1C,IAAI,CAAC;YACH,OAAO,EAAE,CAAC,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ /** Register the scene / resource / registry static-analysis tools. */
3
+ export declare function registerSceneTools(server: McpServer): void;
@@ -0,0 +1,43 @@
1
+ import { z } from "zod";
2
+ import { projectReport } from "../scenes/report.js";
3
+ import { validateProjectConfig, validateRegistries, validateScenes, } from "../scenes/validate.js";
4
+ function ok(data) {
5
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
6
+ }
7
+ function fail(message) {
8
+ return { content: [{ type: "text", text: JSON.stringify({ error: message }, null, 2) }] };
9
+ }
10
+ function guard(fn) {
11
+ try {
12
+ return ok(fn());
13
+ }
14
+ catch (err) {
15
+ return fail(err instanceof Error ? err.message : String(err));
16
+ }
17
+ }
18
+ /** Register the scene / resource / registry static-analysis tools. */
19
+ export function registerSceneTools(server) {
20
+ server.tool("validate_scenes", "Validate every .tscn under a project root: ext_resource paths resolve, " +
21
+ "ExtResource references are declared, and there are no duplicate resource ids.", { path: z.string().describe("Path to the Godot project root") }, async (args) => guard(() => {
22
+ const findings = validateScenes(args.path);
23
+ return { findings, count: findings.length };
24
+ }));
25
+ server.tool("validate_project_config", "Validate project.godot: the main scene exists, autoload paths exist, and " +
26
+ "input actions are well-formed.", { path: z.string().describe("Path to the Godot project root") }, async (args) => guard(() => {
27
+ const findings = validateProjectConfig(args.path);
28
+ return { findings, count: findings.length };
29
+ }));
30
+ server.tool("validate_registries", "Deep-validate data-driven .tres registries in the configured registryDirs: " +
31
+ "each parses, its script resolves (recording class_name), and every " +
32
+ "resource-path property points at an existing file (the dangling-path check).", { path: z.string().describe("Path to the Godot project root") }, async (args) => guard(() => validateRegistries(args.path)));
33
+ server.tool("project_report", "Aggregate all static checks (scenes, project config, registries, and " +
34
+ "GDScript lint) into one report with severity counts.", { path: z.string().describe("Path to the Godot project root") }, async (args) => {
35
+ try {
36
+ return ok(await projectReport(args.path));
37
+ }
38
+ catch (err) {
39
+ return fail(err instanceof Error ? err.message : String(err));
40
+ }
41
+ });
42
+ }
43
+ //# sourceMappingURL=scenes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenes.js","sourceRoot":"","sources":["../../src/tools/scenes.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@tmhs/godot-correctness-mcp",
3
+ "version": "0.1.0",
4
+ "description": "Offline static correctness MCP server for Godot 4.x GDScript projects",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "godot-correctness-mcp": "dist/index.js",
9
+ "gdcorrect": "dist/cli.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "data",
14
+ "skills",
15
+ "README.md",
16
+ "LICENSE"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.json",
20
+ "test": "vitest run",
21
+ "typecheck": "tsc -p tsconfig.json --noEmit",
22
+ "validate:artifacts": "tsx scripts/validate-symbol-db.ts",
23
+ "dev": "tsx watch src/index.ts",
24
+ "prepublishOnly": "npm run build"
25
+ },
26
+ "dependencies": {
27
+ "@modelcontextprotocol/sdk": "^1.29.0",
28
+ "picomatch": "^4.0.5",
29
+ "web-tree-sitter": "^0.26.11",
30
+ "zod": "^3.25.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^22.10.0",
34
+ "@types/picomatch": "^4.0.3",
35
+ "tsx": "^4.19.4",
36
+ "typescript": "^5.6.0",
37
+ "vitest": "^4.1.8"
38
+ },
39
+ "author": "TMHSDigital",
40
+ "license": "CC-BY-NC-ND-4.0",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/TMHSDigital/godot-correctness-mcp.git"
44
+ },
45
+ "homepage": "https://github.com/TMHSDigital/godot-correctness-mcp#readme",
46
+ "bugs": {
47
+ "url": "https://github.com/TMHSDigital/godot-correctness-mcp/issues"
48
+ },
49
+ "keywords": [
50
+ "mcp",
51
+ "model-context-protocol",
52
+ "developer-tools"
53
+ ],
54
+ "engines": {
55
+ "node": ">=20.0.0"
56
+ }
57
+ }
@@ -0,0 +1,78 @@
1
+ # Data-driven resources and registry loading
2
+
3
+ Custom `Resource` types let you drive game content from `.tres` files instead of
4
+ hardcoding it. Keep resources as **pure data**, load them through a validated
5
+ **registry**, and this server's `validate_registries` tool can catch dangling
6
+ references before they fail at runtime.
7
+
8
+ ## Keep Resource state data-only
9
+
10
+ A data resource should hold values, not runtime/session state. Runtime state
11
+ (current health, cooldown timers, whoever owns it) belongs on the node that uses
12
+ the resource, not on the shared resource instance — resources are shared by
13
+ reference, so mutating one at runtime mutates it everywhere and can leak into
14
+ saved copies.
15
+
16
+ ```gdscript
17
+ # item.gd - data only
18
+ extends Resource
19
+ class_name ItemData
20
+
21
+ @export var id: StringName
22
+ @export var display_name: String
23
+ @export var icon: Texture2D
24
+ @export var scene_path: String # path to a .tscn to spawn; validated statically
25
+ ```
26
+
27
+ ```gdscript
28
+ # WRONG: runtime state on the shared data resource
29
+ extends Resource
30
+ class_name ItemData
31
+ @export var display_name: String
32
+ var current_stack := 0 # <- mutated at runtime, shared across every holder
33
+ ```
34
+
35
+ Put `current_stack` on the inventory node instead, keyed by `ItemData`.
36
+
37
+ ## Registry-loading pattern
38
+
39
+ Load a directory of `.tres` into a typed dictionary once, keyed by a stable id.
40
+
41
+ ```gdscript
42
+ # item_registry.gd (autoload)
43
+ extends Node
44
+
45
+ const ITEM_DIR := "res://resources/items"
46
+ var _items: Dictionary[StringName, ItemData] = {}
47
+
48
+ func _ready() -> void:
49
+ for file in DirAccess.get_files_at(ITEM_DIR):
50
+ if not file.ends_with(".tres"):
51
+ continue
52
+ var res := load(ITEM_DIR.path_join(file)) as ItemData
53
+ if res == null:
54
+ push_error("Not an ItemData: %s" % file)
55
+ continue
56
+ _items[res.id] = res
57
+
58
+ func get_item(id: StringName) -> ItemData:
59
+ return _items.get(id)
60
+ ```
61
+
62
+ ## Validate before runtime
63
+
64
+ String path properties like `scene_path` above are invisible failures: a renamed
65
+ or deleted `.tscn` only errors when something actually loads it. Configure the
66
+ registry directory so the static validator checks every `.tres`:
67
+
68
+ ```json
69
+ // godot-correctness.config.json
70
+ {
71
+ "registryDirs": ["resources/items"],
72
+ "pathPropertyPatterns": ["*_path", "*_scene"]
73
+ }
74
+ ```
75
+
76
+ Then `validate_registries` (or `gdcorrect validate-registries .`) confirms each
77
+ `.tres` parses, its script resolves, and every resource-path property points at
78
+ an existing file. See also [[signal-hygiene]] and [[typed-gdscript]].
@@ -0,0 +1,43 @@
1
+ # preload vs load
2
+
3
+ Choose between `preload` and `load` by *when* you need the resource and whether
4
+ the path is known at compile time.
5
+
6
+ ## preload: compile-time, known path
7
+
8
+ `preload` resolves at parse time and bakes the dependency into the script. Use it
9
+ for assets you always need and whose path is a constant literal.
10
+
11
+ ```gdscript
12
+ const BulletScene := preload("res://actors/bullet.tscn")
13
+
14
+ func fire() -> void:
15
+ var b := BulletScene.instantiate()
16
+ add_child(b)
17
+ ```
18
+
19
+ Because `preload` needs a constant literal path, prefer it over `load("res://...")`
20
+ with a hardcoded string — it fails at compile time (not runtime) if the path is
21
+ wrong, and it does not re-hit the disk cache each call.
22
+
23
+ ## load: runtime, dynamic path
24
+
25
+ Use `load` when the path is computed (e.g. from a data resource) or the asset is
26
+ optional/lazy.
27
+
28
+ ```gdscript
29
+ func spawn_from(item: ItemData) -> Node:
30
+ var scene := load(item.scene_path) as PackedScene # path came from data
31
+ return scene.instantiate() if scene else null
32
+ ```
33
+
34
+ Data-driven paths like `item.scene_path` are exactly what
35
+ [[data-driven-resources]] and the `validate_registries` tool guard: a dangling
36
+ `res://` path here is invisible until this line runs. Validate registries in CI
37
+ so a bad path fails the build, not the player's session.
38
+
39
+ ## Do not preload inside hot loops
40
+
41
+ `preload` is resolved once at parse time regardless of where it appears, but keep
42
+ `load()` calls out of `_process`/`_physics_process`; cache the `PackedScene` once.
43
+ See [[typed-gdscript]].
@@ -0,0 +1,46 @@
1
+ # Signal hygiene
2
+
3
+ Godot 4 signals are first-class. Connect with the typed `Callable` API, avoid
4
+ frame-fragile lookups, and disconnect (or use one-shot) when a connection should
5
+ not outlive the node.
6
+
7
+ ## Prefer the typed connect API
8
+
9
+ ```gdscript
10
+ # Godot 4: reference the signal object and pass a Callable
11
+ button.pressed.connect(_on_button_pressed)
12
+
13
+ # Avoid the stringly-typed legacy form
14
+ button.connect("pressed", Callable(self, "_on_button_pressed"))
15
+ ```
16
+
17
+ ## Connect once, disconnect deliberately
18
+
19
+ Connecting in `_ready()` without cleanup risks double-connections (if the node
20
+ re-enters the tree) or dangling references (if the emitter outlives the receiver).
21
+
22
+ ```gdscript
23
+ func _ready() -> void:
24
+ # One-shot: auto-disconnects after the first emission.
25
+ get_tree().create_timer(1.0).timeout.connect(_spawn, CONNECT_ONE_SHOT)
26
+ EventBus.player_died.connect(_on_player_died)
27
+
28
+ func _exit_tree() -> void:
29
+ if EventBus.player_died.is_connected(_on_player_died):
30
+ EventBus.player_died.disconnect(_on_player_died)
31
+ ```
32
+
33
+ This server's `connect-without-disconnect` lint rule flags `connect()` in
34
+ `_ready()` when there is no matching `disconnect()` and no `CONNECT_ONE_SHOT`.
35
+
36
+ ## await a signal instead of polling
37
+
38
+ ```gdscript
39
+ func flash() -> void:
40
+ modulate = Color.RED
41
+ await get_tree().create_timer(0.1).timeout # await a real signal
42
+ modulate = Color.WHITE
43
+ ```
44
+
45
+ Never `await` a plain value — `await` expects a signal or a coroutine
46
+ (the `await-misuse` rule catches `await <literal>`). See [[typed-gdscript]].
@@ -0,0 +1,47 @@
1
+ # Typed GDScript patterns
2
+
3
+ Static types make GDScript faster (typed instructions) and let tools catch
4
+ mistakes. Prefer inference with `:=`, annotate function signatures, and cache
5
+ node references with `@onready`.
6
+
7
+ ## Type every declaration
8
+
9
+ ```gdscript
10
+ var speed := 300.0 # inferred float via :=
11
+ var target: Node2D = null # explicit type when there is no initializer
12
+ const MAX_HP := 100 # inferred int
13
+
14
+ func take_damage(amount: int) -> void:
15
+ _hp = maxi(_hp - amount, 0)
16
+ ```
17
+
18
+ Avoid bare `var x = 3` — it is untyped. Use `:=` to infer or `: Type` to
19
+ annotate. The `untyped-declaration` rule flags declarations with neither.
20
+
21
+ ## Cache node references with @onready
22
+
23
+ ```gdscript
24
+ # Resolved once, after the node enters the tree.
25
+ @onready var sprite: Sprite2D = $Sprite2D
26
+ @onready var _agent: NavigationAgent2D = $NavigationAgent2D
27
+ ```
28
+
29
+ Do not look nodes up every frame:
30
+
31
+ ```gdscript
32
+ # WRONG: $ / get_node() inside _process runs every frame
33
+ func _process(delta: float) -> void:
34
+ $Sprite2D.rotation += delta # get-node-in-process
35
+ ```
36
+
37
+ The `missing-onready` rule flags a class-level node-path member without
38
+ `@onready`; `get-node-in-process` flags per-frame lookups.
39
+
40
+ ## Prefer $ over string paths
41
+
42
+ ```gdscript
43
+ @onready var hp_bar := $UI/HPBar # rename-safe editor reference
44
+ # var hp_bar = get_node("UI/HPBar") # stringly-nodepath: fragile
45
+ ```
46
+
47
+ See [[signal-hygiene]] and [[data-driven-resources]].