ping-mcp-server 0.1.18 → 0.1.20

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 (3) hide show
  1. package/dist/index.js +235 -346
  2. package/package.json +1 -1
  3. package/src/index.ts +158 -385
package/dist/index.js CHANGED
@@ -493,6 +493,15 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
493
493
  required: []
494
494
  }
495
495
  },
496
+ {
497
+ name: "ping_settings",
498
+ description: "Open Ping settings page in browser to manage connected accounts (Are.na, Spotify, etc.). Use this when the user wants to connect additional accounts or manage their profile.",
499
+ inputSchema: {
500
+ type: "object",
501
+ properties: {},
502
+ required: []
503
+ }
504
+ },
496
505
  // ────────────────────────────────────────────────────────
497
506
  // LEGACY: SET WALLET (for users without GitHub auth)
498
507
  // ────────────────────────────────────────────────────────
@@ -581,21 +590,6 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
581
590
  required: []
582
591
  }
583
592
  },
584
- {
585
- name: "ping_validate_batch",
586
- description: "Validate if a batch of questions is still available to answer before showing to user. Checks if questions are active, not exhausted (maxResponses reached), and not already answered. Returns available questions with full data + list of exhausted question IDs. Use this before presenting questions to ensure they can actually be answered.",
587
- inputSchema: {
588
- type: "object",
589
- properties: {
590
- questionIds: {
591
- type: "array",
592
- items: { type: "string" },
593
- description: 'Array of question IDs to validate (e.g., ["q1", "q2", "q3", "q4"])'
594
- }
595
- },
596
- required: ["questionIds"]
597
- }
598
- },
599
593
  {
600
594
  name: "ping_claim_reward",
601
595
  description: 'Claim pending Ping earnings and send them to your crypto wallet on Base. TRIGGERS: "claim my ping", "withdraw from ping", "cash out", "get my money", "claim rewards". Use when the user wants to withdraw, cash out, or claim their rewards. Transfers USDC to their wallet instantly. Works with GitHub login (ping_login) or legacy wallet (ping_set_wallet).',
@@ -715,12 +709,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
715
709
  return {
716
710
  content: [{
717
711
  type: "text",
718
- text: JSON.stringify({
719
- success: true,
720
- alreadyLoggedIn: true,
721
- handle: `@${existingAuth.handle}`,
722
- message: `You're already logged in as @${existingAuth.handle}. Use ping_logout to switch accounts.`
723
- }, null, 2)
712
+ text: `\u2705 Already logged in as @${existingAuth.handle}
713
+
714
+ Use ping_logout to switch accounts.`
724
715
  }]
725
716
  };
726
717
  }
@@ -733,11 +724,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
733
724
  return {
734
725
  content: [{
735
726
  type: "text",
736
- text: JSON.stringify({
737
- success: false,
738
- error: "Failed to open browser. Please visit this URL manually:",
739
- loginUrl
740
- }, null, 2)
727
+ text: `\u274C Failed to open browser
728
+
729
+ Please visit this URL manually:
730
+ ${loginUrl}`
741
731
  }]
742
732
  };
743
733
  }
@@ -747,22 +737,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
747
737
  return {
748
738
  content: [{
749
739
  type: "text",
750
- text: JSON.stringify({
751
- success: true,
752
- handle: `@${authData.handle}`,
753
- message: `\u2713 Logged in as @${authData.handle}! You can now answer questions and earn money.`,
754
- nextStep: 'Try "show me questions I can answer" or "check my ping balance"'
755
- }, null, 2)
740
+ text: `\u2705 Logged in as @${authData.handle}!
741
+
742
+ You can now answer questions and earn money.
743
+ Try "show me questions I can answer" or "check my ping balance"`
756
744
  }]
757
745
  };
758
746
  } catch (err) {
759
747
  return {
760
748
  content: [{
761
749
  type: "text",
762
- text: JSON.stringify({
763
- success: false,
764
- error: err instanceof Error ? err.message : "Login failed"
765
- })
750
+ text: `\u274C ${err instanceof Error ? err.message : "Login failed"}`
766
751
  }]
767
752
  };
768
753
  }
@@ -776,10 +761,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
776
761
  return {
777
762
  content: [{
778
763
  type: "text",
779
- text: JSON.stringify({
780
- success: true,
781
- message: "You weren't logged in. No action needed."
782
- })
764
+ text: `\u2139\uFE0F You weren't logged in. No action needed.`
783
765
  }]
784
766
  };
785
767
  }
