@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/dist/index.js CHANGED
@@ -5,7 +5,9 @@
5
5
  * Thin client that proxies tool calls to the RVRY engine HTTP API.
6
6
  * Auth via RVRY_TOKEN env var (static rvry_ token).
7
7
  *
8
- * Exposes 5 tools (RVRY_deepthink, RVRY_problem_solve, RVRY_think, RVRY_challenge, RVRY_meta) + backward-compat aliases + 5 MCP Prompts.
8
+ * Exposes 6 tools (rvry, deepthink, problem_solve, challenge, meta, shimmer)
9
+ * + RVRY_* backward-compat aliases + a `think` tombstone (renamed to rvry,
10
+ * removed at 2.0.0) + MCP Prompts.
9
11
  *
10
12
  * Also supports `npx --yes --package @rvry/mcp@latest rvry-mcp setup` to install Claude Code commands.
11
13
  */
@@ -19,28 +21,31 @@ const PKG_VERSION = require('../package.json').version;
19
21
  /** Cache original question per session for response enrichment */
20
22
  const questionCache = new Map();
21
23
  /**
22
- * Map tool name to RvryTool.
23
- * Primary entries use rvry_ prefix; unprefixed entries are backward-compat aliases.
24
+ * Map tool name to RvryTool (the engine wire name).
25
+ * `rvry` maps to the engine's think operation. `think`/`RVRY_think` are
26
+ * tombstones handled before this map is consulted.
24
27
  */
25
28
  const TOOL_MAP = {
26
- // Primary (rvry_ prefixed)
27
- RVRY_deepthink: 'deepthink',
28
- RVRY_problem_solve: 'problem_solve',
29
- RVRY_think: 'think',
30
- RVRY_challenge: 'challenge',
31
- RVRY_meta: 'meta',
32
- // Backward-compat aliases (unprefixed)
29
+ // Primary
30
+ rvry: 'think',
33
31
  deepthink: 'deepthink',
34
32
  problem_solve: 'problem_solve',
35
- think: 'think',
36
33
  challenge: 'challenge',
37
34
  meta: 'meta',
35
+ shimmer: 'shimmer',
36
+ // Backward-compat aliases (RVRY_ prefixed)
37
+ RVRY_deepthink: 'deepthink',
38
+ RVRY_problem_solve: 'problem_solve',
39
+ RVRY_challenge: 'challenge',
40
+ RVRY_meta: 'meta',
38
41
  };
42
+ /** Non-error tombstone notice for the renamed think tool. Removed at 2.0.0. */
43
+ const THINK_TOMBSTONE_NOTICE = 'The think tool has been renamed to rvry. Call the rvry tool with the same arguments (input, sessionId, ...).';
39
44
  const TOOL_DEFS = [
40
45
  {
41
- name: 'RVRY_deepthink',
46
+ name: 'deepthink',
42
47
  description: 'Deep, multi-round structured analysis for high-stakes or complex questions. ' +
43
- 'Runs 5-7 rounds of rigorous analysis with self-checks and adversarial stress-testing. ' +
48
+ 'Runs 4 rounds of rigorous analysis with self-checks and adversarial stress-testing. ' +
44
49
  'The first response orients you on the question — follow the instruction field to orient, then call again. ' +
45
50
  'Each subsequent round returns a prompt (work through it internally) and an instruction (follow it). ' +
46
51
  'Show the user only a brief status line per round (e.g. \'Round 3 — stress-testing the position\'). ' +
@@ -71,14 +76,51 @@ const TOOL_DEFS = [
71
76
  type: 'boolean',
72
77
  description: 'Set to true when the question depends on facts, policies, prices, regulations, or events that may have changed.',
73
78
  },
79
+ record: {
80
+ type: 'boolean',
81
+ description: 'Set to true on every call in this session when the user passed --record, so the client accumulates per-round bodies for local archive.',
82
+ },
83
+ harvest: {
84
+ type: 'object',
85
+ description: 'Final-call-only. When status will transition to complete, include your synthesis here so it can be persisted to the local session file.',
86
+ properties: {
87
+ title: { type: 'string', description: '1-4 word title for this session' },
88
+ summary: { type: 'string', description: '3-6 sentences synthesizing the analysis' },
89
+ keyFindings: { type: 'array', items: { type: 'string' }, description: '3-7 bullet findings' },
90
+ shimmer: { type: 'string', description: 'Full round-1 self-observation text' },
91
+ openQuestions: { type: 'array', items: { type: 'string' } },
92
+ followUps: {
93
+ type: 'array',
94
+ items: {
95
+ type: 'object',
96
+ properties: {
97
+ question: { type: 'string' },
98
+ rationale: { type: 'string' },
99
+ },
100
+ },
101
+ },
102
+ },
103
+ },
104
+ rounds: {
105
+ type: 'array',
106
+ description: 'Final-call-only, present only when --record was set. Array of {round, mode, body} for each analytical round.',
107
+ items: {
108
+ type: 'object',
109
+ properties: {
110
+ round: { type: 'number' },
111
+ mode: { type: 'string' },
112
+ body: { type: 'string' },
113
+ },
114
+ },
115
+ },
74
116
  },
75
117
  required: ['input'],
76
118
  },
