code-graph-context 2.3.0 → 2.4.0

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/README.md CHANGED
@@ -65,100 +65,90 @@ The system uses a dual-schema approach:
65
65
 
66
66
  Choose the installation method that works best for you:
67
67
 
68
- #### Option 1: Development Install (From Source)
68
+ #### Option 1: NPM Install (Recommended)
69
69
 
70
- Best for: Contributing to the project or customizing the code
71
-
72
- 1. **Clone the repository:**
73
70
  ```bash
74
- git clone https://github.com/drewdrewH/code-graph-context.git
75
- cd code-graph-context
76
- ```
71
+ # Install globally
72
+ npm install -g code-graph-context
77
73
 
78
- 2. **Install dependencies:**
79
- ```bash
80
- npm install
74
+ # Set up Neo4j (requires Docker)
75
+ code-graph-context init
76
+
77
+ # Add to Claude Code
78
+ claude mcp add code-graph-context code-graph-context
81
79
  ```
82
80
 
83
- 3. **Set up Neo4j using Docker:**
84
- ```bash
85
- docker-compose up -d
81
+ Then configure your OpenAI API key in `~/.config/claude/config.json`:
82
+ ```json
83
+ {
84
+ "mcpServers": {
85
+ "code-graph-context": {
86
+ "command": "code-graph-context",
87
+ "env": {
88
+ "OPENAI_API_KEY": "sk-your-key-here"
89
+ }
90
+ }
91
+ }
92
+ }
86
93
  ```
87
94
 
88
- This will start Neo4j with:
89
- - Web interface: http://localhost:7474
90
- - Bolt connection: bolt://localhost:7687
91
- - Username: `neo4j`, Password: `PASSWORD`
95
+ #### Option 2: From Source
92
96
 
93
- 4. **Configure environment variables:**
94
97
  ```bash
95
- cp .env.example .env
96
- # Edit .env with your configuration
98
+ # Clone and build
99
+ git clone https://github.com/drewdrewH/code-graph-context.git
100
+ cd code-graph-context
101
+ npm install
102
+ npm run build
103
+
104
+ # Set up Neo4j
105
+ code-graph-context init
106
+
107
+ # Add to Claude Code (use absolute path)
108
+ claude mcp add code-graph-context node /absolute/path/to/code-graph-context/dist/cli/cli.js
97
109
  ```
98
110
 
99
- 5. **Build the project:**
111
+ ### CLI Commands
112
+
113
+ The package includes a CLI for managing Neo4j:
114
+
100
115
  ```bash
101
- npm run build
116
+ code-graph-context init [options] # Set up Neo4j container
117
+ code-graph-context status # Check Docker/Neo4j status
118
+ code-graph-context stop # Stop Neo4j container
102
119
  ```
103
120
 
104
- 6. **Add to Claude Code:**
105
- ```bash
106
- claude mcp add code-graph-context node /absolute/path/to/code-graph-context/dist/mcp/mcp.server.js
121
+ **Init options:**
122
+ ```
123
+ -p, --port <port> Bolt port (default: 7687)
124
+ --http-port <port> Browser port (default: 7474)
125
+ --password <password> Neo4j password (default: PASSWORD)
126
+ -m, --memory <size> Heap memory (default: 2G)
127
+ -f, --force Recreate container
107
128
  ```
108
129
 
109
- #### Option 2: NPM Install (Global Package)
130
+ ### Alternative Neo4j Setup
110
131
 
111
- Best for: Easy setup and automatic updates
132
+ If you prefer not to use the CLI, you can set up Neo4j manually:
112
133
 
113
- 1. **Install the package globally:**
134
+ **Docker Compose:**
114
135
  ```bash
115
- npm install -g code-graph-context
136
+ docker-compose up -d
116
137
  ```
117
138
 
118
- 2. **Set up Neo4j** (choose one):
119
-
120
- **Option A: Docker (Recommended)**
139
+ **Docker Run:**
121
140
  ```bash
122
141
  docker run -d \
123
142
  --name code-graph-neo4j \
124
143
  -p 7474:7474 -p 7687:7687 \
125
144
  -e NEO4J_AUTH=neo4j/PASSWORD \
126
- -e NEO4J_PLUGINS='["apoc"]' \
145
+ -e 'NEO4J_PLUGINS=["apoc"]' \
127
146
  neo4j:5.23
128
147
  ```