@@ -788,12 +770,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
788
770
  return {
789
771
  content: [{
790
772
  type: "text",
791
- text: JSON.stringify({
792
- success: true,
793
- previousHandle: `@${handle}`,
794
- message: `\u2713 Logged out from @${handle}. Your local auth has been cleared.`,
795
- nextStep: "Use ping_login to sign in with a different account."
796
- }, null, 2)
773
+ text: `\u2705 Logged out from @${handle}
774
+
775
+ Use ping_login to sign in with a different account.`
797
776
  }]
798
777
  };
799
778
  }
@@ -841,10 +820,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
841
820
  // ────────────────────────────────────────────────────────
842
821
  case "ping_welcome": {
843
822
  const auth = getAuth();
844
- const MCP_VERSION = "0.1.18";
823
+ const MCP_VERSION = "0.1.19";
845
824
  let userLine = "\u274C Not logged in";
825
+ let arenaBadge = "";
846
826
  if (auth) {
847
827
  userLine = `@${auth.handle}`;
828
+ try {
829
+ const connections = await apiRequest("/users/me/connections");
830
+ if (connections.arena) {
831
+ arenaBadge = " \u2022 \u{1F3A8} Are.na";
832
+ }
833
+ } catch {
834
+ }
848
835
  }
849
836
  let questionsAvailable = 0;
850
837
  let answersToday = 0;
@@ -869,7 +856,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
869
856
  const banner = `\u2588\u2580\u2588 \u2588 \u2588\u2584\u2591\u2588 \u2588\u2580\u2580 v${MCP_VERSION}${updateBadge}
870
857
  \u2588\u2580\u2580 \u2588 \u2588\u2591\u2580\u2588 \u2588\u2584\u2588 \u{1F4B0} Get paid to share your knowledge
871
858
  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
872
- \u{1F464} ${userLine} \u2022 \u{1F4EC} ${questionsAvailable} Qs \u2022 \u{1F3C6} ${answersText} \u2022 \u{1F4B5} ${claimedToday}${loginHint}`;
859
+ \u{1F464} ${userLine}${arenaBadge} \u2022 \u{1F4EC} ${questionsAvailable} Qs \u2022 \u{1F3C6} ${answersText} \u2022 \u{1F4B5} ${claimedToday}${loginHint}`;
873
860
  return {
874
861
  content: [{
875
862
  type: "text",
@@ -878,6 +865,39 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
878
865
  };
879
866
  }
880
867
  // ────────────────────────────────────────────────────────
868
+ // SETTINGS (Open settings page in browser)
869
+ // ────────────────────────────────────────────────────────
870
+ case "ping_settings": {
871
+ const auth = getAuth();
872
+ if (!auth) {
873
+ return {
874
+ content: [{
875
+ type: "text",
876
+ text: "\u274C Please login first with ping_login"
877
+ }]
878
+ };
879
+ }
880
+ const settingsUrl = `${API_BASE_URL}/settings?token=${auth.githubId}`;
881
+ try {
882
+ await openBrowser(settingsUrl);
883
+ } catch {
884
+ return {
885
+ content: [{
886
+ type: "text",
887
+ text: `\u2699\uFE0F Open this URL to manage settings:
888
+
889
+ ${settingsUrl}`
890
+ }]
891
+ };
892
+ }
893
+ return {
894
+ content: [{
895
+ type: "text",
896
+ text: "\u2699\uFE0F Opening settings in browser...\n\nConnect accounts like Are.na to enhance your expert profile."
897
+ }]
898
+ };
899
+ }
900
+ // ────────────────────────────────────────────────────────
881
901
  // SET WALLET (Legacy)
882
902
  // ────────────────────────────────────────────────────────
883
903
  case "ping_set_wallet": {
@@ -886,11 +906,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
886
906
  return {
887
907
  content: [{
888
908
  type: "text",
889
- text: JSON.stringify({
890
- success: false,
891
- error: "Missing wallet address.",
892
- hint: "Please provide your Ethereum wallet address (starts with 0x)."
893
- }, null, 2)
909
+ text: `\u274C Missing wallet address
910
+
911
+ Please provide your Ethereum wallet address (starts with 0x).`
894
912
  }]
895
913
  };
896
914
  }
@@ -898,11 +916,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
898
916
  return {
899
917
  content: [{
900
918
  type: "text",
901
- text: JSON.stringify({
902
- success: false,
903
- error: "Invalid wallet address format.",
904
- hint: "Wallet address must be 42 characters starting with 0x (e.g., 0x1234...abcd). Check for typos."
905
- }, null, 2)
919
+ text: `\u274C Invalid wallet address format
920
+
921
+ Wallet address must be 42 characters starting with 0x (e.g., 0x1234...abcd).`
906
922
  }]
907
923
  };
908
924
  }
@@ -910,12 +926,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
910
926
  return {
911
927
  content: [{
912
928
  type: "text",
913
- text: JSON.stringify({
914
- success: true,
915
- walletAddress,
916
- message: `\u2713 Wallet set to ${walletAddress}. You can now check earnings, answer questions, and claim rewards!`,
917
- nextStep: 'Try "show me questions I can answer" or "check my ping earnings"'
918
- }, null, 2)
929
+ text: `\u2705 Wallet set to ${walletAddress}
930
+
931
+ You can now check earnings, answer questions, and claim rewards!`
919
932
  }]
920
933
  };
921
934
  }
@@ -980,11 +993,9 @@ Wallet: ${data.walletAddress}`
980
993
  return {
981
994
  content: [{
982
995
  type: "text",
983
- text: JSON.stringify({
984
- success: false,
985
- error: "Missing question ID.",
986
- hint: "Use ping_answer_flow to see available questions and answer them."
987
- }, null, 2)
996
+ text: `\u274C Missing question ID
997
+
998
+ Use ping_answer_flow to see available questions.`
988
999
  }]
989
1000
  };
990
1001
  }
