Skip to main content
Glama
neo4j-client.ts6.95 kB
import neo4j, { Driver, Session, Result } from 'neo4j-driver'; import { Neo4jConfig, ProjectConfig, ProjectContext } from '../types.js'; export class Neo4jClient { private driver: Driver | null = null; private projectConfig: ProjectConfig; constructor(private config: Neo4jConfig, projectConfig?: ProjectConfig) { this.projectConfig = projectConfig || { isolation_strategy: 'shared_db', cross_project_analysis: true, max_projects_shared_db: 100 }; } async connect(): Promise<void> { try { this.driver = neo4j.driver( this.config.uri, neo4j.auth.basic(this.config.user, this.config.password) ); // Verify connectivity await this.driver.verifyConnectivity(); console.log('Connected to Neo4J database'); } catch (error) { console.error('Failed to connect to Neo4J:', error); throw error; } } async disconnect(): Promise<void> { if (this.driver) { await this.driver.close(); this.driver = null; console.log('Disconnected from Neo4J database'); } } getSession(): Session { if (!this.driver) { throw new Error('Neo4J driver not connected. Call connect() first.'); } return this.driver.session(); } async runQuery(query: string, parameters: Record<string, any> = {}): Promise<Result> { const session = this.getSession(); try { return await session.run(query, parameters); } finally { await session.close(); } } async runTransaction<T>( work: (tx: any) => Promise<T> ): Promise<T> { const session = this.getSession(); try { return await session.executeWrite(work); } finally { await session.close(); } } async healthCheck(): Promise<boolean> { try { const result = await this.runQuery('RETURN 1 as health'); return result.records.length > 0; } catch (error) { console.error('Health check failed:', error); return false; } } async initializeDatabase(): Promise<void> { const session = this.getSession(); try { // Create project-aware constraints and indexes for better performance const constraints = [ // Project-aware core constraints 'CREATE CONSTRAINT IF NOT EXISTS FOR (n:CodeNode) REQUIRE (n.project_id, n.id) IS UNIQUE', 'CREATE CONSTRAINT IF NOT EXISTS FOR (e:CodeEdge) REQUIRE (e.project_id, e.id) IS UNIQUE', // Project context constraints 'CREATE CONSTRAINT IF NOT EXISTS FOR (p:ProjectContext) REQUIRE p.project_id IS UNIQUE', // Project-aware indexes for performance 'CREATE INDEX IF NOT EXISTS FOR (n:CodeNode) ON (n.project_id)', 'CREATE INDEX IF NOT EXISTS FOR (n:CodeNode) ON (n.project_id, n.type)', 'CREATE INDEX IF NOT EXISTS FOR (n:CodeNode) ON (n.project_id, n.name)', 'CREATE INDEX IF NOT EXISTS FOR (n:CodeNode) ON (n.project_id, n.qualified_name)', 'CREATE INDEX IF NOT EXISTS FOR (e:CodeEdge) ON (e.project_id)', 'CREATE INDEX IF NOT EXISTS FOR (e:CodeEdge) ON (e.project_id, e.type)', // Traditional indexes for backward compatibility and cross-project queries 'CREATE INDEX IF NOT EXISTS FOR (n:CodeNode) ON (n.type)', 'CREATE INDEX IF NOT EXISTS FOR (n:CodeNode) ON (n.name)', 'CREATE INDEX IF NOT EXISTS FOR (n:CodeNode) ON (n.qualified_name)', 'CREATE INDEX IF NOT EXISTS FOR (e:CodeEdge) ON (e.type)' ]; for (const constraint of constraints) { await session.run(constraint); } console.log('Database initialized with project-aware constraints and indexes'); } finally { await session.close(); } } // Project management methods async createProject(project: ProjectContext): Promise<ProjectContext> { const query = ` CREATE (p:ProjectContext { project_id: $project_id, name: $name, description: $description, created_at: datetime(), updated_at: datetime() }) RETURN p `; const params = { project_id: project.project_id, name: project.name || project.project_id, description: project.description || null }; const result = await this.runQuery(query, params); if (result.records.length === 0) { throw new Error('Failed to create project'); } const record = result.records[0].get('p'); return { project_id: record.properties.project_id, name: record.properties.name, description: record.properties.description, created_at: record.properties.created_at.toStandardDate(), updated_at: record.properties.updated_at.toStandardDate() }; } async getProject(projectId: string): Promise<ProjectContext | null> { const query = ` MATCH (p:ProjectContext {project_id: $project_id}) RETURN p `; const result = await this.runQuery(query, { project_id: projectId }); if (result.records.length === 0) { return null; } const record = result.records[0].get('p'); return { project_id: record.properties.project_id, name: record.properties.name, description: record.properties.description, created_at: record.properties.created_at?.toStandardDate(), updated_at: record.properties.updated_at?.toStandardDate() }; } async listProjects(): Promise<ProjectContext[]> { const query = ` MATCH (p:ProjectContext) RETURN p ORDER BY p.created_at DESC `; const result = await this.runQuery(query); return result.records.map(record => { const p = record.get('p'); return { project_id: p.properties.project_id, name: p.properties.name, description: p.properties.description, created_at: p.properties.created_at?.toStandardDate(), updated_at: p.properties.updated_at?.toStandardDate() }; }); } async deleteProject(projectId: string): Promise<boolean> { const query = ` MATCH (p:ProjectContext {project_id: $project_id}) OPTIONAL MATCH (n:CodeNode {project_id: $project_id}) OPTIONAL MATCH (e:CodeEdge {project_id: $project_id}) DELETE p, n, e RETURN count(p) as deleted_projects `; const result = await this.runQuery(query, { project_id: projectId }); return result.records[0]?.get('deleted_projects') > 0; } // Utility methods getProjectLabel(projectId: string, nodeType: string): string { return `Project_${projectId}_${nodeType.charAt(0).toUpperCase() + nodeType.slice(1)}`; } generateProjectScopedId(projectId: string, entityId: string): string { return `${projectId}:${entityId}`; } parseProjectScopedId(scopedId: string): { projectId: string; entityId: string } { const [projectId, ...entityParts] = scopedId.split(':'); return { projectId, entityId: entityParts.join(':') }; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/JonnoC/CodeRAG'

If you have feedback or need assistance with the MCP directory API, please join our Discord server