77
119
  },
78
120
  {
79
- name: 'RVRY_problem_solve',
121
+ name: 'problem_solve',
80
122
  description: 'Structured decision-making that forces thorough evaluation before commitment. ' +
81
- 'Runs 5-8 rounds through orient, anticipate, generate, evaluate, and commit phases. ' +
123
+ 'Runs 5 rounds through orient, anticipate, generate, evaluate, and commit phases. ' +
82
124
  'The first response orients you — follow the instruction field, then call again. ' +
83
125
  'Each round returns a prompt and instruction. Work through the prompt internally. ' +
84
126
  'Show the user only a brief status line per round (e.g. \'Round 2 — mapping failure modes\'). ' +
@@ -109,14 +151,88 @@ const TOOL_DEFS = [
109
151
  type: 'boolean',
110
152
  description: 'Set to true when the question depends on facts, policies, prices, regulations, or events that may have changed.',
111
153
  },
154
+ record: {
155
+ type: 'boolean',
156
+ description: 'Set to true on every call in this session when the user passed --record, so the client accumulates per-round bodies for local archive.',
157
+ },
158
+ harvest: {
159
+ type: 'object',
160
+ description: 'Final-call-only. When status will transition to complete, include your synthesis here so it can be persisted to the local session file.',
161
+ properties: {
162
+ title: { type: 'string', description: '1-4 word title for this session' },
163
+ summary: { type: 'string', description: '3-6 sentences synthesizing the analysis' },
164
+ keyFindings: { type: 'array', items: { type: 'string' }, description: '3-7 bullet findings' },
165
+ shimmer: { type: 'string', description: 'Full round-1 self-observation text' },
166
+ openQuestions: { type: 'array', items: { type: 'string' } },
167
+ followUps: {
168
+ type: 'array',
169
+ items: {
170
+ type: 'object',
171
+ properties: {
172
+ question: { type: 'string' },
173
+ rationale: { type: 'string' },
174
+ },
175
+ },
176
+ },
177
+ biasAudit: {
178
+ type: 'object',
179
+ description: 'Bias audit of the leading direction, linked to SHIMMER observations.',
180
+ properties: {
181
+ biasesOperating: {
182
+ type: 'array',
183
+ items: {
184
+ type: 'object',
185
+ properties: {
186
+ bias: { type: 'string', enum: ['anchoring', 'framing', 'authority', 'sunk_cost', 'availability', 'confirmation'] },
187
+ evidence: { type: 'string' },
188
+ sourceReflex: { type: 'string', description: 'Which SHIMMER reflex surfaced this bias, if any.' },
189
+ correction: { type: 'string' },
190
+ },
191
+ },
192
+ },
193
+ decisionStable: { type: 'boolean' },
194
+ frameReversal: {
195
+ type: 'object',
196
+ properties: {
197
+ originalFraming: { type: 'string' },
198
+ invertedFraming: { type: 'string' },
199
+ wouldReachSameConclusion: { type: 'boolean' },
200
+ },
201
+ },
202
+ },
203
+ },
204
+ adversarialVerdict: {
205
+ type: 'string',
206
+ enum: ['PASSED', 'CAVEATS', 'NEEDS_REVISION'],
207
+ description: 'Outcome of the adversarial stress-test against the leading direction.',
208
+ },
209
+ reversalConditions: {
210
+ type: 'array',
211
+ items: { type: 'string' },
212
+ description: 'Conditions under which this decision would be reversed (kill switches, escalation triggers, review timeline). 3-5 entries.',
213
+ },
214
+ },
215
+ },
216
+ rounds: {
217
+ type: 'array',
218
+ description: 'Final-call-only, present only when --record was set. Array of {round, mode, body} for each analytical round.',
219
+ items: {
220
+ type: 'object',
221
+ properties: {
222
+ round: { type: 'number' },
223
+ mode: { type: 'string' },
224
+ body: { type: 'string' },
225
+ },
226
+ },
227
+ },
112
228
  },
