@adiholick/nvstudio-mcp 2.2.0 → 2.2.1

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
@@ -32,8 +32,8 @@ curl -fsSL https://raw.githubusercontent.com/Adiholick/nvstudio-mcp/main/install
32
32
  **Apa saja yang dilakukan installer ini secara otomatis?**
33
33
  1. 📦 Mengunduh source code, memasang dependensi Node.js, dan mengkompilasi TypeScript (*Bridge Server*).
34
34
  2. 🧩 **Memasang Plugin Roblox Studio (`nvstudio_mcp.rbxmx`) otomatis** ke folder sistem Roblox Studio Anda (`%LOCALAPPDATA%\Roblox\Plugins` di Windows atau `~/Documents/ROBLOX/Plugins` di macOS).
35
- 3. 🤖 Mengintegrasikan *AI Agent Skills* anti-halusinasi langsung ke **Antigravity IDE** (`~/.gemini/config/plugins/nvstudio-mcp`).
36
- 4. ⚙️ Melakukan auto-konfigurasi jika mendeteksi editor **Cursor** (`.cursor/mcp.json`).
35
+ 3. 🧠 **Universal AI Skill Installer**: Secara cerdas memindai dan mengunduh library skill 29+ *roblox-brain* dan *nvstudio-mcp-guide*, lalu menginjeksinya langsung ke dalam struktur `skills/` dari **AI Agent apapun** yang Anda gunakan (mendukung **Antigravity IDE**, **Cursor**, **Claude Code**, **Codex**, **OpenCode**, dan **Roo Code**).
36
+ 4. ⚙️ Melakukan auto-konfigurasi jika mendeteksi editor **Cursor** (`.cursor/mcp.json`) atau **Antigravity** (`mcp_config.json`).
37
37
 
38
38
  ---
39
39
 
@@ -126,9 +126,15 @@ Installer `install.sh` sebelumnya mengkloning repositori dan menginstal Node.js
126
126
  rm -rf ~/.nvstudio-mcp
