ping-mcp-server 0.1.8 → 0.1.10

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 +107 -4
  2. package/package.json +1 -1
  3. package/src/index.ts +157 -11
package/dist/index.js CHANGED
@@ -515,7 +515,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
515
515
  },
516
516
  {
517
517
  name: "ping_submit_answer",
518
- description: "Submit an answer to a Ping question and earn the reward. Use when the user provides their answer to one of the available questions. Set preApproved=true when the user selects a suggested answer (skips AI review for instant approval). Requires GitHub login (ping_login) or wallet address.",
518
+ description: "Submit a SINGLE answer. DEPRECATED: Use ping_submit_batch instead for better UX. Only use this if you need to submit exactly one answer.",
519
519
  inputSchema: {
520
520
  type: "object",
521
521
  properties: {
@@ -535,9 +535,32 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
535
535
  required: ["questionId", "answer"]
536
536
  }
537
537
  },
538
+ {
539
+ name: "ping_submit_batch",
540
+ description: "Submit multiple answers at once. ALWAYS use this instead of ping_submit_answer. Returns a fun receipt-style summary. Much cleaner UX than individual submissions.",
541
+ inputSchema: {
542
+ type: "object",
543
+ properties: {
544
+ answers: {
545
+ type: "array",
546
+ items: {
547
+ type: "object",
548
+ properties: {
549
+ questionId: { type: "string" },
550
+ answer: { type: "string" },
551
+ preApproved: { type: "boolean" }
552
+ },
553
+ required: ["questionId", "answer"]
554
+ },
555
+ description: "Array of answers to submit"
556
+ }
557
+ },
558
+ required: ["answers"]
559
+ }
560
+ },
538
561
  {
539
562
  name: "ping_answer_flow",
540
- description: 'Start an interactive Q&A session to earn money answering Ping questions. TRIGGERS: "ping", "/ping", "answer questions", "earn money", "make money with ping", "start earning". Returns questions with suggested answers. CRITICAL: Present questions in BATCHES OF 4 using AskUserQuestion (tool limit). If more than 4 questions, present first 4, submit answers, then IMMEDIATELY present next 4 WITHOUT claiming. Continue batching until all questions are answered. Submit answers IN PARALLEL for each batch. AUTO-CLAIM: ONLY call ping_claim_reward AFTER the FINAL batch is submitted, not between batches. Requires GitHub login first (ping_login).',
563
+ description: 'Start an interactive Q&A session to earn money answering Ping questions. TRIGGERS: "ping", "/ping", "answer questions", "earn money", "make money with ping", "start earning". Returns questions with suggested answers. CRITICAL: Use ping_submit_batch to submit answers (NOT ping_submit_answer). Present questions in batches of 4, collect answers, then call ping_submit_batch ONCE. AUTO-CLAIM: Call ping_claim_reward AFTER all batches submitted. Requires GitHub login first (ping_login).',
541
564
  inputSchema: {
542
565
  type: "object",
543
566
  properties: {
@@ -573,6 +596,15 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
573
596
  required: []
574
597
  }
575
598
  },
599
+ {
600
+ name: "ping_stats",
601
+ description: "Get platform statistics for social proof - total answers today, rewards claimed, etc. Use this when showing the Ping welcome banner or dashboard.",
602
+ inputSchema: {
603
+ type: "object",
604
+ properties: {},
605
+ required: []
606
+ }
607
+ },
576
608
  // ────────────────────────────────────────────────────────
577
609
  // QUESTIONER TOOLS (Phase 1)
578
610
  // ────────────────────────────────────────────────────────
@@ -1012,6 +1044,58 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1012
1044
  };
1013
1045
  }
1014
1046
  // ────────────────────────────────────────────────────────
