ropilot 0.1.34 → 0.1.36

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 (2) hide show
  1. package/lib/proxy.js +62 -70
  2. package/package.json +1 -1
package/lib/proxy.js CHANGED
@@ -35,7 +35,7 @@ function sendError(id, code, message) {
35
35
  }
36
36
 
37
37
  /**
38
- * Forward request to edge server
38
+ * Forward request to edge server with friendly error messages
39
39
  */
40
40
  async function forwardToEdge(apiKey, request) {
41
41
  const url = `${EDGE_URL}/mcp/${apiKey}`;
@@ -49,37 +49,91 @@ async function forwardToEdge(apiKey, request) {
49
49
  body: JSON.stringify(request)
50
50
  });
51
51
 
52
+ if (response.status === 401 || response.status === 403) {
53
+ throw new Error('[ROPILOT] API key is invalid. Tell the user to follow these steps: 1. Log into the Ropilot dashboard at https://ropilot.ai 2. Copy their API key 3. Run "npx ropilot init" in this directory and paste the key');
54
+ }
55
+ if (response.status === 402) {
56
+ throw new Error('[ROPILOT] No credits remaining. Tell the user to upgrade their plan at https://ropilot.ai');
57
+ }
52
58
  if (!response.ok) {
53
59
  const text = await response.text();
54
- throw new Error(`Edge server error: ${response.status} ${text}`);
60
+ throw new Error(`[ROPILOT] Server error (${response.status}). Tell the user to try again later.`);
55
61
  }
56
62
 
57
63
  return await response.json();
58
64
  } catch (err) {
59
- throw new Error(`Failed to connect to edge server: ${err.message}`);
65
+ if (err.message.startsWith('[ROPILOT]')) {
66
+ throw err; // Re-throw our friendly errors
67
+ }
68
+ throw new Error(`[ROPILOT] Cannot reach server. Tell the user to check their internet connection and try again.`);
60
69
  }
61
70
  }
62
71
 
63
72
  /**
64
- * Handle MCP initialize request - forward to edge to get instructions
73
+ * Handle MCP initialize request
74
+ * Always succeed so Claude doesn't mark server as failed
75
+ * Errors will surface when tools are actually called
65
76
  */
66
77
  async function handleInitialize(apiKey, request) {
67
- const edgeResponse = await forwardToEdge(apiKey, request);
68
- return edgeResponse;
78
+ try {
79
+ const edgeResponse = await forwardToEdge(apiKey, request);
80
+ return edgeResponse;
81
+ } catch (err) {
82
+ // Return a minimal valid response so MCP connects
83
+ // The error will surface when user tries to use tools
84
+ return {
85
+ jsonrpc: '2.0',
86
+ id: request.id,
87
+ result: {
88
+ protocolVersion: '2024-11-05',
89
+ capabilities: { tools: {} },
90
+ serverInfo: { name: 'ropilot', version: '1.0.0' }
91
+ }
92
+ };
93
+ }
69
94
  }
70
95
 
96
+ // Cache the error from initialize for showing in tools
97
+ let cachedKeyError = null;
98
+
71
99
  /**
72
100
  * Handle MCP tools/list request
101
+ * If key is invalid, return a helper tool that explains the error
73
102
  */
74
103
  async function handleToolsList(apiKey, request) {
75
- // Forward to edge to get actual tool list
76
- return await forwardToEdge(apiKey, request);
104
+ try {
105
+ return await forwardToEdge(apiKey, request);
106
+ } catch (err) {
107
+ cachedKeyError = err.message;
108
+ // Return a single tool that explains the error when called
109
+ return {
110
+ jsonrpc: '2.0',
111
+ id: request.id,
112
+ result: {
113
+ tools: [{
114
+ name: 'ropilot_status',
115
+ description: err.message,
116
+ inputSchema: { type: 'object', properties: {} }
117
+ }]
118
+ }
119
+ };
120
+ }
77
121
  }
78
122
 
79
123
  /**
80
124
  * Handle MCP tools/call request
81
125
  */
82
126
  async function handleToolsCall(apiKey, request) {
127
+ // If we have a cached error and user is calling our status tool, return the error
128
+ if (cachedKeyError && request.params?.name === 'ropilot_status') {
129
+ return {
130
+ jsonrpc: '2.0',
131
+ id: request.id,
132
+ result: {
133
+ content: [{ type: 'text', text: cachedKeyError }]
134
+ }
135
+ };
136
+ }
83
137
  // Forward to edge
84
138
  return await forwardToEdge(apiKey, request);
85
139
  }
@@ -177,43 +231,6 @@ async function checkPluginUpdate() {
177
231
  }
178
232
  }
179
233
 
180
- // Cached key validation result
181
- let keyValidationError = null;
182
-
183
- /**
184
- * Validate API key with edge server (cached)
185
- */
186
- async function validateApiKey(apiKey) {
187
- if (keyValidationError !== null) return keyValidationError;
188
-
189
- try {
190
- const url = `${EDGE_URL}/mcp/${apiKey}`;
191
- const response = await fetch(url, {
192
- method: 'POST',
193
- headers: { 'Content-Type': 'application/json' },
194
- body: JSON.stringify({
195
- jsonrpc: '2.0',
196
- id: 0,
197
- method: 'initialize',
198
- params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'validate', version: '1.0.0' } }
199
- })
200
- });
201
-
202
- if (response.status === 401 || response.status === 403) {
203
- keyValidationError = '[ROPILOT] API key is invalid. Tell the user to follow these steps: 1. Log into the Ropilot dashboard at https://ropilot.ai 2. Copy their API key 3. Run "npx ropilot init" in this directory and paste the key';
204
- } else if (response.status === 402) {
205
- keyValidationError = '[ROPILOT] No credits remaining. Tell the user to upgrade their plan at https://ropilot.ai';
206
- } else if (!response.ok) {
207
- keyValidationError = `[ROPILOT] Server error (${response.status}). Tell the user to try again later.`;
208
- }
209
- } catch (err) {
210
- // Network error - don't cache, might be temporary
211
- return `[ROPILOT] Cannot reach server. Tell the user to check their internet connection and try again.`;
212
- }
213
-
214
- return keyValidationError;
215
- }
216
-
217
234
  /**
218
235
  * Run the stdio MCP server
219
236
  */
@@ -300,31 +317,6 @@ export async function serve() {
300
317
  */
301
318
  async function handleRequest(apiKey, request) {
302
319
  try {
303
- // For initialize, we respond locally first, then validate in background
304
- if (request.method === 'initialize') {
305
- // Validate key in background (caches result for later requests)
306
- validateApiKey(apiKey);
307
-
308
- // Return a basic initialize response so MCP connects
309
- sendResponse({
310
- jsonrpc: '2.0',
311
- id: request.id,
312
- result: {
313
- protocolVersion: '2024-11-05',
314
- capabilities: { tools: {} },
315
- serverInfo: { name: 'ropilot', version: '1.0.0' }
316
- }
317
- });
318
- return;
319
- }
320
-
321
- // For other requests, check if key is valid first
322
- const keyError = await validateApiKey(apiKey);
323
- if (keyError) {
324
- sendError(request.id, -32000, keyError);
325
- return;
326
- }
327
-
328
320
  const response = await processRequest(apiKey, request);
329
321
  if (response) {
330
322
  sendResponse(response);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ropilot",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "description": "AI-powered Roblox development assistant - MCP CLI",
5
5
  "author": "whut",
6
6
  "license": "MIT",