127
127
  ```
128
128
 
129
- ### 4. Hapus Konfigurasi dari Editor AI Anda
129
+ ### 4. Hapus Konfigurasi dan Skills dari Editor AI Anda
130
130
  Jika Anda memasukkan konfigurasi MCP secara otomatis ke Cursor atau secara manual ke Claude Desktop, buka file pengaturan `mcp.json` tersebut dan **hapus blok `"nvstudio-mcp"`** dari dalam objek `mcpServers`.
131
131
 
132
+ **Untuk membersihkan skill bawaan (Universal Skill Installer):**
133
+ Anda bisa menghapus manual symlink/folder shortcut (yang bernama awalan `roblox-`) dari dalam folder konfigurasi agen Anda:
134
+ - **Cursor / Workspace Lokal**: Hapus isi `.cursor/skills` dan `.cursor/roblox-brain.md`
135
+ - **Claude Code**: Hapus dari `~/.claude/skills`
136
+ - **Codex / OpenCode**: Hapus dari direktori lokal atau global (`~/.codex/skills`, `~/.opencode/skills`).
137
+
132
138
  ---
133
139
 
134
140
  **Selamat Menciptakan Game Impian Anda dengan AI! 🎮🤖**
package/dist/index.js CHANGED
@@ -140,6 +140,23 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
140
140
  let result;
141
141
  if (roblox_bridge_1.isBridgeHosting) {
142
142
  // Instance ini sendiri yang meng-host bridge server
143
+ if (!(0, roblox_bridge_1.isStudioConnected)()) {
144
+ await new Promise(r => setTimeout(r, 2000));
145
+ if (!(0, roblox_bridge_1.isStudioConnected)()) {
146
+ return {
147
+ content: [
148
+ {
149
+ type: "text",
150
+ text: JSON.stringify({
151
+ status: "error",
152
+ error: "Roblox Studio tidak terdeteksi aktif. Pastikan Roblox Studio dibuka, place telah dimuat, dan plugin nvstudio-mcp dalam status terhubung.",
153
+ }),
154
+ },
155
+ ],
156
+ isError: true,
157
+ };
158
+ }
159
+ }
143
160
  result = await (0, task_queue_1.addTaskToQueue)(command, target, data);
144
161
  }
145
162
  else {
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.isBridgeHosting = void 0;
7
+ exports.isStudioConnected = isStudioConnected;
7
8
  exports.setMcpConnected = setMcpConnected;
8
9
  exports.startBridgeServer = startBridgeServer;
9
10
  const express_1 = __importDefault(require("express"));
@@ -31,13 +32,21 @@ const studioEventQueues = new Map();
31
32
  const studioLastSeen = new Map();
32
33
  setInterval(() => {
33
34
  const now = Date.now();
35
+ let changed = false;
34
36
  for (const [sid, lastSeen] of studioLastSeen.entries()) {
35
- if (now - lastSeen > 60000) {
37
+ if (now - lastSeen > 6000) {
36
38
  studioEventQueues.delete(sid);
37
39
  studioLastSeen.delete(sid);
40
+ changed = true;
38
41
  }
39
42
  }
40
- }, 30000);
43
+ if (changed) {
44
+ broadcastStatus();
45
+ }
46
+ }, 3000);
47
+ function isStudioConnected() {
48
+ return studioEventQueues.size > 0;
49
+ }
41
50
  function enqueueForStudio(studioId, event) {
42
51
  if (studioId) {
43
52
  // Event untuk studio tertentu
@@ -92,7 +101,7 @@ function sendEvent(peer, kind, payload) {
92
101
  function broadcastStatus() {
93
102
  const statusEvent = {
94
103
  mcpConnected,
95
- studioCount: Math.max(ssePeers.size, studioEventQueues.size),
104
+ studioCount: studioEventQueues.size,
96
105
  serverVersion: SERVER_VERSION,
97
106
  };
98
107
  broadcastEvent('status', statusEvent);
@@ -179,12 +188,12 @@ function startBridgeServer(port, isDaemon = false) {
179
188
  io.on('connection', (socket) => {
180
189
  socket.emit('init', {
181
190
  logs: activityLogs,
182
- stats: { totalTasks, successCount, errorCount, sessionStart, studioConnected: Math.max(ssePeers.size, studioEventQueues.size) > 0, port },
191
+ stats: { totalTasks, successCount, errorCount, sessionStart, studioConnected: studioEventQueues.size > 0, port },
183
192
  });
184
193
  });
185
194
  // ── /api/stats ─────────────────────────────────────────────────────────────
186
195
  app.get('/api/stats', (req, res) => {
187
- const activeStudios = Math.max(ssePeers.size, studioEventQueues.size);
196
+ const activeStudios = studioEventQueues.size;
188
197
  res.json({
189
198
  totalTasks, successCount, errorCount, sessionStart,
190
199
  studioConnected: activeStudios > 0,
@@ -196,7 +205,7 @@ function startBridgeServer(port, isDaemon = false) {
196
205
  // ── /api/ping ─────────────────────────────────────────────────────────────
197
206
  app.get('/api/ping', (req, res) => {
198
207
  const uptime = Math.floor((Date.now() - new Date(sessionStart).getTime()) / 1000);
199
- const activeStudios = Math.max(ssePeers.size, studioEventQueues.size);
208
+ const activeStudios = studioEventQueues.size;
200
209
  res.json({
201
210
  status: 'ok',
202
211
  version: SERVER_VERSION,
@@ -329,6 +338,16 @@ function startBridgeServer(port, isDaemon = false) {
329
338
  // ── /api/tasks/enqueue (dari proses MCP eksternal / bridge forwarding) ────
330
339
  app.post('/api/tasks/enqueue', async (req, res) => {
331
340
  const { command, target, data } = req.body;
341
+ // Fast-check: pastikan setidaknya 1 Studio aktif polling
342
+ if (studioEventQueues.size === 0) {
343
+ await new Promise(r => setTimeout(r, 2000));
344
+ if (studioEventQueues.size === 0) {
345
+ return res.json({
346
+ status: 'error',
347
+ error: 'Roblox Studio tidak terdeteksi aktif. Pastikan Roblox Studio dibuka, place telah dimuat, dan plugin nvstudio-mcp dalam status terhubung.'
348
+ });
349
+ }
350
+ }
332
351
  try {
333
352
  const result = await (0, task_queue_1.addTaskToQueue)(command, target, data);
334
353
  res.json({ status: 'success', result });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adiholick/nvstudio-mcp",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Local Bridge Server MCP for Roblox Studio",
5
5
  "main": "dist/index.js",
6
6
  "bin": {