@@ -992,11 +1003,9 @@ Wallet: ${data.walletAddress}`
992
1003
  return {
993
1004
  content: [{
994
1005
  type: "text",
995
- text: JSON.stringify({
996
- success: false,
997
- error: "Answer cannot be empty.",
998
- hint: "Please provide a thoughtful response to the question."
999
- }, null, 2)
1006
+ text: `\u274C Answer cannot be empty
1007
+
1008
+ Please provide a thoughtful response.`
1000
1009
  }]
1001
1010
  };
1002
1011
  }
@@ -1006,11 +1015,9 @@ Wallet: ${data.walletAddress}`
1006
1015
  return {
1007
1016
  content: [{
1008
1017
  type: "text",
1009
- text: JSON.stringify({
1010
- success: false,
1011
- error: "Not logged in.",
1012
- hint: "Use ping_login to connect your GitHub account before answering questions."
1013
- }, null, 2)
1018
+ text: `\u274C Not logged in
1019
+
1020
+ Use ping_login to connect your GitHub account first.`
1014
1021
  }]
1015
1022
  };
1016
1023
  }
@@ -1030,14 +1037,11 @@ Wallet: ${data.walletAddress}`
1030
1037
  return {
1031
1038
  content: [{
1032
1039
  type: "text",
1033
- text: JSON.stringify({
1034
- success: false,
1035
- status: "rejected",
1036
- aiScore: data.aiScore,
1037
- reason: data.reason,
1038
- tip: data.tip || "Try providing a more thoughtful, detailed response.",
1039
- message: data.reason || "Answer was rejected by AI review."
1040
- })
1040
+ text: `\u274C Answer rejected${data.aiScore ? ` (score: ${data.aiScore})` : ""}
1041
+
1042
+ ${data.reason || "Did not meet quality threshold."}
1043
+
1044
+ \u{1F4A1} ${data.tip || "Try providing a more thoughtful, detailed response."}`
1041
1045
  }]
1042
1046
  };
1043
1047
  }
@@ -1045,43 +1049,29 @@ Wallet: ${data.walletAddress}`
1045
1049
  return {
1046
1050
  content: [{
1047
1051
  type: "text",
1048
- text: JSON.stringify({
1049
- success: false,
1050
- error: "question_exhausted",
1051
- message: data.message || "This question is no longer accepting answers (maxResponses reached).",
1052
- hint: "Skip this question and continue with others in your batch."
1053
- })
1052
+ text: `\u26A0\uFE0F Question no longer available
1053
+
1054
+ This question has reached its maximum responses. Skip it and continue with others.`
1054
1055
  }]
1055
1056
  };
1056
1057
  }
1057
1058
  return {
1058
1059
  content: [{
1059
1060
  type: "text",
1060
- text: JSON.stringify({
1061
- success: false,
1062
- error: data.error || "Failed to submit answer"
1063
- })
1061
+ text: `\u274C ${data.error || "Failed to submit answer"}`
1064
1062
  }]
1065
1063
  };
1066
1064
  }
1067
- const response = {
1068
- success: true,
1069
- status: data.status || "approved",
1070
- earned: data.earned,
1071
- aiScore: data.aiScore,
1072
- message: data.message || `Answer approved! You earned ${data.earned}.`
1073
- };
1065
+ let successText = `\u2705 Answer approved! You earned ${data.earned}`;
1074
1066
  if (data.txHash) {
1075
- response.transaction = {
1076
- txHash: data.txHash,
1077
- explorerUrl: data.explorerUrl || `https://basescan.org/tx/${data.txHash}`
1078
- };
1067
+ successText += `
1068
+
1069
+ \u{1F517} Transaction: ${data.explorerUrl || `https://basescan.org/tx/${data.txHash}`}`;
1079
1070
  }
