Skip to content
This repository was archived by the owner on Dec 12, 2025. It is now read-only.

Commit 5b327a9

Browse files
authored
Fix Deno runtime support in modelfetch CLI (#96)
1 parent 43947a8 commit 5b327a9

File tree

2 files changed

+72
-36
lines changed

2 files changed

+72
-36
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
__default__: patch
3+
---
4+
5+
Fix Deno runtime support in modelfetch serve command by using native Deno watch-hmr instead of tsx

libs/modelfetch/src/index.ts

Lines changed: 67 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import { watch } from "chokidar";
77
import { Command } from "commander";
88
import { get as getRuntime } from "js-runtime";
99
import { spawn } from "node:child_process";
10-
import { access, readdir } from "node:fs/promises";
10+
import { randomUUID } from "node:crypto";
11+
import { unlinkSync } from "node:fs";
12+
import { access, readdir, writeFile } from "node:fs/promises";
13+
import { tmpdir } from "node:os";
1114
import path from "node:path";
1215
import { fileURLToPath, pathToFileURL } from "node:url";
1316
import { tsImport } from "tsx/esm/api";
@@ -117,44 +120,72 @@ program
117120
defaults: { server: await detectDefaultServer() },
118121
});
119122
if (!config.server) throw new Error("config.server is required");
120-
const transport = new Transport();
121-
const watcher = watch(config.server, { cwd: process.cwd() });
122-
let server: McpServer | undefined;
123-
for (const killSignal of killSignals) {
124-
process.once(killSignal, () => {
125-
transport.__modelfetch_closed__ = true;
126-
void watcher.close();
127-
if (server) void server.close();
128-
else void transport.close();
123+
const runtime = getRuntime();
124+
if (runtime === "deno") {
125+
const serverPath = path.resolve(process.cwd(), config.server);
126+
const mainPath = path.join(tmpdir(), `modelfetch-${randomUUID()}.ts`);
127+
const mainContent = `
128+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
129+
import server from "${pathToFileURL(serverPath).toString()}";
130+
131+
const transport = new StdioServerTransport();
132+
await server.connect(transport);
133+
`;
134+
await writeFile(mainPath, mainContent, "utf8");
135+
process.once("exit", () => {
136+
unlinkSync(mainPath);
129137
});
130-
}
131-
const reload = async () => {
132-
watcher.unwatch("*");
133-
if (server) {
134-
const oldServer = server;
135-
server = undefined;
136-
await oldServer.close();
138+
const deno = spawn("deno", ["run", "-A", "--watch-hmr", mainPath], {
139+
stdio: "inherit",
140+
});
141+
deno.once("exit", (code) => {
142+
process.exit(code);
143+
});
144+
for (const killSignal of killSignals) {
145+
process.once(killSignal, () => {
146+
deno.kill(killSignal);
147+
});
137148
}
138-
const { default: newServer } = (await tsImport(config.server, {
139-
parentURL: pathToFileURL(
140-
path.resolve(process.cwd(), `index${path.extname(config.server)}`),
141-
).toString(),
142-
onImport: (url) => {
143-
watcher.add(fileURLToPath(url));
144-
},
145-
})) as { default?: unknown };
146-
if (!isMcpServer(newServer)) {
147-
throw new Error(
148-
`${config.server} must export a default McpServer instance`,
149-
);
149+
} else {
150+
const transport = new Transport();
151+
const watcher = watch(config.server, { cwd: process.cwd() });
152+
let server: McpServer | undefined;
153+
for (const killSignal of killSignals) {
154+
process.once(killSignal, () => {
155+
transport.__modelfetch_closed__ = true;
156+
void watcher.close();
157+
if (server) void server.close();
158+
else void transport.close();
159+
});
150160
}
151-
server = newServer;
152-
await newServer.connect(transport);
153-
};
154-
watcher.on("change", () => {
155-
void reload();
156-
});
157-
await reload();
161+
const reload = async () => {
162+
watcher.unwatch("*");
163+
if (server) {
164+
const oldServer = server;
165+
server = undefined;
166+
await oldServer.close();
167+
}
168+
const { default: newServer } = (await tsImport(config.server, {
169+
parentURL: pathToFileURL(
170+
path.resolve(process.cwd(), `index${path.extname(config.server)}`),
171+
).toString(),
172+
onImport: (url) => {
173+
watcher.add(fileURLToPath(url));
174+
},
175+
})) as { default?: unknown };
176+
if (!isMcpServer(newServer)) {
177+
throw new Error(
178+
`${config.server} must export a default McpServer instance`,
179+
);
180+
}
181+
server = newServer;
182+
await newServer.connect(transport);
183+
};
184+
watcher.on("change", () => {
185+
void reload();
186+
});
187+
await reload();
188+
}
158189
});
159190

160191
program

0 commit comments

Comments
 (0)