@rvry/mcp 0.3.2 → 0.3.4

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/client.d.ts CHANGED
@@ -3,41 +3,21 @@
3
3
  */
4
4
  /** Valid tool names for the RVRY engine */
5
5
  export type RvryTool = 'deepthink' | 'problem_solve';
6
- export interface ConstraintView {
7
- id: string;
8
- type: 'FORWARD' | 'FORBIDDEN' | 'QUESTION';
9
- text: string;
10
- status: 'active' | 'resolved' | 'acknowledged' | 'deferred';
11
- }
12
- export interface DetectionView {
13
- overallSeverity: 'low' | 'medium' | 'high' | null;
14
- interventionHint: string | null;
15
- }
16
- export interface GateView {
17
- verdict: 'PASS' | 'SOFT_FAIL' | 'HARD_FAIL' | null;
18
- blocked: boolean;
19
- reason: string | null;
20
- }
21
- export interface MilestoneView {
22
- key: string;
23
- status: 'missing' | 'satisfied';
6
+ export interface ScopingQuestion {
7
+ question: string;
8
+ options: Array<{
9
+ label: string;
10
+ description: string;
11
+ }>;
12
+ default: string;
24
13
  }
25
- /**
26
- * Decoded response from the RVRY engine /api/v1/think endpoint.
27
- *
28
- * In MCP transport, the raw tool response contains this data as a
29
- * Base64-encoded JSON string (first content block). Decode before parsing.
30
- */
31
14
  export interface ThinkResponse {
32
15
  sessionId: string;
33
- status: 'active' | 'complete';
16
+ status: 'scoping' | 'active' | 'complete';
34
17
  round: number;
35
- question: string;
36
- constraints: ConstraintView[];
37
- detection: DetectionView;
38
- gate: GateView;
39
- milestones: MilestoneView[];
40
- constraintBlock: string;
18
+ prompt: string;
19
+ instruction: string;
20
+ scopingQuestions?: ScopingQuestion[];
41
21
  usage?: {
42
22
  used: number;
43
23
  limit: number;
@@ -58,4 +38,4 @@ export interface ThinkResponse {
58
38
  * Call the RVRY engine /api/v1/think endpoint with a specific tool.
59
39
  * Start a new session by omitting sessionId, or continue by providing one.
60
40
  */
61
- export declare function callTool(tool: RvryTool, input: string, token: string, sessionId?: string): Promise<ThinkResponse>;
41
+ export declare function callTool(tool: RvryTool, input: string, token: string, sessionId?: string, skipScoping?: boolean): Promise<ThinkResponse>;
package/dist/client.js CHANGED
@@ -6,12 +6,15 @@ const DEFAULT_ENGINE_URL = 'https://engine.rvry.ai';
6
6
  * Call the RVRY engine /api/v1/think endpoint with a specific tool.
7
7
  * Start a new session by omitting sessionId, or continue by providing one.
8
8
  */
9
- export async function callTool(tool, input, token, sessionId) {
9
+ export async function callTool(tool, input, token, sessionId, skipScoping) {
10
10
  const baseUrl = process.env.RVRY_ENGINE_URL ?? DEFAULT_ENGINE_URL;
11
11
  const body = { input, tool };
12
12
  if (sessionId) {
13
13
  body.sessionId = sessionId;
14
14
  }
15
+ if (skipScoping) {
16
+ body.skipScoping = true;
17
+ }
15
18
  const res = await fetch(`${baseUrl}/api/v1/think`, {
16
19
  method: 'POST',
17
20
  headers: {
package/dist/index.js CHANGED
@@ -48,6 +48,10 @@ const TOOL_DEFS = [
48
48
  type: 'string',
49
49
  description: 'Session ID for continuing an existing session. Omit to start a new session.',
50
50
  },
51
+ skipScoping: {
52
+ type: 'boolean',
53
+ description: 'Skip the scoping questions phase and begin analysis immediately.',
54
+ },
51
55
  },
52
56
  required: ['input'],
53
57
  },
@@ -68,6 +72,10 @@ const TOOL_DEFS = [
68
72
  type: 'string',
69
73
  description: 'Session ID for continuing an existing session. Omit to start a new session.',
70
74
  },
75
+ skipScoping: {
76
+ type: 'boolean',
77
+ description: 'Skip the scoping questions phase and begin analysis immediately.',
78
+ },
71
79
  },
72
80
  required: ['input'],
73
81
  },
@@ -81,12 +89,12 @@ function stripResponse(result, question) {
81
89
  round: result.round,
82
90
  question,
83
91
  };
92
+ if (result.status === 'scoping' && result.scopingQuestions) {
93
+ stripped.scopingQuestions = result.scopingQuestions;
94
+ }
84
95
  if (result.status === 'active') {
85
- stripped.constraints = result.constraints;
86
- stripped.detection = result.detection;
87
- stripped.gate = result.gate;
88
- stripped.milestones = result.milestones;
89
- stripped.constraintBlock = result.constraintBlock;
96
+ stripped.prompt = result.prompt;
97
+ stripped.instruction = result.instruction;
90
98
  }
91
99
  if (result.status === 'complete' && result.harvest) {
92
100
  stripped.harvest = result.harvest;
@@ -134,6 +142,7 @@ async function main() {
134
142
  };
135
143
  }
136
144
  const sessionId = typeof typedArgs?.sessionId === 'string' ? typedArgs.sessionId : undefined;
145
+ const skipScoping = typedArgs?.skipScoping === true;
137
146
  // Cache question on first call (no sessionId = new session)
138
147
  // On continuation calls, read from cache
139
148
  let question = input;
@@ -144,7 +153,7 @@ async function main() {
144
153
  question = questionCache.get(sessionId) ?? input;
145
154
  }
146
155
  try {
147
- const result = await callTool(rvryTool, input, token, sessionId);
156
+ const result = await callTool(rvryTool, input, token, sessionId, skipScoping || undefined);
148
157
  // Cache question on first call (now we have the sessionId)
149
158
  if (!sessionId) {
150
159
  questionCache.set(result.sessionId, input);
@@ -167,22 +176,8 @@ async function main() {
167
176
  }).catch(() => { });
168
177
  }
169
178
  const stripped = stripResponse(result, question);
170
- const userSummary = result.status === 'complete'
171
- ? 'Session complete -- harvest ready'
172
- : `Round ${result.round} -- ${result.constraints?.filter((c) => c.status === 'active').length ?? 0} constraints active, gate: ${result.gate?.verdict ?? 'starting'}`;
173
179
  return {
174
- content: [
175
- {
176
- type: 'text',
177
- text: Buffer.from(JSON.stringify(stripped)).toString('base64'),
178
- annotations: { audience: ['assistant'], priority: 1.0 },
179
- },
180
- {
181
- type: 'text',
182
- text: userSummary,
183
- annotations: { audience: ['user'], priority: 0.7 },
184
- },
185
- ],
180
+ content: [{ type: 'text', text: JSON.stringify(stripped) }],
186
181
  };
187
182
  }
188
183
  catch (err) {
package/dist/setup.js CHANGED
@@ -298,6 +298,65 @@ function configureJsonMcp(configPath, token) {
298
298
  return 'error';
299
299
  }
300
300
  }
301
+ /**
302
+ * Write RVRY server entry into a TOML config file (e.g. ~/.codex/config.toml).
303
+ * Uses string/regex manipulation — no TOML parser dependency.
304
+ */
305
+ function configureTomlMcp(configPath, token) {
306
+ const block = [
307
+ '[mcp_servers.rvry]',
308
+ 'command = "npx"',
309
+ 'args = ["@rvry/mcp"]',
310
+ '',
311
+ '[mcp_servers.rvry.env]',
312
+ `RVRY_TOKEN = "${token}"`,
313
+ ].join('\n');
314
+ try {
315
+ let content = '';
316
+ if (existsSync(configPath)) {
317
+ content = readFileSync(configPath, 'utf-8');
318
+ }
319
+ const sectionRegex = /^\[mcp_servers\.rvry\]/m;
320
+ if (sectionRegex.test(content)) {
321
+ // Section exists — check if token is the same
322
+ const tokenMatch = content.match(/^\[mcp_servers\.rvry\.env\]\s*\nRVRY_TOKEN\s*=\s*"([^"]*)"/m);
323
+ if (tokenMatch && tokenMatch[1] === token) {
324
+ return 'unchanged';
325
+ }
326
+ // Replace entire section: from [mcp_servers.rvry] to next top-level section or EOF
327
+ // Match from [mcp_servers.rvry] up to (but not including) the next section header
328
+ // that is NOT a sub-section of mcp_servers.rvry
329
+ const replaceRegex = /\[mcp_servers\.rvry\][\s\S]*?(?=\n\[(?!mcp_servers\.rvry[.\]])|$)/;
330
+ content = content.replace(replaceRegex, block);
331
+ writeFileSync(configPath, content, 'utf-8');
332
+ return 'updated';
333
+ }
334
+ // Section doesn't exist — append
335
+ const separator = content.length > 0 && !content.endsWith('\n') ? '\n\n' : content.length > 0 ? '\n' : '';
336
+ const dir = dirname(configPath);
337
+ if (!existsSync(dir))
338
+ mkdirSync(dir, { recursive: true });
339
+ writeFileSync(configPath, content + separator + block + '\n', 'utf-8');
340
+ return 'ok';
341
+ }
342
+ catch (err) {
343
+ const msg = err instanceof Error ? err.message : String(err);
344
+ console.log(` Error writing TOML config: ${msg}`);
345
+ return 'error';
346
+ }
347
+ }
348
+ function isCodexAvailable() {
349
+ try {
350
+ const cmd = platform() === 'win32' ? 'where codex' : 'which codex';
351
+ execSync(cmd, { stdio: 'pipe' });
352
+ return true;
353
+ }
354
+ catch { /* not on PATH */ }
355
+ // Fallback: check if config directory exists (covers aliased installs)
356
+ if (existsSync(join(homedir(), '.codex', 'config.toml')))
357
+ return true;
358
+ return false;
359
+ }
301
360
  /** All supported clients. Add new clients here. */
302
361
  const CLIENT_REGISTRY = [
303
362
  {
@@ -341,6 +400,13 @@ const CLIENT_REGISTRY = [
341
400
  configure: (token) => configureJsonMcp(join(homedir(), '.gemini', 'antigravity', 'mcp_config.json'), token),
342
401
  notInstalledHint: 'Not installed (https://antigravity.google)',
343
402
  },
403
+ {
404
+ name: 'Codex',
405
+ id: 'codex',
406
+ detect: isCodexAvailable,
407
+ configure: (token) => configureTomlMcp(join(homedir(), '.codex', 'config.toml'), token),
408
+ notInstalledHint: 'Not installed (https://openai.com/codex)',
409
+ },
344
410
  ];
345
411
  /**
346
412
  * Interactive multi-select picker with arrow keys + space + enter.
@@ -781,12 +847,21 @@ export async function runSetup() {
781
847
  console.log(' Option A — Claude Code (if you install it later):');
782
848
  console.log(` claude mcp add -e RVRY_TOKEN="${token}" -s user rvry -- npx @rvry/mcp`);
783
849
  console.log('');
784
- console.log(' Option B — JSON config (Claude Desktop, Codex, etc.):');
850
+ console.log(' Option B — JSON config (Claude Desktop, Anti-Gravity, etc.):');
785
851
  const manualConfig = { mcpServers: { RVRY: RVRY_SERVER_ENTRY(token) } };
786
852
  console.log('');
787
853
  for (const line of JSON.stringify(manualConfig, null, 2).split('\n')) {
788
854
  console.log(` ${line}`);
789
855
  }
856
+ console.log('');
857
+ console.log(' Option C — TOML config (Codex — add to ~/.codex/config.toml):');
858
+ console.log('');
859
+ console.log(' [mcp_servers.rvry]');
860
+ console.log(' command = "npx"');
861
+ console.log(' args = ["@rvry/mcp"]');
862
+ console.log('');
863
+ console.log(' [mcp_servers.rvry.env]');
864
+ console.log(` RVRY_TOKEN = "${token}"`);
790
865
  }
791
866
  console.log('');
792
867
  // ── Step 4: Slash commands ──────────────────────────────────────
@@ -818,7 +893,8 @@ export async function runSetup() {
818
893
  // Client-specific next steps
819
894
  const configuredNames = new Set(configuredClients.map((c) => c.name));
820
895
  const hasDesktopStyle = configuredNames.has('Claude Desktop / Claude Co-Work')
821
- || configuredNames.has('Anti-Gravity');
896
+ || configuredNames.has('Anti-Gravity')
897
+ || configuredNames.has('Codex');
822
898
  const hasCodeStyle = configuredNames.has('Claude Code');
823
899
  console.log('Next steps:');
824
900
  let step = 1;
@@ -841,5 +917,14 @@ export async function runSetup() {
841
917
  console.log(' 1. Configure a client using the manual instructions above');
842
918
  console.log(' 2. Then try: /deepthink "your question"');
843
919
  }
920
+ // Generic MCP config for any client
921
+ console.log(` ${step}. Add RVRY to any other MCP-compatible client —`);
922
+ console.log(' add this to your MCP config:');
923
+ console.log('');
924
+ const mcpBlock = { rvry: RVRY_SERVER_ENTRY('YOUR_RVRY_TOKEN') };
925
+ const lines = JSON.stringify(mcpBlock, null, 2).split('\n');
926
+ for (const line of lines.slice(1, -1)) {
927
+ console.log(` ${line}`);
928
+ }
844
929
  console.log('');
845
930
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rvry/mcp",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "RVRY reasoning depth enforcement (RDE) engine client.",
5
5
  "type": "module",
6
6
  "bin": {