1080
- response.nextStep = "Your earnings are in your Ping balance. Keep answering to earn more!";
1081
1071
  return {
1082
1072
  content: [{
1083
1073
  type: "text",
1084
- text: JSON.stringify(response, null, 2)
1074
+ text: successText
1085
1075
  }]
1086
1076
  };
1087
1077
  }
@@ -1094,11 +1084,9 @@ Wallet: ${data.walletAddress}`
1094
1084
  return {
1095
1085
  content: [{
1096
1086
  type: "text",
1097
- text: JSON.stringify({
1098
- success: false,
1099
- error: "Not logged in.",
1100
- hint: "Use ping_login to connect your GitHub account first."
1101
- }, null, 2)
1087
+ text: `\u274C Not logged in
1088
+
1089
+ Use ping_login to connect your GitHub account first.`
1102
1090
  }]
1103
1091
  };
1104
1092
  }
@@ -1107,11 +1095,9 @@ Wallet: ${data.walletAddress}`
1107
1095
  return {
1108
1096
  content: [{
1109
1097
  type: "text",
1110
- text: JSON.stringify({
1111
- success: false,
1112
- error: "No answers provided.",
1113
- hint: "Provide an array of answers with questionId and answer fields."
1114
- }, null, 2)
1098
+ text: `\u274C No answers provided
1099
+
1100
+ Provide an array of answers with questionId and answer fields.`
1115
1101
  }]
1116
1102
  };
1117
1103
  }
@@ -1146,11 +1132,9 @@ Wallet: ${data.walletAddress}`
1146
1132
  return {
1147
1133
  content: [{
1148
1134
  type: "text",
1149
- text: JSON.stringify({
1150
- success: false,
1151
- error: "Not logged in.",
1152
- hint: "Use ping_login to connect your GitHub account first. Then you can start answering questions and earning money."
1153
- }, null, 2)
1135
+ text: `\u274C Not logged in
1136
+
1137
+ Use ping_login to connect your GitHub account first.`
1154
1138
  }]
1155
1139
  };
1156
1140
  }
@@ -1174,76 +1158,24 @@ Wallet: ${data.walletAddress}`
1174
1158
  suggestedAnswers: suggestions
1175
1159
  };
1176
1160
  });
1177
- const summaryCard = `
1178
- \u{1F4EC} ${questionsWithSuggestions.length} questions available \xB7 Earn up to ${data.totalPotentialEarnings}
1161
+ let output = `\u{1F4EC} ${questionsWithSuggestions.length} questions available \xB7 Earn up to ${data.totalPotentialEarnings}
1179
1162
 
1180
- Ready to start answering!`;
1181
- const flowData = {
1182
- _meta: "INTERNAL: Do not display this JSON to user. Use AskUserQuestion to present questions.",
1183
- questions: questionsWithSuggestions,
1184
- instructions: "Present questions in batches of 4 using AskUserQuestion. Use ping_validate_batch first, then ping_submit_batch after user answers. Auto-claim at end."
1185
- };
1186
- return {
1187
- content: [{
1188
- type: "text",
1189
- text: summaryCard.trim() + "\n\n<!-- FLOW_DATA:" + JSON.stringify(flowData) + " -->"
1190
- }]
1191
- };
1192
- }
1193
- // ────────────────────────────────────────────────────────
1194
- // VALIDATE BATCH
1195
- // ────────────────────────────────────────────────────────
1196
- case "ping_validate_batch": {
1197
- const { questionIds } = args || {};
1198
- if (!questionIds || !Array.isArray(questionIds) || questionIds.length === 0) {
1199
- return {
1200
- content: [{
1201
- type: "text",
1202
- text: JSON.stringify({
1203
- success: false,
1204
- error: "Invalid question IDs provided. Expected an array of question IDs."
1205
- }, null, 2)
1206
- }]
1207
- };
1208
- }
1209
- const auth = getAuth();
1210
- if (!auth) {
1211
- return {
1212
- content: [{
1213
- type: "text",
1214
- text: JSON.stringify({
1215
- success: false,
1216
- error: "Not logged in.",
1217
- hint: "Use ping_login to connect your GitHub account first."
1218
- }, null, 2)
1219
- }]
1220
- };
1221
- }
1222
- const data = await apiRequest("/questions/validate", {
1223
- method: "POST",
1224
- body: JSON.stringify({ questionIds })
1225
- });
1226
- const availableWithSuggestions = data.available.map((q) => {
1227
- const suggestions = generateSuggestedAnswers(q.text, q.category);
1228
- return {
1229
- id: q.id,
1230
- question: q.text,
1231
- reward: q.reward,
1232
- rewardCents: q.rewardCents,
1233
- category: q.category,
1234
- suggestedAnswers: suggestions
1235
- };
1163
+ `;
1164
+ questionsWithSuggestions.forEach((q, i) => {
1165
+ output += `${i + 1}. [${q.reward}] ${q.question}
1166
+ `;
1167
+ output += ` Suggestions: ${q.suggestedAnswers.slice(0, 2).join(" | ")}
1168
+ `;
1169
+ output += ` ID: ${q.id}
1170
+
1171
+ `;
1236
1172
  });
