@champz-llc/legends-mcp-server 1.4.0 → 1.6.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 +518 -126
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -51,10 +51,14 @@ const SIGNATURE = process.env.SIGNATURE;
51
51
  const SIGNED_AT = process.env.SIGNED_AT;
52
52
  const hasWalletAuth = WALLET && SIGNATURE && SIGNED_AT;
53
53
 
54
- // Hardcoded rewards data for demo
54
+ // ============================================================
55
+ // LEGACY: Hardcoded rewards data for BuildOnBase contest demo
56
+ // NOTE: This is no longer used! Real claims now fetched from API
57
+ // with mode=claims parameter. Kept for reference only.
58
+ // ============================================================
55
59
  const DEMO_WALLET = '0xfbc159e35f56580d5d297af18a8c19f83d66088a';
56
60
 
57
- const REWARDS_DATA = {
61
+ const REWARDS_DATA_LEGACY = {
58
62
  wallet: DEMO_WALLET,
59
63
  champz_claims: [
60
64
  {
@@ -155,7 +159,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
155
159
  },
156
160
  {
157
161
  name: 'check_legends_rewards',
158
- description: 'Check claimable rewards in Legends of Champz game. Returns pending CHAMPZ tokens and USDC claims with contract details.',
162
+ description: 'Check YOUR pending claimable rewards in Legends of Champz. Returns CHAMPZ tokens and USDC claims with amounts and expiration dates. Requires wallet authentication.',
159
163
  inputSchema: {
160
164
  type: 'object',
161
165
  properties: {},
@@ -164,7 +168,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
164
168
  },
165
169
  {
166
170
  name: 'get_champz_claim_data',
167
- description: 'Get complete contract call data for claiming CHAMPZ tokens (includes ABI, parameters, signature)',
171
+ description: 'Get complete contract call data for claiming YOUR CHAMPZ token rewards. Returns contract address, ABI, function parameters, and signature ready for Base MCP execution. Requires wallet authentication.',
168
172
  inputSchema: {
169
173
  type: 'object',
170
174
  properties: {},
@@ -173,7 +177,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
173
177
  },
174
178
  {
175
179
  name: 'get_usdc_claim_data',
176
- description: 'Get complete contract call data for claiming USDC rewards (includes ABI, parameters, signature)',
180
+ description: 'Get complete contract call data for claiming YOUR USDC rewards. Returns contract address, ABI, function parameters, and signature ready for Base MCP execution. Requires wallet authentication.',
177
181
  inputSchema: {
178
182
  type: 'object',
179
183
  properties: {},
@@ -351,54 +355,154 @@ Last updated: ${new Date(stats.cached_at * 1000).toLocaleString()}`;
351
355
  }
352
356
 
353
357
  case 'check_legends_rewards':
354
- return {
355
- content: [
356
- {
357
- type: 'text',
358
- text: `Legends of Champz - Claimable Rewards\n\n${REWARDS_DATA.summary.message}\n\nDetails:\n- CHAMPZ: ${REWARDS_DATA.champz_claims[0].amount_human}\n- USDC: ${REWARDS_DATA.usdc_claims[0].amount_human}\n\nBoth claims are ready to execute on Base L2.`,
359
- },
360
- ],
361
- };
358
+ if (!hasWalletAuth) {
359
+ return {
360
+ content: [{ type: 'text', text: 'Authentication required. Visit https://legends.champz.world/mcp-setup' }],
361
+ };
362
+ }
363
+
364
+ try {
365
+ // Fetch pending claims
366
+ const url = `https://api.champz.world/game/spore-trainer/player-data?wallet=${encodeURIComponent(WALLET)}&signature=${encodeURIComponent(SIGNATURE)}&timestamp=${encodeURIComponent(SIGNED_AT)}&mode=claims`;
367
+ const response = await fetch(url);
368
+ const data = await response.json();
369
+
370
+ if (!data.success) {
371
+ throw new Error('Failed to fetch claims data');
372
+ }
373
+
374
+ if (data.champz_claims.length === 0 && data.usdc_claims.length === 0) {
375
+ return {
376
+ content: [
377
+ {
378
+ type: 'text',
379
+ text: 'šŸ„ Legends of Champz - Claimable Rewards\n\nNo pending claims available at the moment.\n\nKeep battling and holding thrones to earn rewards! āš”ļøšŸ‘‘',
380
+ },
381
+ ],
382
+ };
383
+ }
384
+
385
+ let output = 'šŸ„ Legends of Champz - Claimable Rewards\n\n';
386
+ output += `${data.message}\n\n`;
387
+ output += `šŸ’° **Total Claimable:**\n`;
388
+ if (data.total_champz !== '0.00 CHAMPZ') output += ` šŸ„ ${data.total_champz}\n`;
389
+ if (data.total_usdc !== '0.00 USDC') output += ` šŸ’µ ${data.total_usdc}\n`;
390
+ output += `\nšŸ“ Ready to execute on Base L2 (Chain ID: ${data.chain_id})\n`;
391
+
392
+ return {
393
+ content: [{ type: 'text', text: output }],
394
+ };
395
+ } catch (error) {
396
+ return {
397
+ content: [{ type: 'text', text: `Error fetching claims: ${error.message}` }],
398
+ };
399
+ }
362
400
 
363
401
  case 'get_champz_claim_data':
364
- return {
365
- content: [
366
- {
367
- type: 'text',
368
- text: JSON.stringify({
369
- claim_type: 'CHAMPZ Tokens',
370
- contract_address: REWARDS_DATA.champz_claims[0].contract,
371
- chain: 'Base (8453)',
372
- amount: REWARDS_DATA.champz_claims[0].amount_human,
373
- contract_call: {
374
- function: 'claim',
375
- parameters: REWARDS_DATA.champz_claims[0].parameters,
376
- abi: CHAMPZ_REWARDS_ABI
377
- }
378
- }, null, 2),
379
- },
380
- ],
381
- };
402
+ if (!hasWalletAuth) {
403
+ return {
404
+ content: [{ type: 'text', text: 'Authentication required. Visit https://legends.champz.world/mcp-setup' }],
405
+ };
406
+ }
407
+
408
+ try {
409
+ // Fetch pending claims
410
+ const url = `https://api.champz.world/game/spore-trainer/player-data?wallet=${encodeURIComponent(WALLET)}&signature=${encodeURIComponent(SIGNATURE)}&timestamp=${encodeURIComponent(SIGNED_AT)}&mode=claims`;
411
+ const response = await fetch(url);
412
+ const data = await response.json();
413
+
414
+ if (!data.success) {
415
+ throw new Error('Failed to fetch claims data');
416
+ }
417
+
418
+ if (data.champz_claims.length === 0) {
419
+ return {
420
+ content: [{ type: 'text', text: 'No pending CHAMPZ claims available' }],
421
+ };
422
+ }
423
+
424
+ // Return all CHAMPZ claims with contract data
425
+ const claimsOutput = data.champz_claims.map(claim => ({
426
+ claim_id: claim.claim_id,
427
+ claim_type: claim.claim_type_name,
428
+ amount: claim.amount_human,
429
+ contract_address: claim.contract,
430
+ chain: 'Base (8453)',
431
+ expires_at: claim.expires_at,
432
+ contract_call: {
433
+ function: claim.function_name,
434
+ parameters: claim.parameters,
435
+ abi_function: claim.abi_function,
436
+ abi: CHAMPZ_REWARDS_ABI
437
+ }
438
+ }));
439
+
440
+ return {
441
+ content: [
442
+ {
443
+ type: 'text',
444
+ text: JSON.stringify(claimsOutput, null, 2),
445
+ },
446
+ ],
447
+ };
448
+ } catch (error) {
449
+ return {
450
+ content: [{ type: 'text', text: `Error fetching CHAMPZ claims: ${error.message}` }],
451
+ };
452
+ }
382
453
 
383
454
  case 'get_usdc_claim_data':
384
- return {
385
- content: [
386
- {
387
- type: 'text',
388
- text: JSON.stringify({
389
- claim_type: 'USDC Rewards',
390
- contract_address: REWARDS_DATA.usdc_claims[0].contract,
391
- chain: 'Base (8453)',
392
- amount: REWARDS_DATA.usdc_claims[0].amount_human,
393
- contract_call: {
394
- function: 'claim',
395
- parameters: REWARDS_DATA.usdc_claims[0].parameters,
396
- abi: USDC_REWARDS_ABI
397
- }
398
- }, null, 2),
399
- },
400
- ],
401
- };
455
+ if (!hasWalletAuth) {
456
+ return {
457
+ content: [{ type: 'text', text: 'Authentication required. Visit https://legends.champz.world/mcp-setup' }],
458
+ };
459
+ }
460
+
461
+ try {
462
+ // Fetch pending claims
463
+ const url = `https://api.champz.world/game/spore-trainer/player-data?wallet=${encodeURIComponent(WALLET)}&signature=${encodeURIComponent(SIGNATURE)}&timestamp=${encodeURIComponent(SIGNED_AT)}&mode=claims`;
464
+ const response = await fetch(url);
465
+ const data = await response.json();
466
+
467
+ if (!data.success) {
468
+ throw new Error('Failed to fetch claims data');
469
+ }
470
+
471
+ if (data.usdc_claims.length === 0) {
472
+ return {
473
+ content: [{ type: 'text', text: 'No pending USDC claims available' }],
474
+ };
475
+ }
476
+
477
+ // Return all USDC claims with contract data
478
+ const claimsOutput = data.usdc_claims.map(claim => ({
479
+ claim_id: claim.claim_id,
480
+ claim_type: claim.claim_type_name,
481
+ amount: claim.amount_human,
482
+ contract_address: claim.contract,
483
+ chain: 'Base (8453)',
484
+ expires_at: claim.expires_at,
485
+ contract_call: {
486
+ function: claim.function_name,
487
+ parameters: claim.parameters,
488
+ abi_function: claim.abi_function,
489
+ abi: USDC_REWARDS_ABI
490
+ }
491
+ }));
492
+
493
+ return {
494
+ content: [
495
+ {
496
+ type: 'text',
497
+ text: JSON.stringify(claimsOutput, null, 2),
498
+ },
499
+ ],
500
+ };
501
+ } catch (error) {
502
+ return {
503
+ content: [{ type: 'text', text: `Error fetching USDC claims: ${error.message}` }],
504
+ };
505
+ }
402
506
 
403
507
  case 'legends_leaderboard_current':
404
508
  try {
@@ -831,13 +935,17 @@ You can still ask about:
831
935
  };
832
936
  }
833
937
 
834
- // Build card-like output with gaming vibes
835
- const rarityEmojis = {
836
- unique: 'šŸ’Ž',
837
- legendary: '🌟',
838
- epic: '🟣',
839
- rare: 'šŸ”µ',
840
- common: '⚪'
938
+ // Fetch image
939
+ const imageUrl = `https://img.champz.world${legend.image_path}`;
940
+ const base64Data = await fetchImageAsBase64(imageUrl);
941
+
942
+ // Build HTML card with embedded image
943
+ const rarityColors = {
944
+ unique: '#FFD700',
945
+ legendary: '#FF6B35',
946
+ epic: '#9D4EDD',
947
+ rare: '#4EA8DE',
948
+ common: '#CCCCCC'
841
949
  };
842
950
  const elementEmojis = {
843
951
  fire: 'šŸ”„',
@@ -848,46 +956,193 @@ You can still ask about:
848
956
  dark: 'šŸŒ‘'
849
957
  };
850
958
 
851
- let output = `╔════════════════════════╗\n`;
852
- output += `ā•‘ ${rarityEmojis[legend.rarity]} ${legend.rarity.toUpperCase()} LEGEND #${legend.legend_id} ā•‘\n`;
853
- output += `ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•\n\n`;
854
-
855
- output += `šŸ„ **${legend.name}**\n\n`;
856
-
857
- // Elements with emojis
858
- const elementIcons = legend.elements.map(e => `${elementEmojis[e] || '⭐'} ${e.charAt(0).toUpperCase() + e.slice(1)}`).join(' ');
859
- output += `${elementIcons}\n\n`;
860
-
861
- output += `⚔ **Total Power:** ${legend.total_power}\n`;
862
- output += `āš”ļø **ATK:** ${legend.attack}\n`;
863
- output += `šŸ›”ļø **DEF:** ${legend.defense}\n`;
864
- output += `šŸ’Ø **SPD:** ${legend.speed}\n\n`;
865
-
866
- output += `šŸ“… Rolled: ${new Date(legend.rolled_at).toLocaleDateString()}\n`;
867
- output += legend.is_saved ? `⭐ **Saved Trainer** - This legend is in your collection!\n` : `šŸ’” Not saved yet - save this legend to keep it forever!\n`;
868
-
869
- // Fetch image
870
- const imageUrl = `https://img.champz.world${legend.image_path}`;
871
-
872
- // Fetch and convert image to base64
873
- const base64Data = await fetchImageAsBase64(imageUrl);
874
-
875
- const content = [
876
- {
877
- type: 'text',
878
- text: output,
879
- },
880
- ];
881
-
882
- if (base64Data) {
883
- content.push({
884
- type: 'image',
885
- data: base64Data,
886
- mimeType: 'image/jpeg',
887
- });
888
- }
959
+ const elementIcons = legend.elements.map(e => `${elementEmojis[e] || '⭐'} ${e.charAt(0).toUpperCase() + e.slice(1)}`).join(' ');
960
+ const rarityColor = rarityColors[legend.rarity] || '#9D4EDD';
961
+ const rarityBadge = legend.rarity.toUpperCase();
962
+
963
+ const html = `<!DOCTYPE html>
964
+ <html>
965
+ <head>
966
+ <meta charset="UTF-8">
967
+ <style>
968
+ * { margin: 0; padding: 0; box-sizing: border-box; }
969
+ body {
970
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
971
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
972
+ display: flex;
973
+ justify-content: center;
974
+ align-items: center;
975
+ min-height: 100vh;
976
+ padding: 20px;
977
+ }
978
+ .card {
979
+ background: linear-gradient(145deg, #2a2a3e 0%, #1f1f2e 100%);
980
+ border-radius: 16px;
981
+ padding: 24px;
982
+ max-width: 400px;
983
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
984
+ border: 2px solid ${rarityColor};
985
+ position: relative;
986
+ overflow: hidden;
987
+ }
988
+ .card::before {
989
+ content: '';
990
+ position: absolute;
991
+ top: 0;
992
+ left: 0;
993
+ right: 0;
994
+ height: 4px;
995
+ background: linear-gradient(90deg, ${rarityColor}, transparent);
996
+ }
997
+ .header {
998
+ text-align: center;
999
+ margin-bottom: 16px;
1000
+ border-bottom: 2px solid rgba(255,255,255,0.1);
1001
+ padding-bottom: 12px;
1002
+ }
1003
+ .rarity-badge {
1004
+ background: ${rarityColor};
1005
+ color: #000;
1006
+ padding: 4px 12px;
1007
+ border-radius: 12px;
1008
+ font-weight: bold;
1009
+ font-size: 11px;
1010
+ display: inline-block;
1011
+ margin-bottom: 8px;
1012
+ text-transform: uppercase;
1013
+ letter-spacing: 1px;
1014
+ }
1015
+ .legend-id {
1016
+ color: #fff;
1017
+ font-size: 20px;
1018
+ font-weight: bold;
1019
+ margin-bottom: 4px;
1020
+ }
1021
+ .legend-name {
1022
+ color: ${rarityColor};
1023
+ font-size: 18px;
1024
+ font-weight: bold;
1025
+ }
1026
+ .image-container {
1027
+ text-align: center;
1028
+ margin: 16px 0;
1029
+ background: rgba(0,0,0,0.3);
1030
+ border-radius: 12px;
1031
+ padding: 12px;
1032
+ border: 1px solid rgba(255,255,255,0.1);
1033
+ }
1034
+ .legend-image {
1035
+ max-width: 200px;
1036
+ border-radius: 8px;
1037
+ display: block;
1038
+ margin: 0 auto;
1039
+ }
1040
+ .elements {
1041
+ text-align: center;
1042
+ font-size: 14px;
1043
+ color: #fff;
1044
+ margin: 12px 0;
1045
+ padding: 8px;
1046
+ background: rgba(255,255,255,0.05);
1047
+ border-radius: 8px;
1048
+ }
1049
+ .stats {
1050
+ display: grid;
1051
+ grid-template-columns: 1fr 1fr;
1052
+ gap: 12px;
1053
+ margin: 16px 0;
1054
+ }
1055
+ .stat {
1056
+ background: rgba(255,255,255,0.05);
1057
+ padding: 12px;
1058
+ border-radius: 8px;
1059
+ border-left: 3px solid ${rarityColor};
1060
+ }
1061
+ .stat-label {
1062
+ color: #aaa;
1063
+ font-size: 12px;
1064
+ margin-bottom: 4px;
1065
+ }
1066
+ .stat-value {
1067
+ color: #fff;
1068
+ font-size: 18px;
1069
+ font-weight: bold;
1070
+ }
1071
+ .power {
1072
+ grid-column: 1 / -1;
1073
+ text-align: center;
1074
+ background: linear-gradient(135deg, rgba(157, 78, 221, 0.2), rgba(77, 144, 254, 0.2));
1075
+ border: 2px solid ${rarityColor};
1076
+ }
1077
+ .power .stat-value {
1078
+ font-size: 24px;
1079
+ color: ${rarityColor};
1080
+ }
1081
+ .footer {
1082
+ margin-top: 16px;
1083
+ padding-top: 12px;
1084
+ border-top: 1px solid rgba(255,255,255,0.1);
1085
+ text-align: center;
1086
+ color: #888;
1087
+ font-size: 12px;
1088
+ }
1089
+ .saved-badge {
1090
+ display: inline-block;
1091
+ background: linear-gradient(135deg, #FFD700, #FFA500);
1092
+ color: #000;
1093
+ padding: 6px 12px;
1094
+ border-radius: 8px;
1095
+ font-weight: bold;
1096
+ margin-top: 8px;
1097
+ }
1098
+ </style>
1099
+ </head>
1100
+ <body>
1101
+ <div class="card">
1102
+ <div class="header">
1103
+ <div class="rarity-badge">${rarityBadge}</div>
1104
+ <div class="legend-id">Legend #${legend.legend_id}</div>
1105
+ <div class="legend-name">šŸ„ ${legend.name}</div>
1106
+ </div>
1107
+ ${base64Data ? `
1108
+ <div class="image-container">
1109
+ <img src="data:image/jpeg;base64,${base64Data}" alt="${legend.name}" class="legend-image">
1110
+ </div>` : ''}
1111
+ <div class="elements">${elementIcons}</div>
1112
+ <div class="stats">
1113
+ <div class="stat power">
1114
+ <div class="stat-label">⚔ TOTAL POWER</div>
1115
+ <div class="stat-value">${legend.total_power}</div>
1116
+ </div>
1117
+ <div class="stat">
1118
+ <div class="stat-label">āš”ļø ATTACK</div>
1119
+ <div class="stat-value">${legend.attack}</div>
1120
+ </div>
1121
+ <div class="stat">
1122
+ <div class="stat-label">šŸ›”ļø DEFENSE</div>
1123
+ <div class="stat-value">${legend.defense}</div>
1124
+ </div>
1125
+ <div class="stat">
1126
+ <div class="stat-label">šŸ’Ø SPEED</div>
1127
+ <div class="stat-value">${legend.speed}</div>
1128
+ </div>
1129
+ </div>
1130
+ <div class="footer">
1131
+ šŸ“… Rolled: ${new Date(legend.rolled_at).toLocaleDateString()}
1132
+ ${legend.is_saved ? '<div class="saved-badge">⭐ SAVED TRAINER</div>' : ''}
1133
+ </div>
1134
+ </div>
1135
+ </body>
1136
+ </html>`;
889
1137
 
890
- return { content };
1138
+ return {
1139
+ content: [
1140
+ {
1141
+ type: 'text',
1142
+ text: html,
1143
+ },
1144
+ ],
1145
+ };
891
1146
  } catch (error) {
892
1147
  return {
893
1148
  content: [
@@ -935,43 +1190,180 @@ You can still ask about:
935
1190
  };
936
1191
  }
937
1192
 
938
- // Build card-like output with gaming vibes
939
- const rarityEmojis = {
940
- legendary: 'šŸ‘‘',
941
- epic: 'šŸ’œ',
942
- rare: 'šŸ’™',
943
- common: 'šŸ¤'
944
- };
945
-
946
- let output = `╔════════════════════════╗\n`;
947
- output += `ā•‘ ${rarityEmojis[throne.rarity] || 'šŸ†'} ${throne.rarity.toUpperCase()} THRONE #${throne.throne_id} ā•‘\n`;
948
- output += `ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•\n\n`;
949
-
950
- output += `šŸ‘‘ **${throne.name}**\n\n`;
951
- output += `šŸŽ® **Earned in Cycle:** ${throne.cycle_id}\n`;
952
- output += `šŸ“… **Date Earned:** ${new Date(throne.earned_at).toLocaleDateString()}\n\n`;
953
- output += `šŸ† You're a champion! This throne proves you held the Guardian position and won the cycle!\n`;
954
-
955
1193
  // Fetch and convert image to base64
956
1194
  const imageUrl = `https://img.champz.world${throne.image_path}`;
957
1195
  const base64Data = await fetchImageAsBase64(imageUrl);
958
1196
 
959
- const content = [
960
- {
961
- type: 'text',
962
- text: output,
963
- },
964
- ];
1197
+ // Build HTML card with embedded image
1198
+ const rarityColors = {
1199
+ legendary: '#FFD700',
1200
+ epic: '#9D4EDD',
1201
+ rare: '#4EA8DE',
1202
+ common: '#CCCCCC'
1203
+ };
965
1204
 
966
- if (base64Data) {
967
- content.push({
968
- type: 'image',
969
- data: base64Data,
970
- mimeType: 'image/jpeg',
971
- });
972
- }
1205
+ const rarityColor = rarityColors[throne.rarity] || '#FFD700';
1206
+ const rarityBadge = throne.rarity.toUpperCase();
1207
+
1208
+ const html = `<!DOCTYPE html>
1209
+ <html>
1210
+ <head>
1211
+ <meta charset="UTF-8">
1212
+ <style>
1213
+ * { margin: 0; padding: 0; box-sizing: border-box; }
1214
+ body {
1215
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
1216
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
1217
+ display: flex;
1218
+ justify-content: center;
1219
+ align-items: center;
1220
+ min-height: 100vh;
1221
+ padding: 20px;
1222
+ }
1223
+ .card {
1224
+ background: linear-gradient(145deg, #2a2a3e 0%, #1f1f2e 100%);
1225
+ border-radius: 16px;
1226
+ padding: 24px;
1227
+ max-width: 400px;
1228
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
1229
+ border: 2px solid ${rarityColor};
1230
+ position: relative;
1231
+ overflow: hidden;
1232
+ }
1233
+ .card::before {
1234
+ content: '';
1235
+ position: absolute;
1236
+ top: 0;
1237
+ left: 0;
1238
+ right: 0;
1239
+ height: 4px;
1240
+ background: linear-gradient(90deg, ${rarityColor}, transparent);
1241
+ }
1242
+ .header {
1243
+ text-align: center;
1244
+ margin-bottom: 16px;
1245
+ border-bottom: 2px solid rgba(255,255,255,0.1);
1246
+ padding-bottom: 12px;
1247
+ }
1248
+ .rarity-badge {
1249
+ background: ${rarityColor};
1250
+ color: #000;
1251
+ padding: 4px 12px;
1252
+ border-radius: 12px;
1253
+ font-weight: bold;
1254
+ font-size: 11px;
1255
+ display: inline-block;
1256
+ margin-bottom: 8px;
1257
+ text-transform: uppercase;
1258
+ letter-spacing: 1px;
1259
+ }
1260
+ .throne-id {
1261
+ color: #fff;
1262
+ font-size: 20px;
1263
+ font-weight: bold;
1264
+ margin-bottom: 4px;
1265
+ }
1266
+ .throne-name {
1267
+ color: ${rarityColor};
1268
+ font-size: 18px;
1269
+ font-weight: bold;
1270
+ }
1271
+ .image-container {
1272
+ text-align: center;
1273
+ margin: 16px 0;
1274
+ background: rgba(0,0,0,0.3);
1275
+ border-radius: 12px;
1276
+ padding: 12px;
1277
+ border: 1px solid rgba(255,255,255,0.1);
1278
+ }
1279
+ .throne-image {
1280
+ max-width: 200px;
1281
+ border-radius: 8px;
1282
+ display: block;
1283
+ margin: 0 auto;
1284
+ }
1285
+ .info {
1286
+ background: rgba(255,255,255,0.05);
1287
+ padding: 16px;
1288
+ border-radius: 8px;
1289
+ border-left: 3px solid ${rarityColor};
1290
+ margin: 16px 0;
1291
+ }
1292
+ .info-row {
1293
+ display: flex;
1294
+ justify-content: space-between;
1295
+ color: #fff;
1296
+ padding: 8px 0;
1297
+ border-bottom: 1px solid rgba(255,255,255,0.05);
1298
+ }
1299
+ .info-row:last-child {
1300
+ border-bottom: none;
1301
+ }
1302
+ .info-label {
1303
+ color: #aaa;
1304
+ font-size: 14px;
1305
+ }
1306
+ .info-value {
1307
+ font-weight: bold;
1308
+ font-size: 14px;
1309
+ }
1310
+ .trophy-message {
1311
+ text-align: center;
1312
+ background: linear-gradient(135deg, rgba(255, 215, 0, 0.2), rgba(255, 165, 0, 0.2));
1313
+ border: 2px solid ${rarityColor};
1314
+ padding: 16px;
1315
+ border-radius: 8px;
1316
+ margin-top: 16px;
1317
+ }
1318
+ .trophy-message .emoji {
1319
+ font-size: 32px;
1320
+ display: block;
1321
+ margin-bottom: 8px;
1322
+ }
1323
+ .trophy-message .text {
1324
+ color: #fff;
1325
+ font-weight: bold;
1326
+ font-size: 14px;
1327
+ }
1328
+ </style>
1329
+ </head>
1330
+ <body>
1331
+ <div class="card">
1332
+ <div class="header">
1333
+ <div class="rarity-badge">${rarityBadge} THRONE</div>
1334
+ <div class="throne-id">Throne #${throne.throne_id}</div>
1335
+ <div class="throne-name">šŸ‘‘ ${throne.name}</div>
1336
+ </div>
1337
+ ${base64Data ? `
1338
+ <div class="image-container">
1339
+ <img src="data:image/jpeg;base64,${base64Data}" alt="${throne.name}" class="throne-image">
1340
+ </div>` : ''}
1341
+ <div class="info">
1342
+ <div class="info-row">
1343
+ <span class="info-label">šŸŽ® Cycle</span>
1344
+ <span class="info-value">#${throne.cycle_id}</span>
1345
+ </div>
1346
+ <div class="info-row">
1347
+ <span class="info-label">šŸ“… Date Earned</span>
1348
+ <span class="info-value">${new Date(throne.earned_at).toLocaleDateString()}</span>
1349
+ </div>
1350
+ </div>
1351
+ <div class="trophy-message">
1352
+ <span class="emoji">šŸ†</span>
1353
+ <span class="text">CHAMPION! You held the Guardian position and won Cycle ${throne.cycle_id}!</span>
1354
+ </div>
1355
+ </div>
1356
+ </body>
1357
+ </html>`;
973
1358
 
974
- return { content };
1359
+ return {
1360
+ content: [
1361
+ {
1362
+ type: 'text',
1363
+ text: html,
1364
+ },
1365
+ ],
1366
+ };
975
1367
  } catch (error) {
976
1368
  return {
977
1369
  content: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@champz-llc/legends-mcp-server",
3
- "version": "1.4.0",
3
+ "version": "1.6.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",