129
148
 
130
- **Option B: Neo4j Desktop**
131
- - Download from [neo4j.com/download](https://neo4j.com/download/)
132
- - Install APOC plugin
133
- - Start database
134
-
135
- **Option C: Neo4j Aura (Cloud)**
136
- - Create free account at [neo4j.com/cloud/aura](https://neo4j.com/cloud/platform/aura-graph-database/)
137
- - Note your connection URI and credentials
138
-
139
- 3. **Add to Claude Code:**
140
- ```bash
141
- claude mcp add code-graph-context code-graph-context
142
- ```
143
-
144
- Then configure in your MCP config file (`~/.config/claude/config.json`):
145
- ```json
146
- {
147
- "mcpServers": {
148
- "code-graph-context": {
149
- "command": "code-graph-context",
150
- "env": {
151
- "OPENAI_API_KEY": "sk-your-key-here",
152
- "NEO4J_URI": "bolt://localhost:7687",
153
- "NEO4J_USER": "neo4j",
154
- "NEO4J_PASSWORD": "PASSWORD"
155
- }
156
- }
157
- }
158
- }
159
- ```
149
+ **Neo4j Desktop:** Download from [neo4j.com/download](https://neo4j.com/download/) and install APOC plugin.
160
150
 
161
- **Note:** The env vars can be configured for any Neo4j instance - local, Docker, cloud (Aura), or enterprise.
151
+ **Neo4j Aura (Cloud):** Create account at [neo4j.com/cloud/aura](https://neo4j.com/cloud/platform/aura-graph-database/) and configure connection URI in env vars.
162
152
 
163
153
  ### Verify Installation
164
154
 
@@ -0,0 +1,266 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI Entry Point for Code Graph Context
4
+ *
5
+ * Handles CLI commands (init, status, stop) and delegates to MCP server
6
+ */
7
+ import { readFileSync } from 'fs';
8
+ import { dirname, join } from 'path';
9
+ import { fileURLToPath } from 'url';
10
+ import { Command } from 'commander';
11
+ import { NEO4J_CONFIG, createContainer, getContainerStatus, getFullStatus, isApocAvailable, isDockerInstalled, isDockerRunning, removeContainer, startContainer, stopContainer, waitForNeo4j, } from './neo4j-docker.js';
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = dirname(__filename);
14
+ // ANSI colors
15
+ const c = {
16
+ reset: '\x1b[0m',
17
+ bold: '\x1b[1m',
18
+ dim: '\x1b[2m',
19
+ green: '\x1b[32m',
20
+ red: '\x1b[31m',
21
+ yellow: '\x1b[33m',
22
+ blue: '\x1b[34m',
23
+ cyan: '\x1b[36m',
24
+ };
25
+ const sym = {
26
+ ok: `${c.green}✓${c.reset}`,
27
+ err: `${c.red}✗${c.reset}`,
28
+ warn: `${c.yellow}⚠${c.reset}`,
29
+ info: `${c.blue}ℹ${c.reset}`,
30
+ };
31
+ const log = (symbol, msg) => {
32
+ console.log(` ${symbol} ${msg}`);
33
+ };
34
+ const header = (text) => {
35
+ console.log(`\n${c.bold}${text}${c.reset}\n`);
36
+ };
37
+ /**
38
+ * Spinner for async operations
39
+ */
40
+ const spinner = (msg) => {
41
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
42
+ let i = 0;
43
+ const interval = setInterval(() => {
44
+ process.stdout.write(`\r ${c.blue}${frames[i]}${c.reset} ${msg}`);
45
+ i = (i + 1) % frames.length;
46
+ }, 80);
47
+ return {
48
+ stop: (ok, finalMsg) => {
49
+ clearInterval(interval);
50
+ process.stdout.write(`\r ${ok ? sym.ok : sym.err} ${finalMsg || msg}\n`);
51
+ },
52
+ };
53
+ };
54
+ /**
55
+ * Print config instructions
56
+ */
57
+ const printConfigInstructions = (password, boltPort) => {
58
+ console.log(`
59
+ ${c.bold}Next steps:${c.reset}
60
+
61
+ 1. Add to Claude Code:
62
+ ${c.dim}claude mcp add code-graph-context code-graph-context${c.reset}
63
+
64
+ 2. Configure in ${c.cyan}~/.config/claude/config.json${c.reset}:
65
+
66
+ ${c.dim}{
67
+ "mcpServers": {
68
+ "code-graph-context": {
69
+ "command": "code-graph-context",
70
+ "env": {
71
+ "OPENAI_API_KEY": "sk-..."${password !== NEO4J_CONFIG.defaultPassword
72
+ ? `,
73
+ "NEO4J_PASSWORD": "${password}"`
74
+ : ''}${boltPort !== NEO4J_CONFIG.boltPort
75
+ ? `,
76
+ "NEO4J_URI": "bolt://localhost:${boltPort}"`
77
+ : ''}
78
+ }
79
+ }
80
+ }
81
+ }${c.reset}
82
+
83
+ ${c.yellow}Get your OpenAI API key:${c.reset} https://platform.openai.com/api-keys
84
+
85
+ 3. Restart Claude Code
86
+ `);
87
+ };
88
+ /**
89
+ * Init command - set up Neo4j
90
+ */
91
+ const runInit = async (options) => {
92
+ const boltPort = options.port ? parseInt(options.port, 10) : NEO4J_CONFIG.boltPort;
93
+ const httpPort = options.httpPort ? parseInt(options.httpPort, 10) : NEO4J_CONFIG.httpPort;
94
+ const password = options.password || NEO4J_CONFIG.defaultPassword;
95
+ const memory = options.memory || '4G';
96
+ header('Code Graph Context Setup');
97
+ // Check Docker
98
+ if (!isDockerInstalled()) {
99
+ log(sym.err, 'Docker is not installed');
100
+ console.log(`\n Install Docker: ${c.cyan}https://docs.docker.com/get-docker/${c.reset}\n`);
101
+ process.exit(1);
102
+ }
103
+ log(sym.ok, 'Docker installed');
104
+ if (!isDockerRunning()) {
105
+ log(sym.err, 'Docker daemon is not running');
106
+ console.log(`\n Start Docker Desktop or run: ${c.dim}sudo systemctl start docker${c.reset}\n`);
107
+ process.exit(1);
108
+ }
109
+ log(sym.ok, 'Docker daemon running');
110
+ // Handle existing container
111
+ const status = getContainerStatus();
112
+ if (status === 'running' && !options.force) {
113
+ log(sym.ok, 'Neo4j container already running');
114
+ const apocOk = isApocAvailable(NEO4J_CONFIG.containerName, password);
115
+ log(apocOk ? sym.ok : sym.warn, apocOk ? 'APOC plugin available' : 'APOC plugin not detected');
116
+ console.log(`\n ${c.dim}Use --force to recreate the container${c.reset}`);
117
+ printConfigInstructions(password, boltPort);
118
+ return;
119
+ }
120
+ if (status !== 'not-found' && options.force) {
121
+ const s = spinner('Removing existing container...');
122
+ stopContainer();
123
+ removeContainer();
124
+ s.stop(true, 'Removed existing container');
125
+ }
126
+ if (status === 'stopped' && !options.force) {
127
+ const s = spinner('Starting existing container...');
128
+ const started = startContainer();
129
+ if (!started) {
130
+ s.stop(false, 'Failed to start container');
131
+ console.log(`\n Try: ${c.dim}code-graph-context init --force${c.reset}\n`);
132
+ process.exit(1);
133
+ }
134
+ s.stop(true, 'Container started');
135
+ }
136
+ else if (status === 'not-found' || options.force) {
137
+ const s = spinner('Creating Neo4j container...');
138
+ const created = createContainer({ httpPort, boltPort, password, memory });
139
+ if (!created) {
140
+ s.stop(false, 'Failed to create container');
141
+ console.log(`
142
+ Check if ports are in use:
143
+ ${c.dim}lsof -i :${httpPort}${c.reset}
144
+ ${c.dim}lsof -i :${boltPort}${c.reset}
145
+ `);
146
+ process.exit(1);
147
+ }
148
+ s.stop(true, 'Container created');
149
+ }
150
+ // Wait for Neo4j
151
+ const healthSpinner = spinner('Waiting for Neo4j to be ready (this may take a minute)...');
152
+ const ready = await waitForNeo4j(NEO4J_CONFIG.containerName, password);
153
+ healthSpinner.stop(ready, ready ? 'Neo4j is ready' : 'Neo4j failed to start');
154
+ if (!ready) {
155
+ console.log(`\n Check logs: ${c.dim}docker logs ${NEO4J_CONFIG.containerName}${c.reset}\n`);
156
+ process.exit(1);
157
+ }
158
+ // Check APOC
159
+ const apocOk = isApocAvailable(NEO4J_CONFIG.containerName, password);
160
+ log(apocOk ? sym.ok : sym.warn, apocOk ? 'APOC plugin verified' : 'APOC still loading (should be ready shortly)');
161
+ // Print connection info
162
+ console.log(`
163
+ ${c.bold}Neo4j is ready${c.reset}
164
+
165
+ Browser: ${c.cyan}http://localhost:${httpPort}${c.reset}
166
+ Bolt URI: ${c.cyan}bolt://localhost:${boltPort}${c.reset}
167
+ Credentials: ${c.dim}neo4j / ${password}${c.reset}`);
168
+ printConfigInstructions(password, boltPort);
169
+ };
170
+ /**
171
+ * Status command
172
+ */
173
+ const runStatus = () => {
174
+ header('Code Graph Context Status');
175
+ const status = getFullStatus();
176
+ log(status.dockerInstalled ? sym.ok : sym.err, `Docker installed: ${status.dockerInstalled ? 'yes' : 'no'}`);
177
+ if (!status.dockerInstalled) {
178
+ console.log(`\n Install: ${c.cyan}https://docs.docker.com/get-docker/${c.reset}\n`);
179
+ return;
180
+ }
181
+ log(status.dockerRunning ? sym.ok : sym.err, `Docker running: ${status.dockerRunning ? 'yes' : 'no'}`);
182
+ if (!status.dockerRunning) {
183
+ console.log(`\n Start Docker Desktop or: ${c.dim}sudo systemctl start docker${c.reset}\n`);
184
+ return;
185
+ }
186
+ const containerIcon = status.containerStatus === 'running' ? sym.ok : status.containerStatus === 'stopped' ? sym.warn : sym.err;
187
+ log(containerIcon, `Container: ${status.containerStatus}`);
188
+ if (status.containerStatus === 'running') {
189
+ log(status.neo4jReady ? sym.ok : sym.warn, `Neo4j responding: ${status.neo4jReady ? 'yes' : 'no'}`);
190
+ log(status.apocAvailable ? sym.ok : sym.warn, `APOC plugin: ${status.apocAvailable ? 'available' : 'not available'}`);
191
+ }
192
+ console.log('');
193
+ if (status.containerStatus !== 'running') {
194
+ console.log(` Run ${c.dim}code-graph-context init${c.reset} to start Neo4j\n`);
195
+ }
196
+ else if (!status.apocAvailable) {
197
+ console.log(` APOC may still be loading. Wait a moment and check again.\n`);
198
+ }
199
+ };
200
+ /**
201
+ * Stop command
202
+ */
203
+ const runStop = () => {
204
+ const status = getContainerStatus();
205
+ if (status === 'not-found') {
206
+ log(sym.info, 'No Neo4j container found');
207
+ return;
208
+ }
209
+ if (status === 'stopped') {
210
+ log(sym.info, 'Container already stopped');
211
+ return;
212
+ }
213
+ const s = spinner('Stopping Neo4j...');
214
+ const stopped = stopContainer();
215
+ s.stop(stopped, stopped ? 'Neo4j stopped' : 'Failed to stop container');
216
+ };
217
+ /**
218
+ * Start MCP server
219
+ */
220
+ const startMcpServer = async () => {
221
+ // The MCP server is in a sibling directory after build
222
+ // cli/cli.js -> mcp/mcp.server.js
223
+ const mcpPath = join(__dirname, '..', 'mcp', 'mcp.server.js');
224
+ await import(mcpPath);
225
+ };
226
+ /**
227
+ * Get package version
228
+ */
229
+ const getVersion = () => {
230
+ try {
231
+ // Go up from dist/cli to root
232
+ const pkgPath = join(__dirname, '..', '..', 'package.json');
233
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
234
+ return pkg.version;
235
+ }
236
+ catch {
237
+ return 'unknown';
238
+ }
239
+ };
240
+ // Build CLI
241
+ const program = new Command();
242
+ program.name('code-graph-context').description('MCP server for code graph analysis with Neo4j').version(getVersion());
243
+ program
244
+ .command('init')
245
+ .description('Set up Neo4j container and show configuration steps')
246
+ .option('-p, --port <port>', 'Neo4j Bolt port', '7687')
247
+ .option('--http-port <port>', 'Neo4j Browser port', '7474')
248
+ .option('--password <password>', 'Neo4j password', 'PASSWORD')
249
+ .option('-m, --memory <size>', 'Max heap memory (e.g., 2G, 4G)', '2G')
250
+ .option('-f, --force', 'Recreate container even if exists')
251
+ .action(runInit);
252
+ program.command('status').description('Check Neo4j and Docker status').action(runStatus);
253
+ program.command('stop').description('Stop the Neo4j container').action(runStop);
254
+ // Default action: start MCP server if no command given
255
+ const knownCommands = ['init', 'status', 'stop', 'help'];
256
+ const args = process.argv.slice(2);
257
+ const hasCommand = args.some((arg) => knownCommands.includes(arg) || arg.startsWith('-'));
258
+ if (args.length === 0 || !hasCommand) {
259
+ startMcpServer().catch((err) => {
260
+ console.error('Failed to start MCP server:', err);
261
+ process.exit(1);
262
+ });
263
+ }
264
+ else {
265
+ program.parse();
266
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Neo4j Docker Management
3
+ *
4
+ * Handles Docker container lifecycle for Neo4j
5
+ */
6
+ import { execSync } from 'child_process';
7
+ // Container configuration
8
+ export const NEO4J_CONFIG = {
9
+ containerName: 'code-graph-neo4j',
10
+ image: 'neo4j:5.23',
11
+ httpPort: 7474,
12
+ boltPort: 7687,
13
+ defaultPassword: 'PASSWORD',
14
+ defaultUser: 'neo4j',
15
+ healthCheckTimeoutMs: 120000,
16
+ healthCheckIntervalMs: 2000,
17
+ };
18
+ /**
19
+ * Execute a command and return stdout, or null if failed
20
+ */
21
+ const exec = (command) => {
22
+ try {
23
+ return execSync(command, {
24
+ encoding: 'utf-8',
25
+ stdio: ['pipe', 'pipe', 'pipe'],
26
+ }).trim();
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ };
32
+ /**
33
+ * Check if Docker CLI is available
34
+ */
35
+ export const isDockerInstalled = () => exec('docker --version') !== null;
36
+ /**
37
+ * Check if Docker daemon is running
38
+ */
39
+ export const isDockerRunning = () => exec('docker info') !== null;
40
+ /**
41
+ * Get container status
42
+ */
43
+ export const getContainerStatus = (containerName = NEO4J_CONFIG.containerName) => {
44
+ const result = exec(`docker inspect --format='{{.State.Running}}' ${containerName} 2>/dev/null`);
45
+ if (result === null)
46
+ return 'not-found';
47
+ return result === 'true' ? 'running' : 'stopped';
48
+ };
49
+ /**
50
+ * Start an existing stopped container
51
+ */
52
+ export const startContainer = (containerName = NEO4J_CONFIG.containerName) => exec(`docker start ${containerName}`) !== null;
53
+ /**
54
+ * Stop a running container
55
+ */
56
+ export const stopContainer = (containerName = NEO4J_CONFIG.containerName) => exec(`docker stop ${containerName}`) !== null;
57
+ /**
58
+ * Remove a container
59
+ */
60
+ export const removeContainer = (containerName = NEO4J_CONFIG.containerName) => exec(`docker rm ${containerName}`) !== null;
61
+ /**
62
+ * Create and start a new Neo4j container
63
+ */
64
+ export const createContainer = (options = {}) => {
65
+ const { containerName = NEO4J_CONFIG.containerName, httpPort = NEO4J_CONFIG.httpPort, boltPort = NEO4J_CONFIG.boltPort, password = NEO4J_CONFIG.defaultPassword, memory = '2G', } = options;
66
+ const cmd = [
67
+ 'docker run -d',
68
+ `--name ${containerName}`,
69
+ `--restart unless-stopped`,
70
+ `-p ${httpPort}:7474`,
71
+ `-p ${boltPort}:7687`,
72
+ `-e NEO4J_AUTH=neo4j/${password}`,
73
+ `-e 'NEO4J_PLUGINS=["apoc"]'`,
74
+ `-e NEO4J_dbms_security_procedures_unrestricted=apoc.*`,
75
+ `-e NEO4J_server_memory_heap_initial__size=1G`,
76
+ `-e NEO4J_server_memory_heap_max__size=${memory}`,
77
+ `-e NEO4J_server_memory_pagecache_size=2G`,
78
+ NEO4J_CONFIG.image,
79
+ ].join(' ');
80
+ return exec(cmd) !== null;
81
+ };
82
+ /**
83
+ * Check if Neo4j is accepting connections
84
+ */
85
+ export const isNeo4jReady = (containerName = NEO4J_CONFIG.containerName, password = NEO4J_CONFIG.defaultPassword) => {
86
+ const result = exec(`docker exec ${containerName} cypher-shell -u neo4j -p ${password} "RETURN 1" 2>/dev/null`);
87
+ return result !== null;
88
+ };
89
+ /**
90
+ * Check if APOC plugin is available
91
+ */
92
+ export const isApocAvailable = (containerName = NEO4J_CONFIG.containerName, password = NEO4J_CONFIG.defaultPassword) => {
93
+ const result = exec(`docker exec ${containerName} cypher-shell -u neo4j -p ${password} "CALL apoc.help('apoc') YIELD name RETURN count(name)" 2>/dev/null`);
94
+ return result !== null && !result.includes('error');
95
+ };
96
+ /**
97
+ * Wait for Neo4j to be ready
98
+ */
99
+ export const waitForNeo4j = async (containerName = NEO4J_CONFIG.containerName, password = NEO4J_CONFIG.defaultPassword, timeoutMs = NEO4J_CONFIG.healthCheckTimeoutMs) => {
100
+ const startTime = Date.now();
101
+ while (Date.now() - startTime < timeoutMs) {
102
+ if (isNeo4jReady(containerName, password)) {
103
+ return true;
104
+ }
105
+ await new Promise((resolve) => setTimeout(resolve, NEO4J_CONFIG.healthCheckIntervalMs));
106
+ }
107
+ return false;
108
+ };
109
+ /**
110
+ * Ensure Neo4j is running - start if needed
111
+ */
112
+ export const ensureNeo4jRunning = async (options = {}) => {
113
+ const containerName = options.containerName ?? NEO4J_CONFIG.containerName;
114
+ const status = getContainerStatus(containerName);
115
+ if (status === 'running') {
116
+ return { success: true, action: 'already-running' };
117
+ }
118
+ if (!isDockerInstalled()) {
119
+ return { success: false, action: 'failed', error: 'Docker not installed' };
120
+ }
121
+ if (!isDockerRunning()) {
122
+ return { success: false, action: 'failed', error: 'Docker daemon not running' };
123
+ }
124
+ // Start existing container
125
+ if (status === 'stopped') {
126
+ if (startContainer(containerName)) {
127
+ const ready = await waitForNeo4j(containerName, options.password);
128
+ return ready
129
+ ? { success: true, action: 'started' }
130
+ : { success: false, action: 'failed', error: 'Container started but Neo4j not responding' };
131
+ }
132
+ return { success: false, action: 'failed', error: 'Failed to start existing container' };
133
+ }
134
+ // Create new container
135
+ if (createContainer(options)) {
136
+ const ready = await waitForNeo4j(containerName, options.password);
137
+ return ready
138
+ ? { success: true, action: 'created' }
139
+ : { success: false, action: 'failed', error: 'Container created but Neo4j not responding' };
140
+ }
141
+ return { success: false, action: 'failed', error: 'Failed to create container' };
142
+ };
143
+ /**
144
+ * Get full status for diagnostics
145
+ */
146
+ export const getFullStatus = () => {
147
+ const dockerInstalled = isDockerInstalled();
148
+ const dockerRunning = dockerInstalled && isDockerRunning();
149
+ const containerStatus = dockerRunning ? getContainerStatus() : 'not-found';
150
+ const neo4jReady = containerStatus === 'running' && isNeo4jReady();
151
+ const apocAvailable = neo4jReady && isApocAvailable();
152
+ return {
153
+ dockerInstalled,
154
+ dockerRunning,
155
+ containerStatus,
156
+ neo4jReady,
157
+ apocAvailable,
158
+ };
159
+ };
@@ -4,14 +4,67 @@
4
4
  */
5
5
  import fs from 'fs/promises';
6
6
  import { join } from 'path';
7
+ import { ensureNeo4jRunning, isDockerInstalled, isDockerRunning, } from '../cli/neo4j-docker.js';
7
8
  import { Neo4jService, QUERIES } from '../storage/neo4j/neo4j.service.js';
8
9
  import { FILE_PATHS, LOG_CONFIG } from './constants.js';
9
10
  import { initializeNaturalLanguageService } from './tools/natural-language-to-cypher.tool.js';
10
11
  import { debugLog } from './utils.js';
12
+ /**
13
+ * Log startup warnings for missing configuration
14
+ */
15
+ const checkConfiguration = async () => {
16
+ if (!process.env.OPENAI_API_KEY) {
17
+ console.error(JSON.stringify({
18
+ level: 'warn',
19
+ message: '[code-graph-context] OPENAI_API_KEY not set. Semantic search and NL queries unavailable.',
20
+ }));
21
+ await debugLog('Configuration warning', { warning: 'OPENAI_API_KEY not set' });
22
+ }
23
+ };
24
+ /**
25
+ * Ensure Neo4j is running - auto-start if Docker available, fail if not
26
+ */
27
+ const ensureNeo4j = async () => {
28
+ // Check if Docker is available
29
+ if (!isDockerInstalled()) {
30
+ const msg = 'Docker not installed. Install Docker or run: code-graph-context init';
31
+ console.error(JSON.stringify({ level: 'error', message: `[code-graph-context] ${msg}` }));
32
+ throw new Error(msg);
33
+ }
34
+ if (!isDockerRunning()) {
35
+ const msg = 'Docker not running. Start Docker or run: code-graph-context init';
36
+ console.error(JSON.stringify({ level: 'error', message: `[code-graph-context] ${msg}` }));
37
+ throw new Error(msg);
38
+ }
39
+ const result = await ensureNeo4jRunning();
40
+ if (!result.success) {
41
+ const msg = `Neo4j failed to start: ${result.error}. Run: code-graph-context init`;
42
+ console.error(JSON.stringify({ level: 'error', message: `[code-graph-context] ${msg}` }));
43
+ throw new Error(msg);
44
+ }
45
+ if (result.action === 'created') {
46
+ console.error(JSON.stringify({
47
+ level: 'info',
48
+ message: '[code-graph-context] Neo4j container created and started',
49
+ }));
50
+ }
51
+ else if (result.action === 'started') {
52
+ console.error(JSON.stringify({
53
+ level: 'info',
54
+ message: '[code-graph-context] Neo4j container started',
55
+ }));
56
+ }
57
+ await debugLog('Neo4j ready', result);
58
+ };
11
59
  /**
12
60
  * Initialize all external services required by the MCP server
13
61
  */
14
62
  export const initializeServices = async () => {
63
+ // Check for missing configuration (non-fatal warnings)
64
+ await checkConfiguration();
65
+ // Ensure Neo4j is running (fatal if not)
66
+ await ensureNeo4j();
67
+ // Initialize services
15
68
  await Promise.all([initializeNeo4jSchema(), initializeNaturalLanguageService()]);
16
69
  };
17
70
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-graph-context",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "MCP server that builds code graphs to provide rich context to LLMs",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/drewdrewH/code-graph-context#readme",
@@ -30,7 +30,7 @@
30
30
  "license": "MIT",
31
31
  "main": "dist/mcp/mcp.server.js",
32
32
  "bin": {
33
- "code-graph-context": "dist/mcp/mcp.server.js"
33
+ "code-graph-context": "dist/cli/cli.js"
34
34
  },
35
35
  "files": [
36
36
  "dist/**/*",