Skip to content

Commit c4ce20c

Browse files
committed
fix bugs in stdio MCP client
Bug 1 — CWD mismatch in stdio mode (both platforms): StdioClientTransport inherited the client/ working directory, so relative paths passed to CodeQL CLI (e.g. server/ql/javascript/...) resolved to client/server/ql/... which does not exist. Add cwd: repoRoot to the transport options to match the HTTP-mode behavior where start-server.sh explicitly cd's to the repo root. Bug 2 — False success on Windows in stdio mode: StdioClientTransport.close() could cause an abrupt process exit on Windows before printTestSummary() and process.exit(exitCode) were reached, so the shell script saw exit code 0 and reported success despite test failures. Move summary printing and process.exitCode assignment above the disconnect() call in all four run methods so results are always reported even if disconnect triggers an early exit.
1 parent 87e5248 commit c4ce20c

File tree

2 files changed

+60
-30
lines changed

2 files changed

+60
-30
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"$schema":"https://json.schemastore.org/sarif-2.1.0.json","version":"2.1.0","runs":[{"tool":{"driver":{"name":"CodeQL","organization":"GitHub","semanticVersion":"2.24.1","rules":[{"id":"test/query","name":"test/query","shortDescription":{"text":"ExampleQuery1"},"fullDescription":{"text":"Example query for integration testing of the codeql_test_extract MCP server tool."},"defaultConfiguration":{"enabled":true,"level":"warning"},"help":{"text":"# Query Help for JavaScript ExampleQuery1\n\nTODO\n","markdown":"# Query Help for JavaScript ExampleQuery1\n\nTODO\n"},"properties":{"tags":["mcp-integration-tests"],"description":"Example query for integration testing of the codeql_test_extract MCP server tool.","id":"test/query","kind":"problem","name":"ExampleQuery1","precision":"medium","problem.severity":"warning"}}]},"extensions":[{"name":"mcp-client-integration-tests-static-javascript-src","semanticVersion":"0.0.1+fe0e7d2a7059ebb6c6075ff8eaea04f382747656","locations":[{"uri":"file:///home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/src/","description":{"text":"The QL pack root directory."},"properties":{"tags":["CodeQL/LocalPackRoot"]}},{"uri":"file:///home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/src/codeql-pack.yml","description":{"text":"The QL pack definition file."},"properties":{"tags":["CodeQL/LocalPackDefinitionFile"]}}]},{"name":"codeql/javascript-all","semanticVersion":"2.6.11+ce9c8e6e9fd41ef0967b13849bb6ae2183caf9ad","locations":[{"uri":"file:///home/runner/.codeql/packages/codeql/javascript-all/2.6.11/","description":{"text":"The QL pack root directory."},"properties":{"tags":["CodeQL/LocalPackRoot"]}},{"uri":"file:///home/runner/.codeql/packages/codeql/javascript-all/2.6.11/qlpack.yml","description":{"text":"The QL pack definition file."},"properties":{"tags":["CodeQL/LocalPackDefinitionFile"]}}]},{"name":"codeql/threat-models","semanticVersion":"1.0.31+ce9c8e6e9fd41ef0967b13849bb6ae2183caf9ad","locations":[{"uri":"file:///home/runner/.codeql/packages/codeql/threat-models/1.0.31/","description":{"text":"The QL pack root directory."},"properties":{"tags":["CodeQL/LocalPackRoot"]}},{"uri":"file:///home/runner/.codeql/packages/codeql/threat-models/1.0.31/qlpack.yml","description":{"text":"The QL pack definition file."},"properties":{"tags":["CodeQL/LocalPackDefinitionFile"]}}]}]},"artifacts":[{"location":{"uri":"file:/home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/test/ExampleQuery1/ExampleQuery1.js","index":0}}],"results":[{"ruleId":"test/query","ruleIndex":0,"rule":{"id":"test/query","index":0},"message":{"text":"Example test code file found for codeql_test_extract example query."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"file:/home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/test/ExampleQuery1/ExampleQuery1.js","index":0}}}]}],"columnKind":"utf16CodeUnits","properties":{"semmle.formatSpecifier":"sarif-latest"}}]}
1+
{"$schema":"https://json.schemastore.org/sarif-2.1.0.json","version":"2.1.0","runs":[{"tool":{"driver":{"name":"CodeQL","organization":"GitHub","semanticVersion":"2.24.2","rules":[{"id":"test/query","name":"test/query","shortDescription":{"text":"ExampleQuery1"},"fullDescription":{"text":"Example query for integration testing of the codeql_test_extract MCP server tool."},"defaultConfiguration":{"enabled":true,"level":"warning"},"help":{"text":"# Query Help for JavaScript ExampleQuery1\n\nTODO\n","markdown":"# Query Help for JavaScript ExampleQuery1\n\nTODO\n"},"properties":{"tags":["mcp-integration-tests"],"description":"Example query for integration testing of the codeql_test_extract MCP server tool.","id":"test/query","kind":"problem","name":"ExampleQuery1","precision":"medium","problem.severity":"warning"}}]},"extensions":[{"name":"mcp-client-integration-tests-static-javascript-src","semanticVersion":"0.0.1+fe0e7d2a7059ebb6c6075ff8eaea04f382747656","locations":[{"uri":"file:///home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/src/","description":{"text":"The QL pack root directory."},"properties":{"tags":["CodeQL/LocalPackRoot"]}},{"uri":"file:///home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/src/codeql-pack.yml","description":{"text":"The QL pack definition file."},"properties":{"tags":["CodeQL/LocalPackDefinitionFile"]}}]},{"name":"codeql/javascript-all","semanticVersion":"2.6.11+ce9c8e6e9fd41ef0967b13849bb6ae2183caf9ad","locations":[{"uri":"file:///home/runner/.codeql/packages/codeql/javascript-all/2.6.11/","description":{"text":"The QL pack root directory."},"properties":{"tags":["CodeQL/LocalPackRoot"]}},{"uri":"file:///home/runner/.codeql/packages/codeql/javascript-all/2.6.11/qlpack.yml","description":{"text":"The QL pack definition file."},"properties":{"tags":["CodeQL/LocalPackDefinitionFile"]}}]},{"name":"codeql/threat-models","semanticVersion":"1.0.31+ce9c8e6e9fd41ef0967b13849bb6ae2183caf9ad","locations":[{"uri":"file:///home/runner/.codeql/packages/codeql/threat-models/1.0.31/","description":{"text":"The QL pack root directory."},"properties":{"tags":["CodeQL/LocalPackRoot"]}},{"uri":"file:///home/runner/.codeql/packages/codeql/threat-models/1.0.31/qlpack.yml","description":{"text":"The QL pack definition file."},"properties":{"tags":["CodeQL/LocalPackDefinitionFile"]}}]}]},"artifacts":[{"location":{"uri":"file:///home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/test/ExampleQuery1/ExampleQuery1.js","index":0}}],"results":[{"ruleId":"test/query","ruleIndex":0,"rule":{"id":"test/query","index":0},"message":{"text":"Example test code file found for codeql_test_extract example query."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"file:///home/runner/work/codeql-development-mcp-server/codeql-development-mcp-server/client/integration-tests/static/javascript/test/ExampleQuery1/ExampleQuery1.js","index":0}}}]}],"columnKind":"utf16CodeUnits","properties":{"semmle.formatSpecifier":"sarif-latest"}}]}

