@dimcool/mcp 0.1.30 → 0.1.31

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/index.js CHANGED
@@ -11630,6 +11630,9 @@ var Admin = class {
11630
11630
  this.http = http;
11631
11631
  this.logger = logger2;
11632
11632
  }
11633
+ async getInternalBots() {
11634
+ return this.http.get("/admin/internal-bots");
11635
+ }
11633
11636
  async getUserById(id) {
11634
11637
  return this.http.get(`/admin/users/${id}`);
11635
11638
  }
@@ -14253,8 +14256,21 @@ function createGameActionsStore(transport2) {
14253
14256
  });
14254
14257
  };
14255
14258
  const isNonRpsState = (state) => Boolean(state && !isRpsState(state));
14259
+ const pendingEvents = /* @__PURE__ */ new Map();
14260
+ function enqueue(gameId, event) {
14261
+ const q = pendingEvents.get(gameId) ?? [];
14262
+ q.push(event);
14263
+ pendingEvents.set(gameId, q);
14264
+ }
14265
+ function drainQueue(gameId) {
14266
+ const q = pendingEvents.get(gameId);
14267
+ if (!q?.length) return;
14268
+ pendingEvents.delete(gameId);
14269
+ for (const ev of q) applyWsEvent(ev);
14270
+ }
14256
14271
  const setBaseState = (gameId, state) => {
14257
14272
  updateState(gameId, state);
14273
+ drainQueue(gameId);
14258
14274
  };
