@rvry/mcp 0.1.0 → 0.1.1
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/README.md +21 -14
- package/dist/setup.d.ts +6 -1
- package/dist/setup.js +358 -61
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,27 +6,36 @@ RVRY is a structural constraint system that forces Large Language Models past th
|
|
|
6
6
|
|
|
7
7
|
## Quick Start
|
|
8
8
|
|
|
9
|
-
The setup wizard handles everything -- token configuration, Claude Code registration, and slash command installation:
|
|
10
|
-
|
|
11
9
|
```bash
|
|
12
10
|
npx @rvry/mcp setup
|
|
13
11
|
```
|
|
14
12
|
|
|
15
13
|
The wizard will:
|
|
16
|
-
1.
|
|
17
|
-
2.
|
|
18
|
-
3.
|
|
14
|
+
1. Open your browser to sign in (or prompt for a token)
|
|
15
|
+
2. Auto-detect Claude Code and Claude Desktop on your machine
|
|
16
|
+
3. Configure both clients automatically
|
|
17
|
+
4. Install slash commands (`/deepthink`, `/problem-solve`)
|
|
18
|
+
|
|
19
|
+
That's it. Restart Claude Desktop if it was running, and RVRY is ready.
|
|
20
|
+
|
|
21
|
+
### Options
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx @rvry/mcp setup --token rvry_abc123 # Skip browser auth, use token directly
|
|
25
|
+
npx @rvry/mcp setup --client code # Only configure Claude Code
|
|
26
|
+
npx @rvry/mcp setup --client desktop # Only configure Claude Desktop
|
|
27
|
+
```
|
|
19
28
|
|
|
20
29
|
### Manual Installation
|
|
21
30
|
|
|
22
|
-
If you prefer to configure manually
|
|
31
|
+
If you prefer to configure manually:
|
|
23
32
|
|
|
33
|
+
**Claude Code:**
|
|
24
34
|
```bash
|
|
25
35
|
claude mcp add -e RVRY_TOKEN=rvry_your_token -s user rvry -- npx @rvry/mcp
|
|
26
36
|
```
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
**Claude Desktop** (`claude_desktop_config.json`):
|
|
30
39
|
```json
|
|
31
40
|
{
|
|
32
41
|
"mcpServers": {
|
|
@@ -41,12 +50,10 @@ Or add the following to your Claude Desktop `claude_desktop_config.json`:
|
|
|
41
50
|
}
|
|
42
51
|
```
|
|
43
52
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
1. Generate a token at [rvry.ai/dashboard](https://rvry.ai/dashboard).
|
|
49
|
-
2. The setup wizard sets `RVRY_TOKEN` automatically. For manual setup, set it in your MCP config.
|
|
53
|
+
Config file locations:
|
|
54
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
55
|
+
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
56
|
+
- Linux: `~/.config/Claude/claude_desktop_config.json`
|
|
50
57
|
|
|
51
58
|
## Tools Provided
|
|
52
59
|
|
package/dist/setup.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @rvry/mcp setup wizard
|
|
3
3
|
*
|
|
4
|
-
* Interactive setup:
|
|
4
|
+
* Interactive setup: device auth flow (browser-based) or manual token paste.
|
|
5
|
+
* Auto-detects Claude Code CLI and Claude Desktop, configures both.
|
|
6
|
+
*
|
|
5
7
|
* Usage: npx @rvry/mcp setup
|
|
8
|
+
* npx @rvry/mcp setup --token <value>
|
|
9
|
+
* npx @rvry/mcp setup --client code (Claude Code only)
|
|
10
|
+
* npx @rvry/mcp setup --client desktop (Claude Desktop only)
|
|
6
11
|
*/
|
|
7
12
|
export declare function runSetup(): Promise<void>;
|
package/dist/setup.js
CHANGED
|
@@ -1,19 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @rvry/mcp setup wizard
|
|
3
3
|
*
|
|
4
|
-
* Interactive setup:
|
|
4
|
+
* Interactive setup: device auth flow (browser-based) or manual token paste.
|
|
5
|
+
* Auto-detects Claude Code CLI and Claude Desktop, configures both.
|
|
6
|
+
*
|
|
5
7
|
* Usage: npx @rvry/mcp setup
|
|
8
|
+
* npx @rvry/mcp setup --token <value>
|
|
9
|
+
* npx @rvry/mcp setup --client code (Claude Code only)
|
|
10
|
+
* npx @rvry/mcp setup --client desktop (Claude Desktop only)
|
|
6
11
|
*/
|
|
7
12
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
8
13
|
import { join, dirname } from 'path';
|
|
14
|
+
import { homedir } from 'os';
|
|
9
15
|
import { createInterface } from 'readline';
|
|
10
16
|
import { execSync } from 'child_process';
|
|
11
17
|
import { platform } from 'os';
|
|
18
|
+
const ENGINE_URL = 'https://engine.rvry.ai';
|
|
12
19
|
const COMMAND_FILES = [
|
|
13
20
|
'deepthink.md',
|
|
14
21
|
'problem-solve.md',
|
|
15
22
|
];
|
|
16
23
|
const TOKEN_REGEX = /^rvry_[0-9a-f]{32,}$/;
|
|
24
|
+
/** Polling interval for device auth: 3 seconds */
|
|
25
|
+
const POLL_INTERVAL_MS = 3000;
|
|
26
|
+
/** Max polling duration: 10 minutes */
|
|
27
|
+
const POLL_TIMEOUT_MS = 10 * 60 * 1000;
|
|
28
|
+
/** RVRY MCP server config block (reused across Desktop + manual output) */
|
|
29
|
+
const RVRY_SERVER_ENTRY = (token) => ({
|
|
30
|
+
command: 'npx',
|
|
31
|
+
args: ['@rvry/mcp'],
|
|
32
|
+
env: { RVRY_TOKEN: token },
|
|
33
|
+
});
|
|
17
34
|
// ── Utility functions ──────────────────────────────────────────────
|
|
18
35
|
/**
|
|
19
36
|
* Find the commands directory shipped with this package.
|
|
@@ -140,6 +157,7 @@ function maskToken(token) {
|
|
|
140
157
|
}
|
|
141
158
|
/**
|
|
142
159
|
* Open a URL in the default browser, platform-aware.
|
|
160
|
+
* Returns true if the browser was opened successfully, false otherwise.
|
|
143
161
|
*/
|
|
144
162
|
function openBrowser(url) {
|
|
145
163
|
const plat = platform();
|
|
@@ -153,11 +171,10 @@ function openBrowser(url) {
|
|
|
153
171
|
else {
|
|
154
172
|
execSync(`xdg-open "${url}"`, { stdio: 'pipe' });
|
|
155
173
|
}
|
|
174
|
+
return true;
|
|
156
175
|
}
|
|
157
176
|
catch {
|
|
158
|
-
|
|
159
|
-
console.log(' Could not open browser automatically.');
|
|
160
|
-
console.log(` Visit: ${url}`);
|
|
177
|
+
return false;
|
|
161
178
|
}
|
|
162
179
|
}
|
|
163
180
|
/**
|
|
@@ -173,11 +190,83 @@ function isClaudeCLIAvailable() {
|
|
|
173
190
|
return false;
|
|
174
191
|
}
|
|
175
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Get the platform-specific Claude Desktop config file path.
|
|
195
|
+
*/
|
|
196
|
+
function getDesktopConfigPath() {
|
|
197
|
+
const plat = platform();
|
|
198
|
+
if (plat === 'darwin') {
|
|
199
|
+
return join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
200
|
+
}
|
|
201
|
+
else if (plat === 'win32') {
|
|
202
|
+
return join(process.env.APPDATA ?? join(homedir(), 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json');
|
|
203
|
+
}
|
|
204
|
+
return join(homedir(), '.config', 'Claude', 'claude_desktop_config.json');
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Check if Claude Desktop config directory exists (app is installed).
|
|
208
|
+
*/
|
|
209
|
+
function isClaudeDesktopInstalled() {
|
|
210
|
+
const configPath = getDesktopConfigPath();
|
|
211
|
+
const configDir = dirname(configPath);
|
|
212
|
+
return existsSync(configDir);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Configure Claude Desktop by merging RVRY into the existing config.
|
|
216
|
+
* Creates the config file if it doesn't exist. Preserves other MCP servers.
|
|
217
|
+
* Returns 'created' | 'updated' | 'unchanged' | 'error'.
|
|
218
|
+
*/
|
|
219
|
+
function configureDesktop(token) {
|
|
220
|
+
const configPath = getDesktopConfigPath();
|
|
221
|
+
try {
|
|
222
|
+
let config = {};
|
|
223
|
+
if (existsSync(configPath)) {
|
|
224
|
+
const raw = readFileSync(configPath, 'utf-8');
|
|
225
|
+
try {
|
|
226
|
+
config = JSON.parse(raw);
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
console.log(` Warning: ${configPath} contains invalid JSON.`);
|
|
230
|
+
console.log(' Creating a backup and writing fresh config.');
|
|
231
|
+
writeFileSync(configPath + '.backup', raw, 'utf-8');
|
|
232
|
+
config = {};
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Ensure mcpServers object exists
|
|
236
|
+
if (!config.mcpServers || typeof config.mcpServers !== 'object') {
|
|
237
|
+
config.mcpServers = {};
|
|
238
|
+
}
|
|
239
|
+
const servers = config.mcpServers;
|
|
240
|
+
const newEntry = RVRY_SERVER_ENTRY(token);
|
|
241
|
+
// Check if RVRY is already configured with the same token
|
|
242
|
+
const existing = servers.RVRY;
|
|
243
|
+
if (existing) {
|
|
244
|
+
const existingEnv = existing.env;
|
|
245
|
+
if (existingEnv?.RVRY_TOKEN === token) {
|
|
246
|
+
return 'unchanged';
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const wasNew = !existing;
|
|
250
|
+
servers.RVRY = newEntry;
|
|
251
|
+
// Write config with clean formatting
|
|
252
|
+
const configDir = dirname(configPath);
|
|
253
|
+
if (!existsSync(configDir)) {
|
|
254
|
+
mkdirSync(configDir, { recursive: true });
|
|
255
|
+
}
|
|
256
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
257
|
+
return wasNew ? 'created' : 'updated';
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
261
|
+
console.log(` Error writing Desktop config: ${msg}`);
|
|
262
|
+
return 'error';
|
|
263
|
+
}
|
|
264
|
+
}
|
|
176
265
|
/**
|
|
177
266
|
* Register RVRY as an MCP server in Claude Code.
|
|
178
267
|
* Removes existing registration first for idempotency.
|
|
179
268
|
*/
|
|
180
|
-
function
|
|
269
|
+
function registerClaudeCode(token) {
|
|
181
270
|
try {
|
|
182
271
|
// Remove existing (ignore error if not registered)
|
|
183
272
|
try {
|
|
@@ -195,40 +284,124 @@ function registerMCP(token) {
|
|
|
195
284
|
}
|
|
196
285
|
}
|
|
197
286
|
/**
|
|
198
|
-
*
|
|
287
|
+
* Verify token by hitting the engine /api/usage endpoint.
|
|
288
|
+
* Returns tier info on success, null on failure.
|
|
289
|
+
*/
|
|
290
|
+
async function verifyToken(token) {
|
|
291
|
+
try {
|
|
292
|
+
const res = await fetch(`${ENGINE_URL}/api/usage`, {
|
|
293
|
+
headers: { 'Authorization': `Bearer ${token}` },
|
|
294
|
+
});
|
|
295
|
+
if (!res.ok)
|
|
296
|
+
return null;
|
|
297
|
+
const data = await res.json();
|
|
298
|
+
return data;
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Sleep for a given number of milliseconds.
|
|
306
|
+
*/
|
|
307
|
+
function sleep(ms) {
|
|
308
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
309
|
+
}
|
|
310
|
+
// ── Spinner animation ──────────────────────────────────────────────
|
|
311
|
+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
312
|
+
/**
|
|
313
|
+
* Create a spinner that animates in the terminal.
|
|
314
|
+
* Returns start/stop controls.
|
|
199
315
|
*/
|
|
200
|
-
function
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
},
|
|
316
|
+
function createSpinner(message) {
|
|
317
|
+
let frameIndex = 0;
|
|
318
|
+
let timer = null;
|
|
319
|
+
return {
|
|
320
|
+
start() {
|
|
321
|
+
process.stdout.write(` ${SPINNER_FRAMES[0]} ${message}`);
|
|
322
|
+
timer = setInterval(() => {
|
|
323
|
+
frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
|
|
324
|
+
process.stdout.write(`\r ${SPINNER_FRAMES[frameIndex]} ${message}`);
|
|
325
|
+
}, 80);
|
|
326
|
+
},
|
|
327
|
+
stop(finalMessage) {
|
|
328
|
+
if (timer) {
|
|
329
|
+
clearInterval(timer);
|
|
330
|
+
timer = null;
|
|
331
|
+
}
|
|
332
|
+
process.stdout.write('\r' + ' '.repeat(message.length + 10) + '\r');
|
|
333
|
+
if (finalMessage) {
|
|
334
|
+
console.log(` ${finalMessage}`);
|
|
335
|
+
}
|
|
210
336
|
},
|
|
211
337
|
};
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
338
|
+
}
|
|
339
|
+
// ── Device auth flow ───────────────────────────────────────────────
|
|
340
|
+
/**
|
|
341
|
+
* Device authorization flow: open browser, poll for token.
|
|
342
|
+
* Returns the token on success, or null on failure/timeout.
|
|
343
|
+
*/
|
|
344
|
+
async function deviceAuthFlow() {
|
|
345
|
+
// Step 1: Request a device code from the engine
|
|
346
|
+
let deviceCode;
|
|
347
|
+
let verificationUrl;
|
|
348
|
+
try {
|
|
349
|
+
const res = await fetch(`${ENGINE_URL}/api/device/start`, { method: 'POST' });
|
|
350
|
+
if (!res.ok) {
|
|
351
|
+
const body = await res.json().catch(() => ({ error: 'Unknown error' }));
|
|
352
|
+
console.log(` Could not start device auth: ${body.error ?? res.statusText}`);
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
const data = await res.json();
|
|
356
|
+
deviceCode = data.device_code;
|
|
357
|
+
verificationUrl = data.verification_url;
|
|
221
358
|
}
|
|
222
|
-
|
|
223
|
-
console.log('
|
|
224
|
-
|
|
359
|
+
catch (err) {
|
|
360
|
+
console.log(' Could not connect to RVRY engine. Is engine.rvry.ai reachable?');
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
// Step 2: Open browser
|
|
364
|
+
console.log('');
|
|
365
|
+
const browserOpened = openBrowser(verificationUrl);
|
|
366
|
+
if (browserOpened) {
|
|
367
|
+
console.log(' Browser opened. Complete sign-in to continue.');
|
|
225
368
|
}
|
|
226
369
|
else {
|
|
227
|
-
console.log('
|
|
228
|
-
console.log(
|
|
370
|
+
console.log(' Could not open browser automatically.');
|
|
371
|
+
console.log(` Visit: ${verificationUrl}`);
|
|
372
|
+
}
|
|
373
|
+
console.log('');
|
|
374
|
+
// Step 3: Poll for token
|
|
375
|
+
const spinner = createSpinner('Waiting for login in browser...');
|
|
376
|
+
spinner.start();
|
|
377
|
+
const startTime = Date.now();
|
|
378
|
+
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
|
|
379
|
+
await sleep(POLL_INTERVAL_MS);
|
|
380
|
+
try {
|
|
381
|
+
const res = await fetch(`${ENGINE_URL}/api/device/${deviceCode}/token`);
|
|
382
|
+
if (!res.ok) {
|
|
383
|
+
spinner.stop('Polling error. Falling back to manual token entry.');
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
const data = await res.json();
|
|
387
|
+
if (data.status === 'complete' && data.token) {
|
|
388
|
+
spinner.stop('Login successful!');
|
|
389
|
+
return { token: data.token, warning: data.warning };
|
|
390
|
+
}
|
|
391
|
+
if (data.status === 'expired') {
|
|
392
|
+
spinner.stop('Device code expired.');
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
// status === 'pending' — keep polling
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
// Network error during poll — keep trying
|
|
399
|
+
}
|
|
229
400
|
}
|
|
401
|
+
spinner.stop('Timed out waiting for login.');
|
|
402
|
+
return null;
|
|
230
403
|
}
|
|
231
|
-
// ──
|
|
404
|
+
// ── Manual token input flow ────────────────────────────────────────
|
|
232
405
|
async function getToken() {
|
|
233
406
|
while (true) {
|
|
234
407
|
const token = (await readHiddenInput(' Enter your RVRY token: ')).trim();
|
|
@@ -281,57 +454,181 @@ async function installCommands() {
|
|
|
281
454
|
}
|
|
282
455
|
// ── Main setup flow ────────────────────────────────────────────────
|
|
283
456
|
const BANNER = `
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
░██ ░██ ░███ ░██ ░██ ░██
|
|
457
|
+
_____ __ __ _____ __ __
|
|
458
|
+
| __ \\\\ \\\\ / /| __ \\\\ \\\\ / /
|
|
459
|
+
| |__) |\\\\ \\\\ / / | |__) |\\\\ \\\\_/ /
|
|
460
|
+
| _ / \\\\ \\\\/ / | _ / \\\\ /
|
|
461
|
+
| | \\\\ \\\\ \\\\ / | | \\\\ \\\\ | |
|
|
462
|
+
|_| \\\\_\\\\ \\\\/ |_| \\\\_\\\\ |_|
|
|
291
463
|
`;
|
|
292
464
|
export async function runSetup() {
|
|
293
465
|
console.log(BANNER);
|
|
294
466
|
console.log('Setup');
|
|
295
467
|
console.log('');
|
|
296
|
-
//
|
|
297
|
-
|
|
298
|
-
const
|
|
468
|
+
// Parse flags
|
|
469
|
+
const tokenFlagIndex = process.argv.indexOf('--token');
|
|
470
|
+
const clientFlagIndex = process.argv.indexOf('--client');
|
|
471
|
+
const clientFilter = clientFlagIndex !== -1 ? process.argv[clientFlagIndex + 1] : null;
|
|
472
|
+
// ── Step 1: Authentication ──────────────────────────────────────
|
|
473
|
+
let token = null;
|
|
474
|
+
let warning;
|
|
475
|
+
if (tokenFlagIndex !== -1 && process.argv[tokenFlagIndex + 1]) {
|
|
476
|
+
const flagValue = process.argv[tokenFlagIndex + 1];
|
|
477
|
+
if (!isValidToken(flagValue)) {
|
|
478
|
+
console.log(' Invalid token format. Expected: rvry_ followed by 32+ hex characters.');
|
|
479
|
+
process.exit(1);
|
|
480
|
+
}
|
|
481
|
+
token = flagValue;
|
|
482
|
+
console.log('[1/4] Authentication (from --token flag)');
|
|
483
|
+
console.log(` Token: ${maskToken(token)}`);
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
console.log('[1/4] Authentication');
|
|
487
|
+
console.log(' Opening browser for sign-in...');
|
|
488
|
+
const result = await deviceAuthFlow();
|
|
489
|
+
if (result) {
|
|
490
|
+
token = result.token;
|
|
491
|
+
warning = result.warning;
|
|
492
|
+
console.log(` Token: ${maskToken(token)}`);
|
|
493
|
+
if (warning) {
|
|
494
|
+
console.log(` Warning: ${warning}`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
console.log('');
|
|
499
|
+
console.log(' Falling back to manual token entry.');
|
|
500
|
+
console.log(' Generate a token at https://rvry.ai/dashboard');
|
|
501
|
+
console.log('');
|
|
502
|
+
token = await getToken();
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
// Verify token against engine
|
|
506
|
+
const spinner = createSpinner('Verifying token...');
|
|
507
|
+
spinner.start();
|
|
508
|
+
const usage = await verifyToken(token);
|
|
509
|
+
if (usage) {
|
|
510
|
+
spinner.stop(`Verified. Plan: ${usage.tier} (${usage.used}/${usage.limit} sessions used)`);
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
spinner.stop('Could not verify token (engine may be unreachable). Continuing anyway.');
|
|
514
|
+
}
|
|
515
|
+
console.log('');
|
|
516
|
+
// ── Step 2: Detect clients ──────────────────────────────────────
|
|
517
|
+
console.log('[2/4] Detecting Claude clients');
|
|
518
|
+
const hasClaudeCode = isClaudeCLIAvailable();
|
|
519
|
+
const hasDesktop = isClaudeDesktopInstalled();
|
|
520
|
+
const desktopConfigPath = getDesktopConfigPath();
|
|
521
|
+
const wantCode = !clientFilter || clientFilter === 'code';
|
|
522
|
+
const wantDesktop = !clientFilter || clientFilter === 'desktop';
|
|
523
|
+
if (hasClaudeCode && wantCode) {
|
|
524
|
+
console.log(' Found: Claude Code (claude CLI)');
|
|
525
|
+
}
|
|
526
|
+
if (hasDesktop && wantDesktop) {
|
|
527
|
+
console.log(` Found: Claude Desktop (${desktopConfigPath})`);
|
|
528
|
+
}
|
|
529
|
+
if (!hasClaudeCode && !hasDesktop) {
|
|
530
|
+
console.log(' No Claude clients detected.');
|
|
531
|
+
}
|
|
299
532
|
console.log('');
|
|
300
|
-
// Step
|
|
301
|
-
console.log('[
|
|
302
|
-
let
|
|
303
|
-
|
|
304
|
-
|
|
533
|
+
// ── Step 3: Configure clients ───────────────────────────────────
|
|
534
|
+
console.log('[3/4] Client Configuration');
|
|
535
|
+
let codeConfigured = false;
|
|
536
|
+
let desktopConfigured = 'skipped';
|
|
537
|
+
let neitherConfigured = true;
|
|
538
|
+
// Claude Code
|
|
539
|
+
if (hasClaudeCode && wantCode) {
|
|
540
|
+
const success = registerClaudeCode(token);
|
|
305
541
|
if (success) {
|
|
306
|
-
|
|
307
|
-
|
|
542
|
+
codeConfigured = true;
|
|
543
|
+
neitherConfigured = false;
|
|
544
|
+
console.log(' Claude Code: Registered (user scope -- works in all projects)');
|
|
308
545
|
}
|
|
309
546
|
else {
|
|
310
|
-
console.log(' Failed to register
|
|
311
|
-
printManualConfig(token);
|
|
547
|
+
console.log(' Claude Code: Failed to register via CLI');
|
|
312
548
|
}
|
|
313
549
|
}
|
|
314
|
-
else {
|
|
315
|
-
|
|
550
|
+
else if (wantCode && !hasClaudeCode) {
|
|
551
|
+
console.log(' Claude Code: CLI not found (install: https://docs.anthropic.com/en/docs/claude-code)');
|
|
552
|
+
}
|
|
553
|
+
// Claude Desktop
|
|
554
|
+
if (hasDesktop && wantDesktop) {
|
|
555
|
+
desktopConfigured = configureDesktop(token);
|
|
556
|
+
switch (desktopConfigured) {
|
|
557
|
+
case 'created':
|
|
558
|
+
neitherConfigured = false;
|
|
559
|
+
console.log(' Claude Desktop: Added RVRY to config');
|
|
560
|
+
break;
|
|
561
|
+
case 'updated':
|
|
562
|
+
neitherConfigured = false;
|
|
563
|
+
console.log(' Claude Desktop: Updated RVRY token in config');
|
|
564
|
+
break;
|
|
565
|
+
case 'unchanged':
|
|
566
|
+
neitherConfigured = false;
|
|
567
|
+
console.log(' Claude Desktop: Already configured with this token');
|
|
568
|
+
break;
|
|
569
|
+
case 'error':
|
|
570
|
+
console.log(' Claude Desktop: Failed to write config (see error above)');
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
else if (wantDesktop && !hasDesktop) {
|
|
575
|
+
console.log(' Claude Desktop: Not installed');
|
|
576
|
+
}
|
|
577
|
+
// Fallback: print manual config if nothing was configured
|
|
578
|
+
if (neitherConfigured) {
|
|
579
|
+
console.log('');
|
|
580
|
+
console.log(' Manual configuration:');
|
|
581
|
+
console.log('');
|
|
582
|
+
console.log(' Option A — Claude Code (if you install it later):');
|
|
583
|
+
console.log(` claude mcp add -e RVRY_TOKEN="${token}" -s user rvry -- npx @rvry/mcp`);
|
|
584
|
+
console.log('');
|
|
585
|
+
console.log(' Option B — Claude Desktop config JSON:');
|
|
586
|
+
const manualConfig = { mcpServers: { RVRY: RVRY_SERVER_ENTRY(token) } };
|
|
587
|
+
console.log(` File: ${desktopConfigPath}`);
|
|
588
|
+
console.log('');
|
|
589
|
+
for (const line of JSON.stringify(manualConfig, null, 2).split('\n')) {
|
|
590
|
+
console.log(` ${line}`);
|
|
591
|
+
}
|
|
316
592
|
}
|
|
317
593
|
console.log('');
|
|
318
|
-
// Step
|
|
319
|
-
console.log('[
|
|
594
|
+
// ── Step 4: Slash commands ──────────────────────────────────────
|
|
595
|
+
console.log('[4/4] Slash Commands');
|
|
320
596
|
const commandCount = await installCommands();
|
|
321
597
|
if (commandCount > 0) {
|
|
322
|
-
console.log(` Installed ${commandCount}
|
|
598
|
+
console.log(` Installed ${commandCount} command${commandCount > 1 ? 's' : ''} to .claude/commands/`);
|
|
323
599
|
}
|
|
324
600
|
else {
|
|
325
|
-
console.log(' No new commands installed.');
|
|
601
|
+
console.log(' No new commands installed (already up to date).');
|
|
326
602
|
}
|
|
327
603
|
console.log('');
|
|
328
|
-
// Summary
|
|
604
|
+
// ── Summary ─────────────────────────────────────────────────────
|
|
605
|
+
console.log('─'.repeat(50));
|
|
606
|
+
console.log('');
|
|
329
607
|
console.log('RVRY Setup Complete');
|
|
330
608
|
console.log('');
|
|
331
|
-
console.log(` Token:
|
|
332
|
-
|
|
333
|
-
|
|
609
|
+
console.log(` Token: ${maskToken(token)}${usage ? ` (${usage.tier})` : ''}`);
|
|
610
|
+
if (codeConfigured) {
|
|
611
|
+
console.log(' Claude Code: Configured');
|
|
612
|
+
}
|
|
613
|
+
if (desktopConfigured !== 'skipped' && desktopConfigured !== 'error') {
|
|
614
|
+
console.log(' Claude Desktop: Configured');
|
|
615
|
+
}
|
|
616
|
+
console.log(` Commands: ${commandCount} installed`);
|
|
334
617
|
console.log('');
|
|
335
|
-
|
|
618
|
+
// Client-specific next steps
|
|
619
|
+
console.log('Next steps:');
|
|
620
|
+
if (desktopConfigured !== 'skipped' && desktopConfigured !== 'error') {
|
|
621
|
+
console.log(' 1. Restart Claude Desktop for the new MCP server to load');
|
|
622
|
+
console.log(' RVRY tools will appear when Claude needs structured analysis');
|
|
623
|
+
}
|
|
624
|
+
if (codeConfigured) {
|
|
625
|
+
console.log(` ${desktopConfigured !== 'skipped' && desktopConfigured !== 'error' ? '2' : '1'}. In Claude Code, try:`);
|
|
626
|
+
console.log(' /deepthink "your question"');
|
|
627
|
+
console.log(' /problem-solve "your decision"');
|
|
628
|
+
}
|
|
629
|
+
if (!codeConfigured && (desktopConfigured === 'skipped' || desktopConfigured === 'error')) {
|
|
630
|
+
console.log(' 1. Configure a Claude client using the manual instructions above');
|
|
631
|
+
console.log(' 2. Then try: /deepthink "your question"');
|
|
632
|
+
}
|
|
336
633
|
console.log('');
|
|
337
634
|
}
|