Skip to content

[Bug] Watch mode breaks after code reload when app.mcpTool(...) is registered (No script host available) #781

@maaaNu

Description

@maaaNu

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

  1. Create a new Functions app (TypeScript).
  2. Install deps and run in watch mode (commands below).
  3. Start with a single HTTP endpoint → reloads fine.
  4. Add an MCP tool registration (see code below).
  5. 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

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions