unity-mcp-server 1.1.0 → 1.2.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 CHANGED
@@ -18,26 +18,38 @@ Unity MCP Server runs as a separate process and reads your Unity project from di
18
18
 
19
19
  ## Tools (by category)
20
20
 
21
- **Project & build** — `get_project_info`, `list_build_scenes`, `get_player_settings`, `list_packages`, `get_quality_settings`, `get_scripting_defines`, `get_project_version`, `get_changelog`
21
+ **Project & build** — `get_project_info`, `list_build_scenes`, `get_player_settings`, `list_packages`, `get_quality_settings`, `get_scripting_defines`, `get_physics_settings`, `get_project_version`, `get_changelog`
22
22
 
23
- **Code & assemblies** — `list_assemblies`, `list_scripts`, `find_scripts_by_content`
23
+ **Code & assemblies** — `list_assemblies`, `list_scripts`, `find_scripts_by_content`, `get_assembly_dependency_graph`, `list_editor_scripts`
24
24
 
25
- **Scenes & prefabs** — `list_all_scenes`, `get_scene_summary`, `list_prefabs`
25
+ **Scenes & prefabs** — `list_all_scenes`, `get_scene_summary`, `list_prefabs`, `get_prefab_script_guids`
26
26
 
27
- **Assets & references** — `get_asset_folder_tree`, `list_assets_by_extension`, `find_references` (by path or GUID)
27
+ **Assets & references** — `get_asset_folder_tree`, `list_assets_by_extension`, `find_references`, `list_large_assets`
28
28
 
29
- **Materials & shaders** — `list_materials`, `list_shaders`
29
+ **Materials & shaders** — `list_materials`, `list_shaders`, `list_shader_graphs`, `list_vfx_graphs`
30
30
 
31
- **Animation** — `list_animator_controllers`, `list_animation_clips`, `get_animator_states`
31
+ **Animation** — `list_animator_controllers`, `list_animation_clips`, `get_animator_states`, `list_timeline_playables`
32
32
 
33
- **Audio** — `list_audio_clips`, `list_audio_mixers`
33
+ **2D & sprites** — `list_sprite_atlases`, `list_tilemap_assets`
34
+
35
+ **Rendering** — `list_render_pipelines` (URP/HDRP, volume profiles)
36
+
37
+ **TextMeshPro & UI** — `list_tmp_fonts`, `get_tmp_settings_path`, `list_ui_documents` (UXML, USS)
38
+
39
+ **Input** — `get_input_axes`, `list_input_action_assets`, `get_input_actions_summary`
40
+
41
+ **Tags & layers** — `get_tags_and_layers`
34
42
 
35
43
  **Addressables & localization** — `get_addressables_info`, `get_localization_tables`
36
44
 
37
- **Input, tags & layers** — `get_input_axes`, `get_tags_and_layers`
45
+ **Audio** — `list_audio_clips`, `list_audio_mixers`
38
46
 
39
47
  **Testing & docs** — `list_test_assemblies`, `get_repo_docs`, `read_agent_docs`
40
48
 
49
+ **CI & version control** — `list_ci_configs`, `list_presets`, `get_git_lfs_tracked`, `get_plastic_config`
50
+
51
+ **Integrations (config discovery)** — `get_playfab_config`, `list_figma_related_assets`, `get_firebase_config`, `get_steam_config`, `get_discord_config`, `get_fmod_config`, `get_wwise_config`, `list_substance_assets`, `list_speedtree_assets`, `list_lottie_assets`, `get_analytics_or_crash_config`, `get_ads_config`
52
+
41
53
  All tools read from the project filesystem only; no Unity Editor is required.
42
54
 
43
55
  ---
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ async function main() {
20
20
  const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
21
21
  const server = new McpServer({
22
22
  name: "unity-mcp-server",
23
- version: "1.0.0",
23
+ version: "1.2.0",
24
24
  });
25
25
  const text = (s) => ({ content: [{ type: "text", text: s }] });
26
26
  const json = (o) => text(JSON.stringify(o, null, 2));
@@ -168,6 +168,66 @@ async function main() {
168
168
  const content = R.getChangelog(projectRoot);
169
169
  return text(content ?? "(No CHANGELOG found)");
170
170
  });
171
+ // --- 16. Physics ---
172
+ server.registerTool("get_physics_settings", { description: "Get Physics/DynamicsManager or Physics2D settings (key-value).", inputSchema: {} }, async () => json(R.getPhysicsSettings(projectRoot)));
173
+ // --- 17. Render pipelines ---
174
+ server.registerTool("list_render_pipelines", { description: "List render pipeline assets and volume profiles (URP/HDRP).", inputSchema: {} }, async () => json(R.listRenderPipelines(projectRoot)));
175
+ // --- 18. Timeline ---
176
+ server.registerTool("list_timeline_playables", { description: "List Timeline .playable assets.", inputSchema: {} }, async () => json(R.listTimelinePlayables(projectRoot)));
177
+ // --- 19. Sprites / 2D ---
178
+ server.registerTool("list_sprite_atlases", { description: "List Sprite Atlas (.spriteatlas) assets.", inputSchema: {} }, async () => json(R.listSpriteAtlases(projectRoot)));
179
+ server.registerTool("list_tilemap_assets", { description: "List tilemap-related .asset files.", inputSchema: {} }, async () => json(R.listTilemapAssets(projectRoot)));
180
+ // --- 20. Shader Graph / VFX ---
181
+ server.registerTool("list_shader_graphs", { description: "List Shader Graph (.shadergraph) assets.", inputSchema: {} }, async () => json(R.listShaderGraphs(projectRoot)));
182
+ server.registerTool("list_vfx_graphs", { description: "List VFX Graph (.vfx) assets.", inputSchema: {} }, async () => json(R.listVfxGraphs(projectRoot)));
183
+ // --- 21. TextMeshPro ---
184
+ server.registerTool("list_tmp_fonts", { description: "List TMP/font-related assets.", inputSchema: {} }, async () => json(R.listTmpFonts(projectRoot)));
185
+ server.registerTool("get_tmp_settings_path", { description: "Get path to TMP Settings asset if present.", inputSchema: {} }, async () => json(R.getTmpSettingsPath(projectRoot)));
186
+ // --- 22. UI Toolkit ---
187
+ server.registerTool("list_ui_documents", { description: "List UI Toolkit .uxml and .uss files.", inputSchema: {} }, async () => json(R.listUiDocuments(projectRoot)));
188
+ // --- 23. New Input System ---
189
+ server.registerTool("list_input_action_assets", { description: "List New Input System .inputactions assets.", inputSchema: {} }, async () => json(R.listInputActionAssets(projectRoot)));
190
+ server.registerTool("get_input_actions_summary", { description: "Get action maps and actions from an .inputactions file.", inputSchema: { path: z.string().describe("e.g. Assets/Input/Player.inputactions") } }, async (args) => json(R.getInputActionsSummary(projectRoot, args.path)));
191
+ // --- 24. Presets ---
192
+ server.registerTool("list_presets", { description: "List .preset assets.", inputSchema: {} }, async () => json(R.listPresets(projectRoot)));
193
+ // --- 25. Editor scripts ---
194
+ server.registerTool("list_editor_scripts", { description: "List C# scripts in Editor folders or with Editor in path.", inputSchema: {} }, async () => json(R.listEditorScripts(projectRoot)));
195
+ // --- 26. Prefab script refs ---
196
+ server.registerTool("get_prefab_script_guids", { description: "Get script GUIDs (MonoBehaviour) referenced by a prefab.", inputSchema: { prefab_path: z.string().describe("e.g. Assets/Prefabs/Player.prefab") } }, async (args) => json(R.getPrefabScriptGuids(projectRoot, args.prefab_path)));
197
+ // --- 27. Assembly dependency graph ---
198
+ server.registerTool("get_assembly_dependency_graph", { description: "Get assembly definition dependency graph (nodes and edges).", inputSchema: {} }, async () => json(R.getAssemblyDependencyGraph(projectRoot)));
199
+ // --- 28. CI configs ---
200
+ server.registerTool("list_ci_configs", { description: "List CI config files (.github/workflows, Jenkinsfile, unity-cloud-build).", inputSchema: {} }, async () => json(R.listCiConfigs(projectRoot)));
201
+ // --- 29. Large assets ---
202
+ server.registerTool("list_large_assets", { description: "List assets over a size threshold (default 5 MB).", inputSchema: { min_size_mb: z.number().optional().default(5) } }, async (args) => json(R.listLargeAssets(projectRoot, args?.min_size_mb ?? 5)));
203
+ // --- 30. PlayFab ---
204
+ server.registerTool("get_playfab_config", { description: "Get PlayFab config (title ID, config paths) from project. No secrets.", inputSchema: {} }, async () => json(R.getPlayFabConfig(projectRoot)));
205
+ // --- 31. Figma ---
206
+ server.registerTool("list_figma_related_assets", { description: "List assets in Figma folder or with figma in path.", inputSchema: {} }, async () => json(R.listFigmaRelatedAssets(projectRoot)));
207
+ // --- 32. Firebase ---
208
+ server.registerTool("get_firebase_config", { description: "Get Firebase config (GoogleServices path, project ID). No secrets.", inputSchema: {} }, async () => json(R.getFirebaseConfig(projectRoot)));
209
+ // --- 33. Steam ---
210
+ server.registerTool("get_steam_config", { description: "Get Steam config (steam_appid.txt, Steamworks path).", inputSchema: {} }, async () => json(R.getSteamConfig(projectRoot)));
211
+ // --- 34. Discord ---
212
+ server.registerTool("get_discord_config", { description: "Detect Discord SDK path in Plugins.", inputSchema: {} }, async () => json(R.getDiscordConfig(projectRoot)));
213
+ // --- 35. FMOD ---
214
+ server.registerTool("get_fmod_config", { description: "Get FMOD config (banks path, bank files).", inputSchema: {} }, async () => json(R.getFmodConfig(projectRoot)));
215
+ // --- 36. Wwise ---
216
+ server.registerTool("get_wwise_config", { description: "Get Wwise config (sound banks, project paths).", inputSchema: {} }, async () => json(R.getWwiseConfig(projectRoot)));
217
+ // --- 37. Substance ---
218
+ server.registerTool("list_substance_assets", { description: "List Substance .sbsar and .sbs assets.", inputSchema: {} }, async () => json(R.listSubstanceAssets(projectRoot)));
219
+ // --- 38. SpeedTree ---
220
+ server.registerTool("list_speedtree_assets", { description: "List SpeedTree .spm and .stm assets.", inputSchema: {} }, async () => json(R.listSpeedTreeAssets(projectRoot)));
221
+ // --- 39. Lottie ---
222
+ server.registerTool("list_lottie_assets", { description: "List Lottie-related JSON assets.", inputSchema: {} }, async () => json(R.listLottieAssets(projectRoot)));
223
+ // --- 40. Analytics / crash ---
224
+ server.registerTool("get_analytics_or_crash_config", { description: "Detect analytics/crash reporting services (Unity, Sentry, Crashlytics, BugSnag).", inputSchema: {} }, async () => json(R.getAnalyticsOrCrashConfig(projectRoot)));
225
+ // --- 41. Ads ---
226
+ server.registerTool("get_ads_config", { description: "Detect ad SDK presence (Unity Ads, AdMob, ironSource).", inputSchema: {} }, async () => json(R.getAdsConfig(projectRoot)));
227
+ // --- 42. Git LFS ---
228
+ server.registerTool("get_git_lfs_tracked", { description: "Get Git LFS patterns from .gitattributes.", inputSchema: {} }, async () => json(R.getGitLfsTracked(projectRoot)));
229
+ // --- 43. Plastic SCM ---
230
+ server.registerTool("get_plastic_config", { description: "Get Plastic SCM config (.plastic, workspace name).", inputSchema: {} }, async () => json(R.getPlasticConfig(projectRoot)));
171
231
  const transport = new StdioServerTransport();