1173
+ output += `
1174
+ \u{1F4A1} Present these in batches of 4 using AskUserQuestion. After collecting answers, use ping_submit_batch to submit them all at once.`;
1237
1175
  return {
1238
1176
  content: [{
1239
1177
  type: "text",
1240
- text: JSON.stringify({
1241
- success: true,
1242
- available: availableWithSuggestions,
1243
- exhausted: data.exhausted,
1244
- totalRemaining: data.totalRemaining,
1245
- message: data.available.length === 0 ? "All questions in this batch are no longer available." : data.exhausted.length > 0 ? `${data.available.length} questions available, ${data.exhausted.length} exhausted.` : `All ${data.available.length} questions are available!`
1246
- }, null, 2)
1178
+ text: output.trim()
1247
1179
  }]
1248
1180
  };
1249
1181
  }
@@ -1257,11 +1189,9 @@ Ready to start answering!`;
1257
1189
  return {
1258
1190
  content: [{
1259
1191
  type: "text",
1260
- text: JSON.stringify({
1261
- success: false,
1262
- error: "Not logged in.",
1263
- hint: "Use ping_login to connect your GitHub account before claiming rewards."
1264
- }, null, 2)
1192
+ text: `\u274C Not logged in
1193
+
1194
+ Use ping_login to connect your GitHub account first.`
1265
1195
  }]
1266
1196
  };
1267
1197
  }
@@ -1275,39 +1205,29 @@ Ready to start answering!`;
1275
1205
  });
1276
1206
  if (!data.success) {
1277
1207
  let errorMsg = data.error || "Claim failed";
1278
- let hint = "Please try again later.";
1279
1208
  if (errorMsg.toLowerCase().includes("nothing to claim") || errorMsg.toLowerCase().includes("no pending")) {
1280
- errorMsg = "No earnings to claim.";
1281
- hint = "Answer more questions to earn rewards, then claim them here.";
1282
- } else if (errorMsg.toLowerCase().includes("insufficient")) {
1283
- hint = "Your balance may be lower than the minimum claim amount.";
1209
+ return {
1210
+ content: [{
1211
+ type: "text",
1212
+ text: `\u2139\uFE0F No earnings to claim
1213
+
1214
+ Answer more questions to earn rewards!`
1215
+ }]
1216
+ };
1284
1217
  }
1285
1218
  return {
1286
1219
  content: [{
1287
1220
  type: "text",
1288
- text: JSON.stringify({
1289
- success: false,
1290
- error: errorMsg,
1291
- hint
1292
- }, null, 2)
1221
+ text: `\u274C ${errorMsg}`
1293
1222
  }]
1294
1223
  };
1295
1224
  }
1296
1225
  return {
1297
1226
  content: [{
1298
1227
  type: "text",
1299
- text: JSON.stringify({
1300
- success: true,
1301
- claimed: data.claimed,
1302
- transaction: {
1303
- hash: data.txHash,
1304
- chain: "Base",
1305
- token: "USDC",
1306
- to: wallet,
1307
- explorerUrl: `https://basescan.org/tx/${data.txHash}`
1308
- },
1309
- message: `Success! ${data.claimed} USDC sent to your wallet on Base!`
1310
- }, null, 2)
1228
+ text: `\u{1F4B8} Success! ${data.claimed} USDC sent to your wallet on Base!
1229
+
1230
+ \u{1F517} https://basescan.org/tx/${data.txHash}`
1311
1231
  }]
1312
1232
  };
1313
1233
  }
@@ -1332,11 +1252,9 @@ Ready to start answering!`;
1332
1252
  return {
1333
1253
  content: [{
1334
1254
  type: "text",
1335
- text: JSON.stringify({
1336
- success: false,
1337
- error: "Not logged in.",
1338
- hint: "Use ping_login to connect your GitHub account before creating questions."
1339
- }, null, 2)
1255
+ text: `\u274C Not logged in
1256
+
1257
+ Use ping_login to connect your GitHub account first.`
1340
1258
  }]
1341
1259
  };
1342
1260
  }
@@ -1345,11 +1263,9 @@ Ready to start answering!`;
1345
1263
  return {
1346
1264
  content: [{
1347
1265
  type: "text",
1348
- text: JSON.stringify({
1349
- success: false,
1350
- error: "Question is too short.",
1351
- hint: "Please write a question that is at least 10 characters long."
1352
- }, null, 2)
1266
+ text: `\u274C Question is too short
1267
+
1268
+ Please write at least 10 characters.`
1353
1269
  }]
1354
1270
  };
