refactor(server): add shutdown idempotency guard and test teardown blocks
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
|
||||
# MCP Stdio Server Assembly & Integration Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
@@ -12,7 +12,10 @@ async function main(): Promise<void> {
|
||||
const mcpServer = createServer(config);
|
||||
|
||||
// Bind graceful termination
|
||||
let isShuttingDown = false;
|
||||
const shutdown = async () => {
|
||||
if (isShuttingDown) return;
|
||||
isShuttingDown = true;
|
||||
console.error("\n[openproject-mcp] Received termination signal, shutting down...");
|
||||
await mcpServer.stop();
|
||||
process.exit(0);
|
||||
|
||||
+41
-37
@@ -199,32 +199,34 @@ describe("End-to-End Live Tool Calling over MCP Client", () => {
|
||||
const client = new Client({ name: "test-runner", version: "1" });
|
||||
await client.connect(clientTransport);
|
||||
|
||||
// Call openproject_list_projects
|
||||
const projRes = (await client.callTool({
|
||||
name: "openproject_list_projects",
|
||||
arguments: { pageSize: 5 },
|
||||
})) as {
|
||||
content: Array<{ type: string; text: string }>;
|
||||
isError?: boolean;
|
||||
};
|
||||
expect(projRes.isError).toBeFalsy();
|
||||
const projData = JSON.parse(projRes.content[0]?.text ?? "{}");
|
||||
expect(projData.projects.length).toBeGreaterThan(0);
|
||||
try {
|
||||
// Call openproject_list_projects
|
||||
const projRes = (await client.callTool({
|
||||
name: "openproject_list_projects",
|
||||
arguments: { pageSize: 5 },
|
||||
})) as {
|
||||
content: Array<{ type: string; text: string }>;
|
||||
isError?: boolean;
|
||||
};
|
||||
expect(projRes.isError).toBeFalsy();
|
||||
const projData = JSON.parse(projRes.content[0]?.text ?? "{}");
|
||||
expect(projData.projects.length).toBeGreaterThan(0);
|
||||
|
||||
// Call openproject_get_work_package
|
||||
const wpRes = (await client.callTool({
|
||||
name: "openproject_get_work_package",
|
||||
arguments: { workPackageId: 38 },
|
||||
})) as {
|
||||
content: Array<{ type: string; text: string }>;
|
||||
isError?: boolean;
|
||||
};
|
||||
expect(wpRes.isError).toBeFalsy();
|
||||
const wpData = JSON.parse(wpRes.content[0]?.text ?? "{}");
|
||||
expect(wpData.id).toBe(38);
|
||||
|
||||
await client.close();
|
||||
await mcpServer.stop();
|
||||
// Call openproject_get_work_package
|
||||
const wpRes = (await client.callTool({
|
||||
name: "openproject_get_work_package",
|
||||
arguments: { workPackageId: 38 },
|
||||
})) as {
|
||||
content: Array<{ type: string; text: string }>;
|
||||
isError?: boolean;
|
||||
};
|
||||
expect(wpRes.isError).toBeFalsy();
|
||||
const wpData = JSON.parse(wpRes.content[0]?.text ?? "{}");
|
||||
expect(wpData.id).toBe(38);
|
||||
} finally {
|
||||
await client.close();
|
||||
await mcpServer.stop();
|
||||
}
|
||||
});
|
||||
|
||||
test("tool error propagation returns isError: true without crashing client", async () => {
|
||||
@@ -235,18 +237,20 @@ describe("End-to-End Live Tool Calling over MCP Client", () => {
|
||||
const client = new Client({ name: "test-runner", version: "1" });
|
||||
await client.connect(clientTransport);
|
||||
|
||||
const errRes = (await client.callTool({
|
||||
name: "openproject_get_project",
|
||||
arguments: { projectId: 999999 },
|
||||
})) as {
|
||||
content: Array<{ type: string; text: string }>;
|
||||
isError?: boolean;
|
||||
};
|
||||
expect(errRes.isError).toBe(true);
|
||||
expect(errRes.content[0]?.text).toContain("OPENPROJECT_NOT_FOUND");
|
||||
|
||||
await client.close();
|
||||
await mcpServer.stop();
|
||||
try {
|
||||
const errRes = (await client.callTool({
|
||||
name: "openproject_get_project",
|
||||
arguments: { projectId: 999999 },
|
||||
})) as {
|
||||
content: Array<{ type: string; text: string }>;
|
||||
isError?: boolean;
|
||||
};
|
||||
expect(errRes.isError).toBe(true);
|
||||
expect(errRes.content[0]?.text).toContain("OPENPROJECT_NOT_FOUND");
|
||||
} finally {
|
||||
await client.close();
|
||||
await mcpServer.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user