172
232
  await server.connect(transport);
173
233
  }
package/dist/readers.d.ts CHANGED
@@ -88,3 +88,79 @@ export declare function getTestAssemblies(root: string): AsmDefInfo[];
88
88
  export declare function getRepoDocs(root: string): Record<string, string>;
89
89
  export declare function getProjectVersion(root: string): string;
90
90
  export declare function getChangelog(root: string): string | null;
91
+ export declare function getPhysicsSettings(root: string): {
92
+ dynamics?: Record<string, string>;
93
+ physics2d?: Record<string, string>;
94
+ };
95
+ export declare function listRenderPipelines(root: string): {
96
+ pipelines: string[];
97
+ volumeProfiles: string[];
98
+ };
99
+ export declare function listTimelinePlayables(root: string): string[];
100
+ export declare function listSpriteAtlases(root: string): string[];
101
+ export declare function listTilemapAssets(root: string): string[];
102
+ export declare function listShaderGraphs(root: string): string[];
103
+ export declare function listVfxGraphs(root: string): string[];
104
+ export declare function listTmpFonts(root: string): string[];
105
+ export declare function getTmpSettingsPath(root: string): string | null;
106
+ export declare function listUiDocuments(root: string): {
107
+ uxml: string[];
108
+ uss: string[];
109
+ };
110
+ export declare function listInputActionAssets(root: string): string[];
111
+ export declare function getInputActionsSummary(root: string, path: string): {
112
+ maps: string[];
113
+ actions: string[];
114
+ };
115
+ export declare function listPresets(root: string): string[];
116
+ export declare function listEditorScripts(root: string): string[];
117
+ export declare function getPrefabScriptGuids(root: string, prefabPath: string): string[];
118
+ export declare function getAssemblyDependencyGraph(root: string): {
119
+ nodes: string[];
120
+ edges: [string, string][];
121
+ };
122
+ export declare function listCiConfigs(root: string): string[];
123
+ export declare function listLargeAssets(root: string, minSizeMb?: number): {
124
+ path: string;
125
+ sizeMb: number;
126
+ }[];
127
+ export declare function getPlayFabConfig(root: string): {
128
+ titleId?: string;
129
+ configPaths: string[];
130
+ };
131
+ export declare function listFigmaRelatedAssets(root: string): string[];
132
+ export declare function getFirebaseConfig(root: string): {
133
+ googleServicesJson?: string;
134
+ plist?: string;
135
+ projectId?: string;
136
+ };
137
+ export declare function getSteamConfig(root: string): {
138
+ steamAppIdTxt?: string;
139
+ steamworksPath?: string;
140
+ };
141
+ export declare function getDiscordConfig(root: string): {
142
+ sdkPath?: string;
143
+ };
144
+ export declare function getFmodConfig(root: string): {
145
+ banksPath?: string;
146
+ projectPath?: string;
147
+ bankFiles: string[];
148
+ };
149
+ export declare function getWwiseConfig(root: string): {
150
+ soundBanksPath?: string;
151
+ projectPaths: string[];
152
+ };
153
+ export declare function listSubstanceAssets(root: string): string[];
154
+ export declare function listSpeedTreeAssets(root: string): string[];
155
+ export declare function listLottieAssets(root: string): string[];
156
+ export declare function getAnalyticsOrCrashConfig(root: string): {
157
+ services: string[];
158
+ };
159
+ export declare function getAdsConfig(root: string): {
160
+ sdkPresence: string[];
161
+ };
162
+ export declare function getGitLfsTracked(root: string): string[];
163
+ export declare function getPlasticConfig(root: string): {
164
+ plasticDir: boolean;
165
+ workspaceName?: string;
166
+ };
package/dist/readers.js CHANGED
@@ -500,3 +500,291 @@ export function getProjectVersion(root) {
500
500
  export function getChangelog(root) {
501
501
  return readFileSafe(root, "CHANGELOG.md") || readFileSafe(root, "CHANGELOG") || readFileSafe(root, "changelog.md");
502
502
  }
503
+ // --- 16. Physics ---
504
+ export function getPhysicsSettings(root) {
505
+ const dynamicsContent = readFileSafe(root, PROJECT_SETTINGS, "DynamicsManager.asset");
506
+ const physics2dContent = readFileSafe(root, PROJECT_SETTINGS, "Physics2DSettings.asset");
507
+ const out = {};
508
+ if (dynamicsContent)
509
+ out.dynamics = parseUnityKeyValue(dynamicsContent);
510
+ if (physics2dContent)
511
+ out.physics2d = parseUnityKeyValue(physics2dContent);
512
+ return out;
513
+ }
514
+ // --- 17. Render pipelines (URP/HDRP) ---
515
+ export function listRenderPipelines(root) {
516
+ const pipelines = listFilesRecursive(root, ASSETS, { ext: ".asset" }).filter((p) => p.toLowerCase().includes("pipeline") || p.toLowerCase().includes("render"));
517
+ const settings = join(ASSETS, "Settings");
518
+ const vol = listFilesRecursive(root, settings, { ext: ".asset" }).filter((p) => p.toLowerCase().includes("volume") || p.toLowerCase().includes("profile"));
519
+ return { pipelines: pipelines.slice(0, 50), volumeProfiles: vol.slice(0, 30) };
520
+ }
521
+ // --- 18. Timeline ---
522
+ export function listTimelinePlayables(root) {
523
+ return listFilesRecursive(root, ASSETS, { ext: ".playable" });
524
+ }
525
+ // --- 19. Sprites / 2D ---
526
+ export function listSpriteAtlases(root) {
527
+ return listFilesRecursive(root, ASSETS, { ext: ".spriteatlas" });
528
+ }
529
+ export function listTilemapAssets(root) {
530
+ return listFilesRecursive(root, ASSETS, { ext: ".asset" }).filter((p) => p.toLowerCase().includes("tilemap") || p.toLowerCase().includes("tile"));
531
+ }
532
+ // --- 20. Shader Graph / VFX ---
533
+ export function listShaderGraphs(root) {
534
+ return listFilesRecursive(root, ASSETS, { ext: ".shadergraph" });
535
+ }
536
+ export function listVfxGraphs(root) {
537
+ return listFilesRecursive(root, ASSETS, { ext: ".vfx" });
538
+ }
539
+ // --- 21. TextMeshPro ---
540
+ export function listTmpFonts(root) {
541
+ return listFilesRecursive(root, ASSETS, { ext: ".asset" }).filter((p) => p.toLowerCase().includes("tmp") || p.toLowerCase().includes("font"));
542
+ }
543
+ export function getTmpSettingsPath(root) {
544
+ const paths = ["Assets/Resources/TMP Settings.asset", "Assets/TextMesh Pro/Resources/TMP Settings.asset"];
545
+ for (const p of paths)
546
+ if (existsSync(join(root, p)))
547
+ return p;
548
+ return null;
549
+ }
550
+ // --- 22. UI Toolkit ---
551
+ export function listUiDocuments(root) {
552
+ const uxml = listFilesRecursive(root, ASSETS, { ext: ".uxml" });
553
+ const uss = listFilesRecursive(root, ASSETS, { ext: ".uss" });
554
+ return { uxml, uss };
555
+ }
556
+ // --- 23. New Input System (.inputactions) ---
557
+ export function listInputActionAssets(root) {
558
+ return listFilesRecursive(root, ASSETS, { ext: ".inputactions" });
559
+ }
560
+ export function getInputActionsSummary(root, path) {
561
+ const j = readJsonSafe(root, path);
562
+ if (!j)
563
+ return { maps: [], actions: [] };
564
+ return {
565
+ maps: (j.maps || []).map((m) => m.name),
566
+ actions: (j.actions || []).map((a) => a.name),
567
+ };
568
+ }
569
+ // --- 24. Presets ---
570
+ export function listPresets(root) {
571
+ return listFilesRecursive(root, ASSETS, { ext: ".preset" });
572
+ }
573
+ // --- 25. Editor scripts ---
574
+ export function listEditorScripts(root) {
575
+ const all = listScripts(root);
576
+ return all.filter((p) => p.includes("Editor") || p.toLowerCase().includes("/editor/"));
577
+ }
578
+ // --- 26. Prefab → script refs ---
579
+ export function getPrefabScriptGuids(root, prefabPath) {
580
+ const content = readFileSafe(root, prefabPath);
581
+ if (!content)
582
+ return [];
583
+ const guids = [];
584
+ const re = /guid:\s*([a-f0-9]{32})/g;
585
+ let m;
586
+ while ((m = re.exec(content)) !== null)
587
+ guids.push(m[1]);
588
+ const scriptRef = /MonoBehaviour:\s*\n[\s\S]*?m_Script:\s*\{\s*fileID:\s*\d+,\s*guid:\s*([a-f0-9]{32})/g;
589
+ while ((m = scriptRef.exec(content)) !== null)
590
+ guids.push(m[1]);
591
+ return [...new Set(guids)];
592
+ }
593
+ // --- 27. Assembly dependency graph ---
594
+ export function getAssemblyDependencyGraph(root) {
595
+ const asms = getAssemblyDefinitions(root);
596
+ const nodes = asms.map((a) => a.name);
597
+ const edges = [];
598
+ for (const a of asms)
599
+ for (const ref of a.references)
600
+ edges.push([a.name, ref]);
601
+ return { nodes, edges };
602
+ }
603
+ // --- 28. CI configs ---
604
+ export function listCiConfigs(root) {
605
+ const out = [];
606
+ const gh = join(root, ".github", "workflows");
607
+ if (existsSync(gh))
608
+ readdirSync(gh).filter((e) => e.endsWith(".yml") || e.endsWith(".yaml")).forEach((e) => out.push(`.github/workflows/${e}`));
609
+ if (existsSync(join(root, "Jenkinsfile")))
610
+ out.push("Jenkinsfile");
611
+ if (existsSync(join(root, "unity-cloud-build.json")))
612
+ out.push("unity-cloud-build.json");
613
+ return out;
614
+ }
615
+ // --- 29. Large assets ---
616
+ export function listLargeAssets(root, minSizeMb = 5) {
617
+ const out = [];
618
+ const threshold = minSizeMb * 1024 * 1024;
619
+ const stack = [ASSETS];
620
+ while (stack.length) {
621
+ const d = stack.pop();
622
+ const fullD = join(root, d);
623
+ try {
624
+ for (const e of readdirSync(fullD)) {
625
+ const rel = join(d, e);
626
+ const fullPath = join(root, rel);
627
+ if (statSync(fullPath).isDirectory()) {
628
+ if (!e.startsWith("."))
629
+ stack.push(rel);
630
+ }
631
+ else {
632
+ const size = statSync(fullPath).size;
633
+ if (size >= threshold)
634
+ out.push({ path: rel, sizeMb: Math.round((size / 1024 / 1024) * 100) / 100 });
635
+ }
636
+ }
637
+ }
638
+ catch {
639
+ /* */
640
+ }
641
+ }
642
+ return out.sort((a, b) => b.sizeMb - a.sizeMb);
643
+ }
644
+ // --- 30. PlayFab ---
645
+ export function getPlayFabConfig(root) {
646
+ const configPaths = [];
647
+ const candidates = listFilesRecursive(root, ASSETS, { ext: ".asset" }).filter((p) => p.toLowerCase().includes("playfab"));
648
+ const res = listFilesRecursive(root, join(ASSETS, "Resources"), { ext: ".json" });
649
+ candidates.forEach((p) => configPaths.push(p));
650
+ res.filter((p) => p.toLowerCase().includes("playfab")).forEach((p) => configPaths.push(p));
651
+ let titleId;
652
+ for (const p of configPaths) {
653
+ const content = readFileSafe(root, p);
654
+ const m = content?.match(/titleId|TitleId|title_id["\s:]+([a-f0-9]{5,})/i);
655
+ if (m?.[1])
656
+ titleId = m[1];
657
+ }
658
+ return { titleId, configPaths };
659
+ }
660
+ // --- 31. Figma-related assets ---
661
+ export function listFigmaRelatedAssets(root) {
662
+ const figmaDir = join(ASSETS, "Figma");
663
+ if (existsSync(join(root, figmaDir)))
664
+ return listFilesRecursive(root, figmaDir);
665
+ return listFilesRecursive(root, ASSETS).filter((p) => p.toLowerCase().includes("figma"));
666
+ }
667
+ // --- 32. Firebase ---
668
+ export function getFirebaseConfig(root) {
669
+ const plist = "GoogleService-Info.plist";
670
+ const json = "google-services.json";
671
+ const paths = [join(ASSETS, json), join(ASSETS, plist), json, plist];
672
+ let content = null;
673
+ let found;
674
+ for (const p of paths) {
675
+ content = readFileSafe(root, p);
676
+ if (content) {
677
+ found = p;
678
+ break;
679
+ }
680
+ }
681
+ if (!content)
682
+ return {};
683
+ const projectId = content.match(/project_id["\s:]+["']?([^"'\s]+)/i)?.[1];
684
+ return { googleServicesJson: found?.includes(".json") ? found : undefined, plist: found?.includes(".plist") ? found : undefined, projectId };
685
+ }
686
+ // --- 33. Steam ---
687
+ export function getSteamConfig(root) {
688
+ const appId = readFileSafe(root, "steam_appid.txt");
689
+ let steamworksPath;
690
+ const plug = join(ASSETS, "Plugins");
691
+ if (existsSync(join(root, plug))) {
692
+ const entries = readdirSync(join(root, plug));
693
+ if (entries.some((e) => e.toLowerCase().includes("steam")))
694
+ steamworksPath = join(plug, entries.find((e) => e.toLowerCase().includes("steam")));
695
+ }
696
+ return { steamAppIdTxt: appId ? "steam_appid.txt" : undefined, steamworksPath };
697
+ }
698
+ // --- 34. Discord ---
699
+ export function getDiscordConfig(root) {
700
+ const plug = join(ASSETS, "Plugins");
701
+ const full = join(root, plug);
702
+ if (!existsSync(full))
703
+ return {};
704
+ const entries = readdirSync(full);
705
+ const discord = entries.find((e) => e.toLowerCase().includes("discord"));
706
+ return discord ? { sdkPath: join(plug, discord) } : {};
707
+ }
708
+ // --- 35. FMOD ---
709
+ export function getFmodConfig(root) {
710
+ const bankFiles = listFilesRecursive(root, ASSETS, { ext: ".bank" });
711
+ const meta = listFilesRecursive(root, ASSETS, { ext: ".meta" }).filter((p) => p.includes("FMOD") || p.includes("fmod"));
712
+ let banksPath;
713
+ let projectPath;
714
+ for (const m of meta) {
715
+ const content = readFileSafe(root, m);
716
+ if (content?.includes("bank"))
717
+ banksPath = m.replace(/\.meta$/, "").replace(/[^/]+$/, "");
718
+ if (content?.includes("project"))
719
+ projectPath = m.replace(/\.meta$/, "");
720
+ }
721
+ return { banksPath, projectPath, bankFiles };
722
+ }
723
+ // --- 36. Wwise ---
724
+ export function getWwiseConfig(root) {
725
+ const wproj = listFilesRecursive(root, ASSETS).filter((p) => p.endsWith(".wproj") || p.endsWith(".wwise"));
726
+ const banks = listFilesRecursive(root, ASSETS).filter((p) => p.toLowerCase().includes("soundbank") || p.toLowerCase().includes("audiobank"));
727
+ return { soundBanksPath: banks[0]?.replace(/[^/]+$/, "") || undefined, projectPaths: wproj };
728
+ }
729
+ // --- 37. Substance ---
730
+ export function listSubstanceAssets(root) {
731
+ const sbsar = listFilesRecursive(root, ASSETS, { ext: ".sbsar" });
732
+ const sbs = listFilesRecursive(root, ASSETS, { ext: ".sbs" });
733
+ return [...sbsar, ...sbs];
734
+ }
735
+ // --- 38. SpeedTree ---
736
+ export function listSpeedTreeAssets(root) {
737
+ return listFilesRecursive(root, ASSETS, { ext: ".spm" }).concat(listFilesRecursive(root, ASSETS, { ext: ".stm" }));
738
+ }
739
+ // --- 39. Lottie ---
740
+ export function listLottieAssets(root) {
741
+ const lottieDir = join(ASSETS, "Lottie");
742
+ if (existsSync(join(root, lottieDir)))
743
+ return listFilesRecursive(root, lottieDir, { ext: ".json" });
744
+ return listFilesRecursive(root, ASSETS, { ext: ".json" }).filter((p) => p.toLowerCase().includes("lottie"));
745
+ }
746
+ // --- 40. Analytics / crash reporting ---
747
+ export function getAnalyticsOrCrashConfig(root) {
748
+ const services = [];
749
+ const packages = getPackages(root).dependencies.map((d) => d.name.toLowerCase());
750
+ if (packages.some((p) => p.includes("analytics")))
751
+ services.push("Unity Analytics");
752
+ if (packages.some((p) => p.includes("sentry")))
753
+ services.push("Sentry");
754
+ if (packages.some((p) => p.includes("crashlytics")))
755
+ services.push("Crashlytics");
756
+ if (packages.some((p) => p.includes("bugsnag")))
757
+ services.push("BugSnag");
758
+ const assets = listFilesRecursive(root, ASSETS).filter((p) => p.toLowerCase().includes("sentry") || p.toLowerCase().includes("crashlytics"));
759
+ if (assets.length && !services.length)
760
+ services.push("Crash/Analytics (asset detected)");
761
+ return { services };
762
+ }
763
+ // --- 41. Ads ---
764
+ export function getAdsConfig(root) {
765
+ const packages = getPackages(root).dependencies.map((d) => d.name.toLowerCase());
766
+ const sdkPresence = [];
767
+ if (packages.some((p) => p.includes("advertisement") || p.includes("unity-ads")))
768
+ sdkPresence.push("Unity Ads");
769
+ if (packages.some((p) => p.includes("admob") || p.includes("google-mobile-ads")))
770
+ sdkPresence.push("AdMob");
771
+ if (packages.some((p) => p.includes("ironsource")))
772
+ sdkPresence.push("ironSource");
773
+ return { sdkPresence };
774
+ }
775
+ // --- 42. Git LFS ---
776
+ export function getGitLfsTracked(root) {
777
+ const content = readFileSafe(root, ".gitattributes");
778
+ if (!content)
779
+ return [];
780
+ return content.split("\n").filter((l) => l.includes("lfs") || l.includes("filter=lfs"));
781
+ }
782
+ // --- 43. Plastic SCM ---
783
+ export function getPlasticConfig(root) {
784
+ const plasticDir = existsSync(join(root, ".plastic"));
785
+ let workspaceName;
786
+ const conf = readFileSafe(root, ".plastic", "plastic.workspace");
787
+ if (conf)
788
+ workspaceName = conf.match(/workspace\s+([^\n]+)/)?.[1]?.trim();
789
+ return { plasticDir, workspaceName };
790
+ }
@@ -0,0 +1,142 @@
1
+ # Unity MCP Server – Audit: Day-to-Day Tools & Connected Integrations
2
+
3
+ This doc lists (1) additional Unity project tools developers use daily that we could add, and (2) connected interfaces (PlayFab, Figma, etc.) and what tooling is feasible.
4
+
5
+ ---
6
+
7
+ ## Part 1: Unity project tools (filesystem-only, no Editor)
8
+
9
+ ### Already implemented (v1.1.0)
10
+
11
+ Project/build, packages, quality, scripting defines, version, changelog · Assemblies, scripts, find by content · Scenes (all + summary), prefabs · Asset tree, list by extension, find references · Materials, shaders · Animator controllers, clips, states · Audio clips, mixers · Addressables, localization · Input axes, tags & layers · Test assemblies · Repo docs, agent docs.
12
+
13
+ ---
14
+
15
+ ### High value – day-to-day (can add with current approach)
16
+
17
+ | Area | Tool idea | What it would do |
18
+ |------|-----------|-------------------|
19
+ | **Physics** | `get_physics_settings` | Read Physics / Physics2D settings (layers, collision matrix, gravity). |
20
+ | **Graphics / URP/HDRP** | `list_render_pipelines` | List pipeline assets (.asset under Settings), volume profiles. |
21
+ | **Timeline** | `list_timeline_playables` | List .playable assets (Timeline). |
22
+ | **Sprites / 2D** | `list_sprite_atlases`, `list_tilemap_assets` | List sprite atlases, tilemap assets. |
23
+ | **Shader Graph / VFX** | `list_shader_graphs`, `list_vfx_graphs` | List .shadergraph, .vfx assets. |
24
+ | **TextMeshPro** | `list_tmp_fonts`, `get_tmp_settings` | List TMP fonts, read TMP Settings. |
25
+ | **UI Toolkit** | `list_ui_documents` | List .uxml, .uss under Assets. |
26
+ | **Input System (new)** | `list_input_action_assets` | List .inputactions (JSON) and summarize actions/maps. |
27
+ | **Presets** | `list_presets` | List .preset files. |
28
+ | **Editor scripts** | `list_editor_scripts` | List scripts under Editor/ (or *Editor*.cs). |
29
+ | **Prefab → script refs** | `get_prefab_scripts` | For a prefab, list MonoBehaviour script GUIDs (from YAML). |
30
+ | **Assembly graph** | `get_assembly_dependency_graph` | Which asmdef references which (we have list; add “depends on” graph). |
31
+ | **CI / build config** | `list_ci_configs` | List GitHub Actions, Jenkinsfile, Unity Cloud Build configs in repo. |
32
+ | **Large assets** | `list_large_assets` | List files over N MB under Assets (from filesystem). |
33
+ | **Package vulnerabilities** | *(stretch)* | Run `npm audit`-style for Packages (Unity doesn’t expose this the same way; could parse manifest only). |
34
+
35
+ ---
36
+
37
+ ### Medium value – nice to have
38
+
39
+ | Area | Tool idea | Notes |
40
+ |------|-----------|--------|
41
+ | **NavMesh** | List NavMesh data | Often in Library/; optional or path-only. |
42
+ | **ScriptableObjects** | List .asset by importer type | Possible via .meta; fuzzy. |
43
+ | **DOTween / other packages** | `list_animation_packages` | Already covered by `list_packages`; could add “known middleware” filter. |
44
+ | **Version control** | `get_vcs_status` | Would need `git`; list modified/untracked (optional). |
45
+ | **Layers collision matrix** | `get_physics_layer_collision_matrix` | Parse Physics2DSettings / Physics settings. |
46
+
47
+ ---
48
+
49
+ ## Part 2: Connected interfaces (PlayFab, Figma, etc.)
50
+
51
+ These are external services or tools that Unity projects often connect to. We list what’s feasible **without** running the Unity Editor and without storing API keys in the MCP server (config discovery only, or optional read-only APIs with user-supplied keys).
52
+
53
+ ### PlayFab (Microsoft)
54
+
55
+ | What | Feasible tooling |
56
+ |------|-------------------|
57
+ | **In Unity project** | `get_playfab_config` – Find PlayFab title ID, paths (e.g. from ScriptableObject/config, or common paths like `Resources/PlayFab`, `Plugins/PlayFab`). Read title ID and comment if secret key path is present (do not read secret). |
58
+ | **PlayFab API** | Optional separate tool: “list titles”, “get economy config” – would require user to supply API key in env; MCP server calls PlayFab REST API. Prefer keeping server key-agnostic and documenting “use env for key”. |
59
+
60
+ ### Figma
61
+
62
+ | What | Feasible tooling |
63
+ |------|-------------------|
64
+ | **In Unity project** | `list_figma_related_assets` – List assets in a folder like `Figma/` or named by convention (e.g. `*_figma*`), or read a manifest if a Figma→Unity plugin writes one. No Figma API in core server (would need Figma token). |
65
+ | **Figma API** | Optional: “list Figma files”, “get frame” – needs FIGMA_TOKEN in env; could be a separate MCP server (Figma MCP) that this server doesn’t implement. |
66
+
67
+ ### Analytics & crash reporting
68
+
69
+ | Service | In-project / tool idea |
70
+ |---------|-------------------------|
71
+ | **Unity Analytics** | Detect from ProjectSettings or Packages; `get_analytics_config` (project/org IDs if present). |
72
+ | **Firebase** | `get_firebase_config` – Read `GoogleServices-Info.plist` / `google-services.json` (project IDs, no secrets). |
73
+ | **Sentry / Crashlytics / BugSnag** | `get_crash_reporting_config` – Find DSN or config paths; list which service is used (no keys in output). |
74
+
75
+ ### Ads & monetization
76
+
77
+ | Service | In-project / tool idea |
78
+ |---------|-------------------------|
79
+ | **Unity Ads, AdMob, ironSource** | `get_ads_config` – Detect SDK presence (Packages/Assets), list ad network names; do not read app IDs/keys. |
80
+ | **Unity IAP, RevenueCat** | `get_iap_config` – Presence of IAP package or RevenueCat config path. |
81
+
82
+ ### Platform SDKs & distribution
83
+
84
+ | Service | In-project / tool idea |
85
+ |---------|-------------------------|
86
+ | **Steam / Steamworks** | `get_steam_config` – Find `steam_appid.txt`, Steamworks SDK path, list presence. |
87
+ | **Discord** | `get_discord_config` – Detect Discord SDK or Rich Presence config path. |
88
+ | **Epic / Consoles** | Usually binary SDKs; `list_platform_plugins` – list known plugin folders (e.g. Xbox, PlayStation) by name. |
89
+
90
+ ### Audio middleware
91
+
92
+ | Service | In-project / tool idea |
93
+ |---------|-------------------------|
94
+ | **FMOD** | `get_fmod_config` – Banks path, .bank list, project path from common locations. |
95
+ | **Wwise** | `get_wwise_config` – Project path, SoundBanks path if present. |
96
+
97
+ ### Design & art pipelines
98
+
99
+ | Service | In-project / tool idea |
100
+ |---------|-------------------------|
101
+ | **Substance** | `list_substance_assets` – List .sbsar, .sbs. |
102
+ | **SpeedTree** | `list_speedtree_assets` – List .spm, .stm. |
103
+ | **Blender** | No direct config; we already have `list_assets_by_extension` (.fbx, .blend). |
104
+ | **Lottie** | `list_lottie_assets` – List .json in Lottie folder or known paths. |
105
+
106
+ ### Version control & CI
107
+
108
+ | Service | In-project / tool idea |
109
+ |---------|-------------------------|
110
+ | **Git / Git LFS** | `get_git_lfs_tracked` – List .gitattributes and LFS patterns; optional `get_git_status` (if git in path). |
111
+ | **Plastic SCM** | `get_plastic_config` – .plastic folder, workspace name. |
112
+ | **GitHub Actions / Jenkins / Unity Cloud** | `list_ci_configs` – List .github/workflows, Jenkinsfile, Unity Cloud config files. |
113
+
114
+ ### Task & collaboration (outside repo)
115
+
116
+ | Service | Note |
117
+ |---------|------|
118
+ | **Jira, Trello, Shortcut, Asana** | No standard Unity project files; integration is usually API keys in env or CI. Could document “use env + separate MCP” for those. |
119
+ | **Slack / Discord** | Webhooks or bots; not stored in Unity project. |
120
+
121
+ ---
122
+
123
+ ## Part 3: Summary list – “all of them”
124
+
125
+ **Unity day-to-day (candidates to add)**
126
+ Physics settings · Render pipelines (URP/HDRP) · Timeline playables · Sprite atlases, tilemaps · Shader Graph, VFX Graph · TextMeshPro fonts & settings · UI Toolkit (UXML, USS) · New Input System (.inputactions) · Presets · Editor scripts · Prefab→script refs · Assembly dependency graph · CI configs · Large assets · (Optional) VCS status, layer collision matrix.
127
+
128
+ **Connected interfaces we can add tooling for (config / discovery only)**
129
+ PlayFab (project config, title ID) · Figma (related assets in project) · Firebase · Unity Analytics · Sentry/Crashlytics/BugSnag (config presence) · Unity Ads / AdMob / ironSource (SDK presence) · Unity IAP / RevenueCat · Steam/Steamworks · Discord · FMOD · Wwise · Substance · SpeedTree · Lottie · Git LFS · Plastic SCM · GitHub Actions / Jenkins / Unity Cloud (list configs).
130
+
131
+ **Connected interfaces that need API keys (document or separate MCP)**
132
+ PlayFab API · Figma API · Jira/Trello/Shortcut · Slack/Discord (webhooks). Prefer: user supplies keys in env and uses a dedicated MCP for that service, rather than baking into this server.
133
+
134
+ ---
135
+
136
+ ## Recommendation
137
+
138
+ 1. **Phase 1 (Unity-only):** Add the “high value” Unity tools (physics, pipelines, Timeline, 2D, Input System, TMP, UI Docs, presets, editor scripts, prefab scripts, assembly graph, CI configs, large assets).
139
+ 2. **Phase 2 (connected – discovery only):** Add config/discovery tools for PlayFab, Figma-related assets, Firebase, Steam, FMOD/Wwise, Substance, SpeedTree, Git LFS, Plastic, CI configs. No API keys in server; only read files.
140
+ 3. **Phase 3 (optional):** Separate MCP servers or optional tools that call external APIs (PlayFab, Figma) with user-supplied env keys; keep this server focused on “Unity project + safe config discovery.”
141
+
142
+ If you want, next step can be: pick 5–10 of the “high value” + “connected discovery” items and implement them as new tools in this repo.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unity-mcp-server",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Lightweight MCP server for any Unity project - project info, build scenes, agent docs (no Unity Editor required)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/server.json CHANGED
@@ -7,12 +7,12 @@
7
7
  "url": "https://github.com/rachitkumarrastogi/unity-mcp-server",
8
8
  "source": "github"
9
9
  },
10
- "version": "1.1.0",
10
+ "version": "1.2.0",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "unity-mcp-server",
15
- "version": "1.1.0",
15
+ "version": "1.2.0",
16
16
  "transport": { "type": "stdio" },
17
17
  "environmentVariables": [
18
18
  {
package/src/index.ts CHANGED
@@ -24,7 +24,7 @@ async function main() {
24
24
 
25
25
  const server = new McpServer({
26
26
  name: "unity-mcp-server",
27
- version: "1.0.0",
27
+ version: "1.2.0",
28
28
  });
29
29
 
30
30
  const text = (s: string) => ({ content: [{ type: "text" as const, text: s }] });
@@ -326,6 +326,106 @@ async function main() {
326
326
  }
327
327
  );
328
328
 
329
+ // --- 16. Physics ---
330
+ server.registerTool("get_physics_settings", { description: "Get Physics/DynamicsManager or Physics2D settings (key-value).", inputSchema: {} }, async () => json(R.getPhysicsSettings(projectRoot)));
331
+
332
+ // --- 17. Render pipelines ---
333
+ server.registerTool("list_render_pipelines", { description: "List render pipeline assets and volume profiles (URP/HDRP).", inputSchema: {} }, async () => json(R.listRenderPipelines(projectRoot)));
334
+
335
+ // --- 18. Timeline ---
336
+ server.registerTool("list_timeline_playables", { description: "List Timeline .playable assets.", inputSchema: {} }, async () => json(R.listTimelinePlayables(projectRoot)));
337
+
338
+ // --- 19. Sprites / 2D ---
339
+ server.registerTool("list_sprite_atlases", { description: "List Sprite Atlas (.spriteatlas) assets.", inputSchema: {} }, async () => json(R.listSpriteAtlases(projectRoot)));
340
+ server.registerTool("list_tilemap_assets", { description: "List tilemap-related .asset files.", inputSchema: {} }, async () => json(R.listTilemapAssets(projectRoot)));
341
+
342
+ // --- 20. Shader Graph / VFX ---
343
+ server.registerTool("list_shader_graphs", { description: "List Shader Graph (.shadergraph) assets.", inputSchema: {} }, async () => json(R.listShaderGraphs(projectRoot)));
344
+ server.registerTool("list_vfx_graphs", { description: "List VFX Graph (.vfx) assets.", inputSchema: {} }, async () => json(R.listVfxGraphs(projectRoot)));
345
+
346
+ // --- 21. TextMeshPro ---
347
+ server.registerTool("list_tmp_fonts", { description: "List TMP/font-related assets.", inputSchema: {} }, async () => json(R.listTmpFonts(projectRoot)));
348
+ server.registerTool("get_tmp_settings_path", { description: "Get path to TMP Settings asset if present.", inputSchema: {} }, async () => json(R.getTmpSettingsPath(projectRoot)));
349
+
350
+ // --- 22. UI Toolkit ---
351
+ server.registerTool("list_ui_documents", { description: "List UI Toolkit .uxml and .uss files.", inputSchema: {} }, async () => json(R.listUiDocuments(projectRoot)));
352
+
353
+ // --- 23. New Input System ---
354
+ server.registerTool("list_input_action_assets", { description: "List New Input System .inputactions assets.", inputSchema: {} }, async () => json(R.listInputActionAssets(projectRoot)));
355
+ server.registerTool(
356
+ "get_input_actions_summary",
357
+ { description: "Get action maps and actions from an .inputactions file.", inputSchema: { path: z.string().describe("e.g. Assets/Input/Player.inputactions") } },
358
+ async (args: unknown) => json(R.getInputActionsSummary(projectRoot, (args as { path: string }).path))
359
+ );
360
+
361
+ // --- 24. Presets ---
362
+ server.registerTool("list_presets", { description: "List .preset assets.", inputSchema: {} }, async () => json(R.listPresets(projectRoot)));
363
+
364
+ // --- 25. Editor scripts ---
365
+ server.registerTool("list_editor_scripts", { description: "List C# scripts in Editor folders or with Editor in path.", inputSchema: {} }, async () => json(R.listEditorScripts(projectRoot)));
366
+
367
+ // --- 26. Prefab script refs ---
368
+ server.registerTool(
369
+ "get_prefab_script_guids",
370
+ { description: "Get script GUIDs (MonoBehaviour) referenced by a prefab.", inputSchema: { prefab_path: z.string().describe("e.g. Assets/Prefabs/Player.prefab") } },
371
+ async (args: unknown) => json(R.getPrefabScriptGuids(projectRoot, (args as { prefab_path: string }).prefab_path))
372
+ );
373
+
374
+ // --- 27. Assembly dependency graph ---
375
+ server.registerTool("get_assembly_dependency_graph", { description: "Get assembly definition dependency graph (nodes and edges).", inputSchema: {} }, async () => json(R.getAssemblyDependencyGraph(projectRoot)));
376
+
377
+ // --- 28. CI configs ---
378
+ server.registerTool("list_ci_configs", { description: "List CI config files (.github/workflows, Jenkinsfile, unity-cloud-build).", inputSchema: {} }, async () => json(R.listCiConfigs(projectRoot)));
379
+
380
+ // --- 29. Large assets ---
381
+ server.registerTool(
382
+ "list_large_assets",
383
+ { description: "List assets over a size threshold (default 5 MB).", inputSchema: { min_size_mb: z.number().optional().default(5) } },
384
+ async (args: unknown) => json(R.listLargeAssets(projectRoot, (args as { min_size_mb?: number })?.min_size_mb ?? 5))
385
+ );
386
+
387
+ // --- 30. PlayFab ---
388
+ server.registerTool("get_playfab_config", { description: "Get PlayFab config (title ID, config paths) from project. No secrets.", inputSchema: {} }, async () => json(R.getPlayFabConfig(projectRoot)));
389
+
390
+ // --- 31. Figma ---
391
+ server.registerTool("list_figma_related_assets", { description: "List assets in Figma folder or with figma in path.", inputSchema: {} }, async () => json(R.listFigmaRelatedAssets(projectRoot)));
392
+
393
+ // --- 32. Firebase ---
394
+ server.registerTool("get_firebase_config", { description: "Get Firebase config (GoogleServices path, project ID). No secrets.", inputSchema: {} }, async () => json(R.getFirebaseConfig(projectRoot)));
395
+
396
+ // --- 33. Steam ---
397
+ server.registerTool("get_steam_config", { description: "Get Steam config (steam_appid.txt, Steamworks path).", inputSchema: {} }, async () => json(R.getSteamConfig(projectRoot)));
398
+
399
+ // --- 34. Discord ---
400
+ server.registerTool("get_discord_config", { description: "Detect Discord SDK path in Plugins.", inputSchema: {} }, async () => json(R.getDiscordConfig(projectRoot)));
401
+
402
+ // --- 35. FMOD ---
403
+ server.registerTool("get_fmod_config", { description: "Get FMOD config (banks path, bank files).", inputSchema: {} }, async () => json(R.getFmodConfig(projectRoot)));
404
+
405
+ // --- 36. Wwise ---
406
+ server.registerTool("get_wwise_config", { description: "Get Wwise config (sound banks, project paths).", inputSchema: {} }, async () => json(R.getWwiseConfig(projectRoot)));
407
+
408
+ // --- 37. Substance ---
409
+ server.registerTool("list_substance_assets", { description: "List Substance .sbsar and .sbs assets.", inputSchema: {} }, async () => json(R.listSubstanceAssets(projectRoot)));
410
+
411
+ // --- 38. SpeedTree ---
412
+ server.registerTool("list_speedtree_assets", { description: "List SpeedTree .spm and .stm assets.", inputSchema: {} }, async () => json(R.listSpeedTreeAssets(projectRoot)));
413
+
414
+ // --- 39. Lottie ---
415
+ server.registerTool("list_lottie_assets", { description: "List Lottie-related JSON assets.", inputSchema: {} }, async () => json(R.listLottieAssets(projectRoot)));
416
+
417
+ // --- 40. Analytics / crash ---
418
+ server.registerTool("get_analytics_or_crash_config", { description: "Detect analytics/crash reporting services (Unity, Sentry, Crashlytics, BugSnag).", inputSchema: {} }, async () => json(R.getAnalyticsOrCrashConfig(projectRoot)));
419
+
420
+ // --- 41. Ads ---
421
+ server.registerTool("get_ads_config", { description: "Detect ad SDK presence (Unity Ads, AdMob, ironSource).", inputSchema: {} }, async () => json(R.getAdsConfig(projectRoot)));
422
+
423
+ // --- 42. Git LFS ---
424
+ server.registerTool("get_git_lfs_tracked", { description: "Get Git LFS patterns from .gitattributes.", inputSchema: {} }, async () => json(R.getGitLfsTracked(projectRoot)));
425
+
426
+ // --- 43. Plastic SCM ---
427
+ server.registerTool("get_plastic_config", { description: "Get Plastic SCM config (.plastic, workspace name).", inputSchema: {} }, async () => json(R.getPlasticConfig(projectRoot)));
428
+
329
429
  const transport = new StdioServerTransport();
330
430
  await server.connect(transport);
331
431
  }
package/src/readers.ts CHANGED
@@ -516,3 +516,285 @@ export function getProjectVersion(root: string): string {
516
516
  export function getChangelog(root: string): string | null {
517
517
  return readFileSafe(root, "CHANGELOG.md") || readFileSafe(root, "CHANGELOG") || readFileSafe(root, "changelog.md");
518
518
  }
519
+
520
+ // --- 16. Physics ---
521
+ export function getPhysicsSettings(root: string): { dynamics?: Record<string, string>; physics2d?: Record<string, string> } {
522
+ const dynamicsContent = readFileSafe(root, PROJECT_SETTINGS, "DynamicsManager.asset");
523
+ const physics2dContent = readFileSafe(root, PROJECT_SETTINGS, "Physics2DSettings.asset");
524
+ const out: { dynamics?: Record<string, string>; physics2d?: Record<string, string> } = {};
525
+ if (dynamicsContent) out.dynamics = parseUnityKeyValue(dynamicsContent);
526
+ if (physics2dContent) out.physics2d = parseUnityKeyValue(physics2dContent);
527
+ return out;
528
+ }
529
+
530
+ // --- 17. Render pipelines (URP/HDRP) ---
531
+ export function listRenderPipelines(root: string): { pipelines: string[]; volumeProfiles: string[] } {
532
+ const pipelines = listFilesRecursive(root, ASSETS, { ext: ".asset" }).filter((p) => p.toLowerCase().includes("pipeline") || p.toLowerCase().includes("render"));
533
+ const settings = join(ASSETS, "Settings");
534
+ const vol = listFilesRecursive(root, settings, { ext: ".asset" }).filter((p) => p.toLowerCase().includes("volume") || p.toLowerCase().includes("profile"));
535
+ return { pipelines: pipelines.slice(0, 50), volumeProfiles: vol.slice(0, 30) };
536
+ }
537
+
538
+ // --- 18. Timeline ---
539
+ export function listTimelinePlayables(root: string): string[] {
540
+ return listFilesRecursive(root, ASSETS, { ext: ".playable" });
541
+ }
542
+
543
+ // --- 19. Sprites / 2D ---
544
+ export function listSpriteAtlases(root: string): string[] {
545
+ return listFilesRecursive(root, ASSETS, { ext: ".spriteatlas" });
546
+ }
547
+
548
+ export function listTilemapAssets(root: string): string[] {
549
+ return listFilesRecursive(root, ASSETS, { ext: ".asset" }).filter((p) => p.toLowerCase().includes("tilemap") || p.toLowerCase().includes("tile"));
550
+ }
551
+
552
+ // --- 20. Shader Graph / VFX ---
553
+ export function listShaderGraphs(root: string): string[] {
554
+ return listFilesRecursive(root, ASSETS, { ext: ".shadergraph" });
555
+ }
556
+
557
+ export function listVfxGraphs(root: string): string[] {
558
+ return listFilesRecursive(root, ASSETS, { ext: ".vfx" });
559
+ }
560
+
561
+ // --- 21. TextMeshPro ---
562
+ export function listTmpFonts(root: string): string[] {
563
+ return listFilesRecursive(root, ASSETS, { ext: ".asset" }).filter((p) => p.toLowerCase().includes("tmp") || p.toLowerCase().includes("font"));
564
+ }
565
+
566
+ export function getTmpSettingsPath(root: string): string | null {
567
+ const paths = ["Assets/Resources/TMP Settings.asset", "Assets/TextMesh Pro/Resources/TMP Settings.asset"];
568
+ for (const p of paths) if (existsSync(join(root, p))) return p;
569
+ return null;
570
+ }
571
+
572
+ // --- 22. UI Toolkit ---
573
+ export function listUiDocuments(root: string): { uxml: string[]; uss: string[] } {
574
+ const uxml = listFilesRecursive(root, ASSETS, { ext: ".uxml" });
575
+ const uss = listFilesRecursive(root, ASSETS, { ext: ".uss" });
576
+ return { uxml, uss };
577
+ }
578
+
579
+ // --- 23. New Input System (.inputactions) ---
580
+ export function listInputActionAssets(root: string): string[] {
581
+ return listFilesRecursive(root, ASSETS, { ext: ".inputactions" });
582
+ }
583
+
584
+ export function getInputActionsSummary(root: string, path: string): { maps: string[]; actions: string[] } {
585
+ const j = readJsonSafe<{ maps?: Array<{ name: string }>; actions?: Array<{ name: string }> }>(root, path);
586
+ if (!j) return { maps: [], actions: [] };
587
+ return {
588
+ maps: (j.maps || []).map((m) => m.name),
589
+ actions: (j.actions || []).map((a) => a.name),
590
+ };
591
+ }
592
+
593
+ // --- 24. Presets ---
594
+ export function listPresets(root: string): string[] {
595
+ return listFilesRecursive(root, ASSETS, { ext: ".preset" });
596
+ }
597
+
598
+ // --- 25. Editor scripts ---
599
+ export function listEditorScripts(root: string): string[] {
600
+ const all = listScripts(root);
601
+ return all.filter((p) => p.includes("Editor") || p.toLowerCase().includes("/editor/"));
602
+ }
603
+
604
+ // --- 26. Prefab → script refs ---
605
+ export function getPrefabScriptGuids(root: string, prefabPath: string): string[] {
606
+ const content = readFileSafe(root, prefabPath);
607
+ if (!content) return [];
608
+ const guids: string[] = [];
609
+ const re = /guid:\s*([a-f0-9]{32})/g;
610
+ let m: RegExpExecArray | null;
611
+ while ((m = re.exec(content)) !== null) guids.push(m[1]);
612
+ const scriptRef = /MonoBehaviour:\s*\n[\s\S]*?m_Script:\s*\{\s*fileID:\s*\d+,\s*guid:\s*([a-f0-9]{32})/g;
613
+ while ((m = scriptRef.exec(content)) !== null) guids.push(m[1]);
614
+ return [...new Set(guids)];
615
+ }
616
+
617
+ // --- 27. Assembly dependency graph ---
618
+ export function getAssemblyDependencyGraph(root: string): { nodes: string[]; edges: [string, string][] } {
619
+ const asms = getAssemblyDefinitions(root);
620
+ const nodes = asms.map((a) => a.name);
621
+ const edges: [string, string][] = [];
622
+ for (const a of asms) for (const ref of a.references) edges.push([a.name, ref]);
623
+ return { nodes, edges };
624
+ }
625
+
626
+ // --- 28. CI configs ---
627
+ export function listCiConfigs(root: string): string[] {
628
+ const out: string[] = [];
629
+ const gh = join(root, ".github", "workflows");
630
+ if (existsSync(gh)) readdirSync(gh).filter((e) => e.endsWith(".yml") || e.endsWith(".yaml")).forEach((e) => out.push(`.github/workflows/${e}`));
631
+ if (existsSync(join(root, "Jenkinsfile"))) out.push("Jenkinsfile");
632
+ if (existsSync(join(root, "unity-cloud-build.json"))) out.push("unity-cloud-build.json");
633
+ return out;
634
+ }
635
+
636
+ // --- 29. Large assets ---
637
+ export function listLargeAssets(root: string, minSizeMb: number = 5): { path: string; sizeMb: number }[] {
638
+ const out: { path: string; sizeMb: number }[] = [];
639
+ const threshold = minSizeMb * 1024 * 1024;
640
+ const stack: string[] = [ASSETS];
641
+ while (stack.length) {
642
+ const d = stack.pop()!;
643
+ const fullD = join(root, d);
644
+ try {
645
+ for (const e of readdirSync(fullD)) {
646
+ const rel = join(d, e);
647
+ const fullPath = join(root, rel);
648
+ if (statSync(fullPath).isDirectory()) {
649
+ if (!e.startsWith(".")) stack.push(rel);
650
+ } else {
651
+ const size = statSync(fullPath).size;
652
+ if (size >= threshold) out.push({ path: rel, sizeMb: Math.round((size / 1024 / 1024) * 100) / 100 });
653
+ }
654
+ }
655
+ } catch {
656
+ /* */
657
+ }
658
+ }
659
+ return out.sort((a, b) => b.sizeMb - a.sizeMb);
660
+ }
661
+
662
+ // --- 30. PlayFab ---
663
+ export function getPlayFabConfig(root: string): { titleId?: string; configPaths: string[] } {
664
+ const configPaths: string[] = [];
665
+ const candidates = listFilesRecursive(root, ASSETS, { ext: ".asset" }).filter((p) => p.toLowerCase().includes("playfab"));
666
+ const res = listFilesRecursive(root, join(ASSETS, "Resources"), { ext: ".json" });
667
+ candidates.forEach((p) => configPaths.push(p));
668
+ res.filter((p) => p.toLowerCase().includes("playfab")).forEach((p) => configPaths.push(p));
669
+ let titleId: string | undefined;
670
+ for (const p of configPaths) {
671
+ const content = readFileSafe(root, p);
672
+ const m = content?.match(/titleId|TitleId|title_id["\s:]+([a-f0-9]{5,})/i);
673
+ if (m?.[1]) titleId = m[1];
674
+ }
675
+ return { titleId, configPaths };
676
+ }
677
+
678
+ // --- 31. Figma-related assets ---
679
+ export function listFigmaRelatedAssets(root: string): string[] {
680
+ const figmaDir = join(ASSETS, "Figma");
681
+ if (existsSync(join(root, figmaDir))) return listFilesRecursive(root, figmaDir);
682
+ return listFilesRecursive(root, ASSETS).filter((p) => p.toLowerCase().includes("figma"));
683
+ }
684
+
685
+ // --- 32. Firebase ---
686
+ export function getFirebaseConfig(root: string): { googleServicesJson?: string; plist?: string; projectId?: string } {
687
+ const plist = "GoogleService-Info.plist";
688
+ const json = "google-services.json";
689
+ const paths = [join(ASSETS, json), join(ASSETS, plist), json, plist];
690
+ let content: string | null = null;
691
+ let found: string | undefined;
692
+ for (const p of paths) {
693
+ content = readFileSafe(root, p);
694
+ if (content) { found = p; break; }
695
+ }
696
+ if (!content) return {};
697
+ const projectId = content.match(/project_id["\s:]+["']?([^"'\s]+)/i)?.[1];
698
+ return { googleServicesJson: found?.includes(".json") ? found : undefined, plist: found?.includes(".plist") ? found : undefined, projectId };
699
+ }
700
+
701
+ // --- 33. Steam ---
702
+ export function getSteamConfig(root: string): { steamAppIdTxt?: string; steamworksPath?: string } {
703
+ const appId = readFileSafe(root, "steam_appid.txt");
704
+ let steamworksPath: string | undefined;
705
+ const plug = join(ASSETS, "Plugins");
706
+ if (existsSync(join(root, plug))) {
707
+ const entries = readdirSync(join(root, plug));
708
+ if (entries.some((e) => e.toLowerCase().includes("steam"))) steamworksPath = join(plug, entries.find((e) => e.toLowerCase().includes("steam"))!);
709
+ }
710
+ return { steamAppIdTxt: appId ? "steam_appid.txt" : undefined, steamworksPath };
711
+ }
712
+
713
+ // --- 34. Discord ---
714
+ export function getDiscordConfig(root: string): { sdkPath?: string } {
715
+ const plug = join(ASSETS, "Plugins");
716
+ const full = join(root, plug);
717
+ if (!existsSync(full)) return {};
718
+ const entries = readdirSync(full);
719
+ const discord = entries.find((e) => e.toLowerCase().includes("discord"));
720
+ return discord ? { sdkPath: join(plug, discord) } : {};
721
+ }
722
+
723
+ // --- 35. FMOD ---
724
+ export function getFmodConfig(root: string): { banksPath?: string; projectPath?: string; bankFiles: string[] } {
725
+ const bankFiles = listFilesRecursive(root, ASSETS, { ext: ".bank" });
726
+ const meta = listFilesRecursive(root, ASSETS, { ext: ".meta" }).filter((p) => p.includes("FMOD") || p.includes("fmod"));
727
+ let banksPath: string | undefined;
728
+ let projectPath: string | undefined;
729
+ for (const m of meta) {
730
+ const content = readFileSafe(root, m);
731
+ if (content?.includes("bank")) banksPath = m.replace(/\.meta$/, "").replace(/[^/]+$/, "");
732
+ if (content?.includes("project")) projectPath = m.replace(/\.meta$/, "");
733
+ }
734
+ return { banksPath, projectPath, bankFiles };
735
+ }
736
+
737
+ // --- 36. Wwise ---
738
+ export function getWwiseConfig(root: string): { soundBanksPath?: string; projectPaths: string[] } {
739
+ const wproj = listFilesRecursive(root, ASSETS).filter((p) => p.endsWith(".wproj") || p.endsWith(".wwise"));
740
+ const banks = listFilesRecursive(root, ASSETS).filter((p) => p.toLowerCase().includes("soundbank") || p.toLowerCase().includes("audiobank"));
741
+ return { soundBanksPath: banks[0]?.replace(/[^/]+$/, "") || undefined, projectPaths: wproj };
742
+ }
743
+
744
+ // --- 37. Substance ---
745
+ export function listSubstanceAssets(root: string): string[] {
746
+ const sbsar = listFilesRecursive(root, ASSETS, { ext: ".sbsar" });
747
+ const sbs = listFilesRecursive(root, ASSETS, { ext: ".sbs" });
748
+ return [...sbsar, ...sbs];
749
+ }
750
+
751
+ // --- 38. SpeedTree ---
752
+ export function listSpeedTreeAssets(root: string): string[] {
753
+ return listFilesRecursive(root, ASSETS, { ext: ".spm" }).concat(listFilesRecursive(root, ASSETS, { ext: ".stm" }));
754
+ }
755
+
756
+ // --- 39. Lottie ---
757
+ export function listLottieAssets(root: string): string[] {
758
+ const lottieDir = join(ASSETS, "Lottie");
759
+ if (existsSync(join(root, lottieDir))) return listFilesRecursive(root, lottieDir, { ext: ".json" });
760
+ return listFilesRecursive(root, ASSETS, { ext: ".json" }).filter((p) => p.toLowerCase().includes("lottie"));
761
+ }
762
+
763
+ // --- 40. Analytics / crash reporting ---
764
+ export function getAnalyticsOrCrashConfig(root: string): { services: string[] } {
765
+ const services: string[] = [];
766
+ const packages = getPackages(root).dependencies.map((d) => d.name.toLowerCase());
767
+ if (packages.some((p) => p.includes("analytics"))) services.push("Unity Analytics");
768
+ if (packages.some((p) => p.includes("sentry"))) services.push("Sentry");
769
+ if (packages.some((p) => p.includes("crashlytics"))) services.push("Crashlytics");
770
+ if (packages.some((p) => p.includes("bugsnag"))) services.push("BugSnag");
771
+ const assets = listFilesRecursive(root, ASSETS).filter((p) => p.toLowerCase().includes("sentry") || p.toLowerCase().includes("crashlytics"));
772
+ if (assets.length && !services.length) services.push("Crash/Analytics (asset detected)");
773
+ return { services };
774
+ }
775
+
776
+ // --- 41. Ads ---
777
+ export function getAdsConfig(root: string): { sdkPresence: string[] } {
778
+ const packages = getPackages(root).dependencies.map((d) => d.name.toLowerCase());
779
+ const sdkPresence: string[] = [];
780
+ if (packages.some((p) => p.includes("advertisement") || p.includes("unity-ads"))) sdkPresence.push("Unity Ads");
781
+ if (packages.some((p) => p.includes("admob") || p.includes("google-mobile-ads"))) sdkPresence.push("AdMob");
782
+ if (packages.some((p) => p.includes("ironsource"))) sdkPresence.push("ironSource");
783
+ return { sdkPresence };
784
+ }
785
+
786
+ // --- 42. Git LFS ---
787
+ export function getGitLfsTracked(root: string): string[] {
788
+ const content = readFileSafe(root, ".gitattributes");
789
+ if (!content) return [];
790
+ return content.split("\n").filter((l) => l.includes("lfs") || l.includes("filter=lfs"));
791
+ }
792
+
793
+ // --- 43. Plastic SCM ---
794
+ export function getPlasticConfig(root: string): { plasticDir: boolean; workspaceName?: string } {
795
+ const plasticDir = existsSync(join(root, ".plastic"));
796
+ let workspaceName: string | undefined;
797
+ const conf = readFileSafe(root, ".plastic", "plastic.workspace");
798
+ if (conf) workspaceName = conf.match(/workspace\s+([^\n]+)/)?.[1]?.trim();
799
+ return { plasticDir, workspaceName };
800
+ }