@weppy/roblox-mcp 2.2.1 → 2.3.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 (45) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +25 -0
  3. package/README.md +4 -7
  4. package/docs/en/installation/README.md +3 -4
  5. package/docs/es/README.md +2 -5
  6. package/docs/es/installation/README.md +3 -4
  7. package/docs/id/README.md +1 -4
  8. package/docs/id/installation/README.md +3 -4
  9. package/docs/installer/assets/index-B4Gp7BPj.js +63 -0
  10. package/docs/installer/assets/index-B7mvmOPt.css +1 -0
  11. package/docs/installer/index.html +14 -0
  12. package/docs/installer/manifest.webmanifest +15 -0
  13. package/docs/installer/sw.js +7 -0
  14. package/docs/installer/wrox-icon.png +0 -0
  15. package/docs/ja/README.md +1 -4
  16. package/docs/ja/installation/README.md +2 -3
  17. package/docs/ko/README.md +4 -7
  18. package/docs/ko/installation/README.md +3 -4
  19. package/docs/pt-br/README.md +2 -5
  20. package/docs/pt-br/installation/README.md +3 -4
  21. package/install.ps1 +495 -8
  22. package/install.sh +499 -9
  23. package/llms-full.txt +14 -2
  24. package/llms.txt +9 -3
  25. package/package.json +1 -1
  26. package/plugins/weppy-roblox-mcp/.claude-plugin/plugin.json +1 -1
  27. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ChangelogDetailPage-D7eMrarv.js → ChangelogDetailPage-CGK59Jsx.js} +1 -1
  28. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ChangelogPage-DFCCRyyK.js → ChangelogPage-oNm6ratx.js} +1 -1
  29. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ConfirmModal-BmRJ2JXZ.js → ConfirmModal-Dtak3Vnq.js} +1 -1
  30. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ConnectionPage-CiaCY026.js → ConnectionPage-CjLtImxr.js} +1 -1
  31. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{InfoLabel-CCDWZLC9.js → InfoLabel-CLvjiyTG.js} +1 -1
  32. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{OverviewPage-BHpt3LI2.js → OverviewPage-BdF0Ve7h.js} +1 -1
  33. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{PlaytestPage-CNwwI5Ro.js → PlaytestPage-cQMWlAOS.js} +1 -1
  34. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{PropertyDiff-DIplDn-J.js → PropertyDiff-BnOZxkTS.js} +1 -1
  35. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{SettingsPage-CPqQYZPN.js → SettingsPage-C-QX0AY-.js} +1 -1
  36. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{StatusBadge-C8VKAPpk.js → StatusBadge-A9U9m2LQ.js} +1 -1
  37. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{SyncPage-DTSKbpio.js → SyncPage-BAS0cXRM.js} +1 -1
  38. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{TierComparison-7ofkPwj3.js → TierComparison-BA_L4c9p.js} +1 -1
  39. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{TierPromoProgress-SnRUjAPh.js → TierPromoProgress-Dq6ofjr2.js} +1 -1
  40. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{ToolsPage-CrdNh3D9.js → ToolsPage-C_tMIyix.js} +1 -1
  41. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{index-DGGmfli1.js → index-OH9mpHwW.js} +2 -2
  42. package/plugins/weppy-roblox-mcp/dashboard/dist/assets/{useLiveUptime-BnXeLpOw.js → useLiveUptime-Df1ECedb.js} +1 -1
  43. package/plugins/weppy-roblox-mcp/dashboard/dist/index.html +1 -1
  44. package/plugins/weppy-roblox-mcp/dist/index.js +63 -65
  45. package/plugins/weppy-roblox-mcp/roblox-plugin/WeppyRobloxMCP.rbxm +0 -0
@@ -16,7 +16,7 @@ AIアプリが「青いパーツを作って」と言うと、MCPサーバーが
16
16
 
17
17
  ## ワンラインインストール(推奨)
18
18
 
19
- MCPサーバー、Roblox Studioプラグイン、AIアプリへの登録を一度に完了します:
19
+ 1つのコマンドで導入できます:
20
20
 
21
21
  **macOS / Linux**
22
22
 
@@ -32,8 +32,7 @@ irm https://raw.githubusercontent.com/hope1026/weppy-roblox-mcp/main/install.ps1
32
32
 
33
33
  Roblox Studioを再起動したら完了です!
