ping-mcp-server 0.1.19 → 0.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +256 -94
- package/package.json +1 -1
- package/src/index.ts +278 -82
package/dist/index.js
CHANGED
|
@@ -75,6 +75,66 @@ function clearAuth() {
|
|
|
75
75
|
delete config.serverWalletAddress;
|
|
76
76
|
saveConfig(config);
|
|
77
77
|
}
|
|
78
|
+
var CONTEXT_FILE = path.join(CONFIG_DIR, "context.json");
|
|
79
|
+
function loadContext() {
|
|
80
|
+
try {
|
|
81
|
+
if (fs.existsSync(CONTEXT_FILE)) {
|
|
82
|
+
const data = fs.readFileSync(CONTEXT_FILE, "utf-8");
|
|
83
|
+
return JSON.parse(data);
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
}
|
|
87
|
+
return { problems: [], techStack: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
|
|
88
|
+
}
|
|
89
|
+
function saveContext(context) {
|
|
90
|
+
try {
|
|
91
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
92
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
93
|
+
}
|
|
94
|
+
context.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
95
|
+
fs.writeFileSync(CONTEXT_FILE, JSON.stringify(context, null, 2));
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error("Failed to save context:", error);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function generateQuestionSuggestions(context) {
|
|
101
|
+
const suggestions = [];
|
|
102
|
+
const templates = {
|
|
103
|
+
decision: [
|
|
104
|
+
"Choosing between {X} - what would you pick and why?",
|
|
105
|
+
"Is {X} worth the added complexity?",
|
|
106
|
+
"Anyone regret going with {X}? What would you do differently?"
|
|
107
|
+
],
|
|
108
|
+
debugging: [
|
|
109
|
+
"Seeing weird behavior with {X} - anyone dealt with this?",
|
|
110
|
+
"What's the gotcha everyone hits with {X}?"
|
|
111
|
+
],
|
|
112
|
+
architecture: [
|
|
113
|
+
"What's the production-ready way to handle {X}?",
|
|
114
|
+
"Is {X} overkill for a small team?",
|
|
115
|
+
"How do you think about {X} vs just keeping it simple?"
|
|
116
|
+
],
|
|
117
|
+
tooling: [
|
|
118
|
+
"What's the vibe on {X} in 2025? Worth adopting?",
|
|
119
|
+
"Anyone actually using {X} in production? How's it going?",
|
|
120
|
+
"What do people actually use for {X}? (not what's popular on Twitter)"
|
|
121
|
+
],
|
|
122
|
+
other: [
|
|
123
|
+
"What's your take on {X}?",
|
|
124
|
+
"How do you think about {X}?"
|
|
125
|
+
]
|
|
126
|
+
};
|
|
127
|
+
for (const problem of context.problems.slice(0, 3)) {
|
|
128
|
+
const categoryTemplates = templates[problem.category] || templates.other;
|
|
129
|
+
const template = categoryTemplates[Math.floor(Math.random() * categoryTemplates.length)];
|
|
130
|
+
suggestions.push(template.replace("{X}", problem.summary));
|
|
131
|
+
}
|
|
132
|
+
if (context.techStack.length > 0) {
|
|
133
|
+
const tech = context.techStack[0];
|
|
134
|
+
suggestions.push(`What's something you wish you knew before using ${tech}?`);
|
|
135
|
+
}
|
|
136
|
+
return suggestions;
|
|
137
|
+
}
|
|
78
138
|
function generateSuggestedAnswers(questionText, category) {
|
|
79
139
|
const lowerQuestion = questionText.toLowerCase();
|
|
80
140
|
if (lowerQuestion.includes("coffee") && lowerQuestion.includes("tea")) {
|
|
@@ -503,6 +563,42 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
503
563
|
}
|
|
504
564
|
},
|
|
505
565
|
// ────────────────────────────────────────────────────────
|
|
566
|
+
// CONTEXT TOOLS (for smart question suggestions)
|
|
567
|
+
// ────────────────────────────────────────────────────────
|
|
568
|
+
{
|
|
569
|
+
name: "ping_log_context",
|
|
570
|
+
description: "Log what the user is working on for smarter question suggestions. Call this when you notice the user is: making a decision between options, debugging something tricky, discussing architecture, or evaluating tools. This helps Ping suggest relevant questions later.",
|
|
571
|
+
inputSchema: {
|
|
572
|
+
type: "object",
|
|
573
|
+
properties: {
|
|
574
|
+
summary: {
|
|
575
|
+
type: "string",
|
|
576
|
+
description: 'Brief summary of the problem/decision (e.g., "choosing between Prisma and Drizzle")'
|
|
577
|
+
},
|
|
578
|
+
category: {
|
|
579
|
+
type: "string",
|
|
580
|
+
enum: ["decision", "debugging", "architecture", "tooling", "other"],
|
|
581
|
+
description: "Type of problem"
|
|
582
|
+
},
|
|
583
|
+
tags: {
|
|
584
|
+
type: "array",
|
|
585
|
+
items: { type: "string" },
|
|
586
|
+
description: 'Related technologies or concepts (e.g., ["orm", "database", "typescript"])'
|
|
587
|
+
}
|
|
588
|
+
},
|
|
589
|
+
required: ["summary", "category"]
|
|
590
|
+
}
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
name: "ping_suggest_questions",
|
|
594
|
+
description: "Get question suggestions based on what the user has been working on. Call this when: (1) user wants to create a question but doesn't know what to ask, (2) you notice something that would benefit from human expertise/taste rather than AI knowledge, (3) proactively when the user has been wrestling with a decision. Returns taste/judgment questions that humans answer better than AI.",
|
|
595
|
+
inputSchema: {
|
|
596
|
+
type: "object",
|
|
597
|
+
properties: {},
|
|
598
|
+
required: []
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
// ────────────────────────────────────────────────────────
|
|
506
602
|
// LEGACY: SET WALLET (for users without GitHub auth)
|
|
507
603
|
// ────────────────────────────────────────────────────────
|
|
508
604
|
{
|
|
@@ -820,7 +916,7 @@ Use ping_login to sign in with a different account.`
|
|
|
820
916
|
// ────────────────────────────────────────────────────────
|
|
821
917
|
case "ping_welcome": {
|
|
822
918
|
const auth = getAuth();
|
|
823
|
-
const MCP_VERSION = "0.1.
|
|
919
|
+
const MCP_VERSION = "0.1.21";
|
|
824
920
|
let userLine = "\u274C Not logged in";
|
|
825
921
|
let arenaBadge = "";
|
|
826
922
|
if (auth) {
|
|
@@ -836,9 +932,15 @@ Use ping_login to sign in with a different account.`
|
|
|
836
932
|
let questionsAvailable = 0;
|
|
837
933
|
let answersToday = 0;
|
|
838
934
|
let claimedToday = "$0.00";
|
|
935
|
+
if (auth) {
|
|
936
|
+
try {
|
|
937
|
+
const questionsData = await apiRequest("/questions?limit=1");
|
|
938
|
+
questionsAvailable = questionsData.totalAvailable;
|
|
939
|
+
} catch {
|
|
940
|
+
}
|
|
941
|
+
}
|
|
839
942
|
try {
|
|
840
943
|
const stats = await apiRequest("/stats");
|
|
841
|
-
questionsAvailable = stats.questions.active;
|
|
842
944
|
answersToday = stats.responses.today;
|
|
843
945
|
claimedToday = stats.rewards.claimedToday;
|
|
844
946
|
} catch {
|
|
@@ -898,6 +1000,89 @@ ${settingsUrl}`
|
|
|
898
1000
|
};
|
|
899
1001
|
}
|
|
900
1002
|
// ────────────────────────────────────────────────────────
|
|
1003
|
+
// LOG CONTEXT (for smart question suggestions)
|
|
1004
|
+
// ────────────────────────────────────────────────────────
|
|
1005
|
+
case "ping_log_context": {
|
|
1006
|
+
const { summary, category, tags = [] } = args || {};
|
|
1007
|
+
if (!summary || !category) {
|
|
1008
|
+
return {
|
|
1009
|
+
content: [{
|
|
1010
|
+
type: "text",
|
|
1011
|
+
text: "\u274C Missing required fields: summary and category"
|
|
1012
|
+
}]
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
const context = loadContext();
|
|
1016
|
+
context.problems.unshift({
|
|
1017
|
+
summary,
|
|
1018
|
+
category,
|
|
1019
|
+
tags,
|
|
1020
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1021
|
+
});
|
|
1022
|
+
context.problems = context.problems.slice(0, 10);
|
|
1023
|
+
for (const tag of tags) {
|
|
1024
|
+
if (!context.techStack.includes(tag)) {
|
|
1025
|
+
context.techStack.unshift(tag);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
context.techStack = context.techStack.slice(0, 10);
|
|
1029
|
+
saveContext(context);
|
|
1030
|
+
return {
|
|
1031
|
+
content: [{
|
|
1032
|
+
type: "text",
|
|
1033
|
+
text: `\u{1F4DD} Logged: "${summary}" (${category})
|
|
1034
|
+
|
|
1035
|
+
This will help suggest relevant questions later.`
|
|
1036
|
+
}]
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
// ────────────────────────────────────────────────────────
|
|
1040
|
+
// SUGGEST QUESTIONS (based on stored context)
|
|
1041
|
+
// ────────────────────────────────────────────────────────
|
|
1042
|
+
case "ping_suggest_questions": {
|
|
1043
|
+
const context = loadContext();
|
|
1044
|
+
if (context.problems.length === 0 && context.techStack.length === 0) {
|
|
1045
|
+
return {
|
|
1046
|
+
content: [{
|
|
1047
|
+
type: "text",
|
|
1048
|
+
text: `\u{1F4A1} No context stored yet!
|
|
1049
|
+
|
|
1050
|
+
As you work, I'll learn what you're dealing with and suggest relevant questions.
|
|
1051
|
+
|
|
1052
|
+
**Generic taste/judgment questions:**
|
|
1053
|
+
\u2022 "What's something you wish you knew earlier in your career?"
|
|
1054
|
+
\u2022 "What tool do you use that most people don't know about?"
|
|
1055
|
+
\u2022 "What's overrated in tech right now?"`
|
|
1056
|
+
}]
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
const suggestions = generateQuestionSuggestions(context);
|
|
1060
|
+
let output = `\u{1F4A1} **Question suggestions based on your recent work:**
|
|
1061
|
+
|
|
1062
|
+
`;
|
|
1063
|
+
suggestions.forEach((q, i) => {
|
|
1064
|
+
output += `${i + 1}. "${q}"
|
|
1065
|
+
`;
|
|
1066
|
+
});
|
|
1067
|
+
output += `
|
|
1068
|
+
**Your recent context:**
|
|
1069
|
+
`;
|
|
1070
|
+
output += `\u2022 Problems: ${context.problems.slice(0, 3).map((p) => p.summary).join(", ")}
|
|
1071
|
+
`;
|
|
1072
|
+
if (context.techStack.length > 0) {
|
|
1073
|
+
output += `\u2022 Tech: ${context.techStack.slice(0, 5).join(", ")}
|
|
1074
|
+
`;
|
|
1075
|
+
}
|
|
1076
|
+
output += `
|
|
1077
|
+
_Pick one to ask, edit it, or write your own!_`;
|
|
1078
|
+
return {
|
|
1079
|
+
content: [{
|
|
1080
|
+
type: "text",
|
|
1081
|
+
text: output
|
|
1082
|
+
}]
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
// ────────────────────────────────────────────────────────
|
|
901
1086
|
// SET WALLET (Legacy)
|
|
902
1087
|
// ────────────────────────────────────────────────────────
|
|
903
1088
|
case "ping_set_wallet": {
|
|
@@ -1334,11 +1519,9 @@ ID: ${data.question?.id}`;
|
|
|
1334
1519
|
return {
|
|
1335
1520
|
content: [{
|
|
1336
1521
|
type: "text",
|
|
1337
|
-
text:
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
hint: "Use ping_login to connect your GitHub account."
|
|
1341
|
-
}, null, 2)
|
|
1522
|
+
text: `\u274C Not logged in
|
|
1523
|
+
|
|
1524
|
+
Use ping_login to connect your GitHub account.`
|
|
1342
1525
|
}]
|
|
1343
1526
|
};
|
|
1344
1527
|
}
|
|
@@ -1348,24 +1531,27 @@ ID: ${data.question?.id}`;
|
|
|
1348
1531
|
return {
|
|
1349
1532
|
content: [{
|
|
1350
1533
|
type: "text",
|
|
1351
|
-
text:
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
message: "You haven't created any questions yet.",
|
|
1355
|
-
hint: "Use ping_create_question to ask your first question and get answers from the community!"
|
|
1356
|
-
}, null, 2)
|
|
1534
|
+
text: `\u{1F4ED} No questions yet
|
|
1535
|
+
|
|
1536
|
+
Use ping_create_question to ask your first question!`
|
|
1357
1537
|
}]
|
|
1358
1538
|
};
|
|
1359
1539
|
}
|
|
1540
|
+
let output = `\u{1F4CB} Your Questions (${data.questions.length} total)
|
|
1541
|
+
|
|
1542
|
+
`;
|
|
1543
|
+
data.questions.forEach((q, i) => {
|
|
1544
|
+
const statusEmoji = q.status === "active" ? "\u{1F7E2}" : "\u26AA";
|
|
1545
|
+
output += `${i + 1}. ${statusEmoji} "${q.text}"
|
|
1546
|
+
`;
|
|
1547
|
+
output += ` Reward: ${q.reward} \xB7 Responses: ${q.responseCount}/${q.maxResponses} \xB7 ID: ${q.id}
|
|
1548
|
+
|
|
1549
|
+
`;
|
|
1550
|
+
});
|
|
1360
1551
|
return {
|
|
1361
1552
|
content: [{
|
|
1362
1553
|
type: "text",
|
|
1363
|
-
text:
|
|
1364
|
-
success: true,
|
|
1365
|
-
questions: data.questions,
|
|
1366
|
-
total: data.total,
|
|
1367
|
-
message: `Found ${data.questions.length} question${data.questions.length !== 1 ? "s" : ""}`
|
|
1368
|
-
}, null, 2)
|
|
1554
|
+
text: output.trim()
|
|
1369
1555
|
}]
|
|
1370
1556
|
};
|
|
1371
1557
|
}
|
|
@@ -1378,11 +1564,9 @@ ID: ${data.question?.id}`;
|
|
|
1378
1564
|
return {
|
|
1379
1565
|
content: [{
|
|
1380
1566
|
type: "text",
|
|
1381
|
-
text:
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
hint: "Use ping_login to connect your GitHub account."
|
|
1385
|
-
}, null, 2)
|
|
1567
|
+
text: `\u274C Not logged in
|
|
1568
|
+
|
|
1569
|
+
Use ping_login to connect your GitHub account.`
|
|
1386
1570
|
}]
|
|
1387
1571
|
};
|
|
1388
1572
|
}
|
|
@@ -1391,11 +1575,9 @@ ID: ${data.question?.id}`;
|
|
|
1391
1575
|
return {
|
|
1392
1576
|
content: [{
|
|
1393
1577
|
type: "text",
|
|
1394
|
-
text:
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
hint: "Use ping_my_questions to see your questions and their IDs."
|
|
1398
|
-
}, null, 2)
|
|
1578
|
+
text: `\u274C Missing question ID
|
|
1579
|
+
|
|
1580
|
+
Use ping_my_questions to see your questions and their IDs.`
|
|
1399
1581
|
}]
|
|
1400
1582
|
};
|
|
1401
1583
|
}
|
|
@@ -1404,27 +1586,27 @@ ID: ${data.question?.id}`;
|
|
|
1404
1586
|
return {
|
|
1405
1587
|
content: [{
|
|
1406
1588
|
type: "text",
|
|
1407
|
-
text:
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
responses: [],
|
|
1411
|
-
total: 0,
|
|
1412
|
-
message: "No responses yet.",
|
|
1413
|
-
hint: "Answers usually start arriving within a few hours. Check back later!"
|
|
1414
|
-
}, null, 2)
|
|
1589
|
+
text: `\u{1F4ED} No responses yet for "${data.question.text}"
|
|
1590
|
+
|
|
1591
|
+
Answers usually arrive within a few hours. Check back later!`
|
|
1415
1592
|
}]
|
|
1416
1593
|
};
|
|
1417
1594
|
}
|
|
1595
|
+
let output = `\u{1F4AC} Responses to "${data.question.text}" (${data.responses.length} total)
|
|
1596
|
+
|
|
1597
|
+
`;
|
|
1598
|
+
data.responses.forEach((r, i) => {
|
|
1599
|
+
const statusEmoji = r.status === "approved" ? "\u2705" : r.status === "rejected" ? "\u274C" : "\u23F3";
|
|
1600
|
+
output += `${i + 1}. ${statusEmoji} "${r.answerText.slice(0, 100)}${r.answerText.length > 100 ? "..." : ""}"
|
|
1601
|
+
`;
|
|
1602
|
+
if (r.aiScore) output += ` Score: ${r.aiScore}/100
|
|
1603
|
+
`;
|
|
1604
|
+
output += "\n";
|
|
1605
|
+
});
|
|
1418
1606
|
return {
|
|
1419
1607
|
content: [{
|
|
1420
1608
|
type: "text",
|
|
1421
|
-
text:
|
|
1422
|
-
success: true,
|
|
1423
|
-
question: data.question,
|
|
1424
|
-
responses: data.responses,
|
|
1425
|
-
total: data.total,
|
|
1426
|
-
message: `Found ${data.responses.length} response${data.responses.length !== 1 ? "s" : ""} to your question`
|
|
1427
|
-
}, null, 2)
|
|
1609
|
+
text: output.trim()
|
|
1428
1610
|
}]
|
|
1429
1611
|
};
|
|
1430
1612
|
}
|
|
@@ -1437,11 +1619,9 @@ ID: ${data.question?.id}`;
|
|
|
1437
1619
|
return {
|
|
1438
1620
|
content: [{
|
|
1439
1621
|
type: "text",
|
|
1440
|
-
text:
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
hint: "Use ping_login to connect your GitHub account."
|
|
1444
|
-
}, null, 2)
|
|
1622
|
+
text: `\u274C Not logged in
|
|
1623
|
+
|
|
1624
|
+
Use ping_login to connect your GitHub account.`
|
|
1445
1625
|
}]
|
|
1446
1626
|
};
|
|
1447
1627
|
}
|
|
@@ -1450,11 +1630,9 @@ ID: ${data.question?.id}`;
|
|
|
1450
1630
|
return {
|
|
1451
1631
|
content: [{
|
|
1452
1632
|
type: "text",
|
|
1453
|
-
text:
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
hint: "Use ping_my_questions to see your questions and their IDs."
|
|
1457
|
-
}, null, 2)
|
|
1633
|
+
text: `\u274C Missing question ID
|
|
1634
|
+
|
|
1635
|
+
Use ping_my_questions to see your questions and their IDs.`
|
|
1458
1636
|
}]
|
|
1459
1637
|
};
|
|
1460
1638
|
}
|
|
@@ -1462,32 +1640,20 @@ ID: ${data.question?.id}`;
|
|
|
1462
1640
|
method: "POST"
|
|
1463
1641
|
});
|
|
1464
1642
|
if (!data.success) {
|
|
1465
|
-
|
|
1466
|
-
let hint = "Please try again.";
|
|
1467
|
-
if (errorMsg.toLowerCase().includes("not found")) {
|
|
1468
|
-
hint = "Check that the question ID is correct using ping_my_questions.";
|
|
1469
|
-
} else if (errorMsg.toLowerCase().includes("already closed")) {
|
|
1470
|
-
hint = "This question has already been closed.";
|
|
1471
|
-
}
|
|
1643
|
+
const errorMsg = data.error || "Failed to close question";
|
|
1472
1644
|
return {
|
|
1473
1645
|
content: [{
|
|
1474
1646
|
type: "text",
|
|
1475
|
-
text:
|
|
1476
|
-
success: false,
|
|
1477
|
-
error: errorMsg,
|
|
1478
|
-
hint
|
|
1479
|
-
}, null, 2)
|
|
1647
|
+
text: `\u274C ${errorMsg}`
|
|
1480
1648
|
}]
|
|
1481
1649
|
};
|
|
1482
1650
|
}
|
|
1483
1651
|
return {
|
|
1484
1652
|
content: [{
|
|
1485
1653
|
type: "text",
|
|
1486
|
-
text:
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
message: `Question closed. ${data.refunded} refunded to your balance.`
|
|
1490
|
-
}, null, 2)
|
|
1654
|
+
text: `\u2705 Question closed
|
|
1655
|
+
|
|
1656
|
+
${data.refunded} refunded to your balance.`
|
|
1491
1657
|
}]
|
|
1492
1658
|
};
|
|
1493
1659
|
}
|
|
@@ -1500,11 +1666,9 @@ ID: ${data.question?.id}`;
|
|
|
1500
1666
|
return {
|
|
1501
1667
|
content: [{
|
|
1502
1668
|
type: "text",
|
|
1503
|
-
text:
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
hint: "Use ping_login to connect your GitHub account before adding funds."
|
|
1507
|
-
}, null, 2)
|
|
1669
|
+
text: `\u274C Not logged in
|
|
1670
|
+
|
|
1671
|
+
Use ping_login to connect your GitHub account first.`
|
|
1508
1672
|
}]
|
|
1509
1673
|
};
|
|
1510
1674
|
}
|
|
@@ -1513,11 +1677,9 @@ ID: ${data.question?.id}`;
|
|
|
1513
1677
|
return {
|
|
1514
1678
|
content: [{
|
|
1515
1679
|
type: "text",
|
|
1516
|
-
text:
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
hint: "Minimum deposit is $1.00 (100 cents). Example: 1000 = $10.00"
|
|
1520
|
-
}, null, 2)
|
|
1680
|
+
text: `\u274C Deposit amount too small
|
|
1681
|
+
|
|
1682
|
+
Minimum deposit is $1.00 (100 cents). Example: 1000 = $10.00`
|
|
1521
1683
|
}]
|
|
1522
1684
|
};
|
|
1523
1685
|
}
|
|
@@ -1529,24 +1691,18 @@ ID: ${data.question?.id}`;
|
|
|
1529
1691
|
return {
|
|
1530
1692
|
content: [{
|
|
1531
1693
|
type: "text",
|
|
1532
|
-
text:
|
|
1533
|
-
success: false,
|
|
1534
|
-
error: data.error || "Deposit failed.",
|
|
1535
|
-
hint: "Please try again. If the problem persists, contact support."
|
|
1536
|
-
}, null, 2)
|
|
1694
|
+
text: `\u274C ${data.error || "Deposit failed"}`
|
|
1537
1695
|
}]
|
|
1538
1696
|
};
|
|
1539
1697
|
}
|
|
1540
1698
|
return {
|
|
1541
1699
|
content: [{
|
|
1542
1700
|
type: "text",
|
|
1543
|
-
text:
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
nextStep: "You can now create questions with ping_create_question"
|
|
1549
|
-
}, null, 2)
|
|
1701
|
+
text: `\u2705 Deposited ${data.deposited}!
|
|
1702
|
+
|
|
1703
|
+
New balance: ${data.newBalance}
|
|
1704
|
+
|
|
1705
|
+
You can now create questions with ping_create_question`
|
|
1550
1706
|
}]
|
|
1551
1707
|
};
|
|
1552
1708
|
}
|
|
@@ -1564,10 +1720,16 @@ ID: ${data.question?.id}`;
|
|
|
1564
1720
|
}
|
|
1565
1721
|
} catch (error) {
|
|
1566
1722
|
const errorResponse = formatErrorResponse(error);
|
|
1723
|
+
let errorText = `\u274C ${errorResponse.error}`;
|
|
1724
|
+
if (errorResponse.hint) {
|
|
1725
|
+
errorText += `
|
|
1726
|
+
|
|
1727
|
+
\u{1F4A1} ${errorResponse.hint}`;
|
|
1728
|
+
}
|
|
1567
1729
|
return {
|
|
1568
1730
|
content: [{
|
|
1569
1731
|
type: "text",
|
|
1570
|
-
text:
|
|
1732
|
+
text: errorText
|
|
1571
1733
|
}],
|
|
1572
1734
|
isError: true
|
|
1573
1735
|
};
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -134,6 +134,104 @@ function clearAuth(): void {
|
|
|
134
134
|
saveConfig(config);
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
// ============================================================
|
|
138
|
+
// CONTEXT STORAGE (for smart question suggestions)
|
|
139
|
+
// ============================================================
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Context file stores what the user has been working on across sessions.
|
|
143
|
+
* This enables Ping to suggest relevant questions based on actual problems.
|
|
144
|
+
*/
|
|
145
|
+
const CONTEXT_FILE = path.join(CONFIG_DIR, 'context.json');
|
|
146
|
+
|
|
147
|
+
interface PingContext {
|
|
148
|
+
// Problems/decisions the user is working through
|
|
149
|
+
problems: Array<{
|
|
150
|
+
summary: string;
|
|
151
|
+
category: 'decision' | 'debugging' | 'architecture' | 'tooling' | 'other';
|
|
152
|
+
tags: string[];
|
|
153
|
+
timestamp: string;
|
|
154
|
+
}>;
|
|
155
|
+
// Inferred tech stack from their projects
|
|
156
|
+
techStack: string[];
|
|
157
|
+
// Last updated timestamp
|
|
158
|
+
lastUpdated: string;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function loadContext(): PingContext {
|
|
162
|
+
try {
|
|
163
|
+
if (fs.existsSync(CONTEXT_FILE)) {
|
|
164
|
+
const data = fs.readFileSync(CONTEXT_FILE, 'utf-8');
|
|
165
|
+
return JSON.parse(data);
|
|
166
|
+
}
|
|
167
|
+
} catch (error) {
|
|
168
|
+
// Ignore errors, return empty context
|
|
169
|
+
}
|
|
170
|
+
return { problems: [], techStack: [], lastUpdated: new Date().toISOString() };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function saveContext(context: PingContext): void {
|
|
174
|
+
try {
|
|
175
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
176
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
177
|
+
}
|
|
178
|
+
context.lastUpdated = new Date().toISOString();
|
|
179
|
+
fs.writeFileSync(CONTEXT_FILE, JSON.stringify(context, null, 2));
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error('Failed to save context:', error);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Generate question suggestions based on stored context.
|
|
187
|
+
* Focuses on taste/judgment questions that humans answer better than AI.
|
|
188
|
+
*/
|
|
189
|
+
function generateQuestionSuggestions(context: PingContext): string[] {
|
|
190
|
+
const suggestions: string[] = [];
|
|
191
|
+
|
|
192
|
+
// Question templates focused on taste/judgment (not technical how-to)
|
|
193
|
+
const templates = {
|
|
194
|
+
decision: [
|
|
195
|
+
'Choosing between {X} - what would you pick and why?',
|
|
196
|
+
'Is {X} worth the added complexity?',
|
|
197
|
+
'Anyone regret going with {X}? What would you do differently?',
|
|
198
|
+
],
|
|
199
|
+
debugging: [
|
|
200
|
+
'Seeing weird behavior with {X} - anyone dealt with this?',
|
|
201
|
+
'What\'s the gotcha everyone hits with {X}?',
|
|
202
|
+
],
|
|
203
|
+
architecture: [
|
|
204
|
+
'What\'s the production-ready way to handle {X}?',
|
|
205
|
+
'Is {X} overkill for a small team?',
|
|
206
|
+
'How do you think about {X} vs just keeping it simple?',
|
|
207
|
+
],
|
|
208
|
+
tooling: [
|
|
209
|
+
'What\'s the vibe on {X} in 2025? Worth adopting?',
|
|
210
|
+
'Anyone actually using {X} in production? How\'s it going?',
|
|
211
|
+
'What do people actually use for {X}? (not what\'s popular on Twitter)',
|
|
212
|
+
],
|
|
213
|
+
other: [
|
|
214
|
+
'What\'s your take on {X}?',
|
|
215
|
+
'How do you think about {X}?',
|
|
216
|
+
],
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// Generate suggestions from recent problems
|
|
220
|
+
for (const problem of context.problems.slice(0, 3)) {
|
|
221
|
+
const categoryTemplates = templates[problem.category] || templates.other;
|
|
222
|
+
const template = categoryTemplates[Math.floor(Math.random() * categoryTemplates.length)];
|
|
223
|
+
suggestions.push(template.replace('{X}', problem.summary));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Add tech stack based suggestions if we have them
|
|
227
|
+
if (context.techStack.length > 0) {
|
|
228
|
+
const tech = context.techStack[0];
|
|
229
|
+
suggestions.push(`What's something you wish you knew before using ${tech}?`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return suggestions;
|
|
233
|
+
}
|
|
234
|
+
|
|
137
235
|
// ============================================================
|
|
138
236
|
// SUGGESTED ANSWER GENERATION
|
|
139
237
|
// ============================================================
|
|
@@ -798,6 +896,51 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
798
896
|
},
|
|
799
897
|
},
|
|
800
898
|
// ────────────────────────────────────────────────────────
|
|
899
|
+
// CONTEXT TOOLS (for smart question suggestions)
|
|
900
|
+
// ────────────────────────────────────────────────────────
|
|
901
|
+
{
|
|
902
|
+
name: 'ping_log_context',
|
|
903
|
+
description:
|
|
904
|
+
'Log what the user is working on for smarter question suggestions. ' +
|
|
905
|
+
'Call this when you notice the user is: making a decision between options, ' +
|
|
906
|
+
'debugging something tricky, discussing architecture, or evaluating tools. ' +
|
|
907
|
+
'This helps Ping suggest relevant questions later.',
|
|
908
|
+
inputSchema: {
|
|
909
|
+
type: 'object',
|
|
910
|
+
properties: {
|
|
911
|
+
summary: {
|
|
912
|
+
type: 'string',
|
|
913
|
+
description: 'Brief summary of the problem/decision (e.g., "choosing between Prisma and Drizzle")',
|
|
914
|
+
},
|
|
915
|
+
category: {
|
|
916
|
+
type: 'string',
|
|
917
|
+
enum: ['decision', 'debugging', 'architecture', 'tooling', 'other'],
|
|
918
|
+
description: 'Type of problem',
|
|
919
|
+
},
|
|
920
|
+
tags: {
|
|
921
|
+
type: 'array',
|
|
922
|
+
items: { type: 'string' },
|
|
923
|
+
description: 'Related technologies or concepts (e.g., ["orm", "database", "typescript"])',
|
|
924
|
+
},
|
|
925
|
+
},
|
|
926
|
+
required: ['summary', 'category'],
|
|
927
|
+
},
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
name: 'ping_suggest_questions',
|
|
931
|
+
description:
|
|
932
|
+
'Get question suggestions based on what the user has been working on. ' +
|
|
933
|
+
'Call this when: (1) user wants to create a question but doesn\'t know what to ask, ' +
|
|
934
|
+
'(2) you notice something that would benefit from human expertise/taste rather than AI knowledge, ' +
|
|
935
|
+
'(3) proactively when the user has been wrestling with a decision. ' +
|
|
936
|
+
'Returns taste/judgment questions that humans answer better than AI.',
|
|
937
|
+
inputSchema: {
|
|
938
|
+
type: 'object',
|
|
939
|
+
properties: {},
|
|
940
|
+
required: [],
|
|
941
|
+
},
|
|
942
|
+
},
|
|
943
|
+
// ────────────────────────────────────────────────────────
|
|
801
944
|
// LEGACY: SET WALLET (for users without GitHub auth)
|
|
802
945
|
// ────────────────────────────────────────────────────────
|
|
803
946
|
{
|
|
@@ -1188,7 +1331,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1188
1331
|
// ────────────────────────────────────────────────────────
|
|
1189
1332
|
case 'ping_welcome': {
|
|
1190
1333
|
const auth = getAuth();
|
|
1191
|
-
const MCP_VERSION = '0.1.
|
|
1334
|
+
const MCP_VERSION = '0.1.21';
|
|
1192
1335
|
|
|
1193
1336
|
// Get user handle and connections
|
|
1194
1337
|
let userLine = '❌ Not logged in';
|
|
@@ -1210,18 +1353,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1210
1353
|
}
|
|
1211
1354
|
}
|
|
1212
1355
|
|
|
1213
|
-
// Get
|
|
1356
|
+
// Get USER-SPECIFIC available questions (not platform-wide)
|
|
1214
1357
|
let questionsAvailable = 0;
|
|
1215
1358
|
let answersToday = 0;
|
|
1216
1359
|
let claimedToday = '$0.00';
|
|
1217
1360
|
|
|
1361
|
+
// First get questions available FOR THIS USER
|
|
1362
|
+
if (auth) {
|
|
1363
|
+
try {
|
|
1364
|
+
const questionsData = await apiRequest<{
|
|
1365
|
+
questions: Array<{ id: string }>;
|
|
1366
|
+
totalAvailable: number;
|
|
1367
|
+
}>('/questions?limit=1');
|
|
1368
|
+
questionsAvailable = questionsData.totalAvailable;
|
|
1369
|
+
} catch {
|
|
1370
|
+
// Fall back to 0
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
// Then get platform stats for answers/claimed (these are user-specific)
|
|
1218
1375
|
try {
|
|
1219
1376
|
const stats = await apiRequest<{
|
|
1220
|
-
questions: { active: number };
|
|
1221
1377
|
responses: { today: number };
|
|
1222
1378
|
rewards: { claimedToday: string };
|
|
1223
1379
|
}>('/stats');
|
|
1224
|
-
questionsAvailable = stats.questions.active;
|
|
1225
1380
|
answersToday = stats.responses.today;
|
|
1226
1381
|
claimedToday = stats.rewards.claimedToday;
|
|
1227
1382
|
} catch {
|
|
@@ -1296,6 +1451,97 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1296
1451
|
};
|
|
1297
1452
|
}
|
|
1298
1453
|
|
|
1454
|
+
// ────────────────────────────────────────────────────────
|
|
1455
|
+
// LOG CONTEXT (for smart question suggestions)
|
|
1456
|
+
// ────────────────────────────────────────────────────────
|
|
1457
|
+
case 'ping_log_context': {
|
|
1458
|
+
const { summary, category, tags = [] } = (args || {}) as {
|
|
1459
|
+
summary: string;
|
|
1460
|
+
category: 'decision' | 'debugging' | 'architecture' | 'tooling' | 'other';
|
|
1461
|
+
tags?: string[];
|
|
1462
|
+
};
|
|
1463
|
+
|
|
1464
|
+
if (!summary || !category) {
|
|
1465
|
+
return {
|
|
1466
|
+
content: [{
|
|
1467
|
+
type: 'text',
|
|
1468
|
+
text: '❌ Missing required fields: summary and category',
|
|
1469
|
+
}],
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
// Load existing context
|
|
1474
|
+
const context = loadContext();
|
|
1475
|
+
|
|
1476
|
+
// Add new problem (keep last 10)
|
|
1477
|
+
context.problems.unshift({
|
|
1478
|
+
summary,
|
|
1479
|
+
category,
|
|
1480
|
+
tags,
|
|
1481
|
+
timestamp: new Date().toISOString(),
|
|
1482
|
+
});
|
|
1483
|
+
context.problems = context.problems.slice(0, 10);
|
|
1484
|
+
|
|
1485
|
+
// Update tech stack from tags
|
|
1486
|
+
for (const tag of tags) {
|
|
1487
|
+
if (!context.techStack.includes(tag)) {
|
|
1488
|
+
context.techStack.unshift(tag);
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
context.techStack = context.techStack.slice(0, 10);
|
|
1492
|
+
|
|
1493
|
+
// Save
|
|
1494
|
+
saveContext(context);
|
|
1495
|
+
|
|
1496
|
+
return {
|
|
1497
|
+
content: [{
|
|
1498
|
+
type: 'text',
|
|
1499
|
+
text: `📝 Logged: "${summary}" (${category})\n\nThis will help suggest relevant questions later.`,
|
|
1500
|
+
}],
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
// ────────────────────────────────────────────────────────
|
|
1505
|
+
// SUGGEST QUESTIONS (based on stored context)
|
|
1506
|
+
// ────────────────────────────────────────────────────────
|
|
1507
|
+
case 'ping_suggest_questions': {
|
|
1508
|
+
const context = loadContext();
|
|
1509
|
+
|
|
1510
|
+
// If no context yet, return generic suggestions
|
|
1511
|
+
if (context.problems.length === 0 && context.techStack.length === 0) {
|
|
1512
|
+
return {
|
|
1513
|
+
content: [{
|
|
1514
|
+
type: 'text',
|
|
1515
|
+
text: `💡 No context stored yet!\n\nAs you work, I'll learn what you're dealing with and suggest relevant questions.\n\n**Generic taste/judgment questions:**\n• "What's something you wish you knew earlier in your career?"\n• "What tool do you use that most people don't know about?"\n• "What's overrated in tech right now?"`,
|
|
1516
|
+
}],
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
// Generate suggestions
|
|
1521
|
+
const suggestions = generateQuestionSuggestions(context);
|
|
1522
|
+
|
|
1523
|
+
// Format output
|
|
1524
|
+
let output = `💡 **Question suggestions based on your recent work:**\n\n`;
|
|
1525
|
+
suggestions.forEach((q, i) => {
|
|
1526
|
+
output += `${i + 1}. "${q}"\n`;
|
|
1527
|
+
});
|
|
1528
|
+
|
|
1529
|
+
output += `\n**Your recent context:**\n`;
|
|
1530
|
+
output += `• Problems: ${context.problems.slice(0, 3).map(p => p.summary).join(', ')}\n`;
|
|
1531
|
+
if (context.techStack.length > 0) {
|
|
1532
|
+
output += `• Tech: ${context.techStack.slice(0, 5).join(', ')}\n`;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
output += `\n_Pick one to ask, edit it, or write your own!_`;
|
|
1536
|
+
|
|
1537
|
+
return {
|
|
1538
|
+
content: [{
|
|
1539
|
+
type: 'text',
|
|
1540
|
+
text: output,
|
|
1541
|
+
}],
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1299
1545
|
// ────────────────────────────────────────────────────────
|
|
1300
1546
|
// SET WALLET (Legacy)
|
|
1301
1547
|
// ────────────────────────────────────────────────────────
|
|
@@ -1924,11 +2170,7 @@ Wallet: ${data.walletAddress}`,
|
|
|
1924
2170
|
return {
|
|
1925
2171
|
content: [{
|
|
1926
2172
|
type: 'text',
|
|
1927
|
-
text:
|
|
1928
|
-
success: false,
|
|
1929
|
-
error: 'Not logged in.',
|
|
1930
|
-
hint: 'Use ping_login to connect your GitHub account.',
|
|
1931
|
-
}, null, 2),
|
|
2173
|
+
text: `❌ Not logged in\n\nUse ping_login to connect your GitHub account.`,
|
|
1932
2174
|
}],
|
|
1933
2175
|
};
|
|
1934
2176
|
}
|
|
@@ -1939,11 +2181,7 @@ Wallet: ${data.walletAddress}`,
|
|
|
1939
2181
|
return {
|
|
1940
2182
|
content: [{
|
|
1941
2183
|
type: 'text',
|
|
1942
|
-
text:
|
|
1943
|
-
success: false,
|
|
1944
|
-
error: 'Missing question ID.',
|
|
1945
|
-
hint: 'Use ping_my_questions to see your questions and their IDs.',
|
|
1946
|
-
}, null, 2),
|
|
2184
|
+
text: `❌ Missing question ID\n\nUse ping_my_questions to see your questions and their IDs.`,
|
|
1947
2185
|
}],
|
|
1948
2186
|
};
|
|
1949
2187
|
}
|
|
@@ -1969,28 +2207,24 @@ Wallet: ${data.walletAddress}`,
|
|
|
1969
2207
|
return {
|
|
1970
2208
|
content: [{
|
|
1971
2209
|
type: 'text',
|
|
1972
|
-
text:
|
|
1973
|
-
success: true,
|
|
1974
|
-
question: data.question,
|
|
1975
|
-
responses: [],
|
|
1976
|
-
total: 0,
|
|
1977
|
-
message: 'No responses yet.',
|
|
1978
|
-
hint: 'Answers usually start arriving within a few hours. Check back later!',
|
|
1979
|
-
}, null, 2),
|
|
2210
|
+
text: `📭 No responses yet for "${data.question.text}"\n\nAnswers usually arrive within a few hours. Check back later!`,
|
|
1980
2211
|
}],
|
|
1981
2212
|
};
|
|
1982
2213
|
}
|
|
1983
2214
|
|
|
2215
|
+
// Build human-readable response list
|
|
2216
|
+
let output = `💬 Responses to "${data.question.text}" (${data.responses.length} total)\n\n`;
|
|
2217
|
+
data.responses.forEach((r, i) => {
|
|
2218
|
+
const statusEmoji = r.status === 'approved' ? '✅' : r.status === 'rejected' ? '❌' : '⏳';
|
|
2219
|
+
output += `${i + 1}. ${statusEmoji} "${r.answerText.slice(0, 100)}${r.answerText.length > 100 ? '...' : ''}"\n`;
|
|
2220
|
+
if (r.aiScore) output += ` Score: ${r.aiScore}/100\n`;
|
|
2221
|
+
output += '\n';
|
|
2222
|
+
});
|
|
2223
|
+
|
|
1984
2224
|
return {
|
|
1985
2225
|
content: [{
|
|
1986
2226
|
type: 'text',
|
|
1987
|
-
text:
|
|
1988
|
-
success: true,
|
|
1989
|
-
question: data.question,
|
|
1990
|
-
responses: data.responses,
|
|
1991
|
-
total: data.total,
|
|
1992
|
-
message: `Found ${data.responses.length} response${data.responses.length !== 1 ? 's' : ''} to your question`,
|
|
1993
|
-
}, null, 2),
|
|
2227
|
+
text: output.trim(),
|
|
1994
2228
|
}],
|
|
1995
2229
|
};
|
|
1996
2230
|
}
|
|
@@ -2005,11 +2239,7 @@ Wallet: ${data.walletAddress}`,
|
|
|
2005
2239
|
return {
|
|
2006
2240
|
content: [{
|
|
2007
2241
|
type: 'text',
|
|
2008
|
-
text:
|
|
2009
|
-
success: false,
|
|
2010
|
-
error: 'Not logged in.',
|
|
2011
|
-
hint: 'Use ping_login to connect your GitHub account.',
|
|
2012
|
-
}, null, 2),
|
|
2242
|
+
text: `❌ Not logged in\n\nUse ping_login to connect your GitHub account.`,
|
|
2013
2243
|
}],
|
|
2014
2244
|
};
|
|
2015
2245
|
}
|
|
@@ -2020,11 +2250,7 @@ Wallet: ${data.walletAddress}`,
|
|
|
2020
2250
|
return {
|
|
2021
2251
|
content: [{
|
|
2022
2252
|
type: 'text',
|
|
2023
|
-
text:
|
|
2024
|
-
success: false,
|
|
2025
|
-
error: 'Missing question ID.',
|
|
2026
|
-
hint: 'Use ping_my_questions to see your questions and their IDs.',
|
|
2027
|
-
}, null, 2),
|
|
2253
|
+
text: `❌ Missing question ID\n\nUse ping_my_questions to see your questions and their IDs.`,
|
|
2028
2254
|
}],
|
|
2029
2255
|
};
|
|
2030
2256
|
}
|
|
@@ -2038,23 +2264,11 @@ Wallet: ${data.walletAddress}`,
|
|
|
2038
2264
|
});
|
|
2039
2265
|
|
|
2040
2266
|
if (!data.success) {
|
|
2041
|
-
|
|
2042
|
-
let hint = 'Please try again.';
|
|
2043
|
-
|
|
2044
|
-
if (errorMsg.toLowerCase().includes('not found')) {
|
|
2045
|
-
hint = 'Check that the question ID is correct using ping_my_questions.';
|
|
2046
|
-
} else if (errorMsg.toLowerCase().includes('already closed')) {
|
|
2047
|
-
hint = 'This question has already been closed.';
|
|
2048
|
-
}
|
|
2049
|
-
|
|
2267
|
+
const errorMsg = data.error || 'Failed to close question';
|
|
2050
2268
|
return {
|
|
2051
2269
|
content: [{
|
|
2052
2270
|
type: 'text',
|
|
2053
|
-
text:
|
|
2054
|
-
success: false,
|
|
2055
|
-
error: errorMsg,
|
|
2056
|
-
hint,
|
|
2057
|
-
}, null, 2),
|
|
2271
|
+
text: `❌ ${errorMsg}`,
|
|
2058
2272
|
}],
|
|
2059
2273
|
};
|
|
2060
2274
|
}
|
|
@@ -2062,11 +2276,7 @@ Wallet: ${data.walletAddress}`,
|
|
|
2062
2276
|
return {
|
|
2063
2277
|
content: [{
|
|
2064
2278
|
type: 'text',
|
|
2065
|
-
text:
|
|
2066
|
-
success: true,
|
|
2067
|
-
refunded: data.refunded,
|
|
2068
|
-
message: `Question closed. ${data.refunded} refunded to your balance.`,
|
|
2069
|
-
}, null, 2),
|
|
2279
|
+
text: `✅ Question closed\n\n${data.refunded} refunded to your balance.`,
|
|
2070
2280
|
}],
|
|
2071
2281
|
};
|
|
2072
2282
|
}
|
|
@@ -2081,11 +2291,7 @@ Wallet: ${data.walletAddress}`,
|
|
|
2081
2291
|
return {
|
|
2082
2292
|
content: [{
|
|
2083
2293
|
type: 'text',
|
|
2084
|
-
text:
|
|
2085
|
-
success: false,
|
|
2086
|
-
error: 'Not logged in.',
|
|
2087
|
-
hint: 'Use ping_login to connect your GitHub account before adding funds.',
|
|
2088
|
-
}, null, 2),
|
|
2294
|
+
text: `❌ Not logged in\n\nUse ping_login to connect your GitHub account first.`,
|
|
2089
2295
|
}],
|
|
2090
2296
|
};
|
|
2091
2297
|
}
|
|
@@ -2096,11 +2302,7 @@ Wallet: ${data.walletAddress}`,
|
|
|
2096
2302
|
return {
|
|
2097
2303
|
content: [{
|
|
2098
2304
|
type: 'text',
|
|
2099
|
-
text:
|
|
2100
|
-
success: false,
|
|
2101
|
-
error: 'Deposit amount too small.',
|
|
2102
|
-
hint: 'Minimum deposit is $1.00 (100 cents). Example: 1000 = $10.00',
|
|
2103
|
-
}, null, 2),
|
|
2305
|
+
text: `❌ Deposit amount too small\n\nMinimum deposit is $1.00 (100 cents). Example: 1000 = $10.00`,
|
|
2104
2306
|
}],
|
|
2105
2307
|
};
|
|
2106
2308
|
}
|
|
@@ -2119,11 +2321,7 @@ Wallet: ${data.walletAddress}`,
|
|
|
2119
2321
|
return {
|
|
2120
2322
|
content: [{
|
|
2121
2323
|
type: 'text',
|
|
2122
|
-
text:
|
|
2123
|
-
success: false,
|
|
2124
|
-
error: data.error || 'Deposit failed.',
|
|
2125
|
-
hint: 'Please try again. If the problem persists, contact support.',
|
|
2126
|
-
}, null, 2),
|
|
2324
|
+
text: `❌ ${data.error || 'Deposit failed'}`,
|
|
2127
2325
|
}],
|
|
2128
2326
|
};
|
|
2129
2327
|
}
|
|
@@ -2131,13 +2329,7 @@ Wallet: ${data.walletAddress}`,
|
|
|
2131
2329
|
return {
|
|
2132
2330
|
content: [{
|
|
2133
2331
|
type: 'text',
|
|
2134
|
-
text:
|
|
2135
|
-
success: true,
|
|
2136
|
-
deposited: data.deposited,
|
|
2137
|
-
newBalance: data.newBalance,
|
|
2138
|
-
message: `Deposited ${data.deposited}! Your new balance is ${data.newBalance}.`,
|
|
2139
|
-
nextStep: 'You can now create questions with ping_create_question',
|
|
2140
|
-
}, null, 2),
|
|
2332
|
+
text: `✅ Deposited ${data.deposited}!\n\nNew balance: ${data.newBalance}\n\nYou can now create questions with ping_create_question`,
|
|
2141
2333
|
}],
|
|
2142
2334
|
};
|
|
2143
2335
|
}
|
|
@@ -2155,12 +2347,16 @@ Wallet: ${data.walletAddress}`,
|
|
|
2155
2347
|
};
|
|
2156
2348
|
}
|
|
2157
2349
|
} catch (error) {
|
|
2158
|
-
//
|
|
2350
|
+
// Human-readable error messages
|
|
2159
2351
|
const errorResponse = formatErrorResponse(error);
|
|
2352
|
+
let errorText = `❌ ${errorResponse.error}`;
|
|
2353
|
+
if (errorResponse.hint) {
|
|
2354
|
+
errorText += `\n\n💡 ${errorResponse.hint}`;
|
|
2355
|
+
}
|
|
2160
2356
|
return {
|
|
2161
2357
|
content: [{
|
|
2162
2358
|
type: 'text',
|
|
2163
|
-
text:
|
|
2359
|
+
text: errorText,
|
|
2164
2360
|
}],
|
|
2165
2361
|
isError: true,
|
|
2166
2362
|
};
|