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.
- package/dist/server.js +116 -151
- 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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
261
|
+
console.error(`[BotSDK] Reconnected successfully after ${this.reconnectAttempt} attempt(s)`);
|
|
262
262
|
} catch (e) {
|
|
263
|
-
console.
|
|
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.
|
|
307
|
+
console.error(`[BotSDK] Checking bot status via URL: ${statusUrl}`);
|
|
308
308
|
const response = await fetch(statusUrl);
|
|
309
309
|
if (!response.ok) {
|
|
310
|
-
console.
|
|
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.
|
|
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.
|
|
337
|
+
console.error(`[BotSDK] Bot not connected`);
|
|
338
338
|
return true;
|
|
339
339
|
}
|
|
340
|
-
console.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
775
|
+
console.error(`[BotSDK] Waiting - invalid position: (${s.player?.worldX}, ${s.player?.worldZ})`);
|
|
776
776
|
} else if (!inGame) {
|
|
777
|
-
console.
|
|
777
|
+
console.error("[BotSDK] Waiting - not in game");
|
|
778
778
|
} else if (!hasEntities) {
|
|
779
|
-
console.
|
|
779
|
+
console.error("[BotSDK] Waiting - no entities loaded yet");
|
|
780
780
|
}
|
|
781
781
|
return inGame && validPosition && hasEntities;
|
|
782
782
|
}, timeout);
|
|
783
|
-
console.
|
|
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.
|
|
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.
|
|
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\`
|
|
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
|
|
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
|
-
###
|
|
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
|
|
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
|
-
|
|
4490
|
-
type: "
|
|
4491
|
-
|
|
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
|
|
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
|
|
4993
|
-
const
|
|
4994
|
-
|
|
4995
|
-
|
|
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 ${
|
|
4999
|
-
`Players
|
|
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}
|
|
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
|
|
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}`);
|