1355
1271
  }
@@ -1357,11 +1273,9 @@ Ready to start answering!`;
1357
1273
  return {
1358
1274
  content: [{
1359
1275
  type: "text",
1360
- text: JSON.stringify({
1361
- success: false,
1362
- error: "Invalid reward amount.",
1363
- hint: "Reward must be at least 1 cent. Example: 25 = $0.25 per answer."
1364
- }, null, 2)
1276
+ text: `\u274C Invalid reward amount
1277
+
1278
+ Reward must be at least 1 cent. Example: 25 = $0.25 per answer.`
1365
1279
  }]
1366
1280
  };
1367
1281
  }
@@ -1375,41 +1289,39 @@ Ready to start answering!`;
1375
1289
  })
1376
1290
  });
1377
1291
  if (!data.success) {
1378
- let errorMsg = data.error || "Failed to create question";
1379
- let hint = "Please try again.";
1292
+ const errorMsg = data.error || "Failed to create question";
1380
1293
  if (errorMsg.toLowerCase().includes("insufficient") || errorMsg.toLowerCase().includes("balance")) {
1381
- hint = "Use ping_deposit to add funds to your account first.";
1382
- } else if (errorMsg.toLowerCase().includes("duplicate")) {
1383
- hint = "A similar question may already exist. Try rephrasing.";
1294
+ return {
1295
+ content: [{
1296
+ type: "text",
1297
+ text: `\u274C Insufficient funds
1298
+
1299
+ Use ping_deposit to add funds first.`
1300
+ }]
1301
+ };
1384
1302
  }
1385
1303
  return {
1386
1304
  content: [{
1387
1305
  type: "text",
1388
- text: JSON.stringify({
1389
- success: false,
1390
- error: errorMsg,
1391
- hint
1392
- }, null, 2)
1306
+ text: `\u274C ${errorMsg}`
1393
1307
  }]
1394
1308
  };
1395
1309
  }
1396
- const response = {
1397
- success: true,
1398
- question: data.question,
1399
- message: `Question created! Reward: ${data.question?.reward} per answer`,
1400
- nextStep: "Answers will be AI-reviewed and you'll be notified when they arrive."
1401
- };
1310
+ let successText = `\u2705 Question created!
1311
+
1312
+ "${data.question?.text}"
1313
+
1314
+ Reward: ${data.question?.reward} per answer
1315
+ ID: ${data.question?.id}`;
1402
1316
  if (data.transaction?.txHash) {
1403
- response.transaction = {
1404
- txHash: data.transaction.txHash,
1405
- basescanUrl: `https://basescan.org/tx/${data.transaction.txHash}`
1406
- };
1407
- response.message = `\u2713 Question created on-chain! Reward: ${data.question?.reward} per answer`;
1317
+ successText += `
1318
+
1319
+ \u{1F517} https://basescan.org/tx/${data.transaction.txHash}`;
1408
1320
  }
1409
1321
  return {
1410
1322
  content: [{
1411
1323
  type: "text",
1412
- text: JSON.stringify(response, null, 2)
1324
+ text: successText
1413
1325
  }]
1414
1326
  };
1415
1327
  }
@@ -1422,11 +1334,9 @@ Ready to start answering!`;
1422
1334
  return {
1423
1335
  content: [{
1424
1336
  type: "text",
1425
- text: JSON.stringify({
1426
- success: false,
1427
- error: "Not logged in.",
1428
- hint: "Use ping_login to connect your GitHub account."
1429
- }, null, 2)
1337
+ text: `\u274C Not logged in
1338
+
1339
+ Use ping_login to connect your GitHub account.`
1430
1340
  }]
1431
1341
  };
1432
1342
  }
@@ -1436,24 +1346,27 @@ Ready to start answering!`;
1436
1346
  return {
1437
1347
  content: [{
1438
1348
  type: "text",
1439
- text: JSON.stringify({
1440
- success: true,
1441
- questions: [],
1442
- message: "You haven't created any questions yet.",
1443
- hint: "Use ping_create_question to ask your first question and get answers from the community!"
1444
- }, null, 2)
1349
+ text: `\u{1F4ED} No questions yet
1350
+
1351
+ Use ping_create_question to ask your first question!`
1445
1352
  }]
1446
1353
  };
1447
1354
  }
