-
Notifications
You must be signed in to change notification settings - Fork 44
Open
Description
Title: Watch mode breaks after code reload when app.mcpTool(...) is registered (No script host available)
Description
When using the Node.js worker in watch mode, a simple HTTP trigger reloads fine on file changes. As soon as an MCP tool is registered via app.mcpTool(...), the first start works, but after the first file change triggers a reload, the host becomes unhealthy and the function app stops responding with:
[Tag='azure.functions.script_host.lifecycle']: {status: Unhealthy, description: No script host available}
Cold-restarting func start makes it work again until the next watch reload.
Steps
- Create a new Functions app (TypeScript).
- Install deps and run in watch mode (commands below).
- Start with a single HTTP endpoint → reloads fine.
- Add an MCP tool registration (see code below).
- Save any file to trigger watch reload → host becomes unhealthy and HTTP endpoint stops working.
Expected behavior
- After a file change, the script host should reload and continue serving both the HTTP trigger and the MCP tool without going unhealthy.
Actual behavior
- First run works.
- On first watch-triggered reload, the host reports unhealthy and no longer serves requests until the process is restarted.
Logs
[12:26:44 PM] Found 0 errors. Watching for file changes.
[2025-11-25T11:26:49.013Z] [Tag=''] Process reporting unhealthy: Unhealthy. Health check entries are azure.functions.web_host.lifecycle: {status: Healthy, description: }, azure.functions.script_host.lifecycle: {status: Unhealthy, description: No script host available}
[2025-11-25T11:26:49.014Z] [Tag='azure.functions.readiness'] Process reporting unhealthy: Unhealthy. Health check entries are azure.functions.script_host.lifecycle: {status: Unhealthy, description: No script host available}
Minimal repro (TypeScript)
src/functions/test.ts (HTTP trigger)
import { app, HttpHandler } from "@azure/functions";
const handler: HttpHandler = async (_req, _ctx) => {
return { status: 200, body: JSON.stringify("Hello World!") };
};
app.http("test", {
methods: ["GET"],
authLevel: "anonymous",
route: "test",
handler,
});src/functions/mcp.ts (MCP tool)
import util from "node:util";
import { app, InvocationContext, arg } from "@azure/functions";
export async function mcpToolHello(_toolArguments: unknown, context: InvocationContext): Promise<string> {
console.log(util.inspect(_toolArguments, { depth: null, colors: true }));
if (!context.triggerMetadata) {
throw new Error("triggerMetadata is undefined");
}
const mcptoolargs = context.triggerMetadata.mcptoolargs as { name?: string };
const name = mcptoolargs?.name;
console.info(`Hello ${name}, I am MCP Tool!`);
return `Hello ${name || "World"}, I am MCP Tool!!!`;
}
// Register the hello tool (adding this causes watch reload to fail)
app.mcpTool("hello world", {
toolName: "hello world",
description: "says hello!",
toolProperties: {
name: arg.string().describe("Required property to identify the caller.").optional(),
},
handler: mcpToolHello,
});Commands used
{
"build": "tsc --project tsconfig.json && tsc-alias -p tsconfig.json",
"prestart": "npm run build && func extensions install",
"start:host": "func start --typescript",
"start": "npm-run-all --parallel start:host watch",
"watch": "tsc -w",
"test": "jest"
}Environment
- OS: macOS 15.7.2
- Node.js: 20.18.1
- Azure Functions Core Tools: 4.5.0
@azure/functions: 4.9.0- Language: TypeScript 5.9.3
Notes / Hypothesis
- The issue only manifests after the worker/host tries to reload on file change. Without
app.mcpTool(...), watch reload is stable. - Looks like the MCP tool registration may not be disposed/re-registered cleanly on hot reload, leaving the script host unavailable.
- A full process restart is a reliable workaround for now.
Metadata
Metadata
Assignees
Labels
No labels