From 1d15f5f4d9e88a014ffc6c3b43bd2c7a5d69a93e Mon Sep 17 00:00:00 2001 From: iliya Date: Fri, 20 Mar 2026 13:21:51 +0200 Subject: [PATCH] fix: MCP server entrypoint not found in packaged builds In packaged Electron apps process.cwd() does not point to the app directory so the mcp-server bundle was never found. Additionally the mcp-server dist was not included in the package at all. Changes: - Bundle mcp-server into a single self-contained ESM file (tsup noExternal + createRequire banner for CJS compat) - Ship mcp-server/dist/index.js and package.json via extraResources - Resolve entry via process.resourcesPath when app.isPackaged is true - Build controller + mcp-server in prebuild so dist exists before electron-builder runs - Add mcp-server/dist/index.js to CI verify steps on all platforms - Improve error message to list checked paths for easier debugging --- .github/workflows/release.yml | 3 ++ mcp-server/tsup.config.ts | 20 +++++++++ package.json | 10 ++++- .../services/team/TeamMcpConfigBuilder.ts | 43 +++++++++++++++++-- 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ce1c38cd..0b3f19c2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -113,6 +113,7 @@ jobs: test -f dist-electron/main/index.cjs test -f dist-electron/preload/index.js test -f out/renderer/index.html + test -f mcp-server/dist/index.js - name: Package (macOS ${{ matrix.arch }}) env: @@ -180,6 +181,7 @@ jobs: test -f dist-electron/main/index.cjs test -f dist-electron/preload/index.js test -f out/renderer/index.html + test -f mcp-server/dist/index.js - name: Package (Windows) env: @@ -246,6 +248,7 @@ jobs: test -f dist-electron/main/index.cjs test -f dist-electron/preload/index.js test -f out/renderer/index.html + test -f mcp-server/dist/index.js - name: Package (Linux) env: diff --git a/mcp-server/tsup.config.ts b/mcp-server/tsup.config.ts index c1cf301d..86520de5 100644 --- a/mcp-server/tsup.config.ts +++ b/mcp-server/tsup.config.ts @@ -4,8 +4,28 @@ export default defineConfig({ entry: ['src/index.ts'], format: ['esm'], target: 'node20', + platform: 'node', outDir: 'dist', clean: true, sourcemap: true, dts: false, + // Bundle all dependencies into a single self-contained file so the packaged + // Electron app can run the MCP server without a node_modules tree. + noExternal: [/.*/], + splitting: false, + // Provide a real `require` function for CJS dependencies (e.g. undici) + // that use require() for Node built-in modules. + banner: { + js: `import { createRequire as __bundled_createRequire } from 'module';\nconst require = __bundled_createRequire(import.meta.url);`, + }, + esbuildOptions(options) { + // Optional peer deps of xsschema (pulled in by fastmcp) — we only use zod. + // Mark as external at the esbuild level to avoid resolution errors. + options.external = [ + ...(options.external ?? []), + 'sury', + '@valibot/to-json-schema', + 'effect', + ]; + }, }); diff --git a/package.json b/package.json index 8470c20e..1519519b 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "scripts": { "dev": "electron-vite dev", "dev:kill": "node bin/kill-dev.js", - "prebuild": "tsx scripts/fetch-pricing-data.ts", + "prebuild": "tsx scripts/fetch-pricing-data.ts && pnpm --filter agent-teams-controller build && pnpm --filter agent-teams-mcp build", "build": "electron-vite build", "dist": "electron-builder --mac --win --linux", "dist:mac": "electron-builder --mac", @@ -223,6 +223,14 @@ { "from": "resources/pricing.json", "to": "pricing.json" + }, + { + "from": "mcp-server/dist/index.js", + "to": "mcp-server/index.js" + }, + { + "from": "mcp-server/package.json", + "to": "mcp-server/package.json" } ], "npmRebuild": false, diff --git a/src/main/services/team/TeamMcpConfigBuilder.ts b/src/main/services/team/TeamMcpConfigBuilder.ts index 39dacf77..85947e31 100644 --- a/src/main/services/team/TeamMcpConfigBuilder.ts +++ b/src/main/services/team/TeamMcpConfigBuilder.ts @@ -23,6 +23,24 @@ function isRecord(value: unknown): value is Record { return !!value && typeof value === 'object' && !Array.isArray(value); } +function isPackagedApp(): boolean { + try { + const { app } = require('electron') as typeof import('electron'); + return app.isPackaged; + } catch { + return false; + } +} + +/** + * In a packaged Electron build the mcp-server bundle lives under + * `process.resourcesPath/mcp-server/index.js` (copied via extraResources). + * In dev mode we resolve relative to the workspace root (process.cwd()). + */ +function getPackagedServerEntry(): string { + return path.join(process.resourcesPath, 'mcp-server', 'index.js'); +} + function getWorkspaceRoot(): string { return process.cwd(); } @@ -82,17 +100,34 @@ async function resolveNodePath(): Promise { } async function resolveMcpLaunchSpec(): Promise { + const checked: string[] = []; + + // 1. Packaged Electron app — use extraResources bundle + if (isPackagedApp()) { + const packagedEntry = getPackagedServerEntry(); + checked.push(packagedEntry); + if (await pathExists(packagedEntry)) { + return { + command: await resolveNodePath(), + args: [packagedEntry], + }; + } + logger.warn(`Packaged MCP entry not found at ${packagedEntry}, falling back to workspace`); + } + + // 2. Dev mode — prefer source for hot changes const sourceEntry = getSourceServerEntry(); + checked.push(sourceEntry); if (await pathExists(sourceEntry)) { - // Prefer source in workspace/dev runs so newly added MCP tools are available - // immediately and we do not accidentally serve a stale built dist bundle. return { command: 'pnpm', args: ['--dir', getMcpServerDir(), 'exec', 'tsx', sourceEntry], }; } + // 3. Dev mode — built dist const builtEntry = getBuiltServerEntry(); + checked.push(builtEntry); if (await pathExists(builtEntry)) { return { command: await resolveNodePath(), @@ -100,7 +135,9 @@ async function resolveMcpLaunchSpec(): Promise { }; } - throw new Error('agent-teams-mcp entrypoint not found in mcp-server package'); + throw new Error( + `agent-teams-mcp entrypoint not found. Checked paths:\n${checked.map((p) => ` - ${p}`).join('\n')}` + ); } export class TeamMcpConfigBuilder {