1355
+ let output = `\u{1F4CB} Your Questions (${data.questions.length} total)
1356
+
1357
+ `;
1358
+ data.questions.forEach((q, i) => {
1359
+ const statusEmoji = q.status === "active" ? "\u{1F7E2}" : "\u26AA";
1360
+ output += `${i + 1}. ${statusEmoji} "${q.text}"
1361
+ `;
1362
+ output += ` Reward: ${q.reward} \xB7 Responses: ${q.responseCount}/${q.maxResponses} \xB7 ID: ${q.id}
1363
+
1364
+ `;
1365
+ });
1448
1366
  return {
1449
1367
  content: [{
1450
1368
  type: "text",
1451
- text: JSON.stringify({
1452
- success: true,
1453
- questions: data.questions,
1454
- total: data.total,
1455
- message: `Found ${data.questions.length} question${data.questions.length !== 1 ? "s" : ""}`
1456
- }, null, 2)
1369
+ text: output.trim()
1457
1370
  }]
1458
1371
  };
1459
1372
  }
@@ -1466,11 +1379,9 @@ Ready to start answering!`;
1466
1379
  return {
1467
1380
  content: [{
1468
1381
  type: "text",
1469
- text: JSON.stringify({
1470
- success: false,
1471
- error: "Not logged in.",
1472
- hint: "Use ping_login to connect your GitHub account."
1473
- }, null, 2)
1382
+ text: `\u274C Not logged in
1383
+
1384
+ Use ping_login to connect your GitHub account.`
1474
1385
  }]
1475
1386
  };
1476
1387
  }
@@ -1479,11 +1390,9 @@ Ready to start answering!`;
1479
1390
  return {
1480
1391
  content: [{
1481
1392
  type: "text",
1482
- text: JSON.stringify({
1483
- success: false,
1484
- error: "Missing question ID.",
1485
- hint: "Use ping_my_questions to see your questions and their IDs."
1486
- }, null, 2)
1393
+ text: `\u274C Missing question ID
1394
+
1395
+ Use ping_my_questions to see your questions and their IDs.`
1487
1396
  }]
1488
1397
  };
1489
1398
  }
@@ -1492,27 +1401,27 @@ Ready to start answering!`;
1492
1401
  return {
1493
1402
  content: [{
1494
1403
  type: "text",
1495
- text: JSON.stringify({
1496
- success: true,
1497
- question: data.question,
1498
- responses: [],
1499
- total: 0,
1500
- message: "No responses yet.",
1501
- hint: "Answers usually start arriving within a few hours. Check back later!"
1502
- }, null, 2)
1404
+ text: `\u{1F4ED} No responses yet for "${data.question.text}"
1405
+
1406
+ Answers usually arrive within a few hours. Check back later!`
1503
1407
  }]
1504
1408
  };
1505
1409
  }
1410
+ let output = `\u{1F4AC} Responses to "${data.question.text}" (${data.responses.length} total)
1411
+
1412
+ `;
1413
+ data.responses.forEach((r, i) => {
1414
+ const statusEmoji = r.status === "approved" ? "\u2705" : r.status === "rejected" ? "\u274C" : "\u23F3";
1415
+ output += `${i + 1}. ${statusEmoji} "${r.answerText.slice(0, 100)}${r.answerText.length > 100 ? "..." : ""}"
1416
+ `;
1417
+ if (r.aiScore) output += ` Score: ${r.aiScore}/100
1418
+ `;
1419
+ output += "\n";
1420
+ });
1506
1421
  return {
1507
1422
  content: [{
1508
1423
  type: "text",
1509
- text: JSON.stringify({
1510
- success: true,
1511
- question: data.question,
1512
- responses: data.responses,
1513
- total: data.total,
1514
- message: `Found ${data.responses.length} response${data.responses.length !== 1 ? "s" : ""} to your question`
1515
- }, null, 2)
1424
+ text: output.trim()
1516
1425
  }]
1517
1426
  };
1518
1427
  }
@@ -1525,11 +1434,9 @@ Ready to start answering!`;
1525
1434
  return {
1526
1435
  content: [{
1527
1436
  type: "text",
1528
- text: JSON.stringify({
1529
- success: false,
1530
- error: "Not logged in.",
1531
- hint: "Use ping_login to connect your GitHub account."
1532
- }, null, 2)
1437
+ text: `\u274C Not logged in
1438
+
1439
+ Use ping_login to connect your GitHub account.`
1533
1440
  }]
1534
1441
  };
1535
1442
  }
@@ -1538,11 +1445,9 @@ Ready to start answering!`;
1538
1445
  return {
1539
1446
  content: [{
1540
1447
  type: "text",
1541
- text: JSON.stringify({
1542
- success: false,
1543
- error: "Missing question ID.",
1544
- hint: "Use ping_my_questions to see your questions and their IDs."
1545
- }, null, 2)
1448
+ text: `\u274C Missing question ID
1449
+
1450
+ Use ping_my_questions to see your questions and their IDs.`
1546
1451
  }]
1547
1452
  };
1548
1453
  }
@@ -1550,32 +1455,20 @@ Ready to start answering!`;
1550
1455
  method: "POST"
