From 89230f248017e472c115540dae5f7db5de1fc174 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 26 Apr 2026 23:58:53 -0700 Subject: [PATCH] fix(gateway): defer mcp loopback imports --- CHANGELOG.md | 1 + src/gateway/server.impl.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a7bfad51dc..5e4a339334f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ Docs: https://docs.openclaw.ai - Gateway/startup: move chat run/subscriber registries onto a lightweight state module and defer chat/session event projection until the first event so Gateway boot skips session IO imports. Thanks @vincentkoc. - Gateway/startup: keep node session runtime on a lightweight JSON parser instead of importing gateway method validation helpers during boot. Thanks @vincentkoc. - Gateway/startup: read embedded-run activity from a lightweight shared state module so restart deferral no longer imports the embedded runner during Gateway boot. Thanks @vincentkoc. +- Gateway/startup: defer MCP loopback server imports until Gateway shutdown so normal boot no longer loads the loopback HTTP/tool schema stack just to register close handlers. Thanks @vincentkoc. - CLI/Gateway: use a parse-only config snapshot for plain `gateway status` reads and reuse same-path service config context so status no longer spends tens of seconds in full config validation before printing. Thanks @vincentkoc. - Lobster/Gateway: memoize repeated Ajv schema compilation before loading the embedded Lobster runtime so scheduled workflows and `llm.invoke` loops stop growing gateway heap on content-identical schemas. Fixes #71148. Thanks @cmi525, @vsolaz, and @vincentkoc. - Codex harness: normalize cached input tokens before session/context accounting so prompt cache reads are not double-counted in `/status`, `session_status`, or persisted `sessionEntry.totalTokens`. Fixes #69298. Thanks @richardmqq. diff --git a/src/gateway/server.impl.ts b/src/gateway/server.impl.ts index a4190d53460..ed2e4b993a7 100644 --- a/src/gateway/server.impl.ts +++ b/src/gateway/server.impl.ts @@ -46,7 +46,6 @@ import { } from "../tasks/task-registry.maintenance.js"; import { createAuthRateLimiter, type AuthRateLimiter } from "./auth-rate-limit.js"; import { resolveGatewayAuth } from "./auth.js"; -import { closeMcpLoopbackServer } from "./mcp-http.js"; import { createGatewayAuxHandlers } from "./server-aux-handlers.js"; import { createChannelManager } from "./server-channels.js"; import { createGatewayCloseHandler, runGatewayClosePrelude } from "./server-close.js"; @@ -125,6 +124,11 @@ function getChannelRuntime() { return cachedChannelRuntime; } +async function closeMcpLoopbackServerOnDemand(): Promise { + const { closeMcpLoopbackServer } = await import("./mcp-http.js"); + await closeMcpLoopbackServer(); +} + const logHealth = log.child("health"); const logCron = log.child("cron"); const logReload = log.child("reload"); @@ -593,7 +597,7 @@ export async function startGatewayServer( stopModelPricingRefresh: runtimeState.stopModelPricingRefresh, stopChannelHealthMonitor: () => runtimeState?.channelHealthMonitor?.stop(), clearSecretsRuntimeSnapshot, - closeMcpServer: async () => await closeMcpLoopbackServer(), + closeMcpServer: closeMcpLoopbackServerOnDemand, }); const { getRuntimeSnapshot, startChannels, startChannel, stopChannel, markChannelLoggedOut } = channelManager;