UNPKG

@knowall-ai/mcp-neo4j-agent-memory

Version:

Graph-based memory system for AI agents. Store people, places, organizations as nodes. Build semantic relationships (KNOWS, WORKS_AT, CREATED). Word-tokenized search finds partial matches. Filter by date, traverse relationships, maintain temporal context.

114 lines (113 loc) 4.16 kB
import neo4j, { Node, Relationship, Integer } from 'neo4j-driver'; export class Neo4jClient { driver; database; constructor(uri, username, password, database) { this.driver = neo4j.driver(uri, neo4j.auth.basic(username, password)); this.database = database; } convertNestedIntegers(value) { if (value instanceof Integer) { return value.toNumber(); } if (Array.isArray(value)) { return value.map(item => this.convertNestedIntegers(item)); } if (value && typeof value === 'object' && value.constructor === Object) { const converted = {}; for (const [key, val] of Object.entries(value)) { converted[key] = this.convertNestedIntegers(val); } return converted; } return value; } async executeQuery(query, params = {}) { const session = this.driver.session({ database: this.database }); try { const result = await session.run(query, params); return result.records.map((record) => { const obj = {}; for (const key of record.keys) { const value = record.get(key); if (value instanceof Node || value instanceof Relationship) { obj[key] = { ...this.convertNestedIntegers(value.properties), _id: value.identity.toNumber(), _labels: value instanceof Node ? value.labels : undefined, _type: value instanceof Relationship ? value.type : undefined, }; } else { obj[key] = this.convertNestedIntegers(value); } } return obj; }); } finally { await session.close(); } } async getNodes(label) { return this.executeQuery(`MATCH (n:${label}) RETURN n as memory`); } async createNode(label, properties) { const result = await this.executeQuery(`CREATE (n:${label} $props) RETURN n as memory`, { props: properties }); return result[0]; } async createRelationship(fromNodeId, toNodeId, relationType, properties = {}) { const result = await this.executeQuery(`MATCH (a), (b) WHERE id(a) = $fromId AND id(b) = $toId CREATE (a)-[r:${relationType} $props]->(b) RETURN r as relationship`, { fromId: neo4j.int(fromNodeId), toId: neo4j.int(toNodeId), props: properties, }); return result[0]; } async updateNode(nodeId, properties) { const result = await this.executeQuery(`MATCH (n) WHERE id(n) = $nodeId SET n += $props RETURN n as memory`, { nodeId: neo4j.int(nodeId), props: properties, }); return result[0]; } async updateRelationship(fromNodeId, toNodeId, relationType, properties) { const result = await this.executeQuery(`MATCH (a)-[r:${relationType}]->(b) WHERE id(a) = $fromId AND id(b) = $toId SET r += $props RETURN r as relationship`, { fromId: neo4j.int(fromNodeId), toId: neo4j.int(toNodeId), props: properties, }); return result[0]; } async deleteNode(nodeId) { const result = await this.executeQuery(`MATCH (n) WHERE id(n) = $nodeId DETACH DELETE n RETURN count(n) as deletedCount`, { nodeId: neo4j.int(nodeId), }); return result[0]; } async deleteRelationship(fromNodeId, toNodeId, relationType) { const result = await this.executeQuery(`MATCH (a)-[r:${relationType}]->(b) WHERE id(a) = $fromId AND id(b) = $toId DELETE r RETURN count(r) as deletedCount`, { fromId: neo4j.int(fromNodeId), toId: neo4j.int(toNodeId), }); return result[0]; } async close() { await this.driver.close(); } }