diff --git a/gitnexus-desktop/src/main/main.ts b/gitnexus-desktop/src/main/main.ts index f8a198a75..6d4d8714f 100644 --- a/gitnexus-desktop/src/main/main.ts +++ b/gitnexus-desktop/src/main/main.ts @@ -270,7 +270,7 @@ const spawnGitNexusServer = (): ChildProcess => { [getGitNexusCliEntry(), 'serve', '--host', GITNEXUS_HOST], { cwd: getGitNexusRuntimeDir(), - env: getNodeProcessEnvironment(), + env: getNodeProcessEnvironment(app.isPackaged ? { GITNEXUS_DISABLE_MCP_HTTP: '1' } : {}), stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true, }, diff --git a/gitnexus/src/server/api.ts b/gitnexus/src/server/api.ts index d9aa00ae6..85dfaaec3 100644 --- a/gitnexus/src/server/api.ts +++ b/gitnexus/src/server/api.ts @@ -29,7 +29,6 @@ import { hybridSearch } from '../core/search/hybrid-search.js'; // Embedding imports are lazy (dynamic import) to avoid loading onnxruntime-node // at server startup — crashes on unsupported Node ABI versions (#89) import { LocalBackend } from '../mcp/local/local-backend.js'; -import { mountMCPEndpoints } from './mcp-http.js'; import { fork } from 'child_process'; import { fileURLToPath, pathToFileURL } from 'url'; import { JobManager } from './analyze-job.js'; @@ -38,6 +37,10 @@ import { extractRepoName, getCloneDir, cloneOrPull } from './git-clone.js'; const _require = createRequire(import.meta.url); const pkg = _require('../../package.json'); +export const shouldEnableMcpHttp = (): boolean => { + return process.env.GITNEXUS_DISABLE_MCP_HTTP !== '1'; +}; + /** * Determine whether an HTTP Origin header value is allowed by CORS policy. * @@ -560,7 +563,15 @@ export const createServer = async (port: number, host: string = '127.0.0.1') => // Initialize MCP backend (multi-repo, shared across all MCP sessions) const backend = new LocalBackend(); await backend.init(); - const cleanupMcp = mountMCPEndpoints(app, backend); + let cleanupMcp: () => Promise = async () => {}; + + // Desktop packaged runtime disables MCP HTTP because the SDK import tree + // currently trips Electron's embedded Node resolver during server startup. + if (shouldEnableMcpHttp()) { + const { mountMCPEndpoints } = await import('./mcp-http.js'); + cleanupMcp = mountMCPEndpoints(app, backend); + } + const jobManager = new JobManager(); // Shared repo lock — prevents concurrent analyze + embed on the same repo path, diff --git a/gitnexus/test/unit/web-ui-serving.test.ts b/gitnexus/test/unit/web-ui-serving.test.ts index d3f085422..40570b488 100644 --- a/gitnexus/test/unit/web-ui-serving.test.ts +++ b/gitnexus/test/unit/web-ui-serving.test.ts @@ -1,7 +1,7 @@ import path from 'node:path'; import http from 'node:http'; import express from 'express'; -import { describe, expect, it, vi, beforeEach } from 'vitest'; +import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; const { accessMock } = vi.hoisted(() => ({ accessMock: vi.fn(), @@ -17,6 +17,7 @@ import { resolveWebDistDir, landingPageHtml, SPA_FALLBACK_REGEX, + shouldEnableMcpHttp, staticCacheControlSetHeaders, } from '../../src/server/api.js'; @@ -94,6 +95,22 @@ describe('landingPageHtml', () => { }); }); +describe('shouldEnableMcpHttp', () => { + afterEach(() => { + vi.unstubAllEnvs(); + }); + + it('enables MCP HTTP by default', () => { + expect(shouldEnableMcpHttp()).toBe(true); + }); + + it('disables MCP HTTP when requested by desktop runtime', () => { + vi.stubEnv('GITNEXUS_DISABLE_MCP_HTTP', '1'); + + expect(shouldEnableMcpHttp()).toBe(false); + }); +}); + describe('SPA fallback regex', () => { it('allows root path', () => { expect(SPA_FALLBACK_REGEX.test('/')).toBe(true);