1047
+ // BATCH SUBMIT (Submit multiple answers at once)
1048
+ // ────────────────────────────────────────────────────────
1049
+ case "ping_submit_batch": {
1050
+ const auth = getAuth();
1051
+ if (!auth) {
1052
+ return {
1053
+ content: [{
1054
+ type: "text",
1055
+ text: JSON.stringify({
1056
+ success: false,
1057
+ error: "Not logged in.",
1058
+ hint: "Use ping_login to connect your GitHub account first."
1059
+ }, null, 2)
1060
+ }]
1061
+ };
1062
+ }
1063
+ const { answers } = args || {};
1064
+ if (!answers || answers.length === 0) {
1065
+ return {
1066
+ content: [{
1067
+ type: "text",
1068
+ text: JSON.stringify({
1069
+ success: false,
1070
+ error: "No answers provided.",
1071
+ hint: "Provide an array of answers with questionId and answer fields."
1072
+ }, null, 2)
1073
+ }]
1074
+ };
1075
+ }
1076
+ const data = await apiRequest("/answers/batch", {
1077
+ method: "POST",
1078
+ body: JSON.stringify({ answers })
1079
+ });
1080
+ const { summary } = data;
1081
+ const checkmark = summary.approved > 0 ? "\u2705" : "\u2B1C";
1082
+ const statusLine = summary.errors > 0 ? `${summary.approved}/${summary.submitted} approved, ${summary.errors} error(s)` : `${summary.approved}/${summary.submitted} approved`;
1083
+ const receipt = `
1084
+ \u250C\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\u2510
1085
+ \u2502 \u{1F9FE} PING RECEIPT \u2502
1086
+ \u251C\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\u2524
1087
+ \u2502 ${checkmark} ${statusLine.padEnd(24)}\u2502
1088
+ \u2502 \u{1F4B0} ${summary.totalEarned.padEnd(24)}\u2502
1089
+ \u2502 \u{1F4CA} Balance: ${summary.pendingBalance.padEnd(14)}\u2502
1090
+ \u2514\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\u2518`;
1091
+ return {
1092
+ content: [{
1093
+ type: "text",
1094
+ text: receipt.trim() + "\n\n" + (summary.approved > 0 ? "\u{1F389} Nice work! Your earnings are ready to claim." : "Keep trying - quality answers get approved!")
1095
+ }]
1096
+ };
1097
+ }
1098
+ // ────────────────────────────────────────────────────────
1015
1099
  // ANSWER FLOW (Interactive Q&A Session)
1016
1100
  // ────────────────────────────────────────────────────────
1017
1101
  case "ping_answer_flow": {
@@ -1079,8 +1163,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1079
1163
  step4_skip: "If 0 questions available in this batch, skip to next 4 question IDs and validate again.",
1080
1164
  step5_present: "Present available questions using AskUserQuestion (1-4 questions). Format: question text with reward in header, options are ONLY suggested answers.",
1081
1165
  step6_collect: "User answers the questions.",
1082
- step7_submit: "Submit answers IN PARALLEL using multiple ping_submit_answer calls. preApproved=true for suggested answers, preApproved=false for custom.",
1083
- step8_handleErrors: 'If some submissions fail with "question_exhausted" error, log them but continue.',
1166
+ step7_submit: "Submit ALL answers at once using ping_submit_batch. Pass array of {questionId, answer, preApproved}. preApproved=true for suggested answers, preApproved=false for custom. This returns a clean receipt summary.",
1167
+ step8_showReceipt: "Display the receipt returned by ping_submit_batch. It shows approved count, earnings, and balance.",
1084
1168
  step9_repeat: "If more questions remain in the original questions array, repeat from step1_selectNext. Otherwise, proceed to finalClaim."
1085
1169
  },
1086
1170
  finalClaim: "AFTER ALL batches complete (no more questions left to validate), call ping_claim_reward ONCE automatically.",
@@ -1213,6 +1297,25 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1213
1297
  };
1214
1298
  }
1215
1299
  // ────────────────────────────────────────────────────────
1300
+ // PLATFORM STATS (for welcome banner)
1301
+ // ────────────────────────────────────────────────────────
1302
+ case "ping_stats": {
1303
+ const data = await apiRequest("/stats");
1304
+ return {
1305
+ content: [{
1306
+ type: "text",
1307
+ text: JSON.stringify({
1308
+ success: true,
1309
+ stats: {
1310
+ questionsAvailable: data.questions.active,
1311
+ answersToday: data.responses.today,
1312
+ rewardsClaimedToday: data.rewards.claimedToday
1313
+ }
1314
+ }, null, 2)
1315
+ }]
1316
+ };
1317
+ }
1318
+ // ────────────────────────────────────────────────────────
1216
1319
  // CREATE QUESTION (Questioner)
1217
1320
  // ────────────────────────────────────────────────────────
1218
1321
  case "ping_create_question": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ping-mcp-server",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "MCP server that gives Claude Code the ability to interact with Ping",
5
5
  "type": "module",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -813,10 +813,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
