1515 * ```
1616 */
1717
18+ import * as path from 'path' ;
1819import CodeGraph , { findNearestCodeGraphRoot } from '../index' ;
1920import { StdioTransport , JsonRpcRequest , JsonRpcNotification , ErrorCodes } from './transport' ;
2021import { tools , ToolHandler } from './tools' ;
2122import { initSentry , captureException } from '../sentry' ;
2223
2324initSentry ( { processName : 'codegraph-mcp' } ) ;
2425
26+ /**
27+ * Convert a file:// URI to a filesystem path.
28+ * Handles URL encoding and Windows drive letter paths.
29+ */
30+ function fileUriToPath ( uri : string ) : string {
31+ try {
32+ const url = new URL ( uri ) ;
33+ let filePath = decodeURIComponent ( url . pathname ) ;
34+ // On Windows, file:///C:/path produces pathname /C:/path — strip leading /
35+ if ( process . platform === 'win32' && / ^ \/ [ a - z A - Z ] : / . test ( filePath ) ) {
36+ filePath = filePath . slice ( 1 ) ;
37+ }
38+ return path . resolve ( filePath ) ;
39+ } catch {
40+ // Fallback for non-standard URIs
41+ return uri . replace ( / ^ f i l e : \/ \/ \/ ? / , '' ) ;
42+ }
43+ }
44+
2545/**
2646 * MCP Server Info
2747 */
@@ -44,13 +64,14 @@ const PROTOCOL_VERSION = '2024-11-05';
4464export class MCPServer {
4565 private transport : StdioTransport ;
4666 private cg : CodeGraph | null = null ;
47- private toolHandler : ToolHandler | null = null ;
67+ private toolHandler : ToolHandler ;
4868 private projectPath : string | null ;
49- private initError : string | null = null ;
5069
5170 constructor ( projectPath ?: string ) {
5271 this . projectPath = projectPath || null ;
5372 this . transport = new StdioTransport ( ) ;
73+ // Create ToolHandler eagerly — cross-project queries work even without a default project
74+ this . toolHandler = new ToolHandler ( null ) ;
5475 }
5576
5677 /**
@@ -70,30 +91,53 @@ export class MCPServer {
7091 }
7192
7293 /**
73- * Initialize CodeGraph for the project
94+ * Try to initialize CodeGraph for the default project.
7495 *
7596 * Walks up parent directories to find the nearest .codegraph/ folder,
7697 * similar to how git finds .git/ directories.
98+ *
99+ * If initialization fails, the error is recorded but the server continues
100+ * to work — cross-project queries and retries on subsequent tool calls
101+ * are still possible.
77102 */
78- private async initializeCodeGraph ( projectPath : string ) : Promise < void > {
103+ private async tryInitializeDefault ( projectPath : string ) : Promise < void > {
79104 // Walk up parent directories to find nearest .codegraph/
80105 const resolvedRoot = findNearestCodeGraphRoot ( projectPath ) ;
81106
82107 if ( ! resolvedRoot ) {
83108 this . projectPath = projectPath ;
84- this . initError = `CodeGraph not initialized in ${ projectPath } . Run 'codegraph init' first.` ;
85109 return ;
86110 }
87111
88112 this . projectPath = resolvedRoot ;
89113
90114 try {
91115 this . cg = await CodeGraph . open ( resolvedRoot ) ;
92- this . toolHandler = new ToolHandler ( this . cg ) ;
93- this . initError = null ;
116+ this . toolHandler . setDefaultCodeGraph ( this . cg ) ;
94117 } catch ( err ) {
95118 captureException ( err ) ;
96- this . initError = `Failed to open CodeGraph: ${ err instanceof Error ? err . message : String ( err ) } ` ;
119+ }
120+ }
121+
122+ /**
123+ * Retry initialization of the default project if it previously failed.
124+ * Called lazily on tool calls that need the default project.
125+ */
126+ private retryInitIfNeeded ( ) : void {
127+ // Already initialized successfully
128+ if ( this . toolHandler . hasDefaultCodeGraph ( ) ) return ;
129+ // No project path to retry with
130+ if ( ! this . projectPath ) return ;
131+
132+ const resolvedRoot = findNearestCodeGraphRoot ( this . projectPath ) ;
133+ if ( ! resolvedRoot ) return ;
134+
135+ try {
136+ this . cg = CodeGraph . openSync ( resolvedRoot ) ;
137+ this . projectPath = resolvedRoot ;
138+ this . toolHandler . setDefaultCodeGraph ( this . cg ) ;
139+ } catch {
140+ // Still failing — will retry on next tool call
97141 }
98142 }
99143
@@ -102,9 +146,7 @@ export class MCPServer {
102146 */
103147 stop ( ) : void {
104148 // Close all cached cross-project connections first
105- if ( this . toolHandler ) {
106- this . toolHandler . closeAll ( ) ;
107- }
149+ this . toolHandler . closeAll ( ) ;
108150 // Close the main CodeGraph instance
109151 if ( this . cg ) {
110152 this . cg . close ( ) ;
@@ -175,19 +217,18 @@ export class MCPServer {
175217 let projectPath = this . projectPath ;
176218
177219 if ( params ?. rootUri ) {
178- // Convert file:// URI to path
179- projectPath = params . rootUri . replace ( / ^ f i l e : \/ \/ / , '' ) ;
220+ projectPath = fileUriToPath ( params . rootUri ) ;
180221 } else if ( params ?. workspaceFolders ?. [ 0 ] ?. uri ) {
181- projectPath = params . workspaceFolders [ 0 ] . uri . replace ( / ^ f i l e : \/ \/ / , '' ) ;
222+ projectPath = fileUriToPath ( params . workspaceFolders [ 0 ] . uri ) ;
182223 }
183224
184225 // Fall back to current working directory if no path provided
185226 if ( ! projectPath ) {
186227 projectPath = process . cwd ( ) ;
187228 }
188229
189- // Initialize CodeGraph if we have a project path
190- await this . initializeCodeGraph ( projectPath ) ;
230+ // Try to initialize the default project (non-fatal if it fails)
231+ await this . tryInitializeDefault ( projectPath ) ;
191232
192233 // We accept the client's protocol version but respond with our supported version
193234 this . transport . sendResult ( request . id , {
@@ -240,19 +281,9 @@ export class MCPServer {
240281 return ;
241282 }
242283
243- // Execute the tool
244- if ( ! this . toolHandler ) {
245- const errorMsg = this . initError ||
246- ( this . projectPath
247- ? `CodeGraph not initialized in ${ this . projectPath } . Run 'codegraph init' first.`
248- : 'No project path provided. Ensure Claude Code is running in a project directory.' ) ;
249- this . transport . sendError (
250- request . id ,
251- ErrorCodes . InternalError ,
252- errorMsg
253- ) ;
254- return ;
255- }
284+ // If the default project isn't initialized yet, retry in case it was
285+ // initialized after the MCP server started (e.g. user ran codegraph init)
286+ this . retryInitIfNeeded ( ) ;
256287
257288 const result = await this . toolHandler . execute ( toolName , toolArgs ) ;
258289
0 commit comments