@rvry/mcp 0.3.3 → 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 +12 -32
- package/dist/client.js +4 -1
- package/dist/index.js +16 -21
- package/dist/setup.js +87 -2
- package/package.json +1 -1
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
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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.
|
|
86
|
-
stripped.
|
|
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,
|
|
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
|
}
|