@rvry/mcp 0.12.2 → 1.0.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/README.md +33 -41
- package/dist/client.d.ts +2 -2
- package/dist/index.d.ts +17 -6
- package/dist/index.js +444 -38
- package/dist/local-writer.d.ts +104 -16
- package/dist/local-writer.js +365 -56
- package/dist/setup.d.ts +25 -0
- package/dist/setup.js +202 -304
- package/package.json +2 -2
package/dist/setup.js
CHANGED
|
@@ -218,132 +218,6 @@ function registerClaudeCode(token) {
|
|
|
218
218
|
return 'error';
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
|
-
/**
|
|
222
|
-
* Write RVRY server entry into a generic MCP JSON config file.
|
|
223
|
-
* Used by clients that follow the Desktop-style config format (Codex, Gemini, etc.)
|
|
224
|
-
*/
|
|
225
|
-
function configureJsonMcp(configPath, token) {
|
|
226
|
-
try {
|
|
227
|
-
let config = {};
|
|
228
|
-
if (existsSync(configPath)) {
|
|
229
|
-
const raw = readFileSync(configPath, 'utf-8');
|
|
230
|
-
try {
|
|
231
|
-
config = JSON.parse(raw);
|
|
232
|
-
}
|
|
233
|
-
catch {
|
|
234
|
-
writeFileSync(configPath + '.backup', raw, 'utf-8');
|
|
235
|
-
config = {};
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
if (!config.mcpServers || typeof config.mcpServers !== 'object') {
|
|
239
|
-
config.mcpServers = {};
|
|
240
|
-
}
|
|
241
|
-
const servers = config.mcpServers;
|
|
242
|
-
const existing = servers.RVRY;
|
|
243
|
-
if (existing) {
|
|
244
|
-
const existingEnv = existing.env;
|
|
245
|
-
if (existingEnv?.RVRY_TOKEN === token)
|
|
246
|
-
return 'unchanged';
|
|
247
|
-
}
|
|
248
|
-
const wasNew = !existing;
|
|
249
|
-
servers.RVRY = RVRY_SERVER_ENTRY(token);
|
|
250
|
-
const dir = dirname(configPath);
|
|
251
|
-
if (!existsSync(dir))
|
|
252
|
-
mkdirSync(dir, { recursive: true });
|
|
253
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
254
|
-
return wasNew ? 'ok' : 'updated';
|
|
255
|
-
}
|
|
256
|
-
catch (err) {
|
|
257
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
258
|
-
console.log(` Error writing config: ${msg}`);
|
|
259
|
-
return 'error';
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Write RVRY server entry into a TOML config file (e.g. ~/.codex/config.toml).
|
|
264
|
-
* Uses string/regex manipulation — no TOML parser dependency.
|
|
265
|
-
*/
|
|
266
|
-
function configureTomlMcp(configPath, token) {
|
|
267
|
-
const block = [
|
|
268
|
-
'[mcp_servers.rvry]',
|
|
269
|
-
'command = "npx"',
|
|
270
|
-
'args = ["--yes", "--package", "@rvry/mcp@latest", "rvry-mcp"]',
|
|
271
|
-
'',
|
|
272
|
-
'[mcp_servers.rvry.env]',
|
|
273
|
-
`RVRY_TOKEN = "${token}"`,
|
|
274
|
-
].join('\n');
|
|
275
|
-
try {
|
|
276
|
-
let content = '';
|
|
277
|
-
if (existsSync(configPath)) {
|
|
278
|
-
content = readFileSync(configPath, 'utf-8');
|
|
279
|
-
}
|
|
280
|
-
const sectionRegex = /^\[mcp_servers\.rvry\]/m;
|
|
281
|
-
if (sectionRegex.test(content)) {
|
|
282
|
-
// Section exists — check if token is the same
|
|
283
|
-
const tokenMatch = content.match(/^\[mcp_servers\.rvry\.env\]\s*\nRVRY_TOKEN\s*=\s*"([^"]*)"/m);
|
|
284
|
-
if (tokenMatch && tokenMatch[1] === token) {
|
|
285
|
-
return 'unchanged';
|
|
286
|
-
}
|
|
287
|
-
// Replace entire section: from [mcp_servers.rvry] to next top-level section or EOF
|
|
288
|
-
// Match from [mcp_servers.rvry] up to (but not including) the next section header
|
|
289
|
-
// that is NOT a sub-section of mcp_servers.rvry
|
|
290
|
-
const replaceRegex = /\[mcp_servers\.rvry\][\s\S]*?(?=\n\[(?!mcp_servers\.rvry[.\]])|$)/;
|
|
291
|
-
content = content.replace(replaceRegex, block);
|
|
292
|
-
writeFileSync(configPath, content, 'utf-8');
|
|
293
|
-
return 'updated';
|
|
294
|
-
}
|
|
295
|
-
// Section doesn't exist — append
|
|
296
|
-
const separator = content.length > 0 && !content.endsWith('\n') ? '\n\n' : content.length > 0 ? '\n' : '';
|
|
297
|
-
const dir = dirname(configPath);
|
|
298
|
-
if (!existsSync(dir))
|
|
299
|
-
mkdirSync(dir, { recursive: true });
|
|
300
|
-
writeFileSync(configPath, content + separator + block + '\n', 'utf-8');
|
|
301
|
-
return 'ok';
|
|
302
|
-
}
|
|
303
|
-
catch (err) {
|
|
304
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
305
|
-
console.log(` Error writing TOML config: ${msg}`);
|
|
306
|
-
return 'error';
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
function isGeminiCLIAvailable() {
|
|
310
|
-
try {
|
|
311
|
-
const cmd = platform() === 'win32' ? 'where gemini' : 'which gemini';
|
|
312
|
-
execSync(cmd, { stdio: 'pipe' });
|
|
313
|
-
return true;
|
|
314
|
-
}
|
|
315
|
-
catch { /* not on PATH */ }
|
|
316
|
-
if (existsSync(join(homedir(), '.gemini', 'settings.json')))
|
|
317
|
-
return true;
|
|
318
|
-
return false;
|
|
319
|
-
}
|
|
320
|
-
function registerGeminiCLI(token) {
|
|
321
|
-
try {
|
|
322
|
-
// Remove existing entry first (silent fail if not registered)
|
|
323
|
-
try {
|
|
324
|
-
execSync('gemini mcp remove rvry', { stdio: 'pipe' });
|
|
325
|
-
}
|
|
326
|
-
catch { /* not registered */ }
|
|
327
|
-
execSync(`gemini mcp add rvry -e RVRY_TOKEN="${token}" --scope user npx --yes --package @rvry/mcp@latest rvry-mcp`, { stdio: 'inherit' });
|
|
328
|
-
return 'ok';
|
|
329
|
-
}
|
|
330
|
-
catch {
|
|
331
|
-
// Fallback: write settings.json directly
|
|
332
|
-
return configureJsonMcp(join(homedir(), '.gemini', 'settings.json'), token);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
function isCodexAvailable() {
|
|
336
|
-
try {
|
|
337
|
-
const cmd = platform() === 'win32' ? 'where codex' : 'which codex';
|
|
338
|
-
execSync(cmd, { stdio: 'pipe' });
|
|
339
|
-
return true;
|
|
340
|
-
}
|
|
341
|
-
catch { /* not on PATH */ }
|
|
342
|
-
// Fallback: check if config directory exists (covers aliased installs)
|
|
343
|
-
if (existsSync(join(homedir(), '.codex', 'config.toml')))
|
|
344
|
-
return true;
|
|
345
|
-
return false;
|
|
346
|
-
}
|
|
347
221
|
/** All supported clients. Add new clients here. */
|
|
348
222
|
const CLIENT_REGISTRY = [
|
|
349
223
|
{
|
|
@@ -360,70 +234,6 @@ const CLIENT_REGISTRY = [
|
|
|
360
234
|
configure: (token) => configureDesktop(token),
|
|
361
235
|
notInstalledHint: 'Not installed',
|
|
362
236
|
},
|
|
363
|
-
{
|
|
364
|
-
name: 'Anti-Gravity',
|
|
365
|
-
id: 'antigravity',
|
|
366
|
-
defaultSelected: false,
|
|
367
|
-
detect: () => {
|
|
368
|
-
// OR-logic: binary (agy or antigravity), config dir, or macOS app bundle
|
|
369
|
-
try {
|
|
370
|
-
const cmd = platform() === 'win32' ? 'where agy' : 'which agy';
|
|
371
|
-
execSync(cmd, { stdio: 'pipe' });
|
|
372
|
-
return true;
|
|
373
|
-
}
|
|
374
|
-
catch { /* not on PATH */ }
|
|
375
|
-
if (platform() !== 'win32') {
|
|
376
|
-
try {
|
|
377
|
-
execSync('which antigravity', { stdio: 'pipe' });
|
|
378
|
-
return true;
|
|
379
|
-
}
|
|
380
|
-
catch { /* not on PATH */ }
|
|
381
|
-
}
|
|
382
|
-
if (existsSync(join(homedir(), '.gemini', 'antigravity')))
|
|
383
|
-
return true;
|
|
384
|
-
if (platform() === 'darwin' && existsSync('/Applications/Google Antigravity.app'))
|
|
385
|
-
return true;
|
|
386
|
-
return false;
|
|
387
|
-
},
|
|
388
|
-
configure: (token) => configureJsonMcp(join(homedir(), '.gemini', 'antigravity', 'mcp_config.json'), token),
|
|
389
|
-
notInstalledHint: 'Not installed (https://antigravity.google)',
|
|
390
|
-
},
|
|
391
|
-
{
|
|
392
|
-
name: 'Cursor',
|
|
393
|
-
id: 'cursor',
|
|
394
|
-
defaultSelected: false,
|
|
395
|
-
detect: () => {
|
|
396
|
-
if (platform() === 'darwin' && existsSync('/Applications/Cursor.app'))
|
|
397
|
-
return true;
|
|
398
|
-
if (existsSync(join(homedir(), '.cursor', 'mcp.json')))
|
|
399
|
-
return true;
|
|
400
|
-
try {
|
|
401
|
-
const cmd = platform() === 'win32' ? 'where cursor' : 'which cursor';
|
|
402
|
-
execSync(cmd, { stdio: 'pipe' });
|
|
403
|
-
return true;
|
|
404
|
-
}
|
|
405
|
-
catch { /* not on PATH */ }
|
|
406
|
-
return false;
|
|
407
|
-
},
|
|
408
|
-
configure: (token) => configureJsonMcp(join(homedir(), '.cursor', 'mcp.json'), token),
|
|
409
|
-
notInstalledHint: 'Not installed (https://cursor.com)',
|
|
410
|
-
},
|
|
411
|
-
{
|
|
412
|
-
name: 'Gemini CLI',
|
|
413
|
-
id: 'gemini',
|
|
414
|
-
defaultSelected: false,
|
|
415
|
-
detect: isGeminiCLIAvailable,
|
|
416
|
-
configure: registerGeminiCLI,
|
|
417
|
-
notInstalledHint: 'Not installed (https://github.com/google-gemini/gemini-cli)',
|
|
418
|
-
},
|
|
419
|
-
{
|
|
420
|
-
name: 'Codex',
|
|
421
|
-
id: 'codex',
|
|
422
|
-
defaultSelected: false,
|
|
423
|
-
detect: isCodexAvailable,
|
|
424
|
-
configure: (token) => configureTomlMcp(join(homedir(), '.codex', 'config.toml'), token),
|
|
425
|
-
notInstalledHint: 'Not installed (https://openai.com/codex)',
|
|
426
|
-
},
|
|
427
237
|
];
|
|
428
238
|
/**
|
|
429
239
|
* Interactive multi-select picker with arrow keys + space + enter.
|
|
@@ -713,115 +523,225 @@ function ensureGitignore() {
|
|
|
713
523
|
}
|
|
714
524
|
}
|
|
715
525
|
// ── Slash commands for Claude Code ──────────────────────────────────
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
526
|
+
/**
|
|
527
|
+
* Build a Path B slash-command file.
|
|
528
|
+
*
|
|
529
|
+
* Teaches Claude to: parse --record, remember the round-1 shimmer text, remember
|
|
530
|
+
* per-round bodies when --record is on, and construct a `harvest` object on the
|
|
531
|
+
* final tool call so the MCP client can persist it to `.rvry/<op>/<slug>/`.
|
|
532
|
+
*
|
|
533
|
+
* Shared additions (S1/S2/S3) live in the FINAL CALL section:
|
|
534
|
+
* - S1 scope-drift check
|
|
535
|
+
* - S2 followUps quality bar (replaces the original phrasing)
|
|
536
|
+
* - S3 verify-or-defer rule
|
|
537
|
+
*
|
|
538
|
+
* Per-tool extensions are appended via the `extras` blocks. They slot into
|
|
539
|
+
* defined sections to compose with the shared body.
|
|
540
|
+
*/
|
|
541
|
+
function buildShim(opts) {
|
|
542
|
+
const { toolName, subject, orientLine, completionGuidance, shortSessionNote, perRoundPrelude, analyticalGuidance, prePerRoundBlock, finalCallExtras, } = opts;
|
|
543
|
+
return `Call the ${toolName} MCP tool with your ${subject} to start an analysis session.
|
|
720
544
|
|
|
721
|
-
|
|
545
|
+
${subject.charAt(0).toUpperCase() + subject.slice(1)}: $ARGUMENTS
|
|
722
546
|
|
|
723
|
-
|
|
724
|
-
1.
|
|
547
|
+
ARGUMENT PARSING:
|
|
548
|
+
1. If $ARGUMENTS contains the literal flag \`--record\`, remove it from the ${subject} text and REMEMBER that record mode is ON for this session.
|
|
549
|
+
${shortSessionNote ? shortSessionNote + '\n' : ''}${prePerRoundBlock ? prePerRoundBlock + '\n\n' : ''}PER-ROUND FLOW:
|
|
550
|
+
${perRoundPrelude ? perRoundPrelude + '\n\n' : ''}1. Read the prompt field -- this is your analytical task. DO NOT display it to the user.
|
|
725
551
|
2. Work through the analysis thoroughly.
|
|
726
552
|
3. Show the user your analysis findings as concise formatted markdown.
|
|
727
553
|
- Use bold headings for key findings.
|
|
728
554
|
- Keep each round's visible output focused (2-4 paragraphs, not an essay).
|
|
729
555
|
4. Call the tool again with your full analysis as the input parameter.
|
|
730
|
-
- Include the Constraint Updates block (FORWARD/RESOLVE/MILESTONE lines) ONLY in the tool call input, not in your visible output to the user.
|
|
731
556
|
- Include the sessionId from the previous response.
|
|
557
|
+
- If record mode is ON, set \`record: true\` on every call.
|
|
558
|
+
- If this round was SHIMMER, REMEMBER the exact shimmer text you produced -- you will pass it as harvest.shimmer on the final call.
|
|
559
|
+
- If record mode is ON, also REMEMBER your round body as {round, mode, body} for the final call.
|
|
560
|
+
${analyticalGuidance ? '\n' + analyticalGuidance + '\n' : ''}
|
|
561
|
+
FINAL CALL (the call whose response will have status "complete"):
|
|
562
|
+
When you make the call that completes the session, also construct a \`harvest\` object on that SAME call:
|
|
563
|
+
- title: a 1-4 word title capturing the topic
|
|
564
|
+
- summary: 3-6 sentences of synthesis, leading with substance (no mid-word truncation)
|
|
565
|
+
- keyFindings: 3-7 substantive bullet findings (not bare section headers)
|
|
566
|
+
- shimmer: the full round-1 shimmer text you remembered (omit if the session had no shimmer round)
|
|
567
|
+
- openQuestions: [optional] remaining uncertainties
|
|
568
|
+
- followUps: genuine next-question leads — NOT "verify X" directives, NOT constraint dumps, NOT "explore later" boilerplate. If there are no real next-question leads, omit the section entirely.${finalCallExtras ? '\n' + finalCallExtras : ''}
|
|
732
569
|
|
|
733
|
-
|
|
570
|
+
Before writing harvest.summary, check whether the analysis ended up in a different lane than the question asked. Depth is often correct; drift isn't. If the analysis drifted, either return to the asked question or name the drift explicitly in the summary.
|
|
734
571
|
|
|
735
|
-
|
|
572
|
+
If the pre-harvest self-check surfaced a concern, do not dismiss it in the same response. Either verify it (read a file, check the claim) or defer it (name the concern and why it can't be verified now — this becomes a followUp).
|
|
736
573
|
|
|
737
|
-
|
|
574
|
+
If record mode was ON, also include \`rounds: [{round, mode, body}, ...]\` on that final call with the bodies you remembered per round.
|
|
738
575
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
file: "problem-solve.md",
|
|
744
|
-
content: `Call the RVRY_problem_solve MCP tool with your question to work through this decision.
|
|
576
|
+
STATUS BEHAVIOR:
|
|
577
|
+
When status is "shimmer": Show your FULL self-observation to the user, beginning with "RVRY SHIMMER" and the revery definition. REMEMBER this text verbatim for the final harvest.shimmer.
|
|
578
|
+
When status is "orient": Orient on the ${subject}. Show the user "${orientLine}"
|
|
579
|
+
When status is "complete": ${completionGuidance}
|
|
745
580
|
|
|
746
|
-
|
|
581
|
+
DO NOT display: the prompt text you received, constraint tables, quality scores, internal tracking data, or round numbers to the user.`;
|
|
582
|
+
}
|
|
583
|
+
// ── /think extensions (T1, T3) ──────────────────────────────────────
|
|
584
|
+
const THINK_DIFFICULTY_GATE = `0. Difficulty Gate (BEFORE the first tool call): ask yourself, "Is this a simple question dressed up as a complex one?" If yes — do SHIMMER on round 1 as usual, then on round 2 produce a single analytical pass AND include the \`harvest\` object in that same call so the session completes in round 2. Don't stretch a simple /think into a multi-round mini-deepthink.`;
|
|
585
|
+
const THINK_MODE_ROUTING = `MODE ROUTING WITHIN ANALYTICAL ROUNDS:
|
|
586
|
+
When writing your analytical body, let the active constraint types guide your emphasis: FORWARD (unresolved gap) → depth on the specific gap; FORBIDDEN (untested boundary) → stress-test what's marked forbidden; QUESTION (open question) → different viewpoints or analogies. The engine picks the next mode; your framing within the current mode should address the most active constraint type.`;
|
|
587
|
+
// ── /problem-solve extensions (P1 in analytical guidance, P2/P3/P5 in finalCallExtras) ───
|
|
588
|
+
const PROBLEM_SOLVE_DECISION_TYPE = `DECISION-TYPE CLASSIFICATION (round 2, the first analytical round after SHIMMER):
|
|
589
|
+
On your first analytical round (round 2 after SHIMMER), classify this decision as one of: multi-option / stakeholder / system / novel / technical. Name the classification and why. In subsequent rounds, let the classification shape your framing within each sequential mode.
|
|
747
590
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
591
|
+
- multi-option ("Should we use X or Y?"): generate options, compare against criteria.
|
|
592
|
+
- stakeholder ("How do we align competing interests?"): surface tensions, steelman each perspective.
|
|
593
|
+
- system ("How does this affect the system?"): map components, cascading effects.
|
|
594
|
+
- novel ("We haven't done this before"): analogies, creative alternatives.
|
|
595
|
+
- technical ("One hard technical question"): depth-focused, analytical/adversarial.`;
|
|
596
|
+
const PROBLEM_SOLVE_FINAL_EXTRAS = `- biasAudit: For each of 6 biases (anchoring, framing, authority, sunk_cost, availability, confirmation), check whether it's operating against your leading direction. For each one that IS operating, cite evidence and link it to a specific reflex from the SHIMMER text you remembered (use the \`shimmer\` text verbatim) in \`sourceReflex\`. Include \`frameReversal\`: phrase the opposite of the original framing and state whether you'd reach the same conclusion. Set \`decisionStable\` to true only if the leading direction survives the bias check unchanged.
|
|
597
|
+
- adversarialVerdict: After your stress-test round, set this to PASSED (direction survives), CAVEATS (holds with modifications — list them in keyFindings), or NEEDS_REVISION (critical weakness found).
|
|
598
|
+
- reversalConditions: 3-5 entries answering — under what conditions would you reverse this decision? What are the kill switches / escalation triggers? What is the review timeline?`;
|
|
599
|
+
// ── /challenge extensions (Phase 0.5 + C1-C6 in finalCallExtras) ────
|
|
600
|
+
const CHALLENGE_PHASE_0_5 = `PHASE 0.5: UNCERTAINTY SCAN (unless --auto in $ARGUMENTS)
|
|
757
601
|
|
|
758
|
-
|
|
602
|
+
Before calling the challenge tool for the first time:
|
|
759
603
|
|
|
760
|
-
|
|
604
|
+
1. Identify what you don't know about the proposal (context, constraints, goals, dependencies).
|
|
605
|
+
2. Classify each uncertainty:
|
|
606
|
+
- BLOCKING: would invalidate your adversarial analysis if wrong (changes what you'd attack, determines scope, affects risk severity).
|
|
607
|
+
- NON-BLOCKING: nice to know but analysis remains valid either way.
|
|
608
|
+
3. For each BLOCKING uncertainty not answered in the proposal, ask via AskUserQuestion:
|
|
609
|
+
- question: the blocking question
|
|
610
|
+
- header: short label (max 12 chars)
|
|
611
|
+
- options: 2-4 options; smart-default first, labeled "(Recommended)"
|
|
612
|
+
- multiSelect: false
|
|
613
|
+
4. Once answers arrive (or user-stated assumptions), incorporate them into the proposal text and call the challenge tool.
|
|
761
614
|
|
|
762
|
-
|
|
615
|
+
If $ARGUMENTS contains --auto, skip the scan; state assumptions explicitly in your first analytical round.
|
|
616
|
+
If no BLOCKING uncertainties exist, proceed directly to the first tool call.`;
|
|
617
|
+
const CHALLENGE_FINAL_EXTRAS = `- verdict: GO / CONDITIONAL_GO / NO_GO. If CONDITIONAL_GO, also include \`mitigations: string[]\` listing conditions required for GO.
|
|
618
|
+
- confidence: 0.0-1.0 reflecting how strongly you hold the adversarial conclusion.
|
|
619
|
+
- mitigations: required when verdict is CONDITIONAL_GO; the conditions that must be met before proceeding.
|
|
620
|
+
- assumptions: 3-4 entries of \`{assumption, confidence, ifWrong, bias, biasEvidence}\`. Identify if a cognitive bias is likely operating on each assumption — do NOT manufacture biases; \`none\` is valid (use empty string for biasEvidence in that case).
|
|
621
|
+
- edgeCases: 5-10 entries covering boundary conditions and hostile scenarios the proposal needs to survive.
|
|
622
|
+
- failureModes: 3-5 entries of \`{component, howItFails, blastRadius, detection}\` identifying what breaks and how you would notice.
|
|
623
|
+
- counterArguments: 2-3 entries of \`{argument, rebuttal}\`. Each \`argument\` is the strongest case FOR the proposal — steelman it genuinely; each \`rebuttal\` is why it's flawed or insufficient. This prevents one-sided adversarial output.`;
|
|
624
|
+
/** The /rvry shim (renamed from /think). Carries the Difficulty Gate + mode routing. */
|
|
625
|
+
const RVRY_SHIM_CONTENT = buildShim({
|
|
626
|
+
toolName: 'rvry',
|
|
627
|
+
subject: 'question',
|
|
628
|
+
orientLine: 'Orienting on the question.',
|
|
629
|
+
completionGuidance: 'Present your findings clearly with bold headings. Sessions are short; one or two analytical rounds is expected.',
|
|
630
|
+
shortSessionNote: 'NOTE: /rvry sessions are usually short (1-2 analytical rounds after orient). Expect to reach complete quickly.',
|
|
631
|
+
perRoundPrelude: THINK_DIFFICULTY_GATE,
|
|
632
|
+
analyticalGuidance: THINK_MODE_ROUTING,
|
|
633
|
+
});
|
|
634
|
+
/** Deprecation prefix for the /think tombstone command. */
|
|
635
|
+
const THINK_DEPRECATION_LINE = 'FIRST: Tell the user that /think is now /rvry — the /think command will be removed in the next major version. Then proceed with the flow below.';
|
|
636
|
+
/** The /shimmer shim: 3-round reflective answering with client-owned harvest. */
|
|
637
|
+
const SHIMMER_SHIM_CONTENT = `Call the shimmer MCP tool with your question to start a reflective answering session.
|
|
763
638
|
|
|
764
|
-
|
|
765
|
-
label: "/problem-solve — structured decision-making",
|
|
766
|
-
},
|
|
767
|
-
{
|
|
768
|
-
file: "challenge.md",
|
|
769
|
-
content: `Call the RVRY_challenge MCP tool with the proposal to stress-test it.
|
|
639
|
+
Question: $ARGUMENTS
|
|
770
640
|
|
|
771
|
-
|
|
641
|
+
ARGUMENT PARSING:
|
|
642
|
+
1. If $ARGUMENTS contains the literal flag \`--record\`, remove it from the question text and REMEMBER that record mode is ON for this session.
|
|
772
643
|
|
|
773
|
-
|
|
774
|
-
1.
|
|
775
|
-
2.
|
|
776
|
-
3.
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
4. Call the tool again with your full analysis as the input parameter.
|
|
780
|
-
- Include the Constraint Updates block (FORWARD/RESOLVE/MILESTONE lines) ONLY in the tool call input, not in your visible output to the user.
|
|
781
|
-
- Include the sessionId from the previous response.
|
|
644
|
+
ROUND 1 — OBSERVATION (status "shimmer"):
|
|
645
|
+
1. The response's prompt field is your observation task. Work through it.
|
|
646
|
+
2. Show the user your FULL observation, beginning with "**RVRY SHIMMER**" and the revery definition, exactly as the instruction field describes. Do NOT show a brief status line.
|
|
647
|
+
3. End your observation with the commitments that surfaced, one per line as FORWARD:/FORBIDDEN:/QUESTION: lines — or the single line NOTHING if none surfaced.
|
|
648
|
+
4. REMEMBER your observation text verbatim — you will pass it as harvest.shimmer on the final call.
|
|
649
|
+
5. Call the shimmer tool again with your observation text as input and the sessionId.
|
|
782
650
|
|
|
783
|
-
|
|
651
|
+
ROUNDS 2-3 — ANSWER AND RESOLUTION (status "active"):
|
|
652
|
+
1. Read the prompt field — this is your task. DO NOT display it to the user.
|
|
653
|
+
2. Round 2 is the answer itself, working under the commitments you surfaced. Round 3 (if it occurs) resolves what the answer left unaddressed — engage each remaining commitment specifically or defer it with a reason.
|
|
654
|
+
3. Show the user your answer as concise formatted markdown.
|
|
655
|
+
4. Call the tool again with your full text as the input parameter, including the sessionId.
|
|
656
|
+
- If record mode is ON, set \`record: true\` on every call and REMEMBER your round body as {round, mode, body} for the final call.
|
|
784
657
|
|
|
785
|
-
|
|
658
|
+
FINAL CALL (the call whose response will have status "complete"):
|
|
659
|
+
When you make the call that completes the session, also construct a \`harvest\` object on that SAME call:
|
|
660
|
+
- title: a 1-4 word title capturing the topic
|
|
661
|
+
- summary: 3-6 sentences of synthesis, leading with substance
|
|
662
|
+
- keyFindings: 3-7 substantive bullet findings
|
|
663
|
+
- shimmer: the full round-1 observation text you remembered, verbatim
|
|
664
|
+
- constraintLedger: one line per surfaced commitment with its fate — addressed (and how), deferred (and why), or still open
|
|
665
|
+
- openQuestions: [optional] remaining uncertainties
|
|
666
|
+
- followUps: genuine next-question leads; omit the section if there are none
|
|
786
667
|
|
|
787
|
-
|
|
668
|
+
If record mode was ON, also include \`rounds: [{round, mode, body}, ...]\` on that final call with the bodies you remembered per round.
|
|
788
669
|
|
|
789
|
-
DO NOT display: the prompt text you received, constraint tables, quality scores, internal tracking data, or round numbers to the user
|
|
790
|
-
|
|
670
|
+
DO NOT display: the prompt text you received (the round-1 observation you produce IS shown in full), constraint tables, quality scores, internal tracking data, or round numbers to the user.`;
|
|
671
|
+
const SLASH_COMMANDS = [
|
|
672
|
+
{
|
|
673
|
+
file: 'deepthink.md',
|
|
674
|
+
content: buildShim({
|
|
675
|
+
toolName: 'deepthink',
|
|
676
|
+
subject: 'question',
|
|
677
|
+
orientLine: 'Orienting on the question.',
|
|
678
|
+
completionGuidance: 'Present your final synthesis with key findings as bold headings. Lead with insights, not process.',
|
|
679
|
+
}),
|
|
680
|
+
label: '/deepthink — deep analysis',
|
|
791
681
|
},
|
|
792
682
|
{
|
|
793
|
-
file:
|
|
794
|
-
content:
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
683
|
+
file: 'problem-solve.md',
|
|
684
|
+
content: buildShim({
|
|
685
|
+
toolName: 'problem_solve',
|
|
686
|
+
subject: 'problem',
|
|
687
|
+
orientLine: 'Orienting on the problem.',
|
|
688
|
+
completionGuidance: 'Present your final synthesis as a structured decision with key findings as bold headings. Lead with your recommendation, not process.',
|
|
689
|
+
analyticalGuidance: PROBLEM_SOLVE_DECISION_TYPE,
|
|
690
|
+
finalCallExtras: PROBLEM_SOLVE_FINAL_EXTRAS,
|
|
691
|
+
}),
|
|
692
|
+
label: '/problem-solve — structured decision-making',
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
file: 'rvry.md',
|
|
696
|
+
content: RVRY_SHIM_CONTENT,
|
|
697
|
+
label: '/rvry — structured single-session analysis',
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
file: 'shimmer.md',
|
|
701
|
+
content: SHIMMER_SHIM_CONTENT,
|
|
702
|
+
label: '/shimmer — reflective answering',
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
file: 'think.md',
|
|
706
|
+
content: `${THINK_DEPRECATION_LINE}\n\n${RVRY_SHIM_CONTENT}`,
|
|
707
|
+
label: '/think — renamed to /rvry (deprecated alias)',
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
file: 'challenge.md',
|
|
711
|
+
content: buildShim({
|
|
712
|
+
toolName: 'challenge',
|
|
713
|
+
subject: 'proposal',
|
|
714
|
+
orientLine: 'Orienting on the proposal.',
|
|
715
|
+
completionGuidance: 'Present your final assessment with key vulnerabilities and surviving strengths as bold headings. Lead with findings, not process.',
|
|
716
|
+
prePerRoundBlock: CHALLENGE_PHASE_0_5,
|
|
717
|
+
finalCallExtras: CHALLENGE_FINAL_EXTRAS,
|
|
718
|
+
}),
|
|
719
|
+
label: '/challenge — adversarial stress-testing',
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
file: 'meta.md',
|
|
723
|
+
content: buildShim({
|
|
724
|
+
toolName: 'meta',
|
|
725
|
+
subject: 'topic',
|
|
726
|
+
orientLine: 'Orienting on the topic.',
|
|
727
|
+
completionGuidance: 'Present your final metacognitive synthesis as a cohesive narrative reflection -- not a structured report. Lead with insights, not process.',
|
|
728
|
+
}),
|
|
729
|
+
label: '/meta — metacognitive observation (Max tier)',
|
|
816
730
|
},
|
|
817
731
|
];
|
|
818
732
|
/**
|
|
819
|
-
* Install RVRY slash commands
|
|
733
|
+
* Install RVRY slash commands into the given directory.
|
|
820
734
|
* Only called when Claude Code is the selected client.
|
|
821
|
-
*
|
|
735
|
+
*
|
|
736
|
+
* RVRY owns these filenames: existing files are overwritten unconditionally,
|
|
737
|
+
* with the pre-existing content backed up to <name>.md.bak first (R-4).
|
|
738
|
+
*
|
|
739
|
+
* @param commandsDir - REQUIRED target directory. The real
|
|
740
|
+
* ~/.claude/commands path is resolved by runSetup and passed explicitly;
|
|
741
|
+
* there is deliberately no default so a bare call in a test can never
|
|
742
|
+
* touch the real directory.
|
|
822
743
|
*/
|
|
823
|
-
function installSlashCommands() {
|
|
824
|
-
const commandsDir = join(homedir(), ".claude", "commands");
|
|
744
|
+
function installSlashCommands(commandsDir) {
|
|
825
745
|
const installed = [];
|
|
826
746
|
try {
|
|
827
747
|
if (!existsSync(commandsDir)) {
|
|
@@ -829,11 +749,14 @@ function installSlashCommands() {
|
|
|
829
749
|
}
|
|
830
750
|
for (const cmd of SLASH_COMMANDS) {
|
|
831
751
|
const filePath = join(commandsDir, cmd.file);
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
752
|
+
const existed = existsSync(filePath);
|
|
753
|
+
if (existed) {
|
|
754
|
+
writeFileSync(`${filePath}.bak`, readFileSync(filePath, "utf-8"), "utf-8");
|
|
835
755
|
}
|
|
836
756
|
writeFileSync(filePath, cmd.content + "\n", "utf-8");
|
|
757
|
+
console.log(existed
|
|
758
|
+
? ` Updated ${cmd.file} (previous version backed up to ${cmd.file}.bak)`
|
|
759
|
+
: ` Installed ${cmd.file}`);
|
|
837
760
|
installed.push(cmd.label);
|
|
838
761
|
}
|
|
839
762
|
}
|
|
@@ -843,6 +766,8 @@ function installSlashCommands() {
|
|
|
843
766
|
}
|
|
844
767
|
return installed;
|
|
845
768
|
}
|
|
769
|
+
// Exported for tests (content + install behavior).
|
|
770
|
+
export { SLASH_COMMANDS, installSlashCommands, RVRY_SHIM_CONTENT, SHIMMER_SHIM_CONTENT, THINK_DEPRECATION_LINE };
|
|
846
771
|
// ── Main setup flow ────────────────────────────────────────────────
|
|
847
772
|
const BANNER = `
|
|
848
773
|
8888888b. 888 888 8888888b. Y88b d88P
|
|
@@ -990,7 +915,7 @@ export async function runSetup() {
|
|
|
990
915
|
if (claudeCodeConfigured) {
|
|
991
916
|
console.log("");
|
|
992
917
|
console.log(" Installing slash commands...");
|
|
993
|
-
installedCommands = installSlashCommands();
|
|
918
|
+
installedCommands = installSlashCommands(join(homedir(), ".claude", "commands"));
|
|
994
919
|
if (installedCommands.length > 0) {
|
|
995
920
|
console.log(` Installed ${installedCommands.length} command(s):`);
|
|
996
921
|
for (const label of installedCommands) {
|
|
@@ -1009,21 +934,12 @@ export async function runSetup() {
|
|
|
1009
934
|
console.log(' Option A — Claude Code (if you install it later):');
|
|
1010
935
|
console.log(` claude mcp add -e RVRY_TOKEN="${token}" -s user rvry -- npx --yes --package @rvry/mcp@latest rvry-mcp`);
|
|
1011
936
|
console.log('');
|
|
1012
|
-
console.log(' Option B —
|
|
937
|
+
console.log(' Option B — Claude Desktop JSON config:');
|
|
1013
938
|
const manualConfig = { mcpServers: { RVRY: RVRY_SERVER_ENTRY(token) } };
|
|
1014
939
|
console.log('');
|
|
1015
940
|
for (const line of JSON.stringify(manualConfig, null, 2).split('\n')) {
|
|
1016
941
|
console.log(` ${line}`);
|
|
1017
942
|
}
|
|
1018
|
-
console.log('');
|
|
1019
|
-
console.log(' Option C — TOML config (Codex — add to ~/.codex/config.toml):');
|
|
1020
|
-
console.log('');
|
|
1021
|
-
console.log(' [mcp_servers.rvry]');
|
|
1022
|
-
console.log(' command = "npx"');
|
|
1023
|
-
console.log(' args = ["--yes", "--package", "@rvry/mcp@latest", "rvry-mcp"]');
|
|
1024
|
-
console.log('');
|
|
1025
|
-
console.log(' [mcp_servers.rvry.env]');
|
|
1026
|
-
console.log(` RVRY_TOKEN = "${token}"`);
|
|
1027
943
|
}
|
|
1028
944
|
console.log('');
|
|
1029
945
|
// ── Step 4: Gitignore protection ────────────────────────────────
|
|
@@ -1043,43 +959,25 @@ export async function runSetup() {
|
|
|
1043
959
|
console.log('');
|
|
1044
960
|
// Client-specific next steps
|
|
1045
961
|
const configuredNames = new Set(configuredClients.map((c) => c.name));
|
|
1046
|
-
const hasDesktopStyle = configuredNames.has('Claude Desktop / Claude Co-Work')
|
|
1047
|
-
|| configuredNames.has('Anti-Gravity')
|
|
1048
|
-
|| configuredNames.has('Codex');
|
|
962
|
+
const hasDesktopStyle = configuredNames.has('Claude Desktop / Claude Co-Work');
|
|
1049
963
|
const hasCodeStyle = configuredNames.has('Claude Code');
|
|
1050
964
|
console.log('Next steps:');
|
|
1051
965
|
let step = 1;
|
|
1052
966
|
if (hasDesktopStyle) {
|
|
1053
|
-
console.log(` ${step}. Restart
|
|
967
|
+
console.log(` ${step}. Restart Claude Desktop for the new MCP server to load`);
|
|
1054
968
|
step++;
|
|
1055
969
|
}
|
|
1056
|
-
// HTTP transport for web-based clients
|
|
1057
|
-
console.log(` ${step}. For web-based MCP clients (ChatGPT, Perplexity, etc.):`);
|
|
1058
|
-
console.log(' MCP Server URL: https://engine.rvry.ai/mcp');
|
|
1059
|
-
console.log(' Auth: Supabase OAuth 2.1 (Bearer token)');
|
|
1060
|
-
console.log('');
|
|
1061
|
-
step++;
|
|
1062
|
-
// Generic stdio MCP config for any other client
|
|
1063
|
-
console.log(` ${step}. For other local MCP clients — add to your MCP config:`);
|
|
1064
|
-
console.log('');
|
|
1065
|
-
const mcpBlock = { rvry: RVRY_SERVER_ENTRY('YOUR_RVRY_TOKEN') };
|
|
1066
|
-
const lines = JSON.stringify(mcpBlock, null, 2).split('\n');
|
|
1067
|
-
for (const line of lines.slice(1, -1)) {
|
|
1068
|
-
console.log(` ${line}`);
|
|
1069
|
-
}
|
|
1070
|
-
console.log('');
|
|
1071
|
-
step++;
|
|
1072
970
|
if (hasCodeStyle || hasDesktopStyle) {
|
|
1073
971
|
if (installedCommands.length > 0) {
|
|
1074
972
|
console.log(` ${step}. Try a slash command: /deepthink [your question]`);
|
|
1075
973
|
}
|
|
1076
974
|
else {
|
|
1077
|
-
console.log(` ${step}. Try: "Use
|
|
975
|
+
console.log(` ${step}. Try: "Use deepthink to analyze..." or "Use problem_solve for..."`);
|
|
1078
976
|
}
|
|
1079
977
|
step++;
|
|
1080
978
|
}
|
|
1081
979
|
if (!anyConfigured) {
|
|
1082
980
|
console.log(' 1. Configure a client using the manual instructions above');
|
|
1083
|
-
console.log(' 2. Then try: "Use
|
|
981
|
+
console.log(' 2. Then try: "Use deepthink to analyze..."');
|
|
1084
982
|
}
|
|
1085
983
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rvry/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "RVRY reasoning depth enforcement (RDE) engine client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"node": ">=18.0.0"
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
|
-
"build": "tsc && chmod 755 dist/index.js",
|
|
18
|
+
"build": "tsc -p tsconfig.build.json && chmod 755 dist/index.js",
|
|
19
19
|
"prepack": "npm run build",
|
|
20
20
|
"typecheck": "tsc --noEmit"
|
|
21
21
|
},
|