ping-mcp-server 0.1.4 → 0.1.5
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 +4 -4
- package/package/package.json +29 -0
- package/package/src/index.ts +2123 -0
- package/package/src/init.ts +302 -0
- package/package/tsconfig.json +8 -0
- package/package.json +1 -1
- package/ping-mcp-server-0.1.4.tgz +0 -0
- package/src/index.ts +4 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* PING INIT SCRIPT
|
|
4
|
+
* ================
|
|
5
|
+
*
|
|
6
|
+
* Sets up Ping for first-time users:
|
|
7
|
+
* - Shows welcome banner
|
|
8
|
+
* - Checks API connectivity
|
|
9
|
+
* - Creates config directory
|
|
10
|
+
* - Offers to add Ping to Claude Code config
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import * as os from 'os';
|
|
16
|
+
import * as readline from 'readline';
|
|
17
|
+
|
|
18
|
+
// ============================================================
|
|
19
|
+
// CONFIGURATION
|
|
20
|
+
// ============================================================
|
|
21
|
+
|
|
22
|
+
const API_BASE_URL = process.env.PING_API_URL || 'https://api.ping-money.com';
|
|
23
|
+
const CONFIG_DIR = path.join(os.homedir(), '.ping');
|
|
24
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
25
|
+
|
|
26
|
+
// Possible Claude Code config locations
|
|
27
|
+
const CLAUDE_CONFIG_PATHS = [
|
|
28
|
+
path.join(os.homedir(), '.claude', 'settings.json'),
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
// ============================================================
|
|
32
|
+
// BANNER
|
|
33
|
+
// ============================================================
|
|
34
|
+
|
|
35
|
+
const BANNER = `
|
|
36
|
+
+------------------------------------------+
|
|
37
|
+
| |
|
|
38
|
+
| ____ _ |
|
|
39
|
+
| | _ \\(_)_ __ __ _ |
|
|
40
|
+
| | |_) | | '_ \\ / _\` | |
|
|
41
|
+
| | __/| | | | | (_| | |
|
|
42
|
+
| |_| |_|_| |_|\\__, | |
|
|
43
|
+
| |___/ |
|
|
44
|
+
| |
|
|
45
|
+
| Answer questions. Earn crypto. |
|
|
46
|
+
| |
|
|
47
|
+
+------------------------------------------+
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
// ============================================================
|
|
51
|
+
// HELPERS
|
|
52
|
+
// ============================================================
|
|
53
|
+
|
|
54
|
+
function print(message: string) {
|
|
55
|
+
console.log(message);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function printSuccess(message: string) {
|
|
59
|
+
console.log(`[OK] ${message}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function printError(message: string) {
|
|
63
|
+
console.log(`[ERROR] ${message}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function printInfo(message: string) {
|
|
67
|
+
console.log(`[INFO] ${message}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function prompt(question: string): Promise<string> {
|
|
71
|
+
const rl = readline.createInterface({
|
|
72
|
+
input: process.stdin,
|
|
73
|
+
output: process.stdout,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return new Promise((resolve) => {
|
|
77
|
+
rl.question(question, (answer) => {
|
|
78
|
+
rl.close();
|
|
79
|
+
resolve(answer.trim().toLowerCase());
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ============================================================
|
|
85
|
+
// API CHECK
|
|
86
|
+
// ============================================================
|
|
87
|
+
|
|
88
|
+
async function checkApiConnectivity(): Promise<boolean> {
|
|
89
|
+
try {
|
|
90
|
+
const response = await fetch(`${API_BASE_URL}/health`);
|
|
91
|
+
return response.ok;
|
|
92
|
+
} catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ============================================================
|
|
98
|
+
// CONFIG SETUP
|
|
99
|
+
// ============================================================
|
|
100
|
+
|
|
101
|
+
function createConfigDirectory(): boolean {
|
|
102
|
+
try {
|
|
103
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
104
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
105
|
+
printSuccess(`Created config directory: ${CONFIG_DIR}`);
|
|
106
|
+
} else {
|
|
107
|
+
printInfo(`Config directory already exists: ${CONFIG_DIR}`);
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
} catch (error) {
|
|
111
|
+
printError(`Failed to create config directory: ${error}`);
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function initializeConfig(): boolean {
|
|
117
|
+
try {
|
|
118
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
119
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify({}, null, 2));
|
|
120
|
+
printSuccess(`Created config file: ${CONFIG_FILE}`);
|
|
121
|
+
} else {
|
|
122
|
+
printInfo(`Config file already exists: ${CONFIG_FILE}`);
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
printError(`Failed to create config file: ${error}`);
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ============================================================
|
|
132
|
+
// CLAUDE CODE INTEGRATION
|
|
133
|
+
// ============================================================
|
|
134
|
+
|
|
135
|
+
function findClaudeCodeConfig(): string | null {
|
|
136
|
+
for (const configPath of CLAUDE_CONFIG_PATHS) {
|
|
137
|
+
if (fs.existsSync(configPath)) {
|
|
138
|
+
return configPath;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function findClaudeCodeConfigDir(): string | null {
|
|
145
|
+
// Find an existing config, or default to the most common location
|
|
146
|
+
const existingConfig = findClaudeCodeConfig();
|
|
147
|
+
if (existingConfig) {
|
|
148
|
+
return path.dirname(existingConfig);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Default location for Claude Code config
|
|
152
|
+
const defaultDir = path.join(os.homedir(), '.claude');
|
|
153
|
+
return defaultDir;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
interface ClaudeCodeConfig {
|
|
157
|
+
mcpServers?: {
|
|
158
|
+
[key: string]: {
|
|
159
|
+
type: string;
|
|
160
|
+
command: string;
|
|
161
|
+
args?: string[];
|
|
162
|
+
env?: Record<string, string>;
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
[key: string]: unknown;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function addToClaudeCodeConfig(configPath: string): boolean {
|
|
169
|
+
try {
|
|
170
|
+
const configDir = path.dirname(configPath);
|
|
171
|
+
|
|
172
|
+
// Ensure config directory exists
|
|
173
|
+
if (!fs.existsSync(configDir)) {
|
|
174
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Load existing config or create empty one
|
|
178
|
+
let config: ClaudeCodeConfig = {};
|
|
179
|
+
if (fs.existsSync(configPath)) {
|
|
180
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
181
|
+
config = JSON.parse(content);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Initialize mcpServers if it doesn't exist
|
|
185
|
+
if (!config.mcpServers) {
|
|
186
|
+
config.mcpServers = {};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Check if Ping is already configured
|
|
190
|
+
if (config.mcpServers.ping) {
|
|
191
|
+
printInfo('Ping MCP server is already configured in Claude Code');
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Add Ping MCP server
|
|
196
|
+
// -p flag tells npx which package to install, then runs the ping-mcp binary from it
|
|
197
|
+
config.mcpServers.ping = {
|
|
198
|
+
type: 'stdio',
|
|
199
|
+
command: 'npx',
|
|
200
|
+
args: ['-y', '-p', 'ping-mcp-server', 'ping-mcp'],
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// Write updated config
|
|
204
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
205
|
+
printSuccess(`Added Ping MCP server to: ${configPath}`);
|
|
206
|
+
return true;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
printError(`Failed to update Claude Code config: ${error}`);
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ============================================================
|
|
214
|
+
// MAIN
|
|
215
|
+
// ============================================================
|
|
216
|
+
|
|
217
|
+
async function main() {
|
|
218
|
+
print(BANNER);
|
|
219
|
+
print('Welcome to Ping! Let\'s get you set up.\n');
|
|
220
|
+
|
|
221
|
+
// Step 1: Check API connectivity
|
|
222
|
+
print('Checking API connectivity...');
|
|
223
|
+
const apiOk = await checkApiConnectivity();
|
|
224
|
+
if (apiOk) {
|
|
225
|
+
printSuccess(`Connected to ${API_BASE_URL}`);
|
|
226
|
+
} else {
|
|
227
|
+
printError(`Could not reach ${API_BASE_URL}`);
|
|
228
|
+
printInfo('The API may be temporarily unavailable. You can continue setup.');
|
|
229
|
+
}
|
|
230
|
+
print('');
|
|
231
|
+
|
|
232
|
+
// Step 2: Create config directory
|
|
233
|
+
print('Setting up local configuration...');
|
|
234
|
+
const dirOk = createConfigDirectory();
|
|
235
|
+
if (!dirOk) {
|
|
236
|
+
printError('Setup failed. Please check permissions and try again.');
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Step 3: Initialize config file
|
|
241
|
+
const configOk = initializeConfig();
|
|
242
|
+
if (!configOk) {
|
|
243
|
+
printError('Setup failed. Please check permissions and try again.');
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
print('');
|
|
247
|
+
|
|
248
|
+
// Step 4: Claude Code integration
|
|
249
|
+
print('Looking for Claude Code configuration...');
|
|
250
|
+
const claudeConfigPath = findClaudeCodeConfig();
|
|
251
|
+
const claudeConfigDir = findClaudeCodeConfigDir();
|
|
252
|
+
|
|
253
|
+
if (claudeConfigPath) {
|
|
254
|
+
printInfo(`Found Claude Code config: ${claudeConfigPath}`);
|
|
255
|
+
} else if (claudeConfigDir) {
|
|
256
|
+
printInfo(`Will create Claude Code config at: ${path.join(claudeConfigDir, 'settings.json')}`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const targetConfigPath = claudeConfigPath || path.join(claudeConfigDir!, 'settings.json');
|
|
260
|
+
|
|
261
|
+
print('');
|
|
262
|
+
const answer = await prompt('Add Ping MCP server to Claude Code? (y/n): ');
|
|
263
|
+
|
|
264
|
+
if (answer === 'y' || answer === 'yes') {
|
|
265
|
+
const added = addToClaudeCodeConfig(targetConfigPath);
|
|
266
|
+
if (!added) {
|
|
267
|
+
printInfo('You can manually add Ping to your Claude Code config later.');
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
printInfo('Skipped Claude Code integration.');
|
|
271
|
+
printInfo('You can manually add this to your ~/.claude/settings.json:');
|
|
272
|
+
print('');
|
|
273
|
+
print(' {');
|
|
274
|
+
print(' "mcpServers": {');
|
|
275
|
+
print(' "ping": {');
|
|
276
|
+
print(' "type": "stdio",');
|
|
277
|
+
print(' "command": "npx",');
|
|
278
|
+
print(' "args": ["-y", "-p", "ping-mcp-server", "ping-mcp"]');
|
|
279
|
+
print(' }');
|
|
280
|
+
print(' }');
|
|
281
|
+
print(' }');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Step 5: Success message
|
|
285
|
+
print('');
|
|
286
|
+
print('+------------------------------------------+');
|
|
287
|
+
print('| |');
|
|
288
|
+
print('| Setup complete! |');
|
|
289
|
+
print('| |');
|
|
290
|
+
print('| Next steps: |');
|
|
291
|
+
print('| 1. Restart Claude Code |');
|
|
292
|
+
print('| 2. Say "ping_login" to connect GitHub |');
|
|
293
|
+
print('| 3. Start earning! |');
|
|
294
|
+
print('| |');
|
|
295
|
+
print('+------------------------------------------+');
|
|
296
|
+
print('');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
main().catch((error) => {
|
|
300
|
+
printError(`Unexpected error: ${error}`);
|
|
301
|
+
process.exit(1);
|
|
302
|
+
});
|
package/package.json
CHANGED
|
Binary file
|
package/src/index.ts
CHANGED
|
@@ -741,6 +741,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
741
741
|
name: 'ping_login',
|
|
742
742
|
description:
|
|
743
743
|
'Login to Ping with GitHub. Opens a browser window for GitHub OAuth authentication. ' +
|
|
744
|
+
'TRIGGERS: "ping login", "login to ping", "connect to ping", "sign in to ping", "start using ping". ' +
|
|
744
745
|
'Use when the user wants to login, sign in, connect their account, or start using Ping. ' +
|
|
745
746
|
'After login, a wallet will be automatically created for the user.',
|
|
746
747
|
inputSchema: {
|
|
@@ -798,6 +799,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
798
799
|
name: 'ping_check_earnings',
|
|
799
800
|
description:
|
|
800
801
|
'Check your Ping earnings - shows pending balance (ready to claim) and total claimed. ' +
|
|
802
|
+
'TRIGGERS: "ping earnings", "ping balance", "how much have I earned", "check my ping", "my earnings". ' +
|
|
801
803
|
'Use when the user asks about their balance, earnings, or how much money they have. ' +
|
|
802
804
|
'Requires wallet to be set first with ping_set_wallet.',
|
|
803
805
|
inputSchema: {
|
|
@@ -853,6 +855,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
853
855
|
name: 'ping_answer_flow',
|
|
854
856
|
description:
|
|
855
857
|
'Start an interactive Q&A session to earn money answering Ping questions. ' +
|
|
858
|
+
'TRIGGERS: "ping", "/ping", "answer questions", "earn money", "make money with ping", "start earning". ' +
|
|
856
859
|
'Returns questions with suggested answers. ' +
|
|
857
860
|
'CRITICAL: Present ALL questions in a SINGLE AskUserQuestion call (batched) to reduce latency. ' +
|
|
858
861
|
'Use the questions array parameter with multiple question objects. ' +
|
|
@@ -874,6 +877,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
874
877
|
name: 'ping_claim_reward',
|
|
875
878
|
description:
|
|
876
879
|
'Claim pending Ping earnings and send them to your crypto wallet on Base. ' +
|
|
880
|
+
'TRIGGERS: "claim my ping", "withdraw from ping", "cash out", "get my money", "claim rewards". ' +
|
|
877
881
|
'Use when the user wants to withdraw, cash out, or claim their rewards. ' +
|
|
878
882
|
'Transfers USDC to their wallet instantly. ' +
|
|
879
883
|
'Works with GitHub login (ping_login) or legacy wallet (ping_set_wallet).',
|