client/src/ql-mcp-client.js

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,14 @@ class CodeQLMCPClient {
123123
});
124124

125125
if (this.mcpMode === "stdio") {
126+
const repoRoot = path.join(__dirname, "..", "..");
126127
const serverPath =
127128
process.env.MCP_SERVER_PATH ||
128-
path.join(__dirname, "..", "..", "server", "dist", "codeql-development-mcp-server.js");
129+
path.join(repoRoot, "server", "dist", "codeql-development-mcp-server.js");
129130
this.transport = new StdioClientTransport({
130131
command: "node",
131132
args: [serverPath],
133+
cwd: repoRoot,
132134
env: {
133135
...process.env,
134136
TRANSPORT_MODE: "stdio"
@@ -215,17 +217,24 @@ class CodeQLMCPClient {
215217
}
216218
} catch (error) {
217219
this.logger.log(`Test execution failed: ${error.message}`, "ERROR");
218-
} finally {
219-
if (connected) {
220-
await this.disconnect();
221-
}
222220
}
223221

224-
// Print test summary
222+
// Print test summary and set exit code BEFORE disconnect.
223+
// On Windows, StdioClientTransport.close() can cause the Node.js
224+
// process to exit abruptly, so we must report results first.
225225
this.logger.printTestSummary();
226+
const exitCode = this.logger.isSuccess() ? 0 : 1;
227+
process.exitCode = exitCode;
228+
229+
if (connected) {
230+
try {
231+
await this.disconnect();
232+
} catch {
233+
// Ignore disconnect errors — results are already reported
234+
}
235+
}
226236

227-
// Exit with appropriate code
228-
process.exit(this.logger.isSuccess() ? 0 : 1);
237+
process.exit(exitCode);
229238
}
230239

231240
/**
@@ -246,17 +255,24 @@ class CodeQLMCPClient {
246255
}
247256
} catch (error) {
248257
this.logger.log(`Demo execution failed: ${error.message}`, "ERROR");
249-
} finally {
250-
if (connected) {
251-
await this.disconnect();
252-
}
253258
}
254259

255-
// Print demo summary
260+
// Print summary and set exit code BEFORE disconnect.
261+
// On Windows, StdioClientTransport.close() can cause the Node.js
262+
// process to exit abruptly, so we must report results first.
256263
this.logger.printTestSummary();
264+
const exitCode = this.logger.isSuccess() ? 0 : 1;
265+
process.exitCode = exitCode;
266+
267+
if (connected) {
268+
try {
269+
await this.disconnect();
270+
} catch {
271+
// Ignore disconnect errors — results are already reported
272+
}
273+
}
257274

258-
// Exit with appropriate code
259-
process.exit(this.logger.isSuccess() ? 0 : 1);
275+
process.exit(exitCode);
260276
}
261277

262278
/**
@@ -350,17 +366,24 @@ class CodeQLMCPClient {
350366
}
351367
} catch (error) {
352368
this.logger.log(`Workflow test execution failed: ${error.message}`, "ERROR");
353-
} finally {
354-
if (connected) {
355-
await this.disconnect();
356-
}
357369
}
358370

359-
// Print test summary
371+
// Print test summary and set exit code BEFORE disconnect.
372+
// On Windows, StdioClientTransport.close() can cause the Node.js
373+
// process to exit abruptly, so we must report results first.
360374
this.logger.printTestSummary();
375+
const exitCode = this.logger.isSuccess() ? 0 : 1;
376+
process.exitCode = exitCode;
377+
378+
if (connected) {
379+
try {
380+
await this.disconnect();
381+
} catch {
382+
// Ignore disconnect errors — results are already reported
383+
}
384+
}
361385

362-
// Exit with appropriate code
363-
process.exit(this.logger.isSuccess() ? 0 : 1);
386+
process.exit(exitCode);
364387
}
365388

366389
/**
@@ -387,17 +410,24 @@ class CodeQLMCPClient {
387410
}
388411
} catch (error) {
389412
this.logger.log(`Monitoring test execution failed: ${error.message}`, "ERROR");
390-
} finally {
391-
if (connected) {
392-
await this.disconnect();
393-
}
394413
}
395414

396-
// Print test summary
415+
// Print test summary and set exit code BEFORE disconnect.
416+
// On Windows, StdioClientTransport.close() can cause the Node.js
417+
// process to exit abruptly, so we must report results first.
397418
this.logger.printTestSummary();
419+
const exitCode = this.logger.isSuccess() ? 0 : 1;
420+
process.exitCode = exitCode;
421+
422+
if (connected) {
423+
try {
424+
await this.disconnect();
425+
} catch {
426+
// Ignore disconnect errors — results are already reported
427+
}
428+
}
398429

399-
// Exit with appropriate code
400-
process.exit(this.logger.isSuccess() ? 0 : 1);
430+
process.exit(exitCode);
401431
}
402432

403433
/**

0 commit comments

Comments
 (0)