Merge pull request #729 from kaoru0822-kitauji/fix/mcp-shutdown-diagnostics
fix(mcp): retain shutdown failure diagnostics
This commit is contained in:
commit
09c249deb8
|
|
@ -29,7 +29,11 @@ export const createShutdown = (input: {
|
|||
return shutdownPromise;
|
||||
};
|
||||
const requestShutdown = (): void => {
|
||||
shutdown().catch(() => {
|
||||
shutdown().catch((cause: unknown) => {
|
||||
serverLogger.debug(
|
||||
{ failure_cause: describeShutdownFailure(cause) },
|
||||
"MCP shutdown rejected",
|
||||
);
|
||||
dependencies.setExitCode(1);
|
||||
serverLogger.error(MCP_SHUTDOWN_FAILED);
|
||||
dependencies.writeStderr(`${MCP_SHUTDOWN_FAILED}\n`);
|
||||
|
|
@ -38,3 +42,27 @@ export const createShutdown = (input: {
|
|||
unregisterShutdown = dependencies.registerShutdown(requestShutdown);
|
||||
return { shutdown, request: requestShutdown };
|
||||
};
|
||||
|
||||
const describeShutdownFailure = (
|
||||
cause: unknown,
|
||||
): Readonly<Record<string, string | number | boolean | null>> => {
|
||||
if (cause instanceof Error) {
|
||||
const code = "code" in cause ? cause.code : undefined;
|
||||
return {
|
||||
name: cause.name,
|
||||
message: cause.message,
|
||||
...(typeof code === "string" ||
|
||||
(typeof code === "number" && Number.isFinite(code))
|
||||
? { code }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
if (
|
||||
cause === null ||
|
||||
typeof cause === "string" ||
|
||||
typeof cause === "boolean" ||
|
||||
(typeof cause === "number" && Number.isFinite(cause))
|
||||
)
|
||||
return { type: cause === null ? "null" : typeof cause, value: cause };
|
||||
return { type: typeof cause };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
import type { StdioServerHandle } from "@modelcontextprotocol/server/stdio";
|
||||
import pino from "pino";
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import { createManagedBinarySession } from "../../../src/application/runtime.js";
|
||||
import { MCP_SHUTDOWN_FAILED } from "../../../src/main/messages.js";
|
||||
import { createShutdown } from "../../../src/main/shutdown.js";
|
||||
import type { RuntimeDependencies } from "../../../src/main/types.js";
|
||||
|
||||
it("keeps shutdown output stable while logging the rejected cause at debug level", async () => {
|
||||
const logs: string[] = [];
|
||||
const output: string[] = [];
|
||||
const exitCodes: number[] = [];
|
||||
const requests: Array<() => void> = [];
|
||||
const failure = Object.assign(
|
||||
new TypeError("fixture transport close failed"),
|
||||
{
|
||||
code: "ECONNRESET",
|
||||
},
|
||||
);
|
||||
const handle: StdioServerHandle = {
|
||||
close: () => Promise.reject(failure),
|
||||
};
|
||||
const dependencies: RuntimeDependencies = {
|
||||
env: {},
|
||||
serve: () => {
|
||||
throw new Error("unused fixture transport");
|
||||
},
|
||||
writeStderr: (text) => output.push(text),
|
||||
setExitCode: (code) => exitCodes.push(code),
|
||||
registerShutdown: (request) => {
|
||||
requests.push(request);
|
||||
return () => undefined;
|
||||
},
|
||||
};
|
||||
const logger = pino({ level: "debug" }, { write: (line) => logs.push(line) });
|
||||
createShutdown({
|
||||
handle,
|
||||
session: createManagedBinarySession(),
|
||||
unregisterReload: () => undefined,
|
||||
dependencies,
|
||||
serverLogger: logger,
|
||||
});
|
||||
|
||||
requests[0]?.();
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
|
||||
expect(exitCodes).toEqual([1]);
|
||||
expect(output).toEqual([`${MCP_SHUTDOWN_FAILED}\n`]);
|
||||
expect(logs.map((line) => JSON.parse(line))).toContainEqual(
|
||||
expect.objectContaining({
|
||||
level: 20,
|
||||
msg: "MCP shutdown rejected",
|
||||
failure_cause: {
|
||||
name: "TypeError",
|
||||
message: "fixture transport close failed",
|
||||
code: "ECONNRESET",
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
Loading…
Reference in New Issue