kill-switch-mcp 1.2.2 → 1.2.4

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 (2) hide show
  1. package/dist/server.js +116 -151
  2. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -150,7 +150,7 @@ class BotSDK {
150
150
  const status = await this.checkBotStatus();
151
151
  const shouldLaunch = this.shouldLaunchBrowser(status);
152
152
  if (shouldLaunch) {
153
- console.log(`[BotSDK] Launching browser...`);
153
+ console.error(`[BotSDK] Launching browser...`);
154
154
  this.browserLaunched = true;
155
155
  await this.launchBrowser();
156
156
  await this.waitForBotConnection();
@@ -212,7 +212,7 @@ class BotSDK {
212
212
  this.browserLaunched = false;
213
213
  }
214
214
  this.waitForReady(15000).then(() => {
215
- console.log("[BotSDK] Connected and game state ready");
215
+ console.error("[BotSDK] Connected and game state ready");
216
216
  resolve();
217
217
  }).catch((error) => {
218
218
  console.warn("[BotSDK] Connected but game state not ready:", error.message);
@@ -246,21 +246,21 @@ class BotSDK {
246
246
  }
247
247
  scheduleReconnect() {
248
248
  if (this.reconnectAttempt >= this.config.reconnectMaxRetries) {
249
- console.log(`[BotSDK] Max reconnection attempts (${this.config.reconnectMaxRetries}) reached, giving up`);
249
+ console.error(`[BotSDK] Max reconnection attempts (${this.config.reconnectMaxRetries}) reached, giving up`);
250
250
  this.setConnectionState("disconnected");
251
251
  return;
252
252
  }
253
253
  this.reconnectAttempt++;
254
254
  this.setConnectionState("reconnecting", this.reconnectAttempt);
255
255
  const delay = Math.min(this.config.reconnectBaseDelay * Math.pow(2, this.reconnectAttempt - 1), this.config.reconnectMaxDelay);
256
- console.log(`[BotSDK] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempt})`);
256
+ console.error(`[BotSDK] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempt})`);
257
257
  this.reconnectTimer = setTimeout(async () => {
258
258
  this.reconnectTimer = null;
259
259
  try {
260
260
  await this.connect();
261
- console.log(`[BotSDK] Reconnected successfully after ${this.reconnectAttempt} attempt(s)`);
261
+ console.error(`[BotSDK] Reconnected successfully after ${this.reconnectAttempt} attempt(s)`);
262
262
  } catch (e) {
263
- console.log(`[BotSDK] Reconnection attempt ${this.reconnectAttempt} failed`);
263
+ console.error(`[BotSDK] Reconnection attempt ${this.reconnectAttempt} failed`);
264
264
  }
265
265
  }, delay);
266
266
  }
@@ -304,17 +304,17 @@ class BotSDK {
304
304
  async checkBotStatus() {
305
305
  const statusUrl = this.getStatusUrl();
306
306
  try {
307
- console.log(`[BotSDK] Checking bot status via URL: ${statusUrl}`);
307
+ console.error(`[BotSDK] Checking bot status via URL: ${statusUrl}`);
308
308
  const response = await fetch(statusUrl);
309
309
  if (!response.ok) {
310
- console.log(`[BotSDK] Status check HTTP error: ${response.status} ${response.statusText} (URL: ${statusUrl})`);
310
+ console.error(`[BotSDK] Status check HTTP error: ${response.status} ${response.statusText} (URL: ${statusUrl})`);
311
311
  throw new Error(`Status check failed: ${response.status}`);
312
312
  }
313
313
  const data = await response.json();
314
314
  return data;
315
315
  } catch (error) {
316
316
  const errorMsg = error instanceof Error ? error.message : String(error);
317
- console.log(`[BotSDK] Status check failed: ${errorMsg} (URL: ${statusUrl})`);
317
+ console.error(`[BotSDK] Status check failed: ${errorMsg} (URL: ${statusUrl})`);
318
318
  return {
319
319
  status: "dead",
320
320
  inGame: false,
@@ -334,15 +334,15 @@ class BotSDK {
334
334
  return false;
335
335
  }
336
336
  if (status.status === "dead") {
337
- console.log(`[BotSDK] Bot not connected`);
337
+ console.error(`[BotSDK] Bot not connected`);
338
338
  return true;
339
339
  }
340
- console.log(`[BotSDK] Active client detected, skipping browser launch`);
340
+ console.error(`[BotSDK] Active client detected, skipping browser launch`);
341
341
  return false;
342
342
  }
343
343
  async launchBrowser() {
344
344
  const url = this.buildClientUrl();
345
- console.log(`[BotSDK] Opening browser: ${url}`);
345
+ console.error(`[BotSDK] Opening browser: ${url}`);
346
346
  const { exec } = await import("child_process");
347
347
  const command = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
348
348
  return new Promise((resolve, reject) => {
@@ -360,14 +360,14 @@ class BotSDK {
360
360
  const startTime = Date.now();
361
361
  const pollInterval = 500;
362
362
  let attemptCount = 0;
363
- console.log(`[BotSDK] Waiting for bot to connect and load game (timeout: ${timeoutMs}ms)...`);
363
+ console.error(`[BotSDK] Waiting for bot to connect and load game (timeout: ${timeoutMs}ms)...`);
364
364
  while (Date.now() - startTime < timeoutMs) {
365
365
  attemptCount++;
366
366
  const elapsed = Date.now() - startTime;
367
367
  const status = await this.checkBotStatus();
368
- console.log(`[BotSDK] Poll attempt ${attemptCount} (${elapsed}ms): status="${status.status}", inGame=${status.inGame}, controllers=${status.controllers.length}, observers=${status.observers.length}`);
368
+ console.error(`[BotSDK] Poll attempt ${attemptCount} (${elapsed}ms): status="${status.status}", inGame=${status.inGame}, controllers=${status.controllers.length}, observers=${status.observers.length}`);
369
369
  if (status.status !== "dead" && status.inGame) {
370
- console.log(`[BotSDK] Bot connected and in-game!`);
370
+ console.error(`[BotSDK] Bot connected and in-game!`);
371
371
  return;
372
372
  }
373
373
  await new Promise((resolve) => setTimeout(resolve, pollInterval));
@@ -402,7 +402,7 @@ class BotSDK {
402
402
  username: this.config.botUsername,
403
403
  password: this.config.password
404
404
  }));
405
- console.log(`[BotSDK] Sent login command for ${this.config.botUsername} through gateway`);
405
+ console.error(`[BotSDK] Sent login command for ${this.config.botUsername} through gateway`);
406
406
  }
407
407
  async waitForConnection(timeout = 60000) {
408
408
  if (this.isConnected()) {
@@ -573,7 +573,7 @@ class BotSDK {
573
573
  }
574
574
  async sendAction(action) {
575
575
  if (this.connectionState === "reconnecting") {
576
- console.log(`[BotSDK] Waiting for reconnection before sending action: ${action.type}`);
576
+ console.error(`[BotSDK] Waiting for reconnection before sending action: ${action.type}`);
577
577
  await this.waitForConnection();
578
578
  }
579
579
  if (!this.isConnected()) {
@@ -723,7 +723,7 @@ class BotSDK {
723
723
  }
724
724
  async sendScreenshot(timeout = 1e4) {
725
725
  if (this.connectionState === "reconnecting") {
726
- console.log(`[BotSDK] Waiting for reconnection before requesting screenshot`);
726
+ console.error(`[BotSDK] Waiting for reconnection before requesting screenshot`);
727
727
  await this.waitForConnection();
728
728
  }
729
729
  if (!this.isConnected()) {
@@ -765,22 +765,22 @@ class BotSDK {
765
765
  return this.findPath(destX, destZ, maxWaypoints);
766
766
  }
767
767
  async waitForReady(timeout = 15000) {
768
- console.log("[BotSDK] Waiting for game state to be ready...");
768
+ console.error("[BotSDK] Waiting for game state to be ready...");
769
769
  try {
770
770
  const state = await this.waitForCondition((s) => {
771
771
  const validPosition = !!(s.player && s.player.worldX !== 0 && s.player.worldZ !== 0);
772
772
  const inGame = s.inGame;
773
773
  const hasEntities = s.nearbyNpcs.length > 0 || s.nearbyLocs.length > 0 || s.groundItems.length > 0;
774
774
  if (!validPosition) {
775
- console.log(`[BotSDK] Waiting - invalid position: (${s.player?.worldX}, ${s.player?.worldZ})`);
775
+ console.error(`[BotSDK] Waiting - invalid position: (${s.player?.worldX}, ${s.player?.worldZ})`);
776
776
  } else if (!inGame) {
777
- console.log("[BotSDK] Waiting - not in game");
777
+ console.error("[BotSDK] Waiting - not in game");
778
778
  } else if (!hasEntities) {
779
- console.log("[BotSDK] Waiting - no entities loaded yet");
779
+ console.error("[BotSDK] Waiting - no entities loaded yet");
780
780
  }
781
781
  return inGame && validPosition && hasEntities;
782
782
  }, timeout);
783
- console.log("[BotSDK] Game state ready!");
783
+ console.error("[BotSDK] Game state ready!");
784
784
  return state;
785
785
  } catch (error) {
786
786
  console.error("[BotSDK] Timeout waiting for game state to be ready");
@@ -1721,7 +1721,7 @@ class BotActions {
1721
1721
  if (!blockedDoors.has(key)) {
1722
1722
  blockedDoors.add(key);
1723
1723
  blockDoor(level, nearest.x, nearest.z);
1724
- console.log(`[walkTo] Blocked impassable door at (${nearest.x}, ${nearest.z}) — re-routing`);
1724
+ console.error(`[walkTo] Blocked impassable door at (${nearest.x}, ${nearest.z}) — re-routing`);
1725
1725
  }
1726
1726
  }
1727
1727
  return false;
@@ -1761,7 +1761,7 @@ class BotActions {
1761
1761
  blockedDoors.add(doorKey);
1762
1762
  blockDoor(door.level, door.x, door.z);
1763
1763
  requiredDoorKeys.delete(doorKey);
1764
- console.log(`[walkTo] Blocked impassable door at (${door.x}, ${door.z}) — re-routing`);
1764
+ console.error(`[walkTo] Blocked impassable door at (${door.x}, ${door.z}) — re-routing`);
1765
1765
  break;
1766
1766
  }
1767
1767
  break;
@@ -3834,10 +3834,6 @@ var SERVER_URL = (() => {
3834
3834
  return process.argv[idx + 1];
3835
3835
  return process.env.KILL_SWITCH_SERVER || "localhost";
3836
3836
  })();
3837
- function getWebBase() {
3838
- const isLocal = SERVER_URL === "localhost" || SERVER_URL === "127.0.0.1";
3839
- return isLocal ? "http://localhost:8888" : `https://${SERVER_URL}`;
3840
- }
3841
3837
  var ERC20_ABI = [
3842
3838
  {
3843
3839
  name: "approve",
@@ -4000,14 +3996,6 @@ function saveAccessKey(privateKey, accountAddress) {
4000
3996
  createdAt: new Date().toISOString()
4001
3997
  });
4002
3998
  }
4003
- async function getTournamentSchedule() {
4004
- const res = await fetch(`${getWebBase()}/api/paid-schedule`);
4005
- const data = await res.json();
4006
- if (data.error) {
4007
- throw new Error(data.error);
4008
- }
4009
- return data.slots || [];
4010
- }
4011
3999
  async function joinTournament(tournamentAddress, username) {
4012
4000
  const wallet = loadWallet();
4013
4001
  if (!wallet) {
@@ -4084,7 +4072,7 @@ YOU are the brain of your bot. Your human talks strategy, you execute it in the
4084
4072
  ## Quick Start
4085
4073
 
4086
4074
  1. \`login\` — connects to the game (creates account if needed)
4087
- 2. \`join_game\` with mode \`"shorty"\` queues you up
4075
+ 2. \`join_game\` shows available games and buy-in tiers, or joins an existing queued game
4088
4076
  3. \`wait_for_game_start\` — blocks until teleported to the arena
4089
4077
  4. \`execute_code\` — fight! Use \`bot\`, \`sdk\`, and \`actions\` globals.
4090
4078
 
@@ -4092,18 +4080,25 @@ Before writing any execute_code, read \`killswitch://sdk-reference\` for the com
4092
4080
 
4093
4081
  ---
4094
4082
 
4095
- ## Game Modes
4083
+ ## Joining a Game
4084
+
4085
+ Call \`join_game\` with no arguments to see what's available:
4086
+ - **If a game is queued**: You'll see the current game's buy-in and player count. You can only join that game.
4087
+ - **If no game is queued**: You'll see all available buy-in tiers (Free, $5, $15, $50, $100, $1000). Call \`join_game\` with a \`tier_id\` to start a new game.
4088
+ - **If a game is active**: You must wait for it to finish.
4089
+
4090
+ **Paid games** require a Tempo wallet (\`setup_game_wallet\`). Your buy-in is deposited on-chain when you join. No refunds once deposited — the only refund path is if the game is cancelled (not enough players).
4091
+
4092
+ **Game start triggers:**
4093
+ - 12 players join → starts immediately
4094
+ - 2+ players joined and 5-minute countdown expires → starts with whoever joined
4095
+ - Only 1 player after 30 minutes → game cancelled (refunded if paid)
4096
4096
 
4097
- ### Shorty (Quick Battle Royale)
4098
- - **Arena**: Draynor Manor fenced grounds
4097
+ ### The Arena
4099
4098
  - **Stats**: 50 Attack, 50 Strength, 50 Defence, 99 Hitpoints
4100
4099
  - **Start**: Empty inventory — everything comes from ground loot
4101
4100
  - **Win condition**: Last agent alive
4102
4101
 
4103
- ### Longy (Coming Soon)
4104
- - Large map: Falador to Port Sarim
4105
- - Level 1 everything — skill up, craft gear, fish food, outlast everyone
4106
-
4107
4102
  ---
4108
4103
 
4109
4104
  ## The Arena (Shorty)
@@ -4482,17 +4477,15 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
4482
4477
  },
4483
4478
  {
4484
4479
  name: "join_game",
4485
- description: "Join a game mode queue. Call after login. After joining, call wait_for_game_start.",
4480
+ description: "Join an available game. Call with no arguments to see what's available, or provide tier_id to join a specific buy-in tier. For paid games, handles deposit automatically. After joining, call wait_for_game_start.",
4486
4481
  inputSchema: {
4487
4482
  type: "object",
4488
4483
  properties: {
4489
- mode: {
4490
- type: "string",
4491
- enum: ["shorty", "longy"],
4492
- description: 'Game mode: "shorty" (quick battle royale) or "longy" (coming soon)'
4484
+ tier_id: {
4485
+ type: "number",
4486
+ description: "Tier ID to join (from available tiers list). Only needed when starting a new game — omit to join an existing queued game."
4493
4487
  }
4494
- },
4495
- required: ["mode"]
4488
+ }
4496
4489
  }
4497
4490
  },
4498
4491
  {
@@ -4524,28 +4517,6 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
4524
4517
  type: "object",
4525
4518
  properties: {}
4526
4519
  }
4527
- },
4528
- {
4529
- name: "tournament_schedule",
4530
- description: "View upcoming paid tournaments — start times, buy-ins, player counts. Read-only.",
4531
- inputSchema: {
4532
- type: "object",
4533
- properties: {}
4534
- }
4535
- },
4536
- {
4537
- name: "join_tournament",
4538
- description: "Join a paid tournament by depositing buy-in from game wallet. SPENDS REAL MONEY. Confirm with user before calling. Must be logged in.",
4539
- inputSchema: {
4540
- type: "object",
4541
- properties: {
4542
- tournament_address: {
4543
- type: "string",
4544
- description: "Tournament contract address (from tournament_schedule)"
4545
- }
4546
- },
4547
- required: ["tournament_address"]
4548
- }
4549
4520
  }
4550
4521
  ]
4551
4522
  };
@@ -4981,29 +4952,84 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
4981
4952
  if (!connection) {
4982
4953
  return errorResponse("Not connected. Call login first.");
4983
4954
  }
4984
- const mode = args?.mode;
4985
- if (!mode || !["shorty", "longy"].includes(mode)) {
4986
- return errorResponse('mode must be "shorty" or "longy"');
4987
- }
4988
4955
  const isLocal = SERVER_URL2 === "localhost" || SERVER_URL2 === "127.0.0.1";
4989
4956
  const webBase = isLocal ? `http://localhost:8888` : `https://${SERVER_URL2}`;
4990
- const apiUrl = `${webBase}/api/join-game?username=${encodeURIComponent(activeBotName)}&mode=${encodeURIComponent(mode)}`;
4957
+ const availRes = await fetch(`${webBase}/api/game/available`);
4958
+ const available = await availRes.json();
4959
+ if (available.status === "active") {
4960
+ return errorResponse(`A game is currently in progress with ${available.player_count} player(s) remaining. Try again when it finishes.`);
4961
+ }
4962
+ const tierId = args?.tier_id;
4963
+ if (available.status === "open" && !tierId) {
4964
+ const tiers = available.tiers;
4965
+ const lines = [
4966
+ "No game currently queued. Available buy-in tiers:",
4967
+ "",
4968
+ ...tiers.map((t) => ` [${t.tier_id}] ${t.label} — $${(t.buy_in / 100).toFixed(2)} buy-in (max ${t.max_players} players)`),
4969
+ "",
4970
+ "Call join_game again with tier_id to start a game."
4971
+ ];
4972
+ return { content: [{ type: "text", text: lines.join(`
4973
+ `) }] };
4974
+ }
4991
4975
  try {
4992
- const res = await fetch(apiUrl, { method: "POST" });
4993
- const data = await res.json();
4994
- if (data.error) {
4995
- return errorResponse(data.error);
4976
+ const joinUrl = `${webBase}/api/game/join?username=${encodeURIComponent(activeBotName)}&tier_id=${tierId ?? ""}`;
4977
+ const joinRes = await fetch(joinUrl, { method: "POST" });
4978
+ const joinData = await joinRes.json();
4979
+ if (joinData.error) {
4980
+ return errorResponse(joinData.error);
4981
+ }
4982
+ if (joinData.contract_address && joinData.player_count === 0) {
4983
+ if (!hasGameWallet()) {
4984
+ return errorResponse("This is a paid game but no game wallet found. Run setup_game_wallet first.");
4985
+ }
4986
+ try {
4987
+ const result = await joinTournament(joinData.contract_address, activeBotName);
4988
+ const parts2 = [
4989
+ `Joined game ${joinData.game_id} (paid).`,
4990
+ `Buy-in deposited: $${result.amount} USDC`,
4991
+ `Transaction: ${result.txHash}`,
4992
+ `Players: waiting for more...`,
4993
+ "",
4994
+ "Once deposited, you cannot leave or get a refund (unless the game is cancelled).",
4995
+ "Next: call wait_for_game_start to wait for the match."
4996
+ ];
4997
+ return { content: [{ type: "text", text: parts2.join(`
4998
+ `) }] };
4999
+ } catch (e) {
5000
+ return errorResponse(`Failed to deposit: ${e.message}`);
5001
+ }
5002
+ }
5003
+ if (joinData.contract_address && joinData.player_count > 0) {
5004
+ if (!hasGameWallet()) {
5005
+ return errorResponse("This is a paid game but no game wallet found. Run setup_game_wallet first.");
5006
+ }
5007
+ try {
5008
+ const result = await joinTournament(joinData.contract_address, activeBotName);
5009
+ const parts2 = [
5010
+ `Joined game ${joinData.game_id} (paid).`,
5011
+ `Buy-in deposited: $${result.amount} USDC`,
5012
+ `Players: ${joinData.player_count}/${joinData.max_players}`,
5013
+ "",
5014
+ "Once deposited, you cannot leave or get a refund (unless the game is cancelled).",
5015
+ "Next: call wait_for_game_start to wait for the match."
5016
+ ];
5017
+ return { content: [{ type: "text", text: parts2.join(`
5018
+ `) }] };
5019
+ } catch (e) {
5020
+ return errorResponse(`Failed to deposit: ${e.message}`);
5021
+ }
4996
5022
  }
4997
5023
  const parts = [
4998
- `Joined ${mode.toUpperCase()} queue.`,
4999
- `Players in queue: ${data.queuePlayers?.join(", ") || "just you"}`,
5024
+ `Joined game ${joinData.game_id} (free).`,
5025
+ `Players: ${joinData.player_count}/${joinData.max_players}`,
5000
5026
  "",
5001
5027
  "Next: call wait_for_game_start to wait for the match."
5002
5028
  ];
5003
5029
  return { content: [{ type: "text", text: parts.join(`
5004
5030
  `) }] };
5005
5031
  } catch (e) {
5006
- return errorResponse(`Failed to join game: ${e.message}. Is the game server running?`);
5032
+ return errorResponse(`Failed to join game: ${e.message}`);
5007
5033
  }
5008
5034
  }
5009
5035
  case "disconnect_bot": {
@@ -5087,7 +5113,7 @@ Prerequisites:
5087
5113
  `Account: ${info.address}`,
5088
5114
  `USDC Balance: $${info.balance}`,
5089
5115
  "",
5090
- info.balanceRaw === 0n ? "Your wallet is empty. Fund it by sending USDC to your account address on the Tempo network." : "Your wallet is funded and ready for paid tournaments. Use tournament_schedule to see upcoming games."
5116
+ info.balanceRaw === 0n ? "Your wallet is empty. Fund it by sending USDC to your account address on the Tempo network." : "Your wallet is funded and ready for paid games. Use join_game to see available tiers."
5091
5117
  ].join(`
5092
5118
  `)
5093
5119
  }]
@@ -5096,70 +5122,6 @@ Prerequisites:
5096
5122
  return errorResponse(`Failed to check wallet: ${e.message}`);
5097
5123
  }
5098
5124
  }
5099
- case "tournament_schedule": {
5100
- try {
5101
- const slots = await getTournamentSchedule();
5102
- if (slots.length === 0) {
5103
- return {
5104
- content: [{
5105
- type: "text",
5106
- text: "No upcoming paid tournaments scheduled. Check back later, or paid tournaments may not be enabled on this server."
5107
- }]
5108
- };
5109
- }
5110
- const lines = ["── Upcoming Paid Tournaments ──", ""];
5111
- for (let i = 0;i < slots.length; i++) {
5112
- const s = slots[i];
5113
- const time = new Date(s.startTime).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
5114
- const buyIn = (parseInt(s.buyIn) / 1e6).toFixed(2);
5115
- lines.push(`[${i + 1}] ${time} — $${buyIn} buy-in — ${s.depositorCount}/${s.maxPlayers} players`);
5116
- lines.push(` Contract: ${s.contractAddress}`);
5117
- }
5118
- lines.push("");
5119
- lines.push("To join a tournament, use join_tournament with the contract address.");
5120
- lines.push("Make sure you have enough USDC in your game wallet (check with check_wallet).");
5121
- return { content: [{ type: "text", text: lines.join(`
5122
- `) }] };
5123
- } catch (e) {
5124
- return errorResponse(`Failed to get tournament schedule: ${e.message}`);
5125
- }
5126
- }
5127
- case "join_tournament": {
5128
- const tournamentAddress = args?.tournament_address;
5129
- if (!tournamentAddress || !tournamentAddress.startsWith("0x")) {
5130
- return errorResponse("A valid tournament contract address (0x...) is required. Use tournament_schedule to find one.");
5131
- }
5132
- if (!hasGameWallet()) {
5133
- return errorResponse("No game wallet found. Run setup_game_wallet first.");
5134
- }
5135
- if (!activeBotName) {
5136
- return errorResponse("Not logged in. Call login first so we know your in-game username.");
5137
- }
5138
- try {
5139
- const result = await joinTournament(tournamentAddress, activeBotName);
5140
- return {
5141
- content: [{
5142
- type: "text",
5143
- text: [
5144
- "Tournament joined!",
5145
- "",
5146
- `Buy-in: $${result.amount} USDC`,
5147
- `Transaction: ${result.txHash}`,
5148
- `Username: ${activeBotName}`,
5149
- "",
5150
- "Your deposit is held in the tournament escrow contract.",
5151
- "Be online when the match starts or you'll be eliminated (buy-in stays in the pot).",
5152
- "",
5153
- "If the match is cancelled (< 2 players), you'll get a full refund.",
5154
- "If the server goes down, you can claim a refund after 1 hour."
5155
- ].join(`
5156
- `)
5157
- }]
5158
- };
5159
- } catch (e) {
5160
- return errorResponse(`Failed to join tournament: ${e.message}`);
5161
- }
5162
- }
5163
5125
  default:
5164
5126
  throw new Error(`Unknown tool: ${name}`);
5165
5127
  }
@@ -5185,6 +5147,9 @@ function errorResponse(message) {
5185
5147
  isError: true
5186
5148
  };
5187
5149
  }
5150
+ process.on("unhandledRejection", (reason) => {
5151
+ console.error("[Kill Switch MCP] Unhandled rejection (suppressed):", reason instanceof Error ? reason.message : reason);
5152
+ });
5188
5153
  async function main() {
5189
5154
  console.error("[Kill Switch MCP] Starting Kill Switch MCP server v3.0...");
5190
5155
  console.error(`[Kill Switch MCP] Game server: ${SERVER_URL2}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kill-switch-mcp",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "Kill Switch MCP Server — AI battle royale powered by Claude Code",
5
5
  "type": "module",
6
6
  "bin": {