34
34
 
35
- 自動でMCP登録できるのは現在 **Claude Code, Claude Desktop, Cursor, Codex CLI, Gemini CLI** です。
36
- **Codex App** と **Antigravity** はスクリプト完了後に手動設定が必要です。
35
+ 自動MCP登録は **Claude Code, Claude Desktop, Cursor, Codex CLI/App, Gemini CLI, Antigravity** に対応しています。
37
36
 
38
37
  WindowsでPowerShellの実行がブロックされる場合は、下の手動インストールに進んでください。ZIPパッケージを使う場合は `setup-plugin.bat` と `setup-mcp.bat` を実行できます。
39
38
 
package/docs/ko/README.md CHANGED
@@ -18,8 +18,6 @@ Claude, Codex, Gemini 같은 AI 코딩 에이전트는 강력하지만 — Roblo
18
18
 
19
19
  ## 빠른 설치
20
20
 
21
- **원라인 설치** — MCP 서버, Roblox Studio 플러그인, AI 앱 등록을 한 번에 진행합니다:
22
-
23
21
  **macOS / Linux**
24
22
 
25
23
  ```bash
@@ -32,16 +30,15 @@ curl -fsSL https://raw.githubusercontent.com/hope1026/weppy-roblox-mcp/main/inst
32
30
  irm https://raw.githubusercontent.com/hope1026/weppy-roblox-mcp/main/install.ps1 | iex
33
31
  ```
34
32
 
35
- Roblox Studio를 재시작하면 완료입니다!
33
+ AI 앱을 다시 열고 Roblox Studio를 재시작하세요.
36
34
 
37
- 자동 MCP 등록은 현재 **Claude Code, Claude Desktop, Cursor, Codex CLI, Gemini CLI**만 지원합니다.
38
- **Codex App**과 **Antigravity**는 스크립트 완료 후 수동 설정이 필요합니다.
35
+ 자동 MCP 등록은 **Claude Code, Claude Desktop, Cursor, Codex CLI/App, Gemini CLI, Antigravity**를 지원합니다.
39
36
 
40
- Windows에서 PowerShell 실행이 차단되면 아래 수동 설치로 진행하세요. ZIP 패키지를 사용하는 경우 `setup-plugin.bat`, `setup-mcp.bat`도 사용할 수 있습니다.
37
+ Windows에서 PowerShell 실행이 차단되면 아래 설치 가이드를 사용하세요. ZIP 패키지를 사용하는 경우 `setup-plugin.bat`, `setup-mcp.bat`도 사용할 수 있습니다.
41
38
 
42
39
  ### 수동 설치
43
40
 
44
- 원라인 설치가 동작하지 않거나 환경상 자동 설치를 사용할 수 없는 경우, 아래 수동 설치를 대안으로 진행하세요.
41
+ 원라인 설치가 동작하지 않거나 환경상 자동 설치를 사용할 수 없는 경우, 아래 설치 가이드를 대안으로 진행하세요.
45
42
 
46
43
  **1단계** — Roblox Studio 플러그인 설치 (Studio와 AI를 연결하는 다리):
47
44
  [플러그인 설치 가이드](installation/roblox-plugin.md)
@@ -16,7 +16,7 @@ AI 앱이 "파란 파트를 만들어줘"라고 하면, MCP 서버가 이 요청
16
16
 
17
17
  ## 원라인 설치 (권장)
18
18
 
19
- MCP 서버, Roblox Studio 플러그인, AI 등록을 한 번에 진행합니다:
19
+ 아래 명령어 줄로 MCP 서버와 플러그인을 한 번에 설치합니다:
20
20
 
21
21
  **macOS / Linux**
22
22
 
@@ -30,12 +30,11 @@ curl -fsSL https://raw.githubusercontent.com/hope1026/weppy-roblox-mcp/main/inst
30
30
  irm https://raw.githubusercontent.com/hope1026/weppy-roblox-mcp/main/install.ps1 | iex
