@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
package/install.sh CHANGED
@@ -117,11 +117,479 @@ try {
117
117
  ' >/dev/null 2>&1
118
118
  }
119
119
 
120
+ # Antigravity 설정에 canonical mcpServers 래퍼로 MCP 서버를 추가하고 legacy flat key를 정리
121
+ add_antigravity_mcp_config() {
122
+ local config_path="$1"
123
+ local parent_dir
124
+ parent_dir=$(dirname "$config_path")
125
+ mkdir -p "$parent_dir"
126
+ MCP_CONFIG_PATH="$config_path" node -e '
127
+ const fs = require("fs");
128
+ const configPath = process.env.MCP_CONFIG_PATH;
129
+ let config = {};
130
+ try { config = JSON.parse(fs.readFileSync(configPath, "utf8")); } catch {}
131
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
132
+ config = {};
133
+ }
134
+ const mcpServers = config.mcpServers;
135
+ if (mcpServers !== undefined && (typeof mcpServers !== "object" || mcpServers === null || Array.isArray(mcpServers))) {
136
+ throw new Error("Antigravity mcpServers must be an object");
137
+ }
138
+ const next = { ...config };
139
+ delete next["weppy-roblox-mcp"];
140
+ next.mcpServers = {
141
+ ...(mcpServers || {}),
142
+ "weppy-roblox-mcp": { command: "npx", args: ["-y", "@weppy/roblox-mcp"] }
143
+ };
144
+ config = next;
145
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
146
+ '
147
+ }
148
+
149
+ is_antigravity_mcp_configured() {
150
+ local config_path="$1"
151
+
152
+ [ -f "$config_path" ] || return 1
153
+
154
+ MCP_CONFIG_PATH="$config_path" node -e '
155
+ const fs = require("fs");
156
+ const configPath = process.env.MCP_CONFIG_PATH;
157
+ function isJsonObject(value) {
158
+ return typeof value === "object" && value !== null && !Array.isArray(value);
159
+ }
160
+ function hasExpectedCommandShape(value) {
161
+ return (
162
+ isJsonObject(value) &&
163
+ value.command === "npx" &&
164
+ Array.isArray(value.args) &&
165
+ value.args.length === 2 &&
166
+ value.args[0] === "-y" &&
167
+ value.args[1] === "@weppy/roblox-mcp"
168
+ );
169
+ }
170
+ try {
171
+ const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
172
+ const canonical = config?.mcpServers?.["weppy-roblox-mcp"];
173
+ const hasLegacyFlatKey = Object.prototype.hasOwnProperty.call(config, "weppy-roblox-mcp");
174
+ process.exit(hasExpectedCommandShape(canonical) && !hasLegacyFlatKey ? 0 : 1);
175
+ } catch {
176
+ process.exit(1);
177
+ }
178
+ ' >/dev/null 2>&1
179
+ }
180
+
120
181
  is_codex_config_configured() {
121
182
  local config_path="$1"
122
183
 
123
184
  [ -f "$config_path" ] || return 1
124
- grep -Eq '^[[:space:]]*\[mcp_servers\.weppy-roblox-mcp\][[:space:]]*$' "$config_path"
185
+ MCP_CODEX_CONFIG_PATH="$config_path" node <<'NODE' >/dev/null 2>&1
186
+ const fs = require("fs");
187
+
188
+ const configPath = process.env.MCP_CODEX_CONFIG_PATH;
189
+ const serverName = "weppy-roblox-mcp";
190
+ const expectedCommand = "npx";
191
+ const expectedArgs = ["-y", "@weppy/roblox-mcp"];
192
+ const headerPattern = new RegExp(
193
+ "^\\s*\\[\\s*mcp_servers\\." + serverName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\s*\\]\\s*(?:#.*)?$",
194
+ );
195
+
196
+ function stripCommentOutsideStrings(line) {
197
+ let inSingle = false;
198
+ let inDouble = false;
199
+ let escaped = false;
200
+
201
+ for (let index = 0; index < line.length; index += 1) {
202
+ const char = line[index];
203
+
204
+ if (char === '"' && !inSingle && !escaped) {
205
+ inDouble = !inDouble;
206
+ } else if (char === "'" && !inDouble && !escaped) {
207
+ inSingle = !inSingle;
208
+ } else if (char === "#" && !inSingle && !inDouble) {
209
+ return line.slice(0, index).trimEnd();
210
+ }
211
+
212
+ escaped = char === "\\" && !escaped;
213
+ if (char !== "\\") {
214
+ escaped = false;
215
+ }
216
+ }
217
+
218
+ return line.trimEnd();
219
+ }
220
+
221
+ function countTripleQuoteToggles(line, quote) {
222
+ let count = 0;
223
+ let inSingle = false;
224
+ let inDouble = false;
225
+ let escaped = false;
226
+
227
+ for (let index = 0; index < line.length; index += 1) {
228
+ const char = line[index] ?? "";
229
+ const nextThree = line.slice(index, index + 3);
230
+ const isOutsideStrings = !inSingle && !inDouble;
231
+
232
+ if (isOutsideStrings && nextThree === quote.repeat(3)) {
233
+ count += 1;
234
+ index += 2;
235
+ escaped = false;
236
+ continue;
237
+ }
238
+
239
+ if (char === '"' && !inSingle && !escaped) {
240
+ inDouble = !inDouble;
241
+ } else if (char === "'" && !inDouble && !escaped) {
242
+ inSingle = !inSingle;
243
+ } else if (char === "#" && !inSingle && !inDouble) {
244
+ break;
245
+ }
246
+
247
+ escaped = char === "\\" && !escaped;
248
+ if (char !== "\\") {
249
+ escaped = false;
250
+ }
251
+ }
252
+
253
+ return count;
254
+ }
255
+
256
+ function advanceTripleQuoteState(line, state) {
257
+ const next = { ...state };
258
+ const tripleDoubleCount = countTripleQuoteToggles(line, '"');
259
+ const tripleSingleCount = countTripleQuoteToggles(line, "'");
260
+
261
+ if (!next.inTripleSingle && tripleDoubleCount % 2 === 1) {
262
+ next.inTripleDouble = !next.inTripleDouble;
263
+ }
264
+
265
+ if (!next.inTripleDouble && tripleSingleCount % 2 === 1) {
266
+ next.inTripleSingle = !next.inTripleSingle;
267
+ }
268
+
269
+ return next;
270
+ }
271
+
272
+ function isTomlTableHeaderLine(line) {
273
+ const normalized = stripCommentOutsideStrings(line).trim();
274
+
275
+ if (normalized.length === 0) {
276
+ return false;
277
+ }
278
+
279
+ return /^\[\[.*\]\]$/.test(normalized) || /^\[.*\]$/.test(normalized);
280
+ }
281
+
282
+ function findAllCodexBlocks(source) {
283
+ const lines = source.split("\n");
284
+ const blocks = [];
285
+ let activeLines = null;
286
+ let state = {
287
+ inTripleDouble: false,
288
+ inTripleSingle: false,
289
+ };
290
+
291
+ for (const line of lines) {
292
+ const isHeaderCandidate = !state.inTripleDouble && !state.inTripleSingle && isTomlTableHeaderLine(line);
293
+ const isCodexHeader = isHeaderCandidate && headerPattern.test(line);
294
+
295
+ if (isCodexHeader) {
296
+ if (activeLines !== null) {
297
+ blocks.push(activeLines.join("\n").trim());
298
+ }
299
+ activeLines = [line];
300
+ } else if (activeLines !== null && isHeaderCandidate) {
301
+ blocks.push(activeLines.join("\n").trim());
302
+ activeLines = null;
303
+ } else if (activeLines !== null) {
304
+ activeLines.push(line);
305
+ }
306
+
307
+ state = advanceTripleQuoteState(line, state);
308
+ }
309
+
310
+ if (activeLines !== null) {
311
+ blocks.push(activeLines.join("\n").trim());
312
+ }
313
+
314
+ return blocks;
315
+ }
316
+
317
+ function parseStringAssignment(value, key) {
318
+ const match = new RegExp("^\\s*" + key + "\\s*=\\s*([\"'])([^\"']+)\\1\\s*$").exec(value);
319
+ return match ? match[2] : null;
320
+ }
321
+
322
+ function parseTomlStringArray(value) {
323
+ const match = /^\s*args\s*=\s*\[(.*)\]\s*$/ms.exec(value.trim());
324
+
325
+ if (match === null) {
326
+ return null;
327
+ }
328
+
329
+ const body = match[1] ?? "";
330
+ const values = [];
331
+ let cursor = 0;
332
+ let expectValue = true;
333
+
334
+ while (cursor < body.length) {
335
+ while (cursor < body.length && /\s/.test(body[cursor] ?? "")) {
336
+ cursor += 1;
337
+ }
338
+
339
+ if (cursor >= body.length) {
340
+ break;
341
+ }
342
+
343
+ if (!expectValue) {
344
+ if (body[cursor] !== ",") {
345
+ return null;
346
+ }
347
+ cursor += 1;
348
+ expectValue = true;
349
+ continue;
350
+ }
351
+
352
+ const quote = body[cursor];
353
+ if (quote !== '"' && quote !== "'") {
354
+ return null;
355
+ }
356
+
357
+ cursor += 1;
358
+ let token = "";
359
+ let escaped = false;
360
+
361
+ while (cursor < body.length) {
362
+ const char = body[cursor] ?? "";
363
+
364
+ if (char === quote && !escaped) {
365
+ cursor += 1;
366
+ values.push(token);
367
+ break;
368
+ }
369
+
370
+ token += char;
371
+ escaped = char === "\\" && !escaped;
372
+ if (char !== "\\") {
373
+ escaped = false;
374
+ }
375
+ cursor += 1;
376
+ }
377
+
378
+ if (cursor > body.length) {
379
+ return null;
380
+ }
381
+
382
+ expectValue = false;
383
+ }
384
+
385
+ const leftover = body.slice(cursor).trim();
386
+ if (leftover === ",") {
387
+ return values;
388
+ }
389
+
390
+ return leftover.length === 0 ? values : null;
391
+ }
392
+
393
+ function collectArrayLines(lines, startIndex) {
394
+ const collected = [stripCommentOutsideStrings(lines[startIndex] ?? "")];
395
+ let bracketDepth = 0;
396
+ let inSingle = false;
397
+ let inDouble = false;
398
+ let escaped = false;
399
+
400
+ for (let lineIndex = startIndex; lineIndex < lines.length; lineIndex += 1) {
401
+ const sanitized = stripCommentOutsideStrings(lines[lineIndex] ?? "");
402
+ if (lineIndex !== startIndex) {
403
+ collected.push(sanitized);
404
+ }
405
+
406
+ for (let index = 0; index < sanitized.length; index += 1) {
407
+ const char = sanitized[index] ?? "";
408
+
409
+ if (char === '"' && !inSingle && !escaped) {
410
+ inDouble = !inDouble;
411
+ } else if (char === "'" && !inDouble && !escaped) {
412
+ inSingle = !inSingle;
413
+ } else if (!inSingle && !inDouble) {
414
+ if (char === "[") {
415
+ bracketDepth += 1;
416
+ } else if (char === "]") {
417
+ bracketDepth -= 1;
418
+ }
419
+ }
420
+
421
+ escaped = char === "\\" && !escaped;
422
+ if (char !== "\\") {
423
+ escaped = false;
424
+ }
425
+ }
426
+
427
+ if (bracketDepth <= 0) {
428
+ return {
429
+ nextIndex: lineIndex,
430
+ text: collected.join("\n"),
431
+ };
432
+ }
433
+ }
434
+
435
+ return null;
436
+ }
437
+
438
+ function parseCodexBlock(blockContent) {
439
+ const lines = blockContent.split("\n");
440
+ let command = null;
441
+ let args = null;
442
+ let hasConflict = false;
443
+ let inTripleDouble = false;
444
+ let inTripleSingle = false;
445
+
446
+ for (let index = 1; index < lines.length; index += 1) {
447
+ const line = lines[index] ?? "";
448
+ const sanitized = stripCommentOutsideStrings(line);
449
+ const trimmed = sanitized.trim();
450
+
451
+ if (inTripleDouble) {
452
+ if (countTripleQuoteToggles(sanitized, '"') % 2 === 1) {
453
+ inTripleDouble = false;
454
+ }
455
+ continue;
456
+ }
457
+
458
+ if (inTripleSingle) {
459
+ if (countTripleQuoteToggles(sanitized, "'") % 2 === 1) {
460
+ inTripleSingle = false;
461
+ }
462
+ continue;
463
+ }
464
+
465
+ if (countTripleQuoteToggles(sanitized, '"') % 2 === 1) {
466
+ inTripleDouble = true;
467
+ continue;
468
+ }
469
+
470
+ if (countTripleQuoteToggles(sanitized, "'") % 2 === 1) {
471
+ inTripleSingle = true;
472
+ continue;
473
+ }
474
+
475
+ if (trimmed.length === 0) {
476
+ continue;
477
+ }
478
+
479
+ if (/^command\s*=/.test(trimmed)) {
480
+ const parsedCommand = parseStringAssignment(trimmed, "command");
481
+ if (command !== null || parsedCommand === null) {
482
+ hasConflict = true;
483
+ } else {
484
+ command = parsedCommand;
485
+ }
486
+ continue;
487
+ }
488
+
489
+ if (/^args\s*=/.test(trimmed)) {
490
+ const collected = collectArrayLines(lines, index);
491
+ const parsedArgs = collected === null ? null : parseTomlStringArray(collected.text);
492
+
493
+ if (args !== null || parsedArgs === null || collected === null) {
494
+ hasConflict = true;
495
+ } else {
496
+ args = parsedArgs;
497
+ index = collected.nextIndex;
498
+ }
499
+ }
500
+ }
501
+
502
+ return {
503
+ args,
504
+ command,
505
+ hasConflict,
506
+ };
507
+ }
508
+
509
+ function isStructurallySafe(source) {
510
+ let bracketDepth = 0;
511
+ let braceDepth = 0;
512
+ let inSingle = false;
513
+ let inDouble = false;
514
+ let escaped = false;
515
+ let tripleState = {
516
+ inTripleDouble: false,
517
+ inTripleSingle: false,
518
+ };
519
+
520
+ for (const line of source.split("\n")) {
521
+ tripleState = advanceTripleQuoteState(line, tripleState);
522
+
523
+ for (let index = 0; index < line.length; index += 1) {
524
+ const char = line[index] ?? "";
525
+
526
+ if (!inSingle && !inDouble && char === "#") {
527
+ break;
528
+ }
529
+
530
+ if (char === '"' && !inSingle && !escaped) {
531
+ inDouble = !inDouble;
532
+ } else if (char === "'" && !inDouble && !escaped) {
533
+ inSingle = !inSingle;
534
+ } else if (!inSingle && !inDouble) {
535
+ if (char === "[") {
536
+ bracketDepth += 1;
537
+ } else if (char === "]") {
538
+ bracketDepth -= 1;
539
+ if (bracketDepth < 0) {
540
+ return false;
541
+ }
542
+ } else if (char === "{") {
543
+ braceDepth += 1;
544
+ } else if (char === "}") {
545
+ braceDepth -= 1;
546
+ if (braceDepth < 0) {
547
+ return false;
548
+ }
549
+ }
550
+ }
551
+
552
+ escaped = char === "\\" && !escaped;
553
+ if (char !== "\\") {
554
+ escaped = false;
555
+ }
556
+ }
557
+ }
558
+
559
+ return (
560
+ !tripleState.inTripleDouble &&
561
+ !tripleState.inTripleSingle &&
562
+ bracketDepth === 0 &&
563
+ braceDepth === 0 &&
564
+ !inSingle &&
565
+ !inDouble
566
+ );
567
+ }
568
+
569
+ try {
570
+ const source = fs.readFileSync(configPath, "utf8");
571
+ if (!isStructurallySafe(source)) {
572
+ process.exit(1);
573
+ }
574
+
575
+ const blocks = findAllCodexBlocks(source);
576
+ if (blocks.length !== 1) {
577
+ process.exit(1);
578
+ }
579
+
580
+ const parsed = parseCodexBlock(blocks[0]);
581
+ const isConfigured =
582
+ !parsed.hasConflict &&
583
+ parsed.command === expectedCommand &&
584
+ Array.isArray(parsed.args) &&
585
+ parsed.args.length === expectedArgs.length &&
586
+ parsed.args.every((entry, index) => entry === expectedArgs[index]);
587
+
588
+ process.exit(isConfigured ? 0 : 1);
589
+ } catch {
590
+ process.exit(1);
591
+ }
592
+ NODE
125
593
  }