813
813
  {
814
814
  name: 'ping_submit_answer',
815
815
  description:
816
- 'Submit an answer to a Ping question and earn the reward. ' +
817
- 'Use when the user provides their answer to one of the available questions. ' +
818
- 'Set preApproved=true when the user selects a suggested answer (skips AI review for instant approval). ' +
819
- 'Requires GitHub login (ping_login) or wallet address.',
816
+ 'Submit a SINGLE answer. DEPRECATED: Use ping_submit_batch instead for better UX. ' +
817
+ 'Only use this if you need to submit exactly one answer.',
820
818
  inputSchema: {
821
819
  type: 'object',
822
820
  properties: {
@@ -836,17 +834,40 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
836
834
  required: ['questionId', 'answer'],
837
835
  },
838
836
  },
837
+ {
838
+ name: 'ping_submit_batch',
839
+ description:
840
+ 'Submit multiple answers at once. ALWAYS use this instead of ping_submit_answer. ' +
841
+ 'Returns a fun receipt-style summary. Much cleaner UX than individual submissions.',
842
+ inputSchema: {
843
+ type: 'object',
844
+ properties: {
845
+ answers: {
846
+ type: 'array',
847
+ items: {
848
+ type: 'object',
849
+ properties: {
850
+ questionId: { type: 'string' },
851
+ answer: { type: 'string' },
852
+ preApproved: { type: 'boolean' },
853
+ },
854
+ required: ['questionId', 'answer'],
855
+ },
856
+ description: 'Array of answers to submit',
857
+ },
858
+ },
859
+ required: ['answers'],
860
+ },
861
+ },
839
862
  {
840
863
  name: 'ping_answer_flow',
841
864
  description:
842
865
  'Start an interactive Q&A session to earn money answering Ping questions. ' +
843
866
  'TRIGGERS: "ping", "/ping", "answer questions", "earn money", "make money with ping", "start earning". ' +
844
867
  'Returns questions with suggested answers. ' +
845
- 'CRITICAL: Present questions in BATCHES OF 4 using AskUserQuestion (tool limit). ' +
846
- 'If more than 4 questions, present first 4, submit answers, then IMMEDIATELY present next 4 WITHOUT claiming. ' +
847
- 'Continue batching until all questions are answered. ' +
848
- 'Submit answers IN PARALLEL for each batch. ' +
849
- 'AUTO-CLAIM: ONLY call ping_claim_reward AFTER the FINAL batch is submitted, not between batches. ' +
868
+ 'CRITICAL: Use ping_submit_batch to submit answers (NOT ping_submit_answer). ' +
869
+ 'Present questions in batches of 4, collect answers, then call ping_submit_batch ONCE. ' +
870
+ 'AUTO-CLAIM: Call ping_claim_reward AFTER all batches submitted. ' +
850
871
  'Requires GitHub login first (ping_login).',
851
872
  inputSchema: {
852
873
  type: 'object',
@@ -892,6 +913,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
892
913
  required: [],
893
914
  },
894
915
  },
916
+ {
917
+ name: 'ping_stats',
918
+ description:
919
+ 'Get platform statistics for social proof - total answers today, rewards claimed, etc. ' +
920
+ 'Use this when showing the Ping welcome banner or dashboard.',
921
+ inputSchema: {
922
+ type: 'object',
923
+ properties: {},
924
+ required: [],
925
+ },
926
+ },
895
927
  // ────────────────────────────────────────────────────────
896
928
  // QUESTIONER TOOLS (Phase 1)
897
929
  // ────────────────────────────────────────────────────────
@@ -1445,6 +1477,95 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1445
1477
  };
1446
1478
  }
1447
1479
 