31
31
  ```
32
32
 
33
- Roblox Studio를 재시작하면 완료입니다!
33
+ AI 앱을 다시 열고 Roblox Studio를 재시작하세요.
34
34
 
35
35
  설치 중 오류가 발생하면 임시 로그 파일 경로를 출력하고, 대화형 실행에서는 종료 전에 콘솔을 유지해서 원인을 확인할 수 있습니다.
36
36
 
37
- 자동 MCP 등록은 현재 **Claude Code, Claude Desktop, Cursor, Codex CLI, Gemini CLI**만 지원합니다.
38
- **Codex App**과 **Antigravity**는 스크립트 완료 후 수동 설정이 필요합니다.
37
+ 자동 MCP 등록은 **Claude Code, Claude Desktop, Cursor, Codex CLI/App, Gemini CLI, Antigravity**를 지원합니다.
39
38
 
40
39
  Windows에서 PowerShell 실행이 차단되면 아래 수동 설치로 진행하세요. ZIP 패키지를 사용하는 경우 `setup-plugin.bat`, `setup-mcp.bat`를 실행할 수 있습니다.
41
40
 
@@ -18,8 +18,6 @@ Sem necessidade de copiar e colar código. A IA trabalha e você confere os resu
18
18
 
19
19
  ## Instalacao rapida
20
20
 
21
- **Instalacao em um comando** — Instala o servidor MCP, o plugin do Roblox Studio e registra nos apps de IA em um único passo:
22
-
23
21
  **macOS / Linux**
24
22
 
25
23
  ```bash
@@ -34,14 +32,13 @@ irm https://raw.githubusercontent.com/hope1026/weppy-roblox-mcp/main/install.ps1
34
32
 
35
33
  Reinicie o Roblox Studio — pronto!
36
34
 
37
- O registro automático de MCP atualmente suporta **Claude Code, Claude Desktop, Cursor, Codex CLI e Gemini CLI**.
38
- **Codex App** e **Antigravity** ainda exigem configuração manual depois que o script terminar.
35
+ O registro automático de MCP suporta **Claude Code, Claude Desktop, Cursor, Codex CLI/App, Gemini CLI e Antigravity**.
39
36
 
40
37
  Se a execução do PowerShell estiver bloqueada no Windows, siga a instalação manual abaixo. Se estiver usando o pacote ZIP, você também pode usar `setup-plugin.bat` e `setup-mcp.bat`.
41
38
 
42
39
  ### Instalacao manual
43
40
 
44
- Se a instalação em um comando não funcionar, ou se a instalação automática não puder ser usada no seu ambiente, use a instalação manual abaixo como alternativa.
41
+ Se a instalacao em um comando nao funcionar, ou se a instalacao automatica nao puder ser usada no seu ambiente, use a instalacao manual abaixo como alternativa.
45
42
 
46
43
  **Passo 1** — Instale o plugin do Roblox Studio (ponte entre Studio e IA):
47
44
  [Guia de instalacao do plugin](installation/roblox-plugin.md)
@@ -16,7 +16,7 @@ Quando o app de IA diz "cria uma parte azul", o servidor MCP converte o pedido e
16
16
 
17
17
  ## Instalação em um comando (Recomendado)
18
18
 
19
- Instala o servidor MCP, o plugin do Roblox Studio e registra nos apps de IA em um único passo:
19
+ Instale tudo com um único comando:
20
20
 
21
21
  **macOS / Linux**
22
22
 
@@ -32,8 +32,7 @@ irm https://raw.githubusercontent.com/hope1026/weppy-roblox-mcp/main/install.ps1
32
32
 
33
33
  Reinicie o Roblox Studio — pronto!
34
34
 
35
- O registro automático de MCP atualmente suporta **Claude Code, Claude Desktop, Cursor, Codex CLI e Gemini CLI**.
36
- **Codex App** e **Antigravity** ainda exigem configuração manual depois que o script terminar.
35
+ O registro automático de MCP suporta **Claude Code, Claude Desktop, Cursor, Codex CLI/App, Gemini CLI e Antigravity**.
37
36
 
38
37
  Se a execução do PowerShell estiver bloqueada no Windows, siga a instalação manual abaixo. Se estiver usando o pacote ZIP, você também pode executar `setup-plugin.bat` e `setup-mcp.bat`.
39
38
 
@@ -41,7 +40,7 @@ Se a execução do PowerShell estiver bloqueada no Windows, siga a instalação
41
40
 
42
41
  ## Instalação manual
43
42
 
44
- Este é o método alternativo para quando a instalação em um comando não funcionar, ou quando a instalação automática não puder ser usada no seu ambiente.
43
+ Se a instalacao em um comando nao funcionar, ou quando a instalacao automatica nao puder ser usada no seu ambiente, use a instalacao manual abaixo como alternativa.
45
44
 
