Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Comment thread
data-douser marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"toolName": "codeql_query_compile",
"arguments": {
Comment thread
data-douser marked this conversation as resolved.
"query": "server/ql/javascript/examples/src/ExampleQuery1/ExampleQuery1.ql"
Comment thread
data-douser marked this conversation as resolved.
},
Comment thread
data-douser marked this conversation as resolved.
"assertions": {
"responseContains": ["DIL file:", ".dil"]
}
}
Comment thread
data-douser marked this conversation as resolved.
74 changes: 56 additions & 18 deletions server/dist/codeql-development-mcp-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -35004,7 +35004,7 @@ var require_view = __commonJS({
var path3 = __require("node:path");
var fs3 = __require("node:fs");
var dirname9 = path3.dirname;
var basename10 = path3.basename;
var basename11 = path3.basename;
var extname3 = path3.extname;
var join22 = path3.join;
var resolve15 = path3.resolve;
Expand Down Expand Up @@ -35043,7 +35043,7 @@ var require_view = __commonJS({
var root = roots[i];
var loc = resolve15(root, name);
var dir = dirname9(loc);
var file = basename10(loc);
var file = basename11(loc);
path4 = this.resolve(dir, file);
}
return path4;
Expand Down Expand Up @@ -35073,7 +35073,7 @@ var require_view = __commonJS({
if (stat && stat.isFile()) {
return path4;
}
path4 = join22(dir, basename10(file, ext), "index" + ext);
path4 = join22(dir, basename11(file, ext), "index" + ext);
stat = tryStat(path4);
if (stat && stat.isFile()) {
return path4;
Expand Down Expand Up @@ -38371,7 +38371,7 @@ var require_content_disposition = __commonJS({
"use strict";
module.exports = contentDisposition;
module.exports.parse = parse4;
var basename10 = __require("path").basename;
var basename11 = __require("path").basename;
var ENCODE_URL_ATTR_CHAR_REGEXP = /[\x00-\x20"'()*,/:;<=>?@[\\\]{}\x7f]/g;
var HEX_ESCAPE_REGEXP = /%[0-9A-Fa-f]{2}/;
var HEX_ESCAPE_REPLACE_REGEXP = /%([0-9A-Fa-f]{2})/g;
Expand Down Expand Up @@ -38406,9 +38406,9 @@ var require_content_disposition = __commonJS({
if (typeof fallback === "string" && NON_LATIN1_REGEXP.test(fallback)) {
throw new TypeError("fallback must be ISO-8859-1 string");
}
var name = basename10(filename);
var name = basename11(filename);
var isQuotedString = TEXT_REGEXP.test(name);
var fallbackName = typeof fallback !== "string" ? fallback && getlatin1(name) : basename10(fallback);
var fallbackName = typeof fallback !== "string" ? fallback && getlatin1(name) : basename11(fallback);
var hasFallback = typeof fallbackName === "string" && fallbackName !== name;
if (hasFallback || !isQuotedString || HEX_ESCAPE_REGEXP.test(name)) {
params["filename*"] = name;
Expand Down Expand Up @@ -190955,7 +190955,7 @@ function cacheDatabaseAnalyzeResults(params, logger2) {
// src/lib/cli-tool-registry.ts
init_package_paths();
import { existsSync as existsSync6, mkdirSync as mkdirSync8, realpathSync, rmSync, writeFileSync as writeFileSync4 } from "fs";
import { delimiter as delimiter5, dirname as dirname5, isAbsolute as isAbsolute4, join as join10, resolve as resolve4 } from "path";
import { delimiter as delimiter5, dirname as dirname5, basename as basename5, isAbsolute as isAbsolute4, join as join10, resolve as resolve4 } from "path";

// ../node_modules/js-yaml/dist/js-yaml.mjs
function isNothing(subject) {
Expand Down Expand Up @@ -193859,6 +193859,19 @@ function registerCLITool(server, definition) {
break;
}
case "codeql_query_compile":
if (query) {
positionalArgs = [...positionalArgs, query];
}
if (options["dump-dil"] === void 0) {
const pending = Array.isArray(options.additionalArgs) ? options.additionalArgs : [];
const hasDilOverride = pending.some(
(arg) => arg === "--no-dump-dil" || arg === "--dump-dil"
);
if (!hasDilOverride) {
options["dump-dil"] = true;
}
}
break;
case "codeql_resolve_metadata":
if (query) {
positionalArgs = [...positionalArgs, query];
Expand Down Expand Up @@ -193908,6 +193921,11 @@ function registerCLITool(server, definition) {
mkdirSync8(outputDir, { recursive: true });
}
}
let compileLogDir;
if (name === "codeql_query_compile" && options["dump-dil"] !== false) {
compileLogDir = getOrCreateLogDirectory(customLogDir);
logger.info(`Using log directory for ${name}: ${compileLogDir}`);
}
const rawAdditionalArgs = Array.isArray(options.additionalArgs) ? options.additionalArgs : [];
delete options.additionalArgs;
const managedFlagNames = /* @__PURE__ */ new Set([
Expand Down Expand Up @@ -194003,7 +194021,24 @@ function registerCLITool(server, definition) {
const resolvedDb = typeof params.database === "string" ? resolveDatabasePath(params.database) : params.database;
cacheDatabaseAnalyzeResults({ ...params, database: resolvedDb, output: options.output, format: options.format }, logger);
}
const processedResult = resultProcessor(result, params);
let dilFilePath;
if (name === "codeql_query_compile" && result.success && compileLogDir && result.stdout) {
try {
const queryBaseName = query ? basename5(query, ".ql") : "query";
dilFilePath = join10(compileLogDir, `${queryBaseName}.dil`);
writeFileSync4(dilFilePath, result.stdout, "utf8");
logger.info(`Saved DIL output to ${dilFilePath}`);
} catch (dilError) {
logger.warn(`Failed to save DIL output: ${dilError}`);
dilFilePath = void 0;
}
}
let processedResult = resultProcessor(result, params);
if (dilFilePath) {
processedResult += `

DIL file: ${dilFilePath}`;
}
return {
content: [{
type: "text",
Expand Down Expand Up @@ -195275,7 +195310,7 @@ var codeqlPackLsTool = {

// src/tools/codeql/profile-codeql-query-from-logs.ts
import { existsSync as existsSync11, mkdirSync as mkdirSync9, writeFileSync as writeFileSync5 } from "fs";
import { basename as basename6, dirname as dirname7, join as join15 } from "path";
import { basename as basename7, dirname as dirname7, join as join15 } from "path";

// src/lib/evaluator-log-parser.ts
init_logger();
Expand Down Expand Up @@ -195607,7 +195642,7 @@ function buildDetailFile(profile, topN) {
lines.push("");
for (let qIdx = 0; qIdx < profile.queries.length; qIdx++) {
const query = profile.queries[qIdx];
const qName = basename6(query.queryName);
const qName = basename7(query.queryName);
lines.push(`== Query: ${qName} ==`);
lines.push(` Total: ${query.totalDurationMs.toFixed(2)}ms | Predicates evaluated: ${query.predicateCount} | Cache hits: ${query.cacheHits}`);
lines.push("");
Expand Down Expand Up @@ -195766,7 +195801,7 @@ init_cli_executor();
init_logger();
import { writeFileSync as writeFileSync6, existsSync as existsSync12 } from "fs";
import { createReadStream as createReadStream2 } from "fs";
import { join as join16, dirname as dirname8, basename as basename7 } from "path";
import { join as join16, dirname as dirname8, basename as basename8 } from "path";
import { mkdirSync as mkdirSync10 } from "fs";
import { createInterface as createInterface2 } from "readline";
async function parseEvaluatorLog2(logPath) {
Expand Down Expand Up @@ -195892,7 +195927,7 @@ function formatAsMermaid(profile) {
lines.push("```mermaid");
lines.push("graph TD");
lines.push("");
lines.push(` QUERY["${basename7(profile.queryName)}<br/>Total: ${profile.totalDuration.toFixed(2)}ms"]`);
lines.push(` QUERY["${basename8(profile.queryName)}<br/>Total: ${profile.totalDuration.toFixed(2)}ms"]`);
lines.push("");
profile.pipelines.forEach((pipeline) => {
const nodeId = `P${pipeline.eventId}`;
Expand Down Expand Up @@ -196023,7 +196058,7 @@ function registerProfileCodeQLQueryTool(server) {
...outputFiles.map((f) => ` - ${f}`),
"",
"Profile Summary:",
` - Query: ${basename7(profile.queryName)}`,
` - Query: ${basename8(profile.queryName)}`,
` - Total Duration: ${profile.totalDuration.toFixed(2)} ms`,
` - Total Pipelines: ${profile.pipelines.length}`,
` - Total Events: ${profile.totalEvents}`,
Expand Down Expand Up @@ -196060,20 +196095,23 @@ function registerProfileCodeQLQueryTool(server) {
// src/tools/codeql/query-compile.ts
var codeqlQueryCompileTool = {
name: "codeql_query_compile",
description: "Compile and validate CodeQL queries",
description: "Compile and validate CodeQL queries. By default, produces a .dil file containing the optimized DIL intermediate representation alongside the compilation output.",
command: "codeql",
subcommand: "query compile",
inputSchema: {
query: external_exports.string().describe("Path to the CodeQL query file (.ql)"),
database: external_exports.string().optional().describe("Path to the CodeQL database"),
"dump-dil": external_exports.boolean().optional().describe("Print the optimized DIL intermediate representation to standard output while compiling. Enabled by default; pass false or --no-dump-dil to disable."),
library: external_exports.string().optional().describe("Path to query library"),
logDir: external_exports.string().optional().describe("Directory to write the .dil file. If not provided, a unique log directory is created automatically."),
output: external_exports.string().optional().describe("Output file path"),
warnings: external_exports.enum(["hide", "show", "error"]).optional().describe("How to handle compilation warnings"),
verbose: external_exports.boolean().optional().describe("Enable verbose output"),
additionalArgs: external_exports.array(external_exports.string()).optional().describe("Additional command-line arguments")
},
examples: [
"codeql query compile --database=/path/to/db MyQuery.ql",
"codeql query compile --dump-dil --database=/path/to/db MyQuery.ql",
"codeql query compile --library=/path/to/lib --output=compiled.qlo MyQuery.ql"
]
};
Expand Down Expand Up @@ -196722,7 +196760,7 @@ var codeqlResolveTestsTool = {

// src/tools/codeql/search-ql-code.ts
import { closeSync as closeSync2, createReadStream as createReadStream3, fstatSync as fstatSync2, lstatSync, openSync as openSync2, readdirSync as readdirSync8, readFileSync as readFileSync12, realpathSync as realpathSync2 } from "fs";
import { basename as basename8, extname as extname2, join as join19, resolve as resolve9 } from "path";
import { basename as basename9, extname as extname2, join as join19, resolve as resolve9 } from "path";
import { createInterface as createInterface3 } from "readline";
init_logger();
var MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024;
Expand All @@ -196748,7 +196786,7 @@ function collectFiles(paths, extensions, fileCount) {
}
fileCount.value++;
} else if (stat.isDirectory()) {
if (SKIP_DIRS2.has(basename8(p))) return;
if (SKIP_DIRS2.has(basename9(p))) return;
let realPath;
try {
realPath = realpathSync2(p);
Expand Down Expand Up @@ -198149,7 +198187,7 @@ function registerLanguageResources(server) {

// src/prompts/workflow-prompts.ts
import { access as access2 } from "fs/promises";
import { basename as basename9, isAbsolute as isAbsolute7, normalize, relative as relative2, resolve as resolve13, sep as sep3 } from "path";
import { basename as basename10, isAbsolute as isAbsolute7, normalize, relative as relative2, resolve as resolve13, sep as sep3 } from "path";
import { fileURLToPath as fileURLToPath3 } from "url";

// src/prompts/check-for-duplicated-code.prompt.md
Expand Down Expand Up @@ -198568,7 +198606,7 @@ ${content}`
if (qpResult.blocked) return blockedPathError(qpResult, "query path");
const resolvedQueryPath = qpResult.resolvedPath;
if (qpResult.warning) warnings.push(qpResult.warning);
const derivedName = workshopName || basename9(resolvedQueryPath).replace(/\.(ql|qlref)$/, "").toLowerCase().replace(/[^a-z0-9]+/g, "-") || "codeql-workshop";
const derivedName = workshopName || basename10(resolvedQueryPath).replace(/\.(ql|qlref)$/, "").toLowerCase().replace(/[^a-z0-9]+/g, "-") || "codeql-workshop";
const contextSection = buildWorkshopContext(
resolvedQueryPath,
language,
Expand Down
6 changes: 3 additions & 3 deletions server/dist/codeql-development-mcp-server.js.map

Large diffs are not rendered by default.

54 changes: 51 additions & 3 deletions server/src/lib/cli-tool-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { resolveQueryPath } from './query-resolver';
import { cacheDatabaseAnalyzeResults, processQueryRunResults } from './result-processor';
import { getUserWorkspaceDir, packageRootDir } from '../utils/package-paths';
import { existsSync, mkdirSync, realpathSync, rmSync, writeFileSync } from 'fs';
import { delimiter, dirname, isAbsolute, join, resolve } from 'path';
import { delimiter, dirname, basename, isAbsolute, join, resolve } from 'path';
Comment thread
data-douser marked this conversation as resolved.
Outdated
import * as yaml from 'js-yaml';
import { createProjectTempDir } from '../utils/temp-dir';

Expand Down Expand Up @@ -459,8 +459,28 @@ export function registerCLITool(server: McpServer, definition: CLIToolDefinition
}

case 'codeql_query_compile':
// Handle query parameter as positional argument
if (query) {
positionalArgs = [...positionalArgs, query as string];
}
// Enable --dump-dil by default unless the user explicitly set
// dump-dil to false or passed --no-dump-dil / --dump-dil in
// additionalArgs (which takes precedence).
if (options['dump-dil'] === undefined) {
const pending = Array.isArray(options.additionalArgs)
? options.additionalArgs as string[]
: [];
const hasDilOverride = pending.some(
arg => arg === '--no-dump-dil' || arg === '--dump-dil'
);
if (!hasDilOverride) {
options['dump-dil'] = true;
}
}
break;

case 'codeql_resolve_metadata':
// Handle query parameter as positional argument for query compilation and metadata tools
// Handle query parameter as positional argument for metadata tools
if (query) {
positionalArgs = [...positionalArgs, query as string];
}
Expand Down Expand Up @@ -536,6 +556,13 @@ export function registerCLITool(server: McpServer, definition: CLIToolDefinition
}
}

// Set up log directory for compile runs to persist DIL output
let compileLogDir: string | undefined;
if (name === 'codeql_query_compile' && options['dump-dil'] !== false) {
compileLogDir = getOrCreateLogDirectory(customLogDir as string | undefined);
logger.info(`Using log directory for ${name}: ${compileLogDir}`);
}
Comment thread
data-douser marked this conversation as resolved.
Outdated
Comment on lines +559 to +571
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

effectiveDumpDilEnabled is computed as disabled when the named parameter sets dump-dil: false, even if additionalArgs includes an explicit --dump-dil. That contradicts the stated precedence of additionalArgs and will skip .dil persistence even though the CLI will likely emit DIL. Consider normalizing options['dump-dil'] based on additionalArgs (treat --dump-dil / --no-dump-dil as authoritative and avoid emitting conflicting flags), and derive effectiveDumpDilEnabled from the resulting effective CLI args.

See below for a potential fix:

        // Extract additionalArgs from options so they are passed as raw CLI
        // arguments instead of being transformed into --additionalArgs=value
        // by buildCodeQLArgs.
        let rawAdditionalArgs = Array.isArray(options.additionalArgs)
          ? options.additionalArgs as string[]
          : [];
        delete options.additionalArgs;

        // Normalize dump-dil handling so explicit raw CLI flags in
        // `additionalArgs` take precedence over the named parameter and the
        // CLI does not receive conflicting duplicates.
        const lastDumpDilFlag = [...rawAdditionalArgs].reverse().find(
          (arg) => arg === '--dump-dil' || arg === '--no-dump-dil',
        );
        if (lastDumpDilFlag === '--dump-dil') {
          options['dump-dil'] = true;
        } else if (lastDumpDilFlag === '--no-dump-dil') {
          options['dump-dil'] = false;
        }
        if (lastDumpDilFlag !== undefined) {
          rawAdditionalArgs = rawAdditionalArgs.filter(
            (arg) => arg !== '--dump-dil' && arg !== '--no-dump-dil',
          );
        }

        // Compute an effective "dump-dil enabled" flag for codeql_query_compile
        // from the normalized effective CLI options. The log directory is
        // created lazily post-success to avoid leaving empty directories
        // behind on compilation failures.
        let effectiveDumpDilEnabled = false;
        if (name === 'codeql_query_compile') {
          effectiveDumpDilEnabled = options['dump-dil'] !== false;
        }

Copilot uses AI. Check for mistakes.

// Extract additionalArgs from options so they are passed as raw CLI
// arguments instead of being transformed into --additionalArgs=value
// by buildCodeQLArgs.
Expand Down Expand Up @@ -697,8 +724,29 @@ export function registerCLITool(server: McpServer, definition: CLIToolDefinition
cacheDatabaseAnalyzeResults({ ...params, database: resolvedDb, output: options.output, format: options.format }, logger);
}

// Post-execution: persist DIL output to a .dil file for codeql_query_compile
let dilFilePath: string | undefined;
if (name === 'codeql_query_compile' && result.success && compileLogDir && result.stdout) {
try {
const queryBaseName = query
? basename(query as string, '.ql')
: 'query';
dilFilePath = join(compileLogDir, `${queryBaseName}.dil`);
writeFileSync(dilFilePath, result.stdout, 'utf8');
logger.info(`Saved DIL output to ${dilFilePath}`);
} catch (dilError) {
logger.warn(`Failed to save DIL output: ${dilError}`);
dilFilePath = undefined;
}
}

// Process the result
const processedResult = resultProcessor(result, params);
let processedResult = resultProcessor(result, params);

// Append DIL file path to the response for codeql_query_compile
if (dilFilePath) {
processedResult += `\n\nDIL file: ${dilFilePath}`;
}

return {
content: [{
Expand Down
7 changes: 6 additions & 1 deletion server/src/tools/codeql/query-compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ import { CLIToolDefinition } from '../../lib/cli-tool-registry';

export const codeqlQueryCompileTool: CLIToolDefinition = {
name: 'codeql_query_compile',
description: 'Compile and validate CodeQL queries',
description: 'Compile and validate CodeQL queries. By default, produces a .dil file containing the optimized DIL intermediate representation alongside the compilation output.',
command: 'codeql',
subcommand: 'query compile',
inputSchema: {
query: z.string().describe('Path to the CodeQL query file (.ql)'),
database: z.string().optional().describe('Path to the CodeQL database'),
'dump-dil': z.boolean().optional()
.describe('Print the optimized DIL intermediate representation to standard output while compiling. Enabled by default; pass false or --no-dump-dil to disable.'),
library: z.string().optional().describe('Path to query library'),
logDir: z.string().optional()
.describe('Directory to write the .dil file. If not provided, a unique log directory is created automatically.'),
Comment thread
data-douser marked this conversation as resolved.
Outdated
output: z.string().optional().describe('Output file path'),
warnings: z.enum(['hide', 'show', 'error']).optional()
.describe('How to handle compilation warnings'),
Expand All @@ -22,6 +26,7 @@ export const codeqlQueryCompileTool: CLIToolDefinition = {
},
examples: [
'codeql query compile --database=/path/to/db MyQuery.ql',
'codeql query compile --dump-dil --database=/path/to/db MyQuery.ql',
'codeql query compile --library=/path/to/lib --output=compiled.qlo MyQuery.ql'
]
};
Loading
Loading