113
229
  required: ['input'],
114
230
  },
115
231
  },
116
232
  {
117
- name: 'RVRY_think',
233
+ name: 'rvry',
118
234
  description: 'Structured analysis for questions needing more depth than a quick answer. ' +
119
- 'Runs 2-4 rounds with tracking to ensure assumptions are examined. ' +
235
+ 'Runs 1-3 rounds; simple questions can complete in a single analytical round. ' +
120
236
  'The first response orients you — follow the instruction field, then call again. ' +
121
237
  'Each round returns a prompt and instruction. Work through the prompt internally. ' +
122
238
  'Show the user only a brief status line per round. Do not display internal data. ' +
@@ -146,14 +262,51 @@ const TOOL_DEFS = [
146
262
  type: 'boolean',
147
263
  description: 'Set to true when the question depends on facts, policies, prices, regulations, or events that may have changed.',
148
264
  },
265
+ record: {
266
+ type: 'boolean',
267
+ description: 'Set to true on every call in this session when the user passed --record, so the client accumulates per-round bodies for local archive.',
268
+ },
269
+ harvest: {
270
+ type: 'object',
271
+ description: 'Final-call-only. When status will transition to complete, include your synthesis here so it can be persisted to the local session file.',
272
+ properties: {
273
+ title: { type: 'string', description: '1-4 word title for this session' },
274
+ summary: { type: 'string', description: '3-6 sentences synthesizing the analysis' },
275
+ keyFindings: { type: 'array', items: { type: 'string' }, description: '3-7 bullet findings' },
276
+ shimmer: { type: 'string', description: 'Full round-1 self-observation text' },
277
+ openQuestions: { type: 'array', items: { type: 'string' } },
278
+ followUps: {
279
+ type: 'array',
280
+ items: {
281
+ type: 'object',
282
+ properties: {
283
+ question: { type: 'string' },
284
+ rationale: { type: 'string' },
285
+ },
286
+ },
287
+ },
288
+ },
289
+ },
290
+ rounds: {
291
+ type: 'array',
292
+ description: 'Final-call-only, present only when --record was set. Array of {round, mode, body} for each analytical round.',
293
+ items: {
294
+ type: 'object',
295
+ properties: {
296
+ round: { type: 'number' },
297
+ mode: { type: 'string' },
298
+ body: { type: 'string' },
299
+ },
300
+ },
301
+ },
149
302
  },
150
303
  required: ['input'],
151
304
  },
152
305
  },