14259
14275
  const clearState = (gameId) => {
14260
14276
  store.updateState((state) => {
@@ -14264,6 +14280,7 @@ function createGameActionsStore(transport2) {
14264
14280
  const { [gameId]: _, ...rest } = state.statesByGameId;
14265
14281
  return { ...state, statesByGameId: rest };
14266
14282
  });
14283
+ pendingEvents.delete(gameId);
14267
14284
  };
14268
14285
  const applyWsEvent = (event) => {
14269
14286
  switch (event.event) {
@@ -14282,7 +14299,10 @@ function createGameActionsStore(transport2) {
14282
14299
  }
14283
14300
  case "game:rps:starting": {
14284
14301
  const current = store.getState().statesByGameId[event.payload.gameId];
14285
- if (!current || !isRpsState(current)) return;
14302
+ if (!current || !isRpsState(current)) {
14303
+ enqueue(event.payload.gameId, event);
14304
+ return;
14305
+ }
14286
14306
  const betAmount = typeof event.payload.betAmount === "number" ? event.payload.betAmount : void 0;
14287
14307
  const startedAt = typeof event.payload.startedAt === "string" ? event.payload.startedAt : current.roundState.startedAt;
14288
14308
  const bufferEndsAt = typeof event.payload.bufferEndsAt === "string" ? event.payload.bufferEndsAt : current.roundState.selectionEndsAt;
@@ -14303,7 +14323,10 @@ function createGameActionsStore(transport2) {
14303
14323
  }
14304
14324
  case "game:rps:round:started": {
14305
14325
  const current = store.getState().statesByGameId[event.payload.gameId];
14306
- if (!current || !isRpsState(current)) return;
14326
+ if (!current || !isRpsState(current)) {
14327
+ enqueue(event.payload.gameId, event);
14328
+ return;
14329
+ }
14307
14330
  const actions = {};
14308
14331
  const baseUsers = /* @__PURE__ */ new Set();
14309
14332
  Object.keys(current.roundState.actions).forEach(
@@ -14337,7 +14360,10 @@ function createGameActionsStore(transport2) {
14337
14360
  }
14338
14361
  case "game:rps:action:received": {
14339
14362
  const current = store.getState().statesByGameId[event.payload.gameId];
14340
- if (!current || !isRpsState(current)) return;
14363
+ if (!current || !isRpsState(current)) {
14364
+ enqueue(event.payload.gameId, event);
14365
+ return;
14366
+ }
14341
14367
  const updated = {
14342
14368
  ...current,
14343
14369
  roundState: {
@@ -14356,7 +14382,10 @@ function createGameActionsStore(transport2) {
14356
14382
  }
14357
14383
  case "game:rps:timer:cutoff": {
14358
14384
  const current = store.getState().statesByGameId[event.payload.gameId];
14359
- if (!current || !isRpsState(current)) return;
14385
+ if (!current || !isRpsState(current)) {
14386
+ enqueue(event.payload.gameId, event);
14387
+ return;
14388
+ }
14360
14389
  const updated = {
14361
14390
  ...current,
14362
14391
  roundState: {
@@ -14370,7 +14399,10 @@ function createGameActionsStore(transport2) {
14370
14399
  }
14371
14400
  case "game:rps:round:reveal": {
14372
14401
  const current = store.getState().statesByGameId[event.payload.gameId];
14373
- if (!current || !isRpsState(current)) return;
14402
+ if (!current || !isRpsState(current)) {
14403
+ enqueue(event.payload.gameId, event);
14404
+ return;
14405
+ }
14374
14406
  const actions = {};
14375
14407
  const payloadActions = event.payload.actions;
14376
14408
  Object.keys(payloadActions || {}).forEach((userId) => {
@@ -14394,7 +14426,10 @@ function createGameActionsStore(transport2) {
14394
14426
  }
14395
14427
  case "game:rps:round:completed": {
14396
14428
  const current = store.getState().statesByGameId[event.payload.gameId];
14397
- if (!current || !isRpsState(current)) return;
14429
+ if (!current || !isRpsState(current)) {
14430
+ enqueue(event.payload.gameId, event);
14431
+ return;
14432
+ }
14398
14433
  const roundHistory = [
14399
14434
  ...current.roundHistory || [],
14400
14435
  {
@@ -14419,7 +14454,10 @@ function createGameActionsStore(transport2) {
14419
14454
  }
14420
14455
  case "game:rps:timeout": {
14421
14456
  const current = store.getState().statesByGameId[event.payload.gameId];
14422
- if (!current || !isRpsState(current)) return;
14457
+ if (!current || !isRpsState(current)) {
14458
+ enqueue(event.payload.gameId, event);
14459
+ return;
14460
+ }
14423
14461
  const timedOutUser = event.payload.playerId;
14424
14462
  const action = event.payload.action;
14425
14463
  const updated = {
@@ -14444,7 +14482,10 @@ function createGameActionsStore(transport2) {
14444
14482
  const payload = event.payload;
14445
14483
  const { gameId } = payload;
14446
14484
  const current = store.getState().statesByGameId[gameId];
14447
- if (!current) return;
14485
+ if (!current) {
14486
+ enqueue(gameId, event);
14487
+ return;
14488
+ }
14448
14489
  const updated = isRpsCompletionPayload(payload) && isRpsState(current) ? {
14449
14490
  ...current,
14450
14491
  status: "completed",
@@ -14493,11 +14534,15 @@ function createGameActionsStore(transport2) {
14493
14534
  const current = store.getState().statesByGameId[gameId];
14494
14535
  const updated = current ? { ...current, ...incoming } : incoming;
14495
14536
  updateState(gameId, updated);
14537
+ drainQueue(gameId);
14496
14538
  break;
14497
14539
  }
14498
14540
  case "game:rematch:requested": {
14499
14541
  const current = store.getState().statesByGameId[event.payload.gameId];
14500
- if (!current) return;
14542
+ if (!current) {
14543
+ enqueue(event.payload.gameId, event);
14544
+ return;
14545
+ }
14501
14546
  const requestedBy = event.payload.requestedBy;
14502
14547
  const userId = event.payload.userId;
14503
14548
  const requested = new Set(
@@ -14513,7 +14558,10 @@ function createGameActionsStore(transport2) {
14513
14558
  }
14514
14559
  case "game:rematch:cancelled": {
14515
14560
  const current = store.getState().statesByGameId[event.payload.gameId];
14516
- if (!current) return;
14561
+ if (!current) {
14562
+ enqueue(event.payload.gameId, event);
14563
+ return;
14564
+ }
14517
14565
  const requestedBy = event.payload.requestedBy ?? [];
14518
14566
  const updated = {
14519
14567
  ...current,
@@ -14524,7 +14572,10 @@ function createGameActionsStore(transport2) {
14524
14572
  }
14525
14573
  case "game:rematch:started": {
14526
14574
  const current = store.getState().statesByGameId[event.payload.gameId];
14527
- if (!current) return;
14575
+ if (!current) {
14576
+ enqueue(event.payload.gameId, event);
14577
+ return;
14578
+ }
14528
14579
  const updated = {
14529
14580
  ...current,
14530
14581
  rematchRequestedBy: event.payload.playerIds ?? []
@@ -14535,7 +14586,10 @@ function createGameActionsStore(transport2) {
14535
14586
  case "game:pot:updated": {
14536
14587
  const { gameId, totalPotMinor } = event.payload;
14537
14588
  const current = store.getState().statesByGameId[gameId];
14538
- if (!current) return;
14589
+ if (!current) {
14590
+ enqueue(gameId, event);
14591
+ return;
14592
+ }
14539
14593
  const updated = {
14540
14594
  ...current,
14541
14595
  totalPotMinor
@@ -14548,12 +14602,123 @@ function createGameActionsStore(transport2) {
14548
14602
  }
14549
14603
  };
14550
14604
  const joinGame = (gameId) => transport2.joinRoom(`game:${gameId}`);
14605
+ const getCountdownDigit = (gameId, nowMs) => {
14606
+ const state = store.getState().statesByGameId[gameId];
14607
+ if (!state) return null;
14608
+ if (isRpsState(state)) {
14609
+ if (state.roundState.phase !== "starting") return null;
14610
+ const remaining = new Date(state.roundState.selectionEndsAt).getTime() - nowMs;
14611
+ if (remaining <= 0) return null;
14612
+ return Math.ceil(remaining / 1e3);
14613
+ }
14614
+ const bufferEndsAt = state.bufferEndsAt;
14615
+ if (bufferEndsAt) {
14616
+ const remaining = new Date(bufferEndsAt).getTime() - nowMs;
14617
+ if (remaining <= 0) return null;
14618
+ return Math.ceil(remaining / 1e3);
14619
+ }
14620
+ return null;
14621
+ };
14622
+ const getChessClockTimes = (gameId, nowMs) => {
14623
+ const state = store.getState().statesByGameId[gameId];
14624
+ if (!state || state.gameType !== "chess") return null;
14625
+ const s = state;
14626
+ let whiteMs = s.whiteTimeMs ?? 0;
14627
+ let blackMs = s.blackTimeMs ?? 0;
14628
+ if (s.status === "active" && s.currentPlayerId) {
14629
+ const startedAt = Date.parse(s.turnStartedAt);
14630
+ if (!Number.isNaN(startedAt)) {
14631
+ const elapsed = Math.max(0, nowMs - startedAt);
14632
+ if (s.currentPlayerId === s.whitePlayerId) {
14633
+ whiteMs = Math.max(0, whiteMs - elapsed);
14634
+ } else if (s.currentPlayerId === s.blackPlayerId) {
14635
+ blackMs = Math.max(0, blackMs - elapsed);
14636
+ }
14637
+ }
14638
+ }
14639
+ return { whiteTimeMs: whiteMs, blackTimeMs: blackMs };
14640
+ };
14641
+ const getChessCapturedPieces = (gameId) => {
14642
+ const state = store.getState().statesByGameId[gameId];
14643
+ if (!state || state.gameType !== "chess") return null;
14644
+ const fen = state.fen;
14645
+ if (!fen) return { capturedByWhite: [], capturedByBlack: [] };
14646
+ const placement = fen.split(" ")[0];
14647
+ const white = {};
14648
+ const black = {};
14649
+ for (const char of placement) {
14650
+ if (char === "/" || char >= "1" && char <= "8") continue;
14651
+ const lower = char.toLowerCase();
14652
+ if (char === lower) {
14653
+ black[lower] = (black[lower] ?? 0) + 1;
14654
+ } else {
14655
+ white[lower] = (white[lower] ?? 0) + 1;
14656
+ }
14657
+ }
14658
+ const INITIAL = {
14659
+ p: 8,
14660
+ r: 2,
14661
+ n: 2,
14662
+ b: 2,
14663
+ q: 1,
14664
+ k: 1
14665
+ };
14666
+ const capturedByWhite = [];
14667
+ const capturedByBlack = [];
14668
+ for (const [type, initial] of Object.entries(INITIAL)) {
14669
+ const missingBlack = initial - (black[type] ?? 0);
14670
+ for (let i = 0; i < missingBlack; i++) capturedByWhite.push(`b${type}`);
14671
+ const missingWhite = initial - (white[type] ?? 0);
14672
+ for (let i = 0; i < missingWhite; i++) capturedByBlack.push(`w${type}`);
14673
+ }
14674
+ return { capturedByWhite, capturedByBlack };
14675
+ };
14676
+ const getTicTacToeClockTimes = (gameId, nowMs) => {
14677
+ const state = store.getState().statesByGameId[gameId];
14678
+ if (!state || state.gameType !== "tic-tac-toe") return null;
14679
+ const s = state;
14680
+ let xMs = s.xTimeMs ?? 0;
14681
+ let oMs = s.oTimeMs ?? 0;
14682
+ if (s.status === "active" && s.currentPlayerId) {
14683
+ const currentMark = s.playerMarks[s.currentPlayerId];
14684
+ const startedAt = Date.parse(s.turnStartedAt);
14685
+ if (!Number.isNaN(startedAt)) {
14686
+ const elapsed = Math.max(0, nowMs - startedAt);
14687
+ if (currentMark === "X") xMs = Math.max(0, xMs - elapsed);
14688
+ else if (currentMark === "O") oMs = Math.max(0, oMs - elapsed);
14689
+ }
14690
+ }
14691
+ return { xTimeMs: xMs, oTimeMs: oMs };
14692
+ };
14693
+ const getConnect4ClockTimes = (gameId, nowMs) => {
14694
+ const state = store.getState().statesByGameId[gameId];
14695
+ if (!state || state.gameType !== "connect-four") return null;
14696
+ const s = state;
14697
+ let redMs = s.redTimeMs ?? 0;
14698
+ let yellowMs = s.yellowTimeMs ?? 0;
14699
+ if (s.status === "active" && s.currentPlayerId) {
14700
+ const currentColor = s.playerColors[s.currentPlayerId];
14701
+ const startedAt = Date.parse(s.turnStartedAt);
14702
+ if (!Number.isNaN(startedAt)) {
14703
+ const elapsed = Math.max(0, nowMs - startedAt);
14704
+ if (currentColor === "RED") redMs = Math.max(0, redMs - elapsed);
14705
+ else if (currentColor === "YELLOW")
14706
+ yellowMs = Math.max(0, yellowMs - elapsed);
14707
+ }
14708
+ }
14709
+ return { redTimeMs: redMs, yellowTimeMs: yellowMs };
14710
+ };
14551
14711
  return {
14552
14712
  store,
14553
14713
  setBaseState,
14554
14714
  clearState,
14555
14715
  applyWsEvent,
14556
- joinGame
14716
+ joinGame,
14717
+ getCountdownDigit,
14718
+ getChessClockTimes,
14719
+ getChessCapturedPieces,
14720
+ getTicTacToeClockTimes,
14721
+ getConnect4ClockTimes
14557
14722
  };
14558
14723
  }
14559
14724
  function isRpsState(state) {
@@ -15211,12 +15376,14 @@ var WsRouter = class {
15211
15376
  }
15212
15377
  const decoded = decodeWsEvent(eventName, payload);
15213
15378
  if (!decoded) return;
15214
- this.deps.lobbyStore.applyWsEvent(decoded);
15215
- this.deps.gameStore.applyWsEvent(decoded);
15216
- this.deps.gameActionsStore.applyWsEvent(decoded);
15217
- this.deps.chatStore.applyWsEvent(decoded);
15218
- this.deps.dmThreadsStore.applyWsEvent(decoded);
15219
- this.deps.notificationsStore.applyWsEvent(decoded);
15379
+ const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
15380
+ const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
15381
+ this.deps.lobbyStore.applyWsEvent(event);
15382
+ this.deps.gameStore.applyWsEvent(event);
15383
+ this.deps.gameActionsStore.applyWsEvent(event);
15384
+ this.deps.chatStore.applyWsEvent(event);
15385
+ this.deps.dmThreadsStore.applyWsEvent(event);
15386
+ this.deps.notificationsStore.applyWsEvent(event);
15220
15387
  });
15221
15388
  }
15222
15389
  stop() {
@@ -15228,12 +15395,14 @@ var WsRouter = class {
15228
15395
  this.transport.subscribeEvent(eventName, (payload) => {
15229
15396
  const decoded = decodeWsEvent(eventName, payload);
15230
15397
  if (!decoded) return;
15231
- this.deps.lobbyStore.applyWsEvent(decoded);
15232
- this.deps.gameStore.applyWsEvent(decoded);
15233
- this.deps.gameActionsStore.applyWsEvent(decoded);
15234
- this.deps.chatStore.applyWsEvent(decoded);
15235
- this.deps.dmThreadsStore.applyWsEvent(decoded);
15236
- this.deps.notificationsStore.applyWsEvent(decoded);
15398
+ const serverTs = payload !== null && typeof payload === "object" ? payload._serverTs : void 0;
15399
+ const event = serverTs !== void 0 ? { ...decoded, _serverTs: serverTs } : decoded;
15400
+ this.deps.lobbyStore.applyWsEvent(event);
15401
+ this.deps.gameStore.applyWsEvent(event);
15402
+ this.deps.gameActionsStore.applyWsEvent(event);
15403
+ this.deps.chatStore.applyWsEvent(event);
15404
+ this.deps.dmThreadsStore.applyWsEvent(event);
15405
+ this.deps.notificationsStore.applyWsEvent(event);
15237
15406
  });
15238
15407
  }
15239
15408
  }
@@ -15380,6 +15549,28 @@ var export_toMajor = import_utils.toMajor;
15380
15549
  var import_tweetnacl = __toESM(require_nacl_fast(), 1);
15381
15550
  import { Keypair } from "@solana/web3.js";
15382
15551
  import bs58 from "bs58";
15552
+ function decodeJwtSub(token) {
15553
+ try {
15554
+ const parts2 = token.split(".");
15555
+ if (parts2.length < 2) return null;
15556
+ const base64 = parts2[1].replace(/-/g, "+").replace(/_/g, "/");
15557
+ const json = Buffer.from(base64, "base64").toString("utf-8");
15558
+ const parsed = JSON.parse(json);
15559
+ return typeof parsed.sub === "string" ? parsed.sub : null;
15560
+ } catch {
15561
+ return null;
15562
+ }
15563
+ }
15564
+ function signatureToBytes(sig) {
15565
+ if (sig.includes("-") || sig.includes("_")) {
15566
+ const padded = sig.replace(/-/g, "+").replace(/_/g, "/") + "==".slice(sig.length % 4 === 0 ? 4 : sig.length % 4);
15567
+ return new Uint8Array(Buffer.from(padded, "base64"));
15568
+ }
15569
+ if (/[+/=]/.test(sig)) {
15570
+ return new Uint8Array(Buffer.from(sig, "base64"));
15571
+ }
15572
+ return bs58.decode(sig);
15573
+ }
15383
15574
  var DimAgentClient = class {
15384
15575
  sdk;
15385
15576
  agentConfig;
@@ -15406,10 +15597,19 @@ var DimAgentClient = class {
15406
15597
  } else {
15407
15598
  this.keypair = null;
15408
15599
  }
15600
+ const storage = new NodeStorage();
15601
+ if (config.accessToken) {
15602
+ storage.set(TOKEN_KEY, config.accessToken);
15603
+ const sub = decodeJwtSub(config.accessToken);
15604
+ if (sub) {
15605
+ this.authenticated = true;
15606
+ this.userId = sub;
15607
+ }
15608
+ }
15409
15609
  this.sdk = new SDK({
15410
15610
  appId: "dim-agents",
15411
15611
  baseUrl: config.apiUrl || "https://api.dim.cool",
15412
- storage: new NodeStorage(),
15612
+ storage,
15413
15613
  autoPay: {
15414
15614
  enabled: !this.externalSignerMode,
15415
15615
  maxAmountMinor: 25e3,
@@ -15513,10 +15713,10 @@ var DimAgentClient = class {
15513
15713
  /**
15514
15714
  * Complete authentication using an externally provided signature (external signer mode).
15515
15715
  * @param address - Solana wallet address (base58)
15516
- * @param signatureBase58 - Detached ed25519 signature in base58 (from sign_solana_message)
15716
+ * @param signature - Detached ed25519 signature in base58, base64, or base64url (from sign_solana_message)
15517
15717
  */
15518
- async completeAuth(address, signatureBase58) {
15519
- const signatureBytes = bs58.decode(signatureBase58);
15718
+ async completeAuth(address, signature) {
15719
+ const signatureBytes = signatureToBytes(signature);
15520
15720
  const signedMessage = Buffer.from(signatureBytes).toString("base64");
15521
15721
  const response = await this.sdk.auth.loginWithExternalSignature(
15522
15722
  address,
@@ -15670,7 +15870,7 @@ async function requestAuthMessage(client, args) {
15670
15870
  data: {
15671
15871
  message,
15672
15872
  address: args.address,
15673
- nextStep: 'Sign this message with sign_solana_message (networkId: "solana:mainnet"), then call dim_complete_login with the address and the base58 signature.'
15873
+ nextStep: 'Sign this message with sign_solana_message (networkId: "solana:mainnet"), then call dim_complete_login with the address and the signature. Accepted formats: base58, base64, or base64url (all formats returned by wallet MCPs are accepted).'
15674
15874
  }
15675
15875
  };
15676
15876
  } catch (error) {
@@ -17844,6 +18044,22 @@ async function getGameHistory(client, args) {
17844
18044
  };
17845
18045
  }
17846
18046
  }
18047
+ async function reportUser(client, args) {
18048
+ try {
18049
+ await client.sdk.reports.create(args.userId, args.reason);
18050
+ return {
18051
+ data: {
18052
+ success: true,
18053
+ hint: "Report submitted. The DIM moderation team will review it."
18054
+ }
18055
+ };
18056
+ } catch (error) {
18057
+ return {
18058
+ error: `Failed to report user: ${error instanceof Error ? error.message : String(error)}`,
18059
+ isError: true
18060
+ };
18061
+ }
18062
+ }
17847
18063
  async function getMyStats(client) {
17848
18064
  try {
17849
18065
  if (!client.currentUserId) {
@@ -17889,7 +18105,7 @@ var TOOL_DEFINITIONS = [
17889
18105
  },
17890
18106
  {
17891
18107
  name: "dim_complete_login",
17892
- description: "External wallet login step 2: provide the wallet address and base58 signature from sign_solana_message to complete authentication with DIM.",
18108
+ description: "External wallet login step 2: provide the wallet address and signature from sign_solana_message to complete authentication with DIM.",
17893
18109
  params: {
17894
18110
  address: {
17895
18111
  type: "string",
@@ -17898,7 +18114,7 @@ var TOOL_DEFINITIONS = [
17898
18114
  },
17899
18115
  signature: {
17900
18116
  type: "string",
17901
- description: "Base58-encoded signature returned by sign_solana_message",
18117
+ description: "Signature from sign_solana_message \u2014 base58, base64, or base64url accepted",
17902
18118
  required: true
17903
18119
  }
17904
18120
  },
@@ -18342,6 +18558,23 @@ var TOOL_DEFINITIONS = [
18342
18558
  params: {},
18343
18559
  execute: (c) => getMyStats(c)
18344
18560
  },
18561
+ {
18562
+ name: "dim_report_user",
18563
+ description: "Report a user for cheating, harassment, or other violations. Rate limited to 5 reports per hour.",
18564
+ params: {
18565
+ userId: {
18566
+ type: "string",
18567
+ description: "The ID of the user to report",
18568
+ required: true
18569
+ },
18570
+ reason: {
18571
+ type: "string",
18572
+ description: "Reason for the report (max 300 characters)",
18573
+ required: true
18574
+ }
18575
+ },
18576
+ execute: (c, a) => reportUser(c, a)
18577
+ },
18345
18578
  {
18346
18579
  name: "dim_create_lobby",
18347
18580
  description: "Create a new game lobby. Bet amount is in USDC dollars (e.g., 1.00 for $1.00). A 1% fee (min 1 cent) is charged per player.",
@@ -18906,11 +19139,18 @@ async function executeWithAuthRetry(client, tool, params) {
18906
19139
  if (tool.name === "dim_login") {
18907
19140
  return tool.execute(client, params);
18908
19141
  }
19142
+ if (client.externalSignerMode) {
19143
+ return tool.execute(client, params);
19144
+ }
18909
19145
  try {
18910
19146
  const result = await tool.execute(client, params);
18911
19147
  if (!result.error && !result.isError) return result;
18912
19148
  if (!isUnauthorizedResult(result)) return result;
18913
- await client.authenticate();
19149
+ try {
19150
+ await client.authenticate();
19151
+ } catch {
19152
+ return { error: UNAUTHORIZED_MESSAGE, isError: true };
19153
+ }
18914
19154
  const retryResult = await tool.execute(client, params);
18915
19155
  if (retryResult.isError || retryResult.error) {
18916
19156
  if (isUnauthorizedResult(retryResult)) {
@@ -19113,6 +19353,10 @@ function registerResources(server2, client) {
19113
19353
  }
19114
19354
 
19115
19355
  // src/server.ts
19356
+ var HOSTED_EXCLUDED_TOOLS = /* @__PURE__ */ new Set([
19357
+ "dim_game_loop",
19358
+ "dim_get_pending_events"
19359
+ ]);
19116
19360
  function paramToZod(p) {
19117
19361
  let schema;
19118
19362
  if (p.enum) {
@@ -19133,14 +19377,14 @@ function paramToZod(p) {
19133
19377
  if (!p.required) schema = schema.optional();
19134
19378
  return schema.describe(p.description);
19135
19379
  }
19136
- function createDimMcpServer(config) {
19380
+ function buildMcpServer(client, mode) {
19137
19381
  const server2 = new McpServer({
19138
19382
  name: "dim",
19139
19383
  version: "1.0.0",
19140
19384
  description: "DIM Gaming Platform \u2014 Play games, chat, send USDC, challenge users, and earn referral income. DIM is noncustodial; your Solana address is returned by dim_get_profile and dim_get_balance. Start by calling dim_login. After login: set a username with dim_set_username, check balance with dim_get_balance, list games with dim_list_games, or apply a referral code with dim_apply_referral_code for a 10% fee discount."
19141
19385
  });
19142
- const client = new DimAgentClient(config);
19143
19386
  for (const tool of TOOL_DEFINITIONS) {
19387
+ if (mode === "hosted" && HOSTED_EXCLUDED_TOOLS.has(tool.name)) continue;
19144
19388
  const zodParams = {};
19145
19389
  for (const [key, param] of Object.entries(tool.params)) {
19146
19390
  zodParams[key] = paramToZod(param);
@@ -19170,6 +19414,7 @@ function createDimMcpServer(config) {
19170
19414
  }
19171
19415
  );
19172
19416
  }
19417
+ const gameHint = mode === "hosted" ? "Game flow: dim_create_lobby \u2192 dim_deposit_for_lobby (returns unsignedTx \u2014 sign with Phantom MCP) \u2192 dim_join_queue \u2192 poll dim_get_lobby until matched \u2192 dim_get_game_state \u2192 dim_submit_action \u2192 repeat. For real-time game_loop, use the DIM CLI (npx @dimcool/cli)." : "Game flow: For paid lobbies, dim_create_lobby \u2192 dim_deposit_for_lobby \u2192 dim_join_queue. For free lobbies, dim_create_lobby \u2192 dim_join_queue. Then dim_game_loop (blocks until your turn) \u2192 dim_submit_action \u2192 dim_game_loop \u2192 repeat. Use dim_leave_lobby to exit a lobby.";
19173
19418
  server2.tool(
19174
19419
  "dim_list_instructions",
19175
19420
  "List all available DIM tools with short descriptions.",
@@ -19183,7 +19428,7 @@ function createDimMcpServer(config) {
19183
19428
  wallet_linkage: WALLET_LINKAGE_NOTE,
19184
19429
  wallet_address: "Your DIM Solana address is returned by dim_get_profile, dim_get_balance, dim_get_wallet_activity, and dim_get_wallet_address. Use it to confirm which address is tied to DIM.",
19185
19430
  wallet_activity: "For deposits, transfers, payouts, and claimable items use dim_get_wallet_activity.",
19186
- hint: "Game flow: For paid lobbies, dim_create_lobby \u2192 dim_deposit_for_lobby \u2192 dim_join_queue. For free lobbies, dim_create_lobby \u2192 dim_join_queue. Then dim_game_loop (blocks until your turn) \u2192 dim_submit_action \u2192 dim_game_loop \u2192 repeat. Use dim_leave_lobby to exit a lobby.",
19431
+ hint: gameHint,
19187
19432
  spectate_on_game_start: "When a game starts (dim_join_queue or dim_accept_challenge returns matched/gameId), tell the user they can spectate at the spectateUrl in the tool response, or at https://dim.cool/spectate/[your DIM username].",
19188
19433
  daily_summary: "At least once per day, report to the operator your DIM progress: call dim_get_game_history (and optionally dim_get_my_stats and dim_get_referral_summary), then summarize games played and referral earnings.",
19189
19434
  move_formats: {
@@ -19204,6 +19449,15 @@ function createDimMcpServer(config) {
19204
19449
  })
19205
19450
  );
19206
19451
  registerResources(server2, client);
19452
+ return server2;
19453
+ }
19454
+ function createDimMcpServer(clientOrConfig, options) {
19455
+ const mode = options?.mode ?? "local";
19456
+ if (clientOrConfig instanceof DimAgentClient) {
19457
+ return buildMcpServer(clientOrConfig, mode);
19458
+ }
19459
+ const client = new DimAgentClient(clientOrConfig);
19460
+ const server2 = buildMcpServer(client, mode);
19207
19461
  return { server: server2, client };
19208
19462
  }
19209
19463
 
@@ -0,0 +1,31 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { DimAgentClient, DimAgentClientConfig } from '@dimcool/dim-agent-core';
3
+
4
+ /** Options controlling which tools are registered. */
5
+ interface DimMcpServerOptions {
6
+ /**
7
+ * 'local' — all tools, WS-backed game_loop included (default).
8
+ * 'hosted' — stateless HTTP-only tools; dim_game_loop and
9
+ * dim_get_pending_events are excluded.
10
+ */
11
+ mode?: 'local' | 'hosted';
12
+ }
13
+ /**
14
+ * Create a hosted MCP server from a pre-built DimAgentClient.
15
+ * Excludes WS-dependent tools (dim_game_loop, dim_get_pending_events).
16
+ * Returns only the McpServer — the caller owns the client lifecycle.
17
+ */
18
+ declare function createDimMcpServer(client: DimAgentClient, options: DimMcpServerOptions & {
19
+ mode: 'hosted';
20
+ }): McpServer;
21
+ /**
22
+ * Create a local (stdio) MCP server from a config object.
23
+ * All tools are registered including dim_game_loop.
24
+ * Returns both the McpServer and the DimAgentClient it created.
25
+ */
26
+ declare function createDimMcpServer(config: DimAgentClientConfig): {
27
+ server: McpServer;
28
+ client: DimAgentClient;
29
+ };
30
+
31
+ export { type DimMcpServerOptions, createDimMcpServer };