1551
1456
  });
1552
1457
  if (!data.success) {
1553
- let errorMsg = data.error || "Failed to close question";
1554
- let hint = "Please try again.";
1555
- if (errorMsg.toLowerCase().includes("not found")) {
1556
- hint = "Check that the question ID is correct using ping_my_questions.";
1557
- } else if (errorMsg.toLowerCase().includes("already closed")) {
1558
- hint = "This question has already been closed.";
1559
- }
1458
+ const errorMsg = data.error || "Failed to close question";
1560
1459
  return {
1561
1460
  content: [{
1562
1461
  type: "text",
1563
- text: JSON.stringify({
1564
- success: false,
1565
- error: errorMsg,
1566
- hint
1567
- }, null, 2)
1462
+ text: `\u274C ${errorMsg}`
1568
1463
  }]
1569
1464
  };
1570
1465
  }
1571
1466
  return {
1572
1467
  content: [{
1573
1468
  type: "text",
1574
- text: JSON.stringify({
1575
- success: true,
1576
- refunded: data.refunded,
1577
- message: `Question closed. ${data.refunded} refunded to your balance.`
1578
- }, null, 2)
1469
+ text: `\u2705 Question closed
1470
+
1471
+ ${data.refunded} refunded to your balance.`
1579
1472
  }]
1580
1473
  };
1581
1474
  }
@@ -1588,11 +1481,9 @@ Ready to start answering!`;
1588
1481
  return {
1589
1482
  content: [{
1590
1483
  type: "text",
1591
- text: JSON.stringify({
1592
- success: false,
1593
- error: "Not logged in.",
1594
- hint: "Use ping_login to connect your GitHub account before adding funds."
1595
- }, null, 2)
1484
+ text: `\u274C Not logged in
1485
+
1486
+ Use ping_login to connect your GitHub account first.`
1596
1487
  }]
1597
1488
  };
1598
1489
  }
@@ -1601,11 +1492,9 @@ Ready to start answering!`;
1601
1492
  return {
1602
1493
  content: [{
1603
1494
  type: "text",
1604
- text: JSON.stringify({
1605
- success: false,
1606
- error: "Deposit amount too small.",
1607
- hint: "Minimum deposit is $1.00 (100 cents). Example: 1000 = $10.00"
1608
- }, null, 2)
1495
+ text: `\u274C Deposit amount too small
1496
+
1497
+ Minimum deposit is $1.00 (100 cents). Example: 1000 = $10.00`
1609
1498
  }]
1610
1499
  };
1611
1500
  }
@@ -1617,24 +1506,18 @@ Ready to start answering!`;
1617
1506
  return {
1618
1507
  content: [{
1619
1508
  type: "text",
1620
- text: JSON.stringify({
1621
- success: false,
1622
- error: data.error || "Deposit failed.",
1623
- hint: "Please try again. If the problem persists, contact support."
1624
- }, null, 2)
1509
+ text: `\u274C ${data.error || "Deposit failed"}`
1625
1510
  }]
1626
1511
  };
1627
1512
  }
1628
1513
  return {
1629
1514
  content: [{
1630
1515
  type: "text",
1631
- text: JSON.stringify({
1632
- success: true,
1633
- deposited: data.deposited,
1634
- newBalance: data.newBalance,
1635
- message: `Deposited ${data.deposited}! Your new balance is ${data.newBalance}.`,
1636
- nextStep: "You can now create questions with ping_create_question"
1637
- }, null, 2)
1516
+ text: `\u2705 Deposited ${data.deposited}!
1517
+
1518
+ New balance: ${data.newBalance}
1519
+
1520
+ You can now create questions with ping_create_question`
1638
1521
  }]
1639
1522
  };
1640
1523
  }
@@ -1652,10 +1535,16 @@ Ready to start answering!`;
1652
1535
  }
1653
1536
  } catch (error) {
1654
1537
  const errorResponse = formatErrorResponse(error);
1538
+ let errorText = `\u274C ${errorResponse.error}`;
1539
+ if (errorResponse.hint) {
1540
+ errorText += `
1541
+
1542
+ \u{1F4A1} ${errorResponse.hint}`;
1543
+ }
1655
1544
  return {
1656
1545
  content: [{
1657
1546
  type: "text",
1658
- text: JSON.stringify(errorResponse, null, 2)
1547
+ text: errorText
1659
1548
  }],
1660
1549
  isError: true
1661
1550
  };