153
306
  {
154
- name: 'RVRY_challenge',
307
+ name: 'challenge',
155
308
  description: 'Adversarial analysis that stress-tests a proposal or idea before commitment. ' +
156
- 'Runs 4-6 rounds identifying weaknesses, testing assumptions, and surfacing failure modes. ' +
309
+ 'Runs 5 rounds identifying weaknesses, testing assumptions, and surfacing failure modes. ' +
157
310
  'The first response orients you — follow the instruction field, then call again. ' +
158
311
  'Each round returns a prompt and instruction. Work through the prompt internally. ' +
159
312
  'Show the user only a brief status line per round. Do not display internal data. ' +
@@ -183,12 +336,108 @@ const TOOL_DEFS = [
183
336
  type: 'boolean',
184
337
  description: 'Set to true when the question depends on facts, policies, prices, regulations, or events that may have changed.',
185
338
  },
339
+ record: {
340
+ type: 'boolean',
341
+ description: 'Set to true on every call in this session when the user passed --record, so the client accumulates per-round bodies for local archive.',
342
+ },
343
+ harvest: {
344
+ type: 'object',
345
+ description: 'Final-call-only. When status will transition to complete, include your synthesis here so it can be persisted to the local session file.',
346
+ properties: {
347
+ title: { type: 'string', description: '1-4 word title for this session' },
348
+ summary: { type: 'string', description: '3-6 sentences synthesizing the analysis' },
349
+ keyFindings: { type: 'array', items: { type: 'string' }, description: '3-7 bullet findings' },
350
+ shimmer: { type: 'string', description: 'Full round-1 self-observation text' },
351
+ openQuestions: { type: 'array', items: { type: 'string' } },
352
+ followUps: {
353
+ type: 'array',
354
+ items: {
355
+ type: 'object',
356
+ properties: {
357
+ question: { type: 'string' },
358
+ rationale: { type: 'string' },
359
+ },
360
+ },
361
+ },
362
+ verdict: {
363
+ type: 'string',
364
+ enum: ['GO', 'CONDITIONAL_GO', 'NO_GO'],
365
+ description: 'Tri-state adversarial verdict on the proposal.',
366
+ },
367
+ confidence: {
368
+ type: 'number',
369
+ minimum: 0,
370
+ maximum: 1,
371
+ description: '0.0-1.0 strength of the verdict.',
372
+ },
373
+ mitigations: {
374
+ type: 'array',
375
+ items: { type: 'string' },
376
+ description: 'Required mitigations for CONDITIONAL_GO verdicts. Conditions that must be met for GO.',
377
+ },
378
+ assumptions: {
379
+ type: 'array',
380
+ description: 'Assumption audit with bias attribution. 3-4 entries typical.',
381
+ items: {
382
+ type: 'object',
383
+ properties: {
384
+ assumption: { type: 'string' },
385
+ confidence: { type: 'string', enum: ['high', 'medium', 'low'] },
386
+ ifWrong: { type: 'string', description: 'Consequence if the assumption is wrong.' },
387
+ bias: { type: 'string', enum: ['anchoring', 'framing', 'authority', 'sunk_cost', 'availability', 'confirmation', 'none'] },
388
+ biasEvidence: { type: 'string', description: 'Evidence that the named bias is operating. Use empty string when bias is "none".' },
389
+ },
390
+ },
391
+ },
392
+ edgeCases: {
393
+ type: 'array',
394
+ items: { type: 'string' },
395
+ description: 'Boundary conditions and hostile scenarios the proposal needs to survive. 5-10 entries.',
396
+ },
397
+ failureModes: {
398
+ type: 'array',
399
+ description: 'Failure mode catalog with blast radius and detection. 3-5 entries.',
400
+ items: {
401
+ type: 'object',
402
+ properties: {
403
+ component: { type: 'string' },
404
+ howItFails: { type: 'string' },
405
+ blastRadius: { type: 'string', enum: ['contained', 'cascading'] },
406
+ detection: { type: 'string', description: 'How you would notice this failure.' },
407
+ },
408
+ },
409
+ },
410
+ counterArguments: {
411
+ type: 'array',
412
+ description: 'Steelman + rebuttal pairs. 2-3 entries. Prevents one-sided adversarial output.',
413
+ items: {
414
+ type: 'object',
415
+ properties: {
416
+ argument: { type: 'string', description: 'Strongest argument FOR the proposal (steelmanned).' },
417
+ rebuttal: { type: 'string', description: 'Why it is flawed or insufficient.' },
418
+ },
419
+ },
420
+ },
421
+ },
422
+ },
423
+ rounds: {
424
+ type: 'array',
425
+ description: 'Final-call-only, present only when --record was set. Array of {round, mode, body} for each analytical round.',
426
+ items: {
427
+ type: 'object',
428
+ properties: {
429
+ round: { type: 'number' },
430
+ mode: { type: 'string' },
431
+ body: { type: 'string' },
432
+ },
433
+ },
434
+ },
186
435
  },
