@champz-llc/legends-mcp-server 1.3.8 → 1.5.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 (2) hide show
  1. package/index.js +415 -79
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -27,13 +27,13 @@ async function fetchImageAsBase64(imageUrl) {
27
27
  const originalBuffer = Buffer.from(arrayBuffer);
28
28
  console.error(`[MCP] Original image size: ${Math.round(originalBuffer.length / 1024)}KB`);
29
29
 
30
- // Resize image to max 300x300 (maintains aspect ratio) and compress
30
+ // Resize image to max 200x200 (perfect for chat) and convert to JPEG for smaller size
31
31
  const resizedBuffer = await sharp(originalBuffer)
32
- .resize(300, 300, {
32
+ .resize(200, 200, {
33
33
  fit: 'inside',
34
34
  withoutEnlargement: true
35
35
  })
36
- .png({ quality: 85, compressionLevel: 9 })
36
+ .jpeg({ quality: 80, mozjpeg: true }) // JPEG is much smaller than PNG
37
37
  .toBuffer();
38
38
 
39
39
  const base64 = resizedBuffer.toString('base64');
@@ -659,39 +659,50 @@ You can still ask about:
659
659
  const stats = data.statistics;
660
660
  const claims = data.claims;
661
661
 
662
- let output = `Legends of Champz - Your Statistics\n\n`;
663
- output += `Player: ${displayName}\n`;
664
- output += `Wallet: ${data.wallet}\n`;
662
+ let output = `🍄 **LEGENDS OF CHAMPZ** - Player Stats 🍄\n\n`;
663
+ output += `👤 **Player:** ${displayName}\n`;
665
664
  if (data.basename) {
666
- output += `Basename: ${data.basename}\n`;
665
+ output += `🎯 **Basename:** ${data.basename}\n`;
667
666
  }
668
- output += `Member since: ${new Date(stats.member_since).toLocaleDateString()}\n\n`;
669
-
670
- output += `📦 Packs & Legends:\n`;
671
- output += ` • Total packs opened: ${stats.total_packs_opened}\n`;
672
- output += ` • CHAMPZ spent on packs: ${stats.champz_spent_on_packs.toLocaleString()}\n`;
673
- output += ` Legends owned: ${stats.legends_owned}\n`;
674
- output += ` Saved trainer slots: ${stats.saved_trainer_slots}\n\n`;
675
-
676
- output += `⚔️ Battle Statistics:\n`;
677
- output += ` • Total battles: ${stats.total_battles}\n`;
678
- output += ` • Wins: ${stats.battles_won} (${(stats.win_rate * 100).toFixed(1)}% win rate)\n`;
679
- output += ` Losses: ${stats.battles_lost}\n`;
680
- output += ` • Current streak: ${stats.current_win_streak}\n`;
681
- output += ` Best streak: ${stats.best_win_streak}\n\n`;
682
-
683
- output += `👑 Thrones & Guardian:\n`;
684
- output += ` Thrones owned: ${stats.thrones_owned}\n`;
685
- output += ` Times held guardian: ${stats.times_held_guardian}\n`;
686
- output += ` CHAMPZ spent on guardian: ${stats.champz_spent_on_guardian.toLocaleString()}\n\n`;
687
-
688
- output += `💰 Claims History:\n`;
689
- output += ` • Total USDC claimed: $${claims.total_usdc_claimed}\n`;
690
- output += ` • Total CHAMPZ claimed: ${claims.total_champz_claimed.toLocaleString()}\n`;
691
- output += ` • Battle USDC (type 6): ${claims.by_type['6'].count} claims, $${claims.by_type['6'].total_amount}\n`;
692
- output += ` Battle CHAMPZ (type 5): ${claims.by_type['5'].count} claims, ${claims.by_type['5'].total_amount.toLocaleString()} tokens\n`;
693
- output += ` Guardian USDC (type 19): ${claims.by_type['19'].count} claims, $${claims.by_type['19'].total_amount}\n`;
694
- output += ` Guardian CHAMPZ (type 18): ${claims.by_type['18'].count} claims, ${claims.by_type['18'].total_amount.toLocaleString()} tokens\n\n`;
667
+ output += `📅 **Member Since:** ${new Date(stats.member_since).toLocaleDateString()} - Let's go! 🎮\n\n`;
668
+
669
+ output += `━━━━━━━━━━━━━━━━━━━━━━\n`;
670
+ output += `📦 **PACKS & LEGENDS**\n`;
671
+ output += `━━━━━━━━━━━━━━━━━━━━━━\n`;
672
+ output += ` 🎲 Packs opened: **${stats.total_packs_opened}** ${stats.total_packs_opened > 50 ? '(Pack master! 🔥)' : ''}\n`;
673
+ output += ` 💎 CHAMPZ spent: **${stats.champz_spent_on_packs.toLocaleString()}** tokens\n`;
674
+ output += ` 🍄 Legends owned: **${stats.legends_owned}** ${stats.legends_owned > 30 ? '(Epic collection! ⭐)' : ''}\n`;
675
+ output += ` Trainer slots: **${stats.saved_trainer_slots}**\n\n`;
676
+
677
+ output += `━━━━━━━━━━━━━━━━━━━━━━\n`;
678
+ output += `⚔️ **BATTLE RECORD**\n`;
679
+ output += `━━━━━━━━━━━━━━━━━━━━━━\n`;
680
+ output += ` 🎯 Total battles: **${stats.total_battles}** fights!\n`;
681
+ const winRate = (stats.win_rate * 100).toFixed(1);
682
+ const winRateEmoji = winRate >= 70 ? '🔥🔥🔥' : winRate >= 60 ? '🔥🔥' : winRate >= 50 ? '🔥' : '💪';
683
+ output += ` 🏆 Wins: **${stats.battles_won}** (${winRate}% win rate ${winRateEmoji})\n`;
684
+ output += ` 💀 Losses: **${stats.battles_lost}**\n`;
685
+ output += ` Current streak: **${stats.current_win_streak}** ${stats.current_win_streak >= 5 ? '(ON FIRE! 🔥)' : ''}\n`;
686
+ output += ` 🌟 Best streak: **${stats.best_win_streak}** ${stats.best_win_streak >= 10 ? '(Legendary! 💎)' : ''}\n\n`;
687
+
688
+ output += `━━━━━━━━━━━━━━━━━━━━━━\n`;
689
+ output += `👑 **THRONES & GUARDIAN**\n`;
690
+ output += `━━━━━━━━━━━━━━━━━━━━━━\n`;
691
+ output += ` 🏆 Thrones owned: **${stats.thrones_owned}** ${stats.thrones_owned > 0 ? '(Champion! 👑)' : ''}\n`;
692
+ output += ` 🛡️ Times held guardian: **${stats.times_held_guardian}** ${stats.times_held_guardian > 5 ? '(Guardian master! ⚡)' : ''}\n`;
693
+ output += ` 💰 CHAMPZ spent: **${stats.champz_spent_on_guardian.toLocaleString()}** tokens\n\n`;
694
+
695
+ output += `━━━━━━━━━━━━━━━━━━━━━━\n`;
696
+ output += `💵 **REWARDS CLAIMED**\n`;
697
+ output += `━━━━━━━━━━━━━━━━━━━━━━\n`;
698
+ const totalUSDC = claims.total_usdc_claimed;
699
+ const totalCHAMPZ = claims.total_champz_claimed;
700
+ output += ` 💵 Total USDC: **$${totalUSDC}** ${totalUSDC > 50 ? '(Big earner! 💰)' : totalUSDC > 20 ? '(Nice! 🎯)' : ''}\n`;
701
+ output += ` 🍄 Total CHAMPZ: **${totalCHAMPZ.toLocaleString()}** tokens ${totalCHAMPZ > 10000 ? '(Whale alert! 🐋)' : ''}\n`;
702
+ if (claims.by_type['6'].count > 0 || claims.by_type['19'].count > 0) {
703
+ output += ` 🎉 You're earning rewards - keep battling! ⚔️\n`;
704
+ }
705
+ output += `\n`;
695
706
 
696
707
  // Build content array with text and images
697
708
  const content = [
@@ -820,37 +831,214 @@ You can still ask about:
820
831
  };
821
832
  }
822
833
 
823
- // Build output
824
- let output = `Legend #${legend.legend_id} - ${legend.name}\n\n`;
825
- output += `Rarity: ${legend.rarity.toUpperCase()}\n`;
826
- output += `Total Power: ${legend.total_power}\n`;
827
- output += `ATK: ${legend.attack} | DEF: ${legend.defense} | SPD: ${legend.speed}\n`;
828
- output += `Elements: ${legend.elements.join(', ')}\n`;
829
- output += `Rolled: ${new Date(legend.rolled_at).toLocaleDateString()}\n`;
830
- output += `Saved: ${legend.is_saved ? 'Yes ⭐' : 'No'}\n`;
831
-
832
834
  // Fetch image
833
835
  const imageUrl = `https://img.champz.world${legend.image_path}`;
834
-
835
- // Fetch and convert image to base64
836
836
  const base64Data = await fetchImageAsBase64(imageUrl);
837
837
 
838
- const content = [
839
- {
840
- type: 'text',
841
- text: output,
842
- },
843
- ];
838
+ // Build HTML card with embedded image
839
+ const rarityColors = {
840
+ unique: '#FFD700',
841
+ legendary: '#FF6B35',
842
+ epic: '#9D4EDD',
843
+ rare: '#4EA8DE',
844
+ common: '#CCCCCC'
845
+ };
846
+ const elementEmojis = {
847
+ fire: '🔥',
848
+ water: '💧',
849
+ wind: '🌪️',
850
+ earth: '🌍',
851
+ light: '✨',
852
+ dark: '🌑'
853
+ };
844
854
 
845
- if (base64Data) {
846
- content.push({
847
- type: 'image',
848
- data: base64Data,
849
- mimeType: 'image/png',
850
- });
851
- }
855
+ const elementIcons = legend.elements.map(e => `${elementEmojis[e] || '⭐'} ${e.charAt(0).toUpperCase() + e.slice(1)}`).join(' ');
856
+ const rarityColor = rarityColors[legend.rarity] || '#9D4EDD';
857
+ const rarityBadge = legend.rarity.toUpperCase();
858
+
859
+ const html = `<!DOCTYPE html>
860
+ <html>
861
+ <head>
862
+ <meta charset="UTF-8">
863
+ <style>
864
+ * { margin: 0; padding: 0; box-sizing: border-box; }
865
+ body {
866
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
867
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
868
+ display: flex;
869
+ justify-content: center;
870
+ align-items: center;
871
+ min-height: 100vh;
872
+ padding: 20px;
873
+ }
874
+ .card {
875
+ background: linear-gradient(145deg, #2a2a3e 0%, #1f1f2e 100%);
876
+ border-radius: 16px;
877
+ padding: 24px;
878
+ max-width: 400px;
879
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
880
+ border: 2px solid ${rarityColor};
881
+ position: relative;
882
+ overflow: hidden;
883
+ }
884
+ .card::before {
885
+ content: '';
886
+ position: absolute;
887
+ top: 0;
888
+ left: 0;
889
+ right: 0;
890
+ height: 4px;
891
+ background: linear-gradient(90deg, ${rarityColor}, transparent);
892
+ }
893
+ .header {
894
+ text-align: center;
895
+ margin-bottom: 16px;
896
+ border-bottom: 2px solid rgba(255,255,255,0.1);
897
+ padding-bottom: 12px;
898
+ }
899
+ .rarity-badge {
900
+ background: ${rarityColor};
901
+ color: #000;
902
+ padding: 4px 12px;
903
+ border-radius: 12px;
904
+ font-weight: bold;
905
+ font-size: 11px;
906
+ display: inline-block;
907
+ margin-bottom: 8px;
908
+ text-transform: uppercase;
909
+ letter-spacing: 1px;
910
+ }
911
+ .legend-id {
912
+ color: #fff;
913
+ font-size: 20px;
914
+ font-weight: bold;
915
+ margin-bottom: 4px;
916
+ }
917
+ .legend-name {
918
+ color: ${rarityColor};
919
+ font-size: 18px;
920
+ font-weight: bold;
921
+ }
922
+ .image-container {
923
+ text-align: center;
924
+ margin: 16px 0;
925
+ background: rgba(0,0,0,0.3);
926
+ border-radius: 12px;
927
+ padding: 12px;
928
+ border: 1px solid rgba(255,255,255,0.1);
929
+ }
930
+ .legend-image {
931
+ max-width: 200px;
932
+ border-radius: 8px;
933
+ display: block;
934
+ margin: 0 auto;
935
+ }
936
+ .elements {
937
+ text-align: center;
938
+ font-size: 14px;
939
+ color: #fff;
940
+ margin: 12px 0;
941
+ padding: 8px;
942
+ background: rgba(255,255,255,0.05);
943
+ border-radius: 8px;
944
+ }
945
+ .stats {
946
+ display: grid;
947
+ grid-template-columns: 1fr 1fr;
948
+ gap: 12px;
949
+ margin: 16px 0;
950
+ }
951
+ .stat {
952
+ background: rgba(255,255,255,0.05);
953
+ padding: 12px;
954
+ border-radius: 8px;
955
+ border-left: 3px solid ${rarityColor};
956
+ }
957
+ .stat-label {
958
+ color: #aaa;
959
+ font-size: 12px;
960
+ margin-bottom: 4px;
961
+ }
962
+ .stat-value {
963
+ color: #fff;
964
+ font-size: 18px;
965
+ font-weight: bold;
966
+ }
967
+ .power {
968
+ grid-column: 1 / -1;
969
+ text-align: center;
970
+ background: linear-gradient(135deg, rgba(157, 78, 221, 0.2), rgba(77, 144, 254, 0.2));
971
+ border: 2px solid ${rarityColor};
972
+ }
973
+ .power .stat-value {
974
+ font-size: 24px;
975
+ color: ${rarityColor};
976
+ }
977
+ .footer {
978
+ margin-top: 16px;
979
+ padding-top: 12px;
980
+ border-top: 1px solid rgba(255,255,255,0.1);
981
+ text-align: center;
982
+ color: #888;
983
+ font-size: 12px;
984
+ }
985
+ .saved-badge {
986
+ display: inline-block;
987
+ background: linear-gradient(135deg, #FFD700, #FFA500);
988
+ color: #000;
989
+ padding: 6px 12px;
990
+ border-radius: 8px;
991
+ font-weight: bold;
992
+ margin-top: 8px;
993
+ }
994
+ </style>
995
+ </head>
996
+ <body>
997
+ <div class="card">
998
+ <div class="header">
999
+ <div class="rarity-badge">${rarityBadge}</div>
1000
+ <div class="legend-id">Legend #${legend.legend_id}</div>
1001
+ <div class="legend-name">🍄 ${legend.name}</div>
1002
+ </div>
1003
+ ${base64Data ? `
1004
+ <div class="image-container">
1005
+ <img src="data:image/jpeg;base64,${base64Data}" alt="${legend.name}" class="legend-image">
1006
+ </div>` : ''}
1007
+ <div class="elements">${elementIcons}</div>
1008
+ <div class="stats">
1009
+ <div class="stat power">
1010
+ <div class="stat-label">⚡ TOTAL POWER</div>
1011
+ <div class="stat-value">${legend.total_power}</div>
1012
+ </div>
1013
+ <div class="stat">
1014
+ <div class="stat-label">⚔️ ATTACK</div>
1015
+ <div class="stat-value">${legend.attack}</div>
1016
+ </div>
1017
+ <div class="stat">
1018
+ <div class="stat-label">🛡️ DEFENSE</div>
1019
+ <div class="stat-value">${legend.defense}</div>
1020
+ </div>
1021
+ <div class="stat">
1022
+ <div class="stat-label">💨 SPEED</div>
1023
+ <div class="stat-value">${legend.speed}</div>
1024
+ </div>
1025
+ </div>
1026
+ <div class="footer">
1027
+ 📅 Rolled: ${new Date(legend.rolled_at).toLocaleDateString()}
1028
+ ${legend.is_saved ? '<div class="saved-badge">⭐ SAVED TRAINER</div>' : ''}
1029
+ </div>
1030
+ </div>
1031
+ </body>
1032
+ </html>`;
852
1033
 
853
- return { content };
1034
+ return {
1035
+ content: [
1036
+ {
1037
+ type: 'text',
1038
+ text: html,
1039
+ },
1040
+ ],
1041
+ };
854
1042
  } catch (error) {
855
1043
  return {
856
1044
  content: [
@@ -898,32 +1086,180 @@ You can still ask about:
898
1086
  };
899
1087
  }
900
1088
 
901
- // Build output
902
- let output = `Throne #${throne.throne_id} - ${throne.name}\n\n`;
903
- output += `Rarity: ${throne.rarity.toUpperCase()}\n`;
904
- output += `Earned in Cycle: ${throne.cycle_id}\n`;
905
- output += `Earned: ${new Date(throne.earned_at).toLocaleDateString()}\n`;
906
-
907
1089
  // Fetch and convert image to base64
908
1090
  const imageUrl = `https://img.champz.world${throne.image_path}`;
909
1091
  const base64Data = await fetchImageAsBase64(imageUrl);
910
1092
 
911
- const content = [
912
- {
913
- type: 'text',
914
- text: output,
915
- },
916
- ];
1093
+ // Build HTML card with embedded image
1094
+ const rarityColors = {
1095
+ legendary: '#FFD700',
1096
+ epic: '#9D4EDD',
1097
+ rare: '#4EA8DE',
1098
+ common: '#CCCCCC'
1099
+ };
917
1100
 
918
- if (base64Data) {
919
- content.push({
920
- type: 'image',
921
- data: base64Data,
922
- mimeType: 'image/png',
923
- });
924
- }
1101
+ const rarityColor = rarityColors[throne.rarity] || '#FFD700';
1102
+ const rarityBadge = throne.rarity.toUpperCase();
1103
+
1104
+ const html = `<!DOCTYPE html>
1105
+ <html>
1106
+ <head>
1107
+ <meta charset="UTF-8">
1108
+ <style>
1109
+ * { margin: 0; padding: 0; box-sizing: border-box; }
1110
+ body {
1111
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
1112
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
1113
+ display: flex;
1114
+ justify-content: center;
1115
+ align-items: center;
1116
+ min-height: 100vh;
1117
+ padding: 20px;
1118
+ }
1119
+ .card {
1120
+ background: linear-gradient(145deg, #2a2a3e 0%, #1f1f2e 100%);
1121
+ border-radius: 16px;
1122
+ padding: 24px;
1123
+ max-width: 400px;
1124
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
1125
+ border: 2px solid ${rarityColor};
1126
+ position: relative;
1127
+ overflow: hidden;
1128
+ }
1129
+ .card::before {
1130
+ content: '';
1131
+ position: absolute;
1132
+ top: 0;
1133
+ left: 0;
1134
+ right: 0;
1135
+ height: 4px;
1136
+ background: linear-gradient(90deg, ${rarityColor}, transparent);
1137
+ }
1138
+ .header {
1139
+ text-align: center;
1140
+ margin-bottom: 16px;
1141
+ border-bottom: 2px solid rgba(255,255,255,0.1);
1142
+ padding-bottom: 12px;
1143
+ }
1144
+ .rarity-badge {
1145
+ background: ${rarityColor};
1146
+ color: #000;
1147
+ padding: 4px 12px;
1148
+ border-radius: 12px;
1149
+ font-weight: bold;
1150
+ font-size: 11px;
1151
+ display: inline-block;
1152
+ margin-bottom: 8px;
1153
+ text-transform: uppercase;
1154
+ letter-spacing: 1px;
1155
+ }
1156
+ .throne-id {
1157
+ color: #fff;
1158
+ font-size: 20px;
1159
+ font-weight: bold;
1160
+ margin-bottom: 4px;
1161
+ }
1162
+ .throne-name {
1163
+ color: ${rarityColor};
1164
+ font-size: 18px;
1165
+ font-weight: bold;
1166
+ }
1167
+ .image-container {
1168
+ text-align: center;
1169
+ margin: 16px 0;
1170
+ background: rgba(0,0,0,0.3);
1171
+ border-radius: 12px;
1172
+ padding: 12px;
1173
+ border: 1px solid rgba(255,255,255,0.1);
1174
+ }
1175
+ .throne-image {
1176
+ max-width: 200px;
1177
+ border-radius: 8px;
1178
+ display: block;
1179
+ margin: 0 auto;
1180
+ }
1181
+ .info {
1182
+ background: rgba(255,255,255,0.05);
1183
+ padding: 16px;
1184
+ border-radius: 8px;
1185
+ border-left: 3px solid ${rarityColor};
1186
+ margin: 16px 0;
1187
+ }
1188
+ .info-row {
1189
+ display: flex;
1190
+ justify-content: space-between;
1191
+ color: #fff;
1192
+ padding: 8px 0;
1193
+ border-bottom: 1px solid rgba(255,255,255,0.05);
1194
+ }
1195
+ .info-row:last-child {
1196
+ border-bottom: none;
1197
+ }
1198
+ .info-label {
1199
+ color: #aaa;
1200
+ font-size: 14px;
1201
+ }
1202
+ .info-value {
1203
+ font-weight: bold;
1204
+ font-size: 14px;
1205
+ }
1206
+ .trophy-message {
1207
+ text-align: center;
1208
+ background: linear-gradient(135deg, rgba(255, 215, 0, 0.2), rgba(255, 165, 0, 0.2));
1209
+ border: 2px solid ${rarityColor};
1210
+ padding: 16px;
1211
+ border-radius: 8px;
1212
+ margin-top: 16px;
1213
+ }
1214
+ .trophy-message .emoji {
1215
+ font-size: 32px;
1216
+ display: block;
1217
+ margin-bottom: 8px;
1218
+ }
1219
+ .trophy-message .text {
1220
+ color: #fff;
1221
+ font-weight: bold;
1222
+ font-size: 14px;
1223
+ }
1224
+ </style>
1225
+ </head>
1226
+ <body>
1227
+ <div class="card">
1228
+ <div class="header">
1229
+ <div class="rarity-badge">${rarityBadge} THRONE</div>
1230
+ <div class="throne-id">Throne #${throne.throne_id}</div>
1231
+ <div class="throne-name">👑 ${throne.name}</div>
1232
+ </div>
1233
+ ${base64Data ? `
1234
+ <div class="image-container">
1235
+ <img src="data:image/jpeg;base64,${base64Data}" alt="${throne.name}" class="throne-image">
1236
+ </div>` : ''}
1237
+ <div class="info">
1238
+ <div class="info-row">
1239
+ <span class="info-label">🎮 Cycle</span>
1240
+ <span class="info-value">#${throne.cycle_id}</span>
1241
+ </div>
1242
+ <div class="info-row">
1243
+ <span class="info-label">📅 Date Earned</span>
1244
+ <span class="info-value">${new Date(throne.earned_at).toLocaleDateString()}</span>
1245
+ </div>
1246
+ </div>
1247
+ <div class="trophy-message">
1248
+ <span class="emoji">🏆</span>
1249
+ <span class="text">CHAMPION! You held the Guardian position and won Cycle ${throne.cycle_id}!</span>
1250
+ </div>
1251
+ </div>
1252
+ </body>
1253
+ </html>`;
925
1254
 
926
- return { content };
1255
+ return {
1256
+ content: [
1257
+ {
1258
+ type: 'text',
1259
+ text: html,
1260
+ },
1261
+ ],
1262
+ };
927
1263
  } catch (error) {
928
1264
  return {
929
1265
  content: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@champz-llc/legends-mcp-server",
3
- "version": "1.3.8",
3
+ "version": "1.5.0",
4
4
  "description": "MCP server for Legends of Champz - Query game stats, access personal data with signature auth, and claim rewards through Claude Desktop",
5
5
  "type": "module",
6
6
  "main": "index.js",