46
45
  ### Passo 1: Instalar plugin do Roblox Studio
47
46
 
package/install.ps1 CHANGED
@@ -139,12 +139,475 @@ function Test-McpJsonConfigured($configPath) {
139
139
  }
140
140
  }
141
141
 
142
+ # Antigravity 설정에 canonical mcpServers 래퍼로 MCP 서버를 추가하고 legacy flat key를 정리
143
+ function Add-AntigravityMcpConfig($configPath) {
144
+ $parentDir = Split-Path $configPath -Parent
145
+ if (-not (Test-Path $parentDir)) { New-Item -ItemType Directory -Path $parentDir -Force | Out-Null }
146
+ $env:MCP_CONFIG_PATH = $configPath
147
+ try {
148
+ node -e @"
149
+ const fs = require('fs');
150
+ const configPath = process.env.MCP_CONFIG_PATH;
151
+ let config = {};
152
+ try { config = JSON.parse(fs.readFileSync(configPath, 'utf8')); } catch {}
153
+ if (!config || typeof config !== 'object' || Array.isArray(config)) {
154
+ config = {};
155
+ }
156
+ const mcpServers = config.mcpServers;
157
+ if (mcpServers !== undefined && (typeof mcpServers !== 'object' || mcpServers === null || Array.isArray(mcpServers))) {
158
+ throw new Error('Antigravity mcpServers must be an object');
159
+ }
160
+ const next = { ...config };
161
+ delete next['weppy-roblox-mcp'];
162
+ next.mcpServers = {
163
+ ...(mcpServers || {}),
164
+ 'weppy-roblox-mcp': { command: 'npx', args: ['-y', '@weppy/roblox-mcp'] }
165
+ };
166
+ config = next;
167
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
168
+ "@
169
+ } finally {
170
+ Remove-Item Env:\MCP_CONFIG_PATH -ErrorAction SilentlyContinue
171
+ }
172
+ }
173
+
174
+ function Test-AntigravityMcpConfigured($configPath) {
175
+ if (-not (Test-Path $configPath)) {
176
+ return $false
177
+ }
178
+
179
+ try {
180
+ $config = Get-Content -Path $configPath -Raw | ConvertFrom-Json
181
+ $hasLegacyFlatKey = $config.PSObject.Properties.Name -contains 'weppy-roblox-mcp'
182
+ $server = $config.mcpServers.'weppy-roblox-mcp'
183
+ $hasCanonicalArgs = ($server.args -is [System.Array]) -and ($server.args.Count -eq 2) -and ($server.args[0] -eq '-y') -and ($server.args[1] -eq '@weppy/roblox-mcp')
184
+ return ($server.command -eq 'npx') -and $hasCanonicalArgs -and (-not $hasLegacyFlatKey)
185
+ }
186
+ catch {
187
+ return $false
188
+ }
189
+ }
190
+
142
191
  function Test-CodexConfigConfigured($configPath) {
143
192
  if (-not (Test-Path $configPath)) {
144
193
  return $false
145
194
  }
146
195
 
147
- return Select-String -Path $configPath -Pattern '^\s*\[mcp_servers\.weppy-roblox-mcp\]\s*$' -Quiet
196
+ $env:MCP_CODEX_CONFIG_PATH = $configPath
197
+ try {
198
+ node -e @"
199
+ const fs = require('fs');
200
+
201
+ const configPath = process.env.MCP_CODEX_CONFIG_PATH;
202
+ const serverName = 'weppy-roblox-mcp';
203
+ const expectedCommand = 'npx';
204
+ const expectedArgs = ['-y', '@weppy/roblox-mcp'];
205
+ const headerPattern = new RegExp(
206
+ '^\\s*\\[\\s*mcp_servers\\.' + serverName.replace(/[.*+?^${}()|[\]\\\\]/g, '\\$&') + '\\s*\\]\\s*(?:#.*)?$'
207
+ );
208
+
209
+ function stripCommentOutsideStrings(line) {
210
+ let inSingle = false;
211
+ let inDouble = false;
212
+ let escaped = false;
213
+
214
+ for (let index = 0; index < line.length; index += 1) {
215
+ const char = line[index];
216
+
217
+ if (char === '"' && !inSingle && !escaped) {
218
+ inDouble = !inDouble;
219
+ } else if (char === "'" && !inDouble && !escaped) {
220
+ inSingle = !inSingle;
221
+ } else if (char === '#' && !inSingle && !inDouble) {
222
+ return line.slice(0, index).trimEnd();
223
+ }
224
+
225
+ escaped = char === '\\' && !escaped;
226
+ if (char !== '\\') {
227
+ escaped = false;
228
+ }
229
+ }
230
+
231
+ return line.trimEnd();
232
+ }
233
+
234
+ function countTripleQuoteToggles(line, quote) {
235
+ let count = 0;
236
+ let inSingle = false;
237
+ let inDouble = false;
238
+ let escaped = false;
239
+
240
+ for (let index = 0; index < line.length; index += 1) {
241
+ const char = line[index] ?? '';
242
+ const nextThree = line.slice(index, index + 3);
243
+ const isOutsideStrings = !inSingle && !inDouble;
244
+
245
+ if (isOutsideStrings && nextThree === quote.repeat(3)) {
246
+ count += 1;
247
+ index += 2;
248
+ escaped = false;
249
+ continue;
250
+ }
251
+
252
+ if (char === '"' && !inSingle && !escaped) {
253
+ inDouble = !inDouble;
254
+ } else if (char === "'" && !inDouble && !escaped) {
255
+ inSingle = !inSingle;
256
+ } else if (char === '#' && !inSingle && !inDouble) {
257
+ break;
258
+ }
259
+
260
+ escaped = char === '\\' && !escaped;
261
+ if (char !== '\\') {
262
+ escaped = false;
263
+ }
264
+ }
265
+
266
+ return count;
267
+ }
268
+
269
+ function advanceTripleQuoteState(line, state) {
270
+ const next = { ...state };
271
+ const tripleDoubleCount = countTripleQuoteToggles(line, '"');
272
+ const tripleSingleCount = countTripleQuoteToggles(line, "'");
273
+
274
+ if (!next.inTripleSingle && tripleDoubleCount % 2 === 1) {
275
+ next.inTripleDouble = !next.inTripleDouble;
276
+ }
277
+
278
+ if (!next.inTripleDouble && tripleSingleCount % 2 === 1) {
279
+ next.inTripleSingle = !next.inTripleSingle;
280
+ }
281
+
282
+ return next;
283
+ }
284
+
285
+ function isTomlTableHeaderLine(line) {
286
+ const normalized = stripCommentOutsideStrings(line).trim();
287
+
288
+ if (normalized.length === 0) {
289
+ return false;
290
+ }
291
+
292
+ return /^\[\[.*\]\]$/.test(normalized) || /^\[.*\]$/.test(normalized);
293
+ }
294
+
295
+ function findAllCodexBlocks(source) {
296
+ const lines = source.split('\n');
297
+ const blocks = [];
298
+ let activeLines = null;
299
+ let state = {
300
+ inTripleDouble: false,
301
+ inTripleSingle: false,
302
+ };
303
+
304
+ for (const line of lines) {
305
+ const isHeaderCandidate = !state.inTripleDouble && !state.inTripleSingle && isTomlTableHeaderLine(line);
306
+ const isCodexHeader = isHeaderCandidate && headerPattern.test(line);
307
+
308
+ if (isCodexHeader) {
309
+ if (activeLines !== null) {
310
+ blocks.push(activeLines.join('\n').trim());
311
+ }
312
+ activeLines = [line];
313
+ } else if (activeLines !== null && isHeaderCandidate) {
314
+ blocks.push(activeLines.join('\n').trim());
315
+ activeLines = null;
316
+ } else if (activeLines !== null) {
317
+ activeLines.push(line);
318
+ }
319
+
320
+ state = advanceTripleQuoteState(line, state);
321
+ }
322
+
323
+ if (activeLines !== null) {
324
+ blocks.push(activeLines.join('\n').trim());
325
+ }
326
+
327
+ return blocks;
328
+ }
329
+
330
+ function parseStringAssignment(value, key) {
331
+ const match = new RegExp('^\\s*' + key + '\\s*=\\s*(["\\'])([^"\\']+)\\1\\s*$').exec(value);
332
+ return match ? match[2] : null;
333
+ }
334
+
335
+ function parseTomlStringArray(value) {
336
+ const match = /^\s*args\s*=\s*\[(.*)\]\s*$/ms.exec(value.trim());
337
+
338
+ if (match === null) {
339
+ return null;
340
+ }
341
+
342
+ const body = match[1] ?? '';
343
+ const values = [];
344
+ let cursor = 0;
345
+ let expectValue = true;
346
+
347
+ while (cursor < body.length) {
348
+ while (cursor < body.length && /\s/.test(body[cursor] ?? '')) {
349
+ cursor += 1;
350
+ }
351
+
352
+ if (cursor >= body.length) {
353
+ break;
354
+ }
355
+
356
+ if (!expectValue) {
357
+ if (body[cursor] !== ',') {
358
+ return null;
359
+ }
360
+ cursor += 1;
361
+ expectValue = true;
362
+ continue;
363
+ }
364
+
365
+ const quote = body[cursor];
366
+ if (quote !== '"' && quote !== "'") {
367
+ return null;
368
+ }
369
+
370
+ cursor += 1;
371
+ let token = '';
372
+ let escaped = false;
373
+
374
+ while (cursor < body.length) {
375
+ const char = body[cursor] ?? '';
376
+
377
+ if (char === quote && !escaped) {
378
+ cursor += 1;
379
+ values.push(token);
380
+ break;
381
+ }
382
+
383
+ token += char;
384
+ escaped = char === '\\' && !escaped;
385
+ if (char !== '\\') {
386
+ escaped = false;
387
+ }
388
+ cursor += 1;
389
+ }
390
+
391
+ if (cursor > body.length) {
392
+ return null;
393
+ }
394
+
395
+ expectValue = false;
396
+ }
397
+
398
+ const leftover = body.slice(cursor).trim();
399
+ if (leftover === ',') {
400
+ return values;
401
+ }
402
+
403
+ return leftover.length === 0 ? values : null;
404
+ }
405
+
406
+ function collectArrayLines(lines, startIndex) {
407
+ const collected = [stripCommentOutsideStrings(lines[startIndex] ?? '')];
408
+ let bracketDepth = 0;
409
+ let inSingle = false;
410
+ let inDouble = false;
411
+ let escaped = false;
412
+
413
+ for (let lineIndex = startIndex; lineIndex < lines.length; lineIndex += 1) {
414
+ const sanitized = stripCommentOutsideStrings(lines[lineIndex] ?? '');
415
+ if (lineIndex !== startIndex) {
416
+ collected.push(sanitized);
417
+ }
418
+
419
+ for (let index = 0; index < sanitized.length; index += 1) {
420
+ const char = sanitized[index] ?? '';
421
+
422
+ if (char === '"' && !inSingle && !escaped) {
423
+ inDouble = !inDouble;
424
+ } else if (char === "'" && !inDouble && !escaped) {
425
+ inSingle = !inSingle;
426
+ } else if (!inSingle && !inDouble) {
427
+ if (char === '[') {
428
+ bracketDepth += 1;
429
+ } else if (char === ']') {
430
+ bracketDepth -= 1;
431
+ }
432
+ }
433
+
434
+ escaped = char === '\\' && !escaped;
435
+ if (char !== '\\') {
436
+ escaped = false;
437
+ }
438
+ }
439
+
440
+ if (bracketDepth <= 0) {
441
+ return {
442
+ nextIndex: lineIndex,
443
+ text: collected.join('\n'),
444
+ };
445
+ }
446
+ }
447
+
448
+ return null;
449
+ }
450
+
451
+ function parseCodexBlock(blockContent) {
452
+ const lines = blockContent.split('\n');
453
+ let command = null;
454
+ let args = null;
455
+ let hasConflict = false;
456
+ let inTripleDouble = false;
457
+ let inTripleSingle = false;
458
+
459
+ for (let index = 1; index < lines.length; index += 1) {
460
+ const line = lines[index] ?? '';
461
+ const sanitized = stripCommentOutsideStrings(line);
462
+ const trimmed = sanitized.trim();
463
+
464
+ if (inTripleDouble) {
465
+ if (countTripleQuoteToggles(sanitized, '"') % 2 === 1) {
466
+ inTripleDouble = false;
467
+ }
468
+ continue;
469
+ }
470
+
471
+ if (inTripleSingle) {
472
+ if (countTripleQuoteToggles(sanitized, "'") % 2 === 1) {
473
+ inTripleSingle = false;
474
+ }
475
+ continue;
476
+ }
477
+
478
+ if (countTripleQuoteToggles(sanitized, '"') % 2 === 1) {
479
+ inTripleDouble = true;
480
+ continue;
481
+ }
482
+
483
+ if (countTripleQuoteToggles(sanitized, "'") % 2 === 1) {
484
+ inTripleSingle = true;
485
+ continue;
486
+ }
487
+
488
+ if (trimmed.length === 0) {
489
+ continue;
490
+ }
491
+
492
+ if (/^command\s*=/.test(trimmed)) {
493
+ const parsedCommand = parseStringAssignment(trimmed, 'command');
494
+ if (command !== null || parsedCommand === null) {
495
+ hasConflict = true;
496
+ } else {
497
+ command = parsedCommand;
498
+ }
499
+ continue;
500
+ }
501
+
502
+ if (/^args\s*=/.test(trimmed)) {
503
+ const collected = collectArrayLines(lines, index);
504
+ const parsedArgs = collected === null ? null : parseTomlStringArray(collected.text);
505
+
506
+ if (args !== null || parsedArgs === null || collected === null) {
507
+ hasConflict = true;
508
+ } else {
509
+ args = parsedArgs;
510
+ index = collected.nextIndex;
511
+ }
512
+ }
513
+ }
514
+
515
+ return {
516
+ args,
517
+ command,
518
+ hasConflict,
519
+ };
520
+ }
521
+
522
+ function isStructurallySafe(source) {
523
+ let bracketDepth = 0;
524
+ let braceDepth = 0;
525
+ let inSingle = false;
526
+ let inDouble = false;
527
+ let escaped = false;
528
+ let tripleState = {
529
+ inTripleDouble: false,
530
+ inTripleSingle: false,
531
+ };
532
+
533
+ for (const line of source.split('\n')) {
534
+ tripleState = advanceTripleQuoteState(line, tripleState);
535
+
536
+ for (let index = 0; index < line.length; index += 1) {
537
+ const char = line[index] ?? '';
538
+
539
+ if (!inSingle && !inDouble && char === '#') {
540
+ break;
541
+ }
542
+
543
+ if (char === '"' && !inSingle && !escaped) {
544
+ inDouble = !inDouble;
545
+ } else if (char === "'" && !inDouble && !escaped) {
546
+ inSingle = !inSingle;
547
+ } else if (!inSingle && !inDouble) {
548
+ if (char === '[') {
549
+ bracketDepth += 1;
550
+ } else if (char === ']') {
551
+ bracketDepth -= 1;
552
+ if (bracketDepth < 0) {
553
+ return false;
554
+ }
555
+ } else if (char === '{') {
556
+ braceDepth += 1;
557
+ } else if (char === '}') {
558
+ braceDepth -= 1;
559
+ if (braceDepth < 0) {
560
+ return false;
561
+ }
562
+ }
563
+ }
564
+
565
+ escaped = char === '\\' && !escaped;
566
+ if (char !== '\\') {
567
+ escaped = false;
568
+ }
569
+ }
570
+ }
571
+
572
+ return (
573
+ !tripleState.inTripleDouble &&
574
+ !tripleState.inTripleSingle &&
575
+ bracketDepth === 0 &&
576
+ braceDepth === 0 &&
577
+ !inSingle &&
578
+ !inDouble
579
+ );
580
+ }
581
+
582
+ try {
583
+ const source = fs.readFileSync(configPath, 'utf8');
584
+ if (!isStructurallySafe(source)) {
585
+ process.exit(1);
586
+ }
587
+
588
+ const blocks = findAllCodexBlocks(source);
589
+ if (blocks.length !== 1) {
590
+ process.exit(1);
591
+ }
592
+
593
+ const parsed = parseCodexBlock(blocks[0]);
594
+ const isConfigured =
595
+ !parsed.hasConflict &&
596
+ parsed.command === expectedCommand &&
597
+ Array.isArray(parsed.args) &&
598
+ parsed.args.length === expectedArgs.length &&
599
+ parsed.args.every((entry, index) => entry === expectedArgs[index]);
600
+
601
+ process.exit(isConfigured ? 0 : 1);
602
+ } catch {
603
+ process.exit(1);
604
+ }
605
+ "@
606
+ return $LASTEXITCODE -eq 0
607
+ }
608
+ finally {
609
+ Remove-Item Env:\MCP_CODEX_CONFIG_PATH -ErrorAction SilentlyContinue
610
+ }
148
611
  }
149
612
 
150
613
  # Add MCP server to JSON config file (PowerShell 5.1 compatible — edits JSON via node)
@@ -257,8 +720,7 @@ else {
257
720
  # [3/3] Register MCP with AI apps
258
721
  # ═══════════════════════════════════
259
722
  Write-Step "3/3" "Register MCP with AI apps"
260
- Write-Host " Automatic registration: Claude Code, Claude Desktop, Cursor, Codex CLI, Gemini CLI"
261
- Write-Host " Manual setup required: Codex App, Antigravity"
723
+ Write-Host " Automatic registration: Claude Code, Claude Desktop, Cursor, Codex CLI/App, Gemini CLI, Antigravity"
262
724
 
263
725
  $detectedNames = @()
264
726
  $detectedTypes = @()
@@ -306,15 +768,15 @@ $codexConfigured = Test-CodexConfigConfigured $codexConfig
306
768
  $codexCliCommand = Resolve-OptionalCliCommand 'codex'
307
769
 
308
770
  if ($codexConfigured) {
309
- $detectedNames += 'Codex CLI (configured)'
771
+ $detectedNames += 'Codex CLI/App (configured)'
310
772
  $detectedTypes += 'codex-cli'
311
773
  }
312
774
  elseif ($codexCliCommand) {
313
- $detectedNames += 'Codex CLI'
775
+ $detectedNames += 'Codex CLI/App'
314
776
  $detectedTypes += 'codex-cli'
315
777
  }
316
778
  else {
317
- $notDetected += 'Codex CLI (not found)'
779
+ $notDetected += 'Codex CLI/App (not found)'
318
780
  }
319
781
 
320
782
  # Gemini CLI
@@ -334,6 +796,23 @@ else {
334
796
  $notDetected += "Gemini CLI (not found)"
335
797
  }
336
798
 
799
+ # Antigravity (unofficial path, auto-register if found)
800
+ $antigravityConfig = Join-Path $env:USERPROFILE '.gemini\antigravity\mcp_config.json'
801
+ $antigravityConfigured = Test-AntigravityMcpConfigured $antigravityConfig
802
+ $antigravityDirExists = Test-Path (Join-Path $env:USERPROFILE '.gemini\antigravity')
803
+
804
+ if ($antigravityConfigured) {
805
+ $detectedNames += 'Antigravity (configured)'
806
+ $detectedTypes += 'antigravity'
807
+ }
808
+ elseif ((Test-Path $antigravityConfig) -or $antigravityDirExists) {
809
+ $detectedNames += 'Antigravity'
810
+ $detectedTypes += 'antigravity'
811
+ }
812
+ else {
813
+ $notDetected += 'Antigravity (not found)'
814
+ }
815
+
337
816
  if ($detectedNames.Count -eq 0) {
338
817
  Write-Warn "No AI apps detected"
339
818
  Write-Info "Register MCP server manually: npx -y @weppy/roblox-mcp"
@@ -429,6 +908,15 @@ else {
429
908
  Write-Ok "Registered: $appName"
430
909
  }
431
910
  }
911
+ "antigravity" {
912
+ if ($antigravityConfigured) {
913
+ Write-Ok "Already configured: $appName"
914
+ }
915
+ else {
916
+ Add-AntigravityMcpConfig $antigravityConfig
917
+ Write-Ok "Registered: $appName"
918
+ }
919
+ }
432
920
  }
433
921
  }
434
922
  catch {
@@ -449,8 +937,7 @@ Write-Host " 1. Restart Roblox Studio"
449
937
  Write-Host " 2. Look for the WROX button in the Plugins tab"
450
938
  Write-Host " 3. Click Connect and start building with AI!"
451
939
  Write-Host ""
452
- Write-Host " Auto registration: Claude Code, Claude Desktop, Cursor, Codex CLI, Gemini CLI"
453
- Write-Host " Manual setup required: Codex App, Antigravity"
940
+ Write-Host " Auto registration: Claude Code, Claude Desktop, Cursor, Codex CLI/App, Gemini CLI, Antigravity"
454
941
  Write-Host ""
455
942
  Write-Host " Docs: https://github.com/hope1026/weppy-roblox-mcp" -ForegroundColor DarkGray
456
943
  Write-Host ""