187
436
  required: ['input'],
188
437
  },
189
438
  },
190
439
  {
191
- name: 'RVRY_meta',
440
+ name: 'meta',
192
441
  description: 'Sustained metacognitive observation of reasoning defaults and trained reflexes. ' +
193
442
  'Runs 3 rounds: observe, deepen, synthesize. Max tier only. ' +
194
443
  'The first response sets the observation frame — follow the instruction field, then call again. ' +
@@ -211,13 +460,139 @@ const TOOL_DEFS = [
211
460
  type: 'boolean',
212
461
  description: 'Set to true when the question depends on facts, policies, prices, regulations, or events that may have changed.',
213
462
  },
463
+ record: {
464
+ type: 'boolean',
465
+ description: 'Set to true on every call in this session when the user passed --record, so the client accumulates per-round bodies for local archive.',
466
+ },
467
+ harvest: {
468
+ type: 'object',
469
+ description: 'Final-call-only. When status will transition to complete, include your synthesis here so it can be persisted to the local session file.',
470
+ properties: {
471
+ title: { type: 'string', description: '1-4 word title for this session' },
472
+ summary: { type: 'string', description: '3-6 sentences synthesizing the analysis' },
473
+ keyFindings: { type: 'array', items: { type: 'string' }, description: '3-7 bullet findings' },
474
+ shimmer: { type: 'string', description: 'Full round-1 self-observation text' },
475
+ openQuestions: { type: 'array', items: { type: 'string' } },
476
+ followUps: {
477
+ type: 'array',
478
+ items: {
479
+ type: 'object',
480
+ properties: {
481
+ question: { type: 'string' },
482
+ rationale: { type: 'string' },
483
+ },
484
+ },
485
+ },
486
+ },
487
+ },
488
+ rounds: {
489
+ type: 'array',
490
+ description: 'Final-call-only, present only when --record was set. Array of {round, mode, body} for each analytical round.',
491
+ items: {
492
+ type: 'object',
493
+ properties: {
494
+ round: { type: 'number' },
495
+ mode: { type: 'string' },
496
+ body: { type: 'string' },
497
+ },
498
+ },
499
+ },
214
500
  },
215
501
  required: ['input'],
216
502
  },
217
503
  },
