Files
zeekay da73310642 dxt ships a server that runs on its own
server.js was mcp's dist/cli.js, which leaves the MCP SDK, commander,
glob and zod to node_modules; the .dxt and the npm package ship none,
and the CLI also read its version from a ../package.json that sits
beside neither. dxt's build now bundles mcp's src/cli.ts with every
dependency inlined, as CommonJS because a .dxt has no package.json to
declare ESM, and the version is imported so the bundler inlines it. The
native optionals stay external: @lancedb/lancedb and
@xenova/transformers back the legacy vector tools and jsautogui backs
--enable-autogui, none of them on the default surface, and each tool
answers an error naming the missing package.

getSystemPrompt imported tools/index.js dynamically though cli.ts
already imports it. That made esbuild wrap the module graph, zod
included, in lazy init, and the SDK read zod before it ran: the bundle
died at load with "Class2 is not a constructor". It is a static import.

The build writes dist/: server.js, the manifest stamped with
package.json's version, the icon, and hanzo-ai.dxt zipped with yazl,
since the runners have no zip. The manifest failed the schema Claude
Desktop installs by (mcpb validate, and the older dxt validate):
`compatibility.runtime` is `runtimes`, and `user_config` has no `env`
key. The server reads none of those variables anyway, and nothing
implements anonymous mode, allowed paths or a write switch, so those
settings go; disabledTools reaches the --disable-tools flag the server
has. The tool list names `ui`, which it serves, not `computer`, which
it does not.

Claude-Session: https://claude.ai/code/session_01Dc8DXc3fEQCRHUa6XWK4Ku
2026-09-29 12:50:30 -07:00

56 lines
2.0 KiB
JavaScript

// Builds the Claude Desktop extension into dist/: server.js, mcp's CLI with
// every dependency inlined (the extension ships no node_modules); the
// manifest, stamped with this package's version; the icon; and hanzo-ai.dxt,
// the zip of those three that Claude Desktop installs.
const esbuild = require('esbuild');
const fs = require('fs');
const path = require('path');
const yazl = require('yazl');
const pkg = require('../package.json');
const root = path.join(__dirname, '..');
const out = (...p) => path.join(root, 'dist', ...p);
// Left as runtime import()s, never bundled: native packages that only tools
// off the default surface load, each of which answers an error naming the
// package when it is absent. @lancedb/lancedb and @xenova/transformers back
// the legacy vector tools; jsautogui backs --enable-autogui.
const optional = ['@lancedb/lancedb', '@xenova/transformers', 'jsautogui'];
async function build() {
fs.rmSync(out(), { recursive: true, force: true });
// CommonJS, because a .dxt has no package.json to declare a module type.
await esbuild.build({
entryPoints: [path.join(root, '..', 'mcp', 'src', 'cli.ts')],
outfile: out('server.js'),
bundle: true,
platform: 'node',
format: 'cjs',
target: 'node18',
minify: true,
external: optional,
logLevel: 'warning',
});
const manifest = JSON.parse(fs.readFileSync(path.join(root, 'manifest.json'), 'utf8'));
manifest.version = pkg.version;
fs.writeFileSync(out('manifest.json'), JSON.stringify(manifest, null, 2) + '\n');
fs.copyFileSync(path.join(root, 'icon.png'), out('icon.png'));
const zip = new yazl.ZipFile();
for (const name of ['manifest.json', 'server.js', 'icon.png']) zip.addFile(out(name), name);
zip.end();
await new Promise((resolve, reject) => {
zip.outputStream
.pipe(fs.createWriteStream(out(`${manifest.name}.dxt`)))
.on('close', resolve)
.on('error', reject);
});
}
build().catch((err) => {
console.error(err);
process.exit(1);
});