Skip to content

Commit 9e308d2

Browse files
committed
feat(ai): Implement Get Record Tool (#8195)
- Add `get_record` tool to Neural Link MCP. - Update `Neo.ai.Client` to handle `get_record` requests with optional store scoping. - Implement ambiguity check for records existing in multiple stores. - Import `Neo.manager.Store` in Client for robust store access.
1 parent e90546e commit 9e308d2

4 files changed

Lines changed: 81 additions & 4 deletions

File tree

ai/mcp/server/neural-link/openapi.yaml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,31 @@ paths:
252252
count:
253253
type: integer
254254

255+
/data/record/get:
256+
post:
257+
summary: Get Record
258+
operationId: get_record
259+
x-pass-as-object: true
260+
description: |
261+
Retrieves a specific data record.
262+
263+
**When to Use:**
264+
To inspect the full data of a single record.
265+
tags: [Data]
266+
requestBody:
267+
required: true
268+
content:
269+
application/json:
270+
schema:
271+
$ref: '#/components/schemas/GetRecordRequest'
272+
responses:
273+
'200':
274+
description: Record data
275+
content:
276+
application/json:
277+
schema:
278+
type: object
279+
255280
/drag/state:
256281
post:
257282
summary: Get Drag State
@@ -622,6 +647,21 @@ components:
622647
type: string
623648
description: The target App Worker Session ID
624649

650+
GetRecordRequest:
651+
type: object
652+
required:
653+
- recordId
654+
properties:
655+
recordId:
656+
type: string
657+
description: The ID of the record to retrieve
658+
storeId:
659+
type: string
660+
description: Optional Store ID to scope the search
661+
sessionId:
662+
type: string
663+
description: The target App Worker Session ID
664+
625665
ErrorResponse:
626666
type: object
627667
properties:

ai/mcp/server/neural-link/services/DataService.mjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ class DataService extends Base {
4545
async listStores({sessionId}) {
4646
return await ConnectionService.call(sessionId, 'list_stores', {});
4747
}
48+
49+
/**
50+
* Retrieves a specific record.
51+
* @param {Object} opts The options object.
52+
* @param {String} opts.recordId The ID of the record.
53+
* @param {String} [opts.storeId] Optional Store ID.
54+
* @param {String} [opts.sessionId] The target session ID.
55+
* @returns {Promise<Object>} The record data.
56+
*/
57+
async getRecord({recordId, storeId, sessionId}) {
58+
return await ConnectionService.call(sessionId, 'get_record', {recordId, storeId});
59+
}
4860
}
4961

5062
export default Neo.setupClass(DataService);

ai/mcp/server/neural-link/services/toolService.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const serviceMapping = {
1616
get_component_property: ComponentService.getComponentProperty.bind(ComponentService),
1717
get_component_tree : ComponentService.getComponentTree.bind(ComponentService),
1818
get_drag_state : InteractionService.getDragState.bind(InteractionService),
19+
get_record : DataService.getRecord.bind(DataService),
1920
get_vdom_tree : ComponentService.getVdomTree.bind(ComponentService),
2021
get_vnode_tree : ComponentService.getVnodeTree.bind(ComponentService),
2122
get_window_topology : RuntimeService.getWindowTopology.bind(RuntimeService),

src/ai/Client.mjs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Base from '../core/Base.mjs';
22
import ClassSystemUtil from '../util/ClassSystem.mjs';
33
import Socket from '../data/connection/WebSocket.mjs';
4+
import StoreManager from '../manager/Store.mjs';
45

56
/**
67
* The AI Client establishes a WebSocket connection to the Neural Link MCP Server.
@@ -266,6 +267,32 @@ class Client extends Base {
266267
if (!component) throw new Error('Root component not found');
267268
return {vnode: component.vnode};
268269

270+
case 'get_record':
271+
let {recordId, storeId} = params,
272+
record;
273+
274+
if (storeId) {
275+
const store = Neo.get(storeId);
276+
if (!store) throw new Error(`Store not found: ${storeId}`);
277+
record = store.get(recordId)
278+
} else {
279+
const matches = [];
280+
StoreManager.items.forEach(store => {
281+
const rec = store.get(recordId);
282+
if (rec) matches.push(rec)
283+
});
284+
285+
if (matches.length > 1) {
286+
throw new Error(`Multiple records found with ID ${recordId}. Please specify storeId.`)
287+
} else if (matches.length === 1) {
288+
record = matches[0]
289+
}
290+
}
291+
292+
if (!record) throw new Error(`Record not found: ${recordId}`);
293+
294+
return record.toJSON();
295+
269296
case 'get_window_info':
270297
const windowManager = Neo.manager?.Window;
271298

@@ -307,11 +334,8 @@ class Client extends Base {
307334
};
308335

309336
case 'list_stores':
310-
const storeManager = Neo.manager?.Store;
311-
if (!storeManager) return {stores: []};
312-
313337
return {
314-
stores: storeManager.items.map(s => ({
338+
stores: StoreManager.items.map(s => ({
315339
id : s.id,
316340
model : s.model?.className || 'N/A',
317341
count : s.count,

0 commit comments

Comments
 (0)