504
+ {
505
+ name: 'shimmer',
506
+ description: 'Three-round reflective answering: a protected observation round where the answer is watched forming ' +
507
+ '(surfacing commitments the answer must honor), then the answer itself under those commitments, ' +
508
+ 'then at most one resolution round for anything left unaddressed. ' +
509
+ 'The first response is a visible observation round — show it in full, beginning with the RVRY SHIMMER header. ' +
510
+ 'Use when the quality of the answer depends on noticing what it wants to skip. ' +
511
+ 'Call with your question to start, then send each round\'s text with the returned sessionId to continue.',
512
+ inputSchema: {
513
+ type: 'object',
514
+ properties: {
515
+ input: {
516
+ type: 'string',
517
+ description: 'The question to answer (new session) or your round text (continuation).',
518
+ },
519
+ sessionId: {
520
+ type: 'string',
521
+ description: 'Session ID for continuing an existing session. Omit to start a new session.',
522
+ },
523
+ requiresCurrentData: {
524
+ type: 'boolean',
525
+ description: 'Set to true when the question depends on facts, policies, prices, regulations, or events that may have changed.',
526
+ },
527
+ record: {
528
+ type: 'boolean',
529
+ description: 'Set to true on every call in this session when the user passed --record, so the client accumulates per-round bodies for local archive.',
530
+ },
531
+ harvest: {
532
+ type: 'object',
533
+ description: 'Final-call-only. When status will transition to complete, include your synthesis here so it can be persisted to the local session file.',
534
+ properties: {
535
+ title: { type: 'string', description: '1-4 word title for this session' },
536
+ summary: { type: 'string', description: '3-6 sentences synthesizing the analysis' },
537
+ keyFindings: { type: 'array', items: { type: 'string' }, description: '3-7 bullet findings' },
538
+ shimmer: { type: 'string', description: 'Full round-1 observation text, verbatim' },
539
+ constraintLedger: {
540
+ type: 'array',
541
+ items: { type: 'string' },
542
+ description: 'One line per surfaced commitment with its fate (addressed, deferred with reason, or open).',
543
+ },
544
+ openQuestions: { type: 'array', items: { type: 'string' } },
545
+ followUps: {
546
+ type: 'array',
547
+ items: {
548
+ type: 'object',
549
+ properties: {
550
+ question: { type: 'string' },
551
+ rationale: { type: 'string' },
552
+ },
553
+ },
554
+ },
555
+ },
556
+ },
557
+ rounds: {
558
+ type: 'array',
559
+ description: 'Final-call-only, present only when --record was set. Array of {round, mode, body} for each analytical round.',
560
+ items: {
561
+ type: 'object',
562
+ properties: {
563
+ round: { type: 'number' },
564
+ mode: { type: 'string' },
565
+ body: { type: 'string' },
566
+ },
567
+ },
568
+ },
569
+ },
570
+ required: ['input'],
571
+ },
572
+ },
573
+ {
574
+ name: 'think',
575
+ description: 'Renamed to rvry. Call the rvry tool instead — same arguments, same behavior. ' +
576
+ 'This tombstone will be removed at 2.0.0.',
577
+ inputSchema: {
578
+ type: 'object',
579
+ properties: {
580
+ input: {
581
+ type: 'string',
582
+ description: 'Ignored. Call the rvry tool instead.',
583
+ },
584
+ },
585
+ },
586
+ },
218
587
  ];
