ping-mcp-server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1534 -0
- package/dist/init.d.ts +1 -0
- package/dist/init.js +206 -0
- package/package.json +28 -0
- package/src/index.ts +2123 -0
- package/src/init.ts +300 -0
- package/tsconfig.json +8 -0
package/src/init.ts
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
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(), '.config', 'claude-code', 'config.json'),
|
|
29
|
+
path.join(os.homedir(), '.claude', 'config.json'),
|
|
30
|
+
path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'config.json'),
|
|
31
|
+
path.join(os.homedir(), '.config', 'claude', 'config.json'),
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// ============================================================
|
|
35
|
+
// BANNER
|
|
36
|
+
// ============================================================
|
|
37
|
+
|
|
38
|
+
const BANNER = `
|
|
39
|
+
+------------------------------------------+
|
|
40
|
+
| |
|
|
41
|
+
| ____ _ |
|
|
42
|
+
| | _ \\(_)_ __ __ _ |
|
|
43
|
+
| | |_) | | '_ \\ / _\` | |
|
|
44
|
+
| | __/| | | | | (_| | |
|
|
45
|
+
| |_| |_|_| |_|\\__, | |
|
|
46
|
+
| |___/ |
|
|
47
|
+
| |
|
|
48
|
+
| Answer questions. Earn crypto. |
|
|
49
|
+
| |
|
|
50
|
+
+------------------------------------------+
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
// ============================================================
|
|
54
|
+
// HELPERS
|
|
55
|
+
// ============================================================
|
|
56
|
+
|
|
57
|
+
function print(message: string) {
|
|
58
|
+
console.log(message);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function printSuccess(message: string) {
|
|
62
|
+
console.log(`[OK] ${message}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function printError(message: string) {
|
|
66
|
+
console.log(`[ERROR] ${message}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function printInfo(message: string) {
|
|
70
|
+
console.log(`[INFO] ${message}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function prompt(question: string): Promise<string> {
|
|
74
|
+
const rl = readline.createInterface({
|
|
75
|
+
input: process.stdin,
|
|
76
|
+
output: process.stdout,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return new Promise((resolve) => {
|
|
80
|
+
rl.question(question, (answer) => {
|
|
81
|
+
rl.close();
|
|
82
|
+
resolve(answer.trim().toLowerCase());
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ============================================================
|
|
88
|
+
// API CHECK
|
|
89
|
+
// ============================================================
|
|
90
|
+
|
|
91
|
+
async function checkApiConnectivity(): Promise<boolean> {
|
|
92
|
+
try {
|
|
93
|
+
const response = await fetch(`${API_BASE_URL}/health`);
|
|
94
|
+
return response.ok;
|
|
95
|
+
} catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ============================================================
|
|
101
|
+
// CONFIG SETUP
|
|
102
|
+
// ============================================================
|
|
103
|
+
|
|
104
|
+
function createConfigDirectory(): boolean {
|
|
105
|
+
try {
|
|
106
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
107
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
108
|
+
printSuccess(`Created config directory: ${CONFIG_DIR}`);
|
|
109
|
+
} else {
|
|
110
|
+
printInfo(`Config directory already exists: ${CONFIG_DIR}`);
|
|
111
|
+
}
|
|
112
|
+
return true;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
printError(`Failed to create config directory: ${error}`);
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function initializeConfig(): boolean {
|
|
120
|
+
try {
|
|
121
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
122
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify({}, null, 2));
|
|
123
|
+
printSuccess(`Created config file: ${CONFIG_FILE}`);
|
|
124
|
+
} else {
|
|
125
|
+
printInfo(`Config file already exists: ${CONFIG_FILE}`);
|
|
126
|
+
}
|
|
127
|
+
return true;
|
|
128
|
+
} catch (error) {
|
|
129
|
+
printError(`Failed to create config file: ${error}`);
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ============================================================
|
|
135
|
+
// CLAUDE CODE INTEGRATION
|
|
136
|
+
// ============================================================
|
|
137
|
+
|
|
138
|
+
function findClaudeCodeConfig(): string | null {
|
|
139
|
+
for (const configPath of CLAUDE_CONFIG_PATHS) {
|
|
140
|
+
if (fs.existsSync(configPath)) {
|
|
141
|
+
return configPath;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function findClaudeCodeConfigDir(): string | null {
|
|
148
|
+
// Find an existing config, or default to the most common location
|
|
149
|
+
const existingConfig = findClaudeCodeConfig();
|
|
150
|
+
if (existingConfig) {
|
|
151
|
+
return path.dirname(existingConfig);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Default location for Claude Code config
|
|
155
|
+
const defaultDir = path.join(os.homedir(), '.config', 'claude-code');
|
|
156
|
+
return defaultDir;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
interface ClaudeCodeConfig {
|
|
160
|
+
mcpServers?: {
|
|
161
|
+
[key: string]: {
|
|
162
|
+
command: string;
|
|
163
|
+
args?: string[];
|
|
164
|
+
env?: Record<string, string>;
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function addToClaudeCodeConfig(configPath: string): boolean {
|
|
170
|
+
try {
|
|
171
|
+
const configDir = path.dirname(configPath);
|
|
172
|
+
|
|
173
|
+
// Ensure config directory exists
|
|
174
|
+
if (!fs.existsSync(configDir)) {
|
|
175
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Load existing config or create empty one
|
|
179
|
+
let config: ClaudeCodeConfig = {};
|
|
180
|
+
if (fs.existsSync(configPath)) {
|
|
181
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
182
|
+
config = JSON.parse(content);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Initialize mcpServers if it doesn't exist
|
|
186
|
+
if (!config.mcpServers) {
|
|
187
|
+
config.mcpServers = {};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Check if Ping is already configured
|
|
191
|
+
if (config.mcpServers.ping) {
|
|
192
|
+
printInfo('Ping MCP server is already configured in Claude Code');
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Add Ping MCP server
|
|
197
|
+
config.mcpServers.ping = {
|
|
198
|
+
command: 'npx',
|
|
199
|
+
args: ['-y', '@ping/mcp-server'],
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// Write updated config
|
|
203
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
204
|
+
printSuccess(`Added Ping MCP server to: ${configPath}`);
|
|
205
|
+
return true;
|
|
206
|
+
} catch (error) {
|
|
207
|
+
printError(`Failed to update Claude Code config: ${error}`);
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ============================================================
|
|
213
|
+
// MAIN
|
|
214
|
+
// ============================================================
|
|
215
|
+
|
|
216
|
+
async function main() {
|
|
217
|
+
print(BANNER);
|
|
218
|
+
print('Welcome to Ping! Let\'s get you set up.\n');
|
|
219
|
+
|
|
220
|
+
// Step 1: Check API connectivity
|
|
221
|
+
print('Checking API connectivity...');
|
|
222
|
+
const apiOk = await checkApiConnectivity();
|
|
223
|
+
if (apiOk) {
|
|
224
|
+
printSuccess(`Connected to ${API_BASE_URL}`);
|
|
225
|
+
} else {
|
|
226
|
+
printError(`Could not reach ${API_BASE_URL}`);
|
|
227
|
+
printInfo('The API may be temporarily unavailable. You can continue setup.');
|
|
228
|
+
}
|
|
229
|
+
print('');
|
|
230
|
+
|
|
231
|
+
// Step 2: Create config directory
|
|
232
|
+
print('Setting up local configuration...');
|
|
233
|
+
const dirOk = createConfigDirectory();
|
|
234
|
+
if (!dirOk) {
|
|
235
|
+
printError('Setup failed. Please check permissions and try again.');
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Step 3: Initialize config file
|
|
240
|
+
const configOk = initializeConfig();
|
|
241
|
+
if (!configOk) {
|
|
242
|
+
printError('Setup failed. Please check permissions and try again.');
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
print('');
|
|
246
|
+
|
|
247
|
+
// Step 4: Claude Code integration
|
|
248
|
+
print('Looking for Claude Code configuration...');
|
|
249
|
+
const claudeConfigPath = findClaudeCodeConfig();
|
|
250
|
+
const claudeConfigDir = findClaudeCodeConfigDir();
|
|
251
|
+
|
|
252
|
+
if (claudeConfigPath) {
|
|
253
|
+
printInfo(`Found Claude Code config: ${claudeConfigPath}`);
|
|
254
|
+
} else if (claudeConfigDir) {
|
|
255
|
+
printInfo(`Will create Claude Code config at: ${path.join(claudeConfigDir, 'config.json')}`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const targetConfigPath = claudeConfigPath || path.join(claudeConfigDir!, 'config.json');
|
|
259
|
+
|
|
260
|
+
print('');
|
|
261
|
+
const answer = await prompt('Add Ping MCP server to Claude Code? (y/n): ');
|
|
262
|
+
|
|
263
|
+
if (answer === 'y' || answer === 'yes') {
|
|
264
|
+
const added = addToClaudeCodeConfig(targetConfigPath);
|
|
265
|
+
if (!added) {
|
|
266
|
+
printInfo('You can manually add Ping to your Claude Code config later.');
|
|
267
|
+
}
|
|
268
|
+
} else {
|
|
269
|
+
printInfo('Skipped Claude Code integration.');
|
|
270
|
+
printInfo('You can manually add this to your Claude Code config:');
|
|
271
|
+
print('');
|
|
272
|
+
print(' {');
|
|
273
|
+
print(' "mcpServers": {');
|
|
274
|
+
print(' "ping": {');
|
|
275
|
+
print(' "command": "npx",');
|
|
276
|
+
print(' "args": ["-y", "@ping/mcp-server"]');
|
|
277
|
+
print(' }');
|
|
278
|
+
print(' }');
|
|
279
|
+
print(' }');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Step 5: Success message
|
|
283
|
+
print('');
|
|
284
|
+
print('+------------------------------------------+');
|
|
285
|
+
print('| |');
|
|
286
|
+
print('| Setup complete! |');
|
|
287
|
+
print('| |');
|
|
288
|
+
print('| Next steps: |');
|
|
289
|
+
print('| 1. Restart Claude Code |');
|
|
290
|
+
print('| 2. Say "ping_login" to connect GitHub |');
|
|
291
|
+
print('| 3. Start earning! |');
|
|
292
|
+
print('| |');
|
|
293
|
+
print('+------------------------------------------+');
|
|
294
|
+
print('');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
main().catch((error) => {
|
|
298
|
+
printError(`Unexpected error: ${error}`);
|
|
299
|
+
process.exit(1);
|
|
300
|
+
});
|