@robosystems/core 0.8.3 → 0.8.5
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/components/console/ConsoleContent.js +80 -32
- package/lib/mcp-connector.d.ts +10 -14
- package/lib/mcp-connector.js +9 -12
- package/package.json +1 -1
|
@@ -68,6 +68,10 @@ export function ConsoleContent({ config }) {
|
|
|
68
68
|
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
69
69
|
const terminalEndRef = useRef(null);
|
|
70
70
|
const terminalScrollRef = useRef(null);
|
|
71
|
+
// Prior operator exchanges, sent as `history` so a follow-up like "and for
|
|
72
|
+
// June?" resolves against the previous answer. Bounded to the last three
|
|
73
|
+
// exchanges — the backend seeds only its last five turns anyway.
|
|
74
|
+
const conversationRef = useRef([]);
|
|
71
75
|
const [currentQueryStartTime, setCurrentQueryStartTime] = useState(null);
|
|
72
76
|
const [apiVersion, setApiVersion] = useState(null);
|
|
73
77
|
const [operatorProgress, setOperatorProgress] = useState({ isRunning: false, message: '' });
|
|
@@ -90,7 +94,8 @@ export function ConsoleContent({ config }) {
|
|
|
90
94
|
(config.enableRecall
|
|
91
95
|
? ` /recall - Recall semantic memories\n`
|
|
92
96
|
: '') +
|
|
93
|
-
` /mcp -
|
|
97
|
+
` /mcp - Connect an MCP client (sign in — no key)\n` +
|
|
98
|
+
` /mcp key - Mint a graph-scoped API key for header-only clients\n` +
|
|
94
99
|
` /help - Show this help message\n` +
|
|
95
100
|
` /clear - Clear console history\n` +
|
|
96
101
|
` /examples - Show example queries`;
|
|
@@ -189,6 +194,9 @@ export function ConsoleContent({ config }) {
|
|
|
189
194
|
streamingQuery.cancelQuery();
|
|
190
195
|
}
|
|
191
196
|
setTerminalMessages([]);
|
|
197
|
+
// A follow-up on the new graph must not resolve against the previous
|
|
198
|
+
// graph's answers.
|
|
199
|
+
conversationRef.current = [];
|
|
192
200
|
setCurrentQueryStartTime(null);
|
|
193
201
|
addSystemMessage(`═══════════════════════════════════════════════════════════════\n` +
|
|
194
202
|
`${config.welcome.contextLabel} context changed: ${previousGraphId.current} → ${graphId}\n` +
|
|
@@ -316,10 +324,11 @@ export function ConsoleContent({ config }) {
|
|
|
316
324
|
const startTime = Date.now();
|
|
317
325
|
try {
|
|
318
326
|
const { clients } = await import('@robosystems/client/clients');
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
327
|
+
const history = conversationRef.current;
|
|
328
|
+
const result = await clients.operator.executeQuery(graphId, Object.assign({ message: userQuery,
|
|
329
|
+
// `standard` gives the operator loop six tool turns; `quick` (three)
|
|
330
|
+
// was exhausted by schema + examples before the first query ran.
|
|
331
|
+
mode: 'standard' }, (history.length > 0 ? { history } : {})), {
|
|
323
332
|
mode: 'auto',
|
|
324
333
|
onProgress: (message, percentage) => {
|
|
325
334
|
setOperatorProgress({
|
|
@@ -330,6 +339,19 @@ export function ConsoleContent({ config }) {
|
|
|
330
339
|
},
|
|
331
340
|
});
|
|
332
341
|
setOperatorProgress({ isRunning: false, message: '' });
|
|
342
|
+
// The API reports operator failures (credit pre-flight, timeouts,
|
|
343
|
+
// unbilled calls) as HTTP 200 with `error_details` set and an
|
|
344
|
+
// explanatory `content`. Render those as errors, not as answers.
|
|
345
|
+
const errorDetails = result.error_details;
|
|
346
|
+
if (errorDetails) {
|
|
347
|
+
const detail = [
|
|
348
|
+
errorDetails.message,
|
|
349
|
+
errorDetails.error,
|
|
350
|
+
errorDetails.detail,
|
|
351
|
+
].find((v) => typeof v === 'string' && v.length > 0);
|
|
352
|
+
addErrorMessage(`Operator error: ${detail || result.content || 'The operator could not complete this request.'}`);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
333
355
|
const duration = Date.now() - startTime;
|
|
334
356
|
const metadata = (result.metadata || {});
|
|
335
357
|
const creditsUsed = metadata.credits_consumed;
|
|
@@ -384,6 +406,13 @@ export function ConsoleContent({ config }) {
|
|
|
384
406
|
// plain footer, matching the direct-query result style.
|
|
385
407
|
if (narrative) {
|
|
386
408
|
addResultMessage(narrative, rows, cypher, { markdown: true, footer });
|
|
409
|
+
// Record the exchange for follow-ups. Only a narrative answer is
|
|
410
|
+
// worth carrying; the answer is clipped so history stays cheap.
|
|
411
|
+
conversationRef.current = [
|
|
412
|
+
...conversationRef.current,
|
|
413
|
+
{ role: 'user', content: userQuery },
|
|
414
|
+
{ role: 'assistant', content: narrative.slice(0, 2000) },
|
|
415
|
+
].slice(-6);
|
|
387
416
|
}
|
|
388
417
|
else {
|
|
389
418
|
addResultMessage(footer, rows, cypher);
|
|
@@ -405,32 +434,54 @@ export function ConsoleContent({ config }) {
|
|
|
405
434
|
}
|
|
406
435
|
}
|
|
407
436
|
};
|
|
408
|
-
const showMcpSetup = async () => {
|
|
409
|
-
addSystemMessage('Creating graph-scoped connector key...', true);
|
|
437
|
+
const showMcpSetup = async (mode) => {
|
|
410
438
|
const apiUrl = process.env.NEXT_PUBLIC_ROBOSYSTEMS_API_URL ||
|
|
411
439
|
'https://api.robosystems.ai';
|
|
412
440
|
const { mcp } = config;
|
|
413
441
|
const contextId = graphId || mcp.contextIdFallback;
|
|
442
|
+
const endpoint = `${apiUrl}/v1/graphs/${contextId}/mcp`;
|
|
443
|
+
const connectorName = `${mcp.serverName}-${contextId}`;
|
|
444
|
+
const exampleLines = mcp.exampleQuestions
|
|
445
|
+
.map((q) => ` • "${q}"`)
|
|
446
|
+
.join('\n');
|
|
447
|
+
// The graph id lives in the URL path and never becomes a tool argument,
|
|
448
|
+
// so one connection is anchored to exactly one graph. OAuth on the
|
|
449
|
+
// per-graph URL shows this graph preselected on the consent screen; no
|
|
450
|
+
// key is minted unless asked for, so a sign-in user never leaves an
|
|
451
|
+
// unused credential behind in Settings → API Keys.
|
|
452
|
+
if (mode !== 'key') {
|
|
453
|
+
addSystemMessage(`MCP — connect ${contextId}\n` +
|
|
454
|
+
`═══════════════════════════════════════════════════════════════\n\n` +
|
|
455
|
+
`Sign in to connect (recommended). Paste the URL; the client sends\n` +
|
|
456
|
+
`you to sign in, and this graph is already selected on the consent\n` +
|
|
457
|
+
`screen. Nothing to copy, store, or rotate.\n\n` +
|
|
458
|
+
`Claude (claude.ai / Desktop)\n` +
|
|
459
|
+
` Settings → Connectors → Add custom connector\n` +
|
|
460
|
+
` ${endpoint}\n\n` +
|
|
461
|
+
`Claude Code (then /mcp to sign in)\n` +
|
|
462
|
+
` claude mcp add --transport http ${connectorName} ${endpoint}\n\n` +
|
|
463
|
+
`Cursor / VS Code (mcp.json)\n` +
|
|
464
|
+
` "${connectorName}": { "url": "${endpoint}" }\n\n` +
|
|
465
|
+
`Any graph from one address: ${apiUrl}/v1/mcp — pick the graph at\n` +
|
|
466
|
+
`sign-in.\n\n` +
|
|
467
|
+
`Scripts, CI, or a client that can't sign in? Type /mcp key to mint\n` +
|
|
468
|
+
`an API key scoped to this graph, with the header snippets filled in.\n\n` +
|
|
469
|
+
`Once connected, ask questions like:\n` +
|
|
470
|
+
`${exampleLines}\n\n` +
|
|
471
|
+
`Clients without HTTP transport support can use the stdio bridge\n` +
|
|
472
|
+
`in proxy mode: https://github.com/RoboFinSystems/robosystems-mcp-client`, true);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
addSystemMessage('Creating graph-scoped API key...', true);
|
|
414
476
|
try {
|
|
415
477
|
const { createMcpConnectorUrl } = await import('../../lib/mcp-connector');
|
|
416
|
-
//
|
|
417
|
-
//
|
|
418
|
-
// add one connector per graph, which is why the name carries the id.
|
|
419
|
-
// The key is graph-scoped: valid only for this graph, revocable in
|
|
420
|
-
// Settings → API Keys, and rejected on every account-level surface.
|
|
478
|
+
// Graph-scoped: valid only for this graph and its subgraphs, rejected on
|
|
479
|
+
// every account-level surface, revocable in Settings → API Keys.
|
|
421
480
|
const connector = await createMcpConnectorUrl(contextId, { apiUrl });
|
|
422
|
-
|
|
423
|
-
const exampleLines = mcp.exampleQuestions
|
|
424
|
-
.map((q) => ` • "${q}"`)
|
|
425
|
-
.join('\n');
|
|
426
|
-
addSystemMessage(`MCP Setup Instructions:\n` +
|
|
481
|
+
addSystemMessage(`API key for ${contextId}\n` +
|
|
427
482
|
`═══════════════════════════════════════════════════════════════\n\n` +
|
|
428
|
-
`
|
|
429
|
-
`
|
|
430
|
-
`Claude (claude.ai / Desktop)\n` +
|
|
431
|
-
` Settings → Connectors → Add custom connector, then paste this\n` +
|
|
432
|
-
` URL (the key rides inside it — no header field needed):\n` +
|
|
433
|
-
` ${connector.url}\n\n` +
|
|
483
|
+
`Scoped to this graph only; revoke it anytime in Settings → API Keys.\n` +
|
|
484
|
+
`It goes in the X-API-Key header — never in the URL.\n\n` +
|
|
434
485
|
`Claude Code\n` +
|
|
435
486
|
` claude mcp add --transport http ${connectorName} \\\n` +
|
|
436
487
|
` ${connector.endpoint} \\\n` +
|
|
@@ -440,16 +491,12 @@ export function ConsoleContent({ config }) {
|
|
|
440
491
|
` "url": "${connector.endpoint}",\n` +
|
|
441
492
|
` "headers": { "X-API-Key": "${connector.apiKey}" }\n` +
|
|
442
493
|
` }\n\n` +
|
|
443
|
-
`
|
|
444
|
-
`${exampleLines}\n\n` +
|
|
445
|
-
`Clients without HTTP transport support can use the stdio bridge\n` +
|
|
446
|
-
`in proxy mode: https://github.com/RoboFinSystems/robosystems-mcp-client\n\n` +
|
|
447
|
-
`The connector URL contains the key — treat it like a password.`, true);
|
|
494
|
+
`Treat the key like a password. Shown once.`, true);
|
|
448
495
|
}
|
|
449
496
|
catch (error) {
|
|
450
|
-
addErrorMessage(`Failed to create
|
|
451
|
-
`You can create
|
|
452
|
-
` URL: ${
|
|
497
|
+
addErrorMessage(`Failed to create API key: ${error.message || 'Unknown error'}\n\n` +
|
|
498
|
+
`You can create a key in Settings and connect with:\n` +
|
|
499
|
+
` URL: ${endpoint}\n` +
|
|
453
500
|
` Header: X-API-Key: <your key>`);
|
|
454
501
|
}
|
|
455
502
|
};
|
|
@@ -592,6 +639,7 @@ export function ConsoleContent({ config }) {
|
|
|
592
639
|
return;
|
|
593
640
|
case '/clear':
|
|
594
641
|
setTerminalMessages([]);
|
|
642
|
+
conversationRef.current = [];
|
|
595
643
|
addSystemMessage('Console cleared.', true);
|
|
596
644
|
return;
|
|
597
645
|
case '/examples': {
|
|
@@ -602,7 +650,7 @@ export function ConsoleContent({ config }) {
|
|
|
602
650
|
return;
|
|
603
651
|
}
|
|
604
652
|
case '/mcp':
|
|
605
|
-
await showMcpSetup();
|
|
653
|
+
await showMcpSetup(command.slice(cmd.length).trim().toLowerCase());
|
|
606
654
|
return;
|
|
607
655
|
default:
|
|
608
656
|
addErrorMessage(`Unknown command: ${cmd}\n\nType /help for available commands.`);
|
package/lib/mcp-connector.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
export interface McpConnectorUrl {
|
|
2
|
-
/**
|
|
3
|
-
url: string;
|
|
4
|
-
/** Bare MCP endpoint URL (no credential) — for header-capable clients */
|
|
2
|
+
/** Bare per-graph MCP endpoint URL (no credential) */
|
|
5
3
|
endpoint: string;
|
|
6
4
|
/** The plaintext graph-scoped API key (returned once at mint) */
|
|
7
5
|
apiKey: string;
|
|
@@ -10,18 +8,16 @@ export interface McpConnectorUrl {
|
|
|
10
8
|
graphId: string;
|
|
11
9
|
}
|
|
12
10
|
/**
|
|
13
|
-
* Mint a graph-scoped API key
|
|
11
|
+
* Mint a graph-scoped API key for the per-graph MCP endpoint.
|
|
14
12
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* Header-capable clients (Claude Code, Cursor, VS Code) should use the
|
|
24
|
-
* bare `endpoint` with the key in an `X-API-Key` header instead.
|
|
13
|
+
* For clients that cannot sign in (scripts, CI, editors without OAuth):
|
|
14
|
+
* the key goes in an `X-API-Key` header on `endpoint`. It works for
|
|
15
|
+
* exactly this graph (and its subgraphs), is rejected on every
|
|
16
|
+
* account-level surface, and can be revoked from Settings → API Keys like
|
|
17
|
+
* any other key. Credentials never travel in the URL — the `?token=`
|
|
18
|
+
* connector URL was the bridge to OAuth and the server no longer honors
|
|
19
|
+
* it. OAuth-capable clients need no key at all: they add `endpoint` (or
|
|
20
|
+
* the graph-agnostic `/v1/mcp`) and sign in.
|
|
25
21
|
*/
|
|
26
22
|
export declare function createMcpConnectorUrl(graphId: string, options?: {
|
|
27
23
|
apiUrl?: string;
|
package/lib/mcp-connector.js
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
const DEFAULT_API_URL = 'https://api.robosystems.ai';
|
|
2
2
|
/**
|
|
3
|
-
* Mint a graph-scoped API key
|
|
3
|
+
* Mint a graph-scoped API key for the per-graph MCP endpoint.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* Header-capable clients (Claude Code, Cursor, VS Code) should use the
|
|
14
|
-
* bare `endpoint` with the key in an `X-API-Key` header instead.
|
|
5
|
+
* For clients that cannot sign in (scripts, CI, editors without OAuth):
|
|
6
|
+
* the key goes in an `X-API-Key` header on `endpoint`. It works for
|
|
7
|
+
* exactly this graph (and its subgraphs), is rejected on every
|
|
8
|
+
* account-level surface, and can be revoked from Settings → API Keys like
|
|
9
|
+
* any other key. Credentials never travel in the URL — the `?token=`
|
|
10
|
+
* connector URL was the bridge to OAuth and the server no longer honors
|
|
11
|
+
* it. OAuth-capable clients need no key at all: they add `endpoint` (or
|
|
12
|
+
* the graph-agnostic `/v1/mcp`) and sign in.
|
|
15
13
|
*/
|
|
16
14
|
export async function createMcpConnectorUrl(graphId, options = {}) {
|
|
17
15
|
var _a, _b;
|
|
@@ -26,7 +24,6 @@ export async function createMcpConnectorUrl(graphId, options = {}) {
|
|
|
26
24
|
}
|
|
27
25
|
const endpoint = `${apiUrl}/v1/graphs/${graphId}/mcp`;
|
|
28
26
|
return {
|
|
29
|
-
url: `${endpoint}?token=${response.data.key}`,
|
|
30
27
|
endpoint,
|
|
31
28
|
apiKey: response.data.key,
|
|
32
29
|
keyName,
|
package/package.json
CHANGED