126
594
 
127
595
  resolve_optional_cli_command() {
@@ -236,8 +704,7 @@ fi
236
704
  # [3/3] Register MCP with AI apps
237
705
  # ═══════════════════════════════════
238
706
  step "3/3" "Register MCP with AI apps"
239
- printf " Automatic registration: Claude Code, Claude Desktop, Cursor, Codex CLI, Gemini CLI\n"
240
- printf " Manual setup required: Codex App, Antigravity\n"
707
+ printf " Automatic registration: Claude Code, Claude Desktop, Cursor, Codex CLI/App, Gemini CLI, Antigravity\n"
241
708
 
242
709
  MCP_COMMAND='npx -y @weppy/roblox-mcp'
243
710
 
@@ -279,17 +746,17 @@ else
279
746
  NOT_DETECTED+=("Cursor (not found)")
280
747
  fi
281
748
 
282
- # Codex CLI
749
+ # Codex CLI / Codex App (share the same ~/.codex/config.toml)
283
750
  CODEX_CONFIG="$HOME/.codex/config.toml"
284
751
  CODEX_CLI_COMMAND="$(resolve_optional_cli_command codex 2>/dev/null || true)"
285
752
  if is_codex_config_configured "$CODEX_CONFIG"; then
286
- DETECTED_NAMES+=("Codex CLI (configured)")
753
+ DETECTED_NAMES+=("Codex CLI/App (configured)")
287
754
  DETECTED_TYPES+=("codex-cli")
288
755
  elif [ -n "$CODEX_CLI_COMMAND" ]; then
289
- DETECTED_NAMES+=("Codex CLI")
756
+ DETECTED_NAMES+=("Codex CLI/App")
290
757
  DETECTED_TYPES+=("codex-cli")
291
758
  else
292
- NOT_DETECTED+=("Codex CLI (not found)")
759
+ NOT_DETECTED+=("Codex CLI/App (not found)")
293
760
  fi
294
761
 
295
762
  # Gemini CLI
@@ -306,6 +773,21 @@ else
306
773
  NOT_DETECTED+=("Gemini CLI (not found)")
307
774
  fi
308
775
 
776
+ # Antigravity (unofficial path, auto-register if found)
777
+ ANTIGRAVITY_CONFIG="$HOME/.gemini/antigravity/mcp_config.json"
778
+ if is_antigravity_mcp_configured "$ANTIGRAVITY_CONFIG"; then
779
+ DETECTED_NAMES+=("Antigravity (configured)")
780
+ DETECTED_TYPES+=("antigravity")
781
+ elif [ -f "$ANTIGRAVITY_CONFIG" ]; then
782
+ DETECTED_NAMES+=("Antigravity")
783
+ DETECTED_TYPES+=("antigravity")
784
+ elif [ -d "$HOME/.gemini/antigravity" ]; then
785
+ DETECTED_NAMES+=("Antigravity")
786
+ DETECTED_TYPES+=("antigravity")
787
+ else
788
+ NOT_DETECTED+=("Antigravity (not found)")
789
+ fi
790
+
309
791
  if [ ${#DETECTED_NAMES[@]} -eq 0 ]; then
310
792
  warn "No AI apps detected"
311
793
  info "Register MCP server manually: $MCP_COMMAND"
@@ -411,6 +893,15 @@ else
411
893
  fail "Failed: $app_name"
412
894
  fi
413
895
  ;;
896
+ antigravity)
897
+ if is_antigravity_mcp_configured "$ANTIGRAVITY_CONFIG"; then
898
+ success "Already configured: $app_name"
899
+ elif add_antigravity_mcp_config "$ANTIGRAVITY_CONFIG"; then
900
+ success "Registered: $app_name"
901
+ else
902
+ fail "Failed: $app_name"
903
+ fi
904
+ ;;
414
905
  esac
415
906
  done
416
907
  fi
@@ -428,7 +919,6 @@ printf " 1. Restart Roblox Studio\n"
428
919
  # shellcheck disable=SC2059
429
920
  printf " 2. Look for the ${BOLD}WROX${NC} button in the Plugins tab\n"
430
921
  printf " 3. Click Connect and start building with AI!\n\n"
431
- printf " Auto registration: Claude Code, Claude Desktop, Cursor, Codex CLI, Gemini CLI\n"
432
- printf " Manual setup required: Codex App, Antigravity\n\n"
922
+ printf " Auto registration: Claude Code, Claude Desktop, Cursor, Codex CLI/App, Gemini CLI, Antigravity\n\n"
433
923
  # shellcheck disable=SC2059
434
924
  printf " ${DIM}Docs: https://github.com/hope1026/weppy-roblox-mcp${NC}\n\n"
package/llms-full.txt CHANGED
@@ -21,9 +21,21 @@ Roblox Studio Plugin ← Executes commands inside Studio
21
21
 
22
22
  When an AI app says "Create a blue part", the MCP server converts this request, and the Roblox Studio plugin actually creates the part.
23
23
 
24
- ## One-Line Install (Recommended)
24
+ ## Web Installer (Recommended)
25
25
 
26
- Installs the MCP server, Roblox Studio plugin, and registers with your AI apps in one step:
26
+ Open the installer page in Chrome, Edge, or Arc for one-click setup:
27
+
28
+ https://hope1026.github.io/weppy-roblox-mcp/installer/
29
+
30
+ It writes the supported AI app config for `npx -y @weppy/roblox-mcp` first.
31
+
32
+ After you reopen the AI app, the first MCP run fetches the package through `npx` and auto-installs the Roblox Studio plugin.
33
+
34
+ For Safari or Firefox, use the installer page as a guide or continue with the manual install below.
35
+
36
+ ## One-Line Install (Fallback)
37
+
38
+ Advanced users can install everything in one command:
27
39
 
28
40
  ```bash
29
41
  # macOS / Linux
package/llms.txt CHANGED
@@ -21,7 +21,11 @@ terrain, and more — inside a live Studio session.
21
21
 
22
22
  ## Installation
23
23
 
24
- **One-line install** (installs MCP server, Roblox Studio plugin, and registers with AI apps):
24
+ **Web installer** (recommended one-click setup for Chrome, Edge, and Arc):
25
+
26
+ https://hope1026.github.io/weppy-roblox-mcp/installer/
27
+
28
+ **One-line install** (advanced fallback):
25
29
 
26
30
  ```bash
27
31
  # macOS / Linux
@@ -31,8 +35,10 @@ curl -fsSL https://raw.githubusercontent.com/hope1026/weppy-roblox-mcp/main/inst
31
35
  irm https://raw.githubusercontent.com/hope1026/weppy-roblox-mcp/main/install.ps1 | iex
32
36
  ```
33
37
 
34
- Or manually:
35
- 1. Install Roblox Studio plugin (bridge between Studio and AI)
38
+ Safari / Firefox users should follow the installer page guide or open the installation docs.
39
+
40
+ Manual alternative:
41
+ 1. Install Roblox Studio plugin via the installation guide
36
42
  2. Register MCP server: `npx -y @weppy/roblox-mcp`
37
43
 
38
44
  ## Tiers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weppy/roblox-mcp",
3
- "version": "2.2.1",
3
+ "version": "2.3.0",
4
4
  "description": "MCP (Model Context Protocol) server for Roblox Studio integration - enables AI coding agents to interact with Roblox Studio in real-time",
5
5
  "main": "plugins/weppy-roblox-mcp/dist/index.js",
6
6
  "type": "module",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "weppy-roblox-mcp",
3
3
  "description": "MCP server for Roblox Studio integration - AI-powered game development with specialized agents and skills",
4
- "version": "2.2.1",
4
+ "version": "2.3.0",
5
5
  "author": {
6
6
  "name": "hope1026"
7
7
  },