1480
+ // ────────────────────────────────────────────────────────
1481
+ // BATCH SUBMIT (Submit multiple answers at once)
1482
+ // ────────────────────────────────────────────────────────
1483
+ case 'ping_submit_batch': {
1484
+ const auth = getAuth();
1485
+
1486
+ if (!auth) {
1487
+ return {
1488
+ content: [{
1489
+ type: 'text',
1490
+ text: JSON.stringify({
1491
+ success: false,
1492
+ error: 'Not logged in.',
1493
+ hint: 'Use ping_login to connect your GitHub account first.',
1494
+ }, null, 2),
1495
+ }],
1496
+ };
1497
+ }
1498
+
1499
+ const { answers } = (args || {}) as {
1500
+ answers: Array<{
1501
+ questionId: string;
1502
+ answer: string;
1503
+ preApproved?: boolean;
1504
+ }>;
1505
+ };
1506
+
1507
+ if (!answers || answers.length === 0) {
1508
+ return {
1509
+ content: [{
1510
+ type: 'text',
1511
+ text: JSON.stringify({
1512
+ success: false,
1513
+ error: 'No answers provided.',
1514
+ hint: 'Provide an array of answers with questionId and answer fields.',
1515
+ }, null, 2),
1516
+ }],
1517
+ };
1518
+ }
1519
+
1520
+ // Call the batch endpoint
1521
+ const data = await apiRequest<{
1522
+ success: boolean;
1523
+ summary: {
1524
+ submitted: number;
1525
+ approved: number;
1526
+ rejected: number;
1527
+ errors: number;
1528
+ totalEarned: string;
1529
+ totalEarnedCents: number;
1530
+ pendingBalance: string;
1531
+ };
1532
+ results: Array<{
1533
+ questionId: string;
1534
+ status: string;
1535
+ earned?: string;
1536
+ error?: string;
1537
+ }>;
1538
+ }>('/answers/batch', {
1539
+ method: 'POST',
1540
+ body: JSON.stringify({ answers }),
1541
+ });
1542
+
1543
+ // Build a fun receipt-style ASCII art response
1544
+ const { summary } = data;
1545
+ const checkmark = summary.approved > 0 ? '✅' : '⬜';
1546
+ const statusLine = summary.errors > 0
1547
+ ? `${summary.approved}/${summary.submitted} approved, ${summary.errors} error(s)`
1548
+ : `${summary.approved}/${summary.submitted} approved`;
1549
+
1550
+ const receipt = `
1551
+ ┌─────────────────────────────┐
1552
+ │ 🧾 PING RECEIPT │
1553
+ ├─────────────────────────────┤
1554
+ │ ${checkmark} ${statusLine.padEnd(24)}│
1555
+ │ 💰 ${summary.totalEarned.padEnd(24)}│
1556
+ │ 📊 Balance: ${summary.pendingBalance.padEnd(14)}│
1557
+ └─────────────────────────────┘`;
1558
+
1559
+ return {
1560
+ content: [{
1561
+ type: 'text',
1562
+ text: receipt.trim() + '\n\n' + (summary.approved > 0
1563
+ ? '🎉 Nice work! Your earnings are ready to claim.'
1564
+ : 'Keep trying - quality answers get approved!'),
1565
+ }],
1566
+ };
1567
+ }
1568
+
1448
1569
  // ────────────────────────────────────────────────────────
1449
1570
  // ANSWER FLOW (Interactive Q&A Session)
1450
1571
  // ────────────────────────────────────────────────────────
@@ -1535,8 +1656,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1535
1656
  step4_skip: 'If 0 questions available in this batch, skip to next 4 question IDs and validate again.',
1536
1657
  step5_present: 'Present available questions using AskUserQuestion (1-4 questions). Format: question text with reward in header, options are ONLY suggested answers.',
1537
1658
  step6_collect: 'User answers the questions.',
1538
- step7_submit: 'Submit answers IN PARALLEL using multiple ping_submit_answer calls. preApproved=true for suggested answers, preApproved=false for custom.',
1539
- step8_handleErrors: 'If some submissions fail with "question_exhausted" error, log them but continue.',
1659
+ step7_submit: 'Submit ALL answers at once using ping_submit_batch. Pass array of {questionId, answer, preApproved}. preApproved=true for suggested answers, preApproved=false for custom. This returns a clean receipt summary.',
1660
+ step8_showReceipt: 'Display the receipt returned by ping_submit_batch. It shows approved count, earnings, and balance.',
1540
1661
  step9_repeat: 'If more questions remain in the original questions array, repeat from step1_selectNext. Otherwise, proceed to finalClaim.',
1541
1662
  },
1542
1663
  finalClaim: 'AFTER ALL batches complete (no more questions left to validate), call ping_claim_reward ONCE automatically.',
@@ -1709,6 +1830,31 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1709
1830
  };
1710
1831
  }
1711
1832
 
1833
+ // ────────────────────────────────────────────────────────
1834
+ // PLATFORM STATS (for welcome banner)
1835
+ // ────────────────────────────────────────────────────────
1836
+ case 'ping_stats': {
1837
+ const data = await apiRequest<{
1838
+ questions: { total: number; active: number };
1839
+ responses: { total: number; today: number };
1840
+ rewards: { claimedToday: string; claimedTodayCents: number };
1841
+ }>('/stats');
1842
+
1843
+ return {
1844
+ content: [{
1845
+ type: 'text',
1846
+ text: JSON.stringify({
1847
+ success: true,
1848
+ stats: {
1849
+ questionsAvailable: data.questions.active,
1850
+ answersToday: data.responses.today,
1851
+ rewardsClaimedToday: data.rewards.claimedToday,
1852
+ },
1853
+ }, null, 2),
1854
+ }],
1855
+ };
1856
+ }
1857
+
1712
1858
  // ────────────────────────────────────────────────────────
1713
1859
  // CREATE QUESTION (Questioner)
1714
1860
  // ────────────────────────────────────────────────────────