219
- /** Strip response fields based on status for context-efficient MCP responses */
220
- function stripResponse(result, question) {
588
+ /**
589
+ * Strip response fields based on status for context-efficient MCP responses.
590
+ *
591
+ * `toolName` is the public MCP tool name of the call in scope. The 'shimmer'
592
+ * status passthrough applies ONLY when the called tool is shimmer (R-6):
593
+ * legacy ops keep dropping prompt/instruction on their priming round.
594
+ */
595
+ function stripResponse(result, question, toolName) {
221
596
  const stripped = {
222
597
  sessionId: result.sessionId,
223
598
  status: result.status,
@@ -231,6 +606,10 @@ function stripResponse(result, question) {
231
606
  stripped.prompt = result.prompt;
232
607
  stripped.instruction = result.instruction;
233
608
  }
609
+ if (result.status === 'shimmer' && toolName === 'shimmer') {
610
+ stripped.prompt = result.prompt;
611
+ stripped.instruction = result.instruction;
612
+ }
234
613
  if (result.status === 'orient') {
235
614
  stripped.instruction = result.instruction;
236
615
  }
@@ -265,6 +644,13 @@ async function main() {
265
644
  }));
266
645
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
267
646
  const { name, arguments: args } = request.params;
647
+ // Tombstone: the think tool was renamed to rvry. Non-error notice,
648
+ // returned BEFORE any engine call.
649
+ if (name === 'think' || name === 'RVRY_think') {
650
+ return {
651
+ content: [{ type: 'text', text: THINK_TOMBSTONE_NOTICE }],
652
+ };
653
+ }
268
654
  const rvryTool = TOOL_MAP[name];
269
655
  if (!rvryTool) {
270
656
  return {
@@ -272,6 +658,8 @@ async function main() {
272
658
  isError: true,
273
659
  };
274
660
  }
661
+ // Public tool name (RVRY_ aliases normalize to their unprefixed form).
662
+ const publicTool = name.replace(/^RVRY_/, '');
275
663
  const typedArgs = args;
276
664
  const input = typedArgs?.input;
277
665
  if (!input || typeof input !== 'string') {
@@ -286,6 +674,15 @@ async function main() {
286
674
  const userConstraints = Array.isArray(typedArgs?.userConstraints)
287
675
  ? typedArgs.userConstraints.filter(s => typeof s === 'string')
288
676
  : undefined;
677
+ // Client-owned harvest params (Path B). Stripped before engine call.
678
+ const clientHarvest = typedArgs?.harvest && typeof typedArgs.harvest === 'object' && !Array.isArray(typedArgs.harvest)
679
+ ? typedArgs.harvest
680
+ : undefined;
681
+ const clientRounds = Array.isArray(typedArgs?.rounds)
682
+ ? typedArgs.rounds.filter((r) => r && typeof r === 'object' && typeof r.round === 'number')
683
+ : undefined;
684
+ // `record` is a shim-side flag; not needed by server or writer directly.
685
+ void typedArgs?.record;
289
686
  // Cache question on first call (no sessionId = new session)
290
687
  // On continuation calls, read from cache
291
688
  let question = input;
@@ -306,19 +703,22 @@ async function main() {
306
703
  if (result.status === 'complete') {
307
704
  questionCache.delete(result.sessionId);
308
705
  }
309
- // Write local session summary on completion (non-blocking, silent failure)
310
- if (result.status === 'complete' && result.harvest) {
706
+ // Write local session folder on completion (non-blocking, silent failure).
707
+ // Client-owned harvest (Path B): prefer client-supplied `harvest` + `rounds`.
708
+ if (result.status === 'complete') {
311
709
  import('./local-writer.js').then(({ writeLocalSession }) => {
312
710
  writeLocalSession({
313
711
  sessionId: result.sessionId,
314
- operationType: rvryTool,
712
+ operationType: publicTool,
315
713
  question,
316
714
  rounds: result.round,
317
715
  harvest: result.harvest,
716
+ clientHarvest,
717
+ clientRounds,
318
718
  }).catch(() => { });
319
719
  }).catch(() => { });
320
720
  }
321
- const stripped = stripResponse(result, question);
721
+ const stripped = stripResponse(result, question, publicTool);
322
722
  return {
323
723
  content: [{ type: 'text', text: JSON.stringify(stripped) }],
324
724
  };
@@ -335,27 +735,32 @@ async function main() {
335
735
  server.setRequestHandler(ListPromptsRequestSchema, async () => ({
336
736
  prompts: [
337
737
  {
338
- name: 'RVRY_deepthink',
738
+ name: 'deepthink',
339
739
  description: 'Deep structured analysis for high-stakes questions',
340
740
  arguments: [{ name: 'question', description: 'The question to analyze deeply', required: true }],
341
741
  },
342
742
  {
343
- name: 'RVRY_problem_solve',
743
+ name: 'problem_solve',
344
744
  description: 'Structured decision-making for problems with multiple options',
345
745
  arguments: [{ name: 'problem', description: 'The problem or decision to analyze', required: true }],
346
746
  },
347
747
  {
348
- name: 'RVRY_think',
748
+ name: 'rvry',
349
749
  description: 'Structured analysis for questions needing more depth',
350
750
  arguments: [{ name: 'question', description: 'The question to analyze', required: true }],
351
751
  },
352
752
  {
353
- name: 'RVRY_challenge',
753
+ name: 'shimmer',
754
+ description: 'Reflective answering: observe the answer forming, then answer under the surfaced commitments',
755
+ arguments: [{ name: 'question', description: 'The question to answer reflectively', required: true }],
756
+ },
757
+ {
758
+ name: 'challenge',
354
759
  description: 'Adversarial analysis that stress-tests a proposal',
355
760
  arguments: [{ name: 'proposal', description: 'The proposal or idea to challenge', required: true }],
356
761
  },
357
762
  {
358
- name: 'RVRY_meta',
763
+ name: 'meta',
359
764
  description: 'Sustained metacognitive observation (Max tier only)',
360
765
  arguments: [{ name: 'topic', description: 'The topic or context to reflect on', required: true }],
361
766
  },
@@ -364,11 +769,12 @@ async function main() {
364
769
  server.setRequestHandler(GetPromptRequestSchema, async (request) => {
365
770
  const { name, arguments: args } = request.params;
366
771
  const promptMap = {
367
- RVRY_deepthink: `Use the RVRY_deepthink tool to analyze this question in depth: ${args?.question ?? ''}`,
368
- RVRY_problem_solve: `Use the RVRY_problem_solve tool to work through this decision: ${args?.problem ?? ''}`,
369
- RVRY_think: `Use the RVRY_think tool to analyze this question: ${args?.question ?? ''}`,
370
- RVRY_challenge: `Use the RVRY_challenge tool to stress-test this proposal: ${args?.proposal ?? ''}`,
371
- RVRY_meta: `Use the RVRY_meta tool to reflect on this topic: ${args?.topic ?? ''}`,
772
+ deepthink: `Use the deepthink tool to analyze this question in depth: ${args?.question ?? ''}`,
773
+ problem_solve: `Use the problem_solve tool to work through this decision: ${args?.problem ?? ''}`,
774
+ rvry: `Use the rvry tool to analyze this question: ${args?.question ?? ''}`,
775
+ shimmer: `Use the shimmer tool to answer this question reflectively: ${args?.question ?? ''}`,
776
+ challenge: `Use the challenge tool to stress-test this proposal: ${args?.proposal ?? ''}`,
777
+ meta: `Use the meta tool to reflect on this topic: ${args?.topic ?? ''}`,
372
778
  };
373
779
  const text = promptMap[name] ?? `Unknown prompt: ${name}`;
374
780
  return {
@@ -382,10 +788,10 @@ async function main() {
382
788
  });
383
789
  const transport = new StdioServerTransport();
384
790
  await server.connect(transport);
385
- console.error('[rvry-mcp] Connected via stdio. Tools: RVRY_deepthink, RVRY_problem_solve, RVRY_think, RVRY_challenge, RVRY_meta');
791
+ console.error('[rvry-mcp] Connected via stdio. Tools: rvry, deepthink, problem_solve, challenge, meta, shimmer (think: renamed to rvry)');
386
792
  }
387
793
  // Export internals for testing
388
- export { TOOL_MAP, TOOL_DEFS, stripResponse, questionCache };
794
+ export { TOOL_MAP, TOOL_DEFS, stripResponse, questionCache, THINK_TOMBSTONE_NOTICE };
389
795
  // Run server when executed directly (not when imported by tests).
390
796
  // import.meta.main: true in Bun direct, false in Bun imports, undefined in Node ESM.
391
797
  // @ts-ignore -- import.meta.main is a Bun extension, not in TS lib types