Skip to content

Commit 1660bae

Browse files
authored
Embed MCP resource content at build time for VSIX compatibility (#111)
1 parent 27c54c1 commit 1660bae

File tree

7 files changed

+83
-118
lines changed

7 files changed

+83
-118
lines changed

server/dist/codeql-development-mcp-server.js

Lines changed: 43 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -31254,10 +31254,10 @@ var require_view = __commonJS({
3125431254
var debug = require_src()("express:view");
3125531255
var path4 = __require("node:path");
3125631256
var fs3 = __require("node:fs");
31257-
var dirname9 = path4.dirname;
31257+
var dirname8 = path4.dirname;
3125831258
var basename7 = path4.basename;
3125931259
var extname2 = path4.extname;
31260-
var join19 = path4.join;
31260+
var join18 = path4.join;
3126131261
var resolve13 = path4.resolve;
3126231262
module.exports = View;
3126331263
function View(name, options) {
@@ -31293,7 +31293,7 @@ var require_view = __commonJS({
3129331293
for (var i = 0; i < roots.length && !path5; i++) {
3129431294
var root = roots[i];
3129531295
var loc = resolve13(root, name);
31296-
var dir = dirname9(loc);
31296+
var dir = dirname8(loc);
3129731297
var file = basename7(loc);
3129831298
path5 = this.resolve(dir, file);
3129931299
}
@@ -31319,12 +31319,12 @@ var require_view = __commonJS({
3131931319
};
3132031320
View.prototype.resolve = function resolve14(dir, file) {
3132131321
var ext = this.ext;
31322-
var path5 = join19(dir, file);
31322+
var path5 = join18(dir, file);
3132331323
var stat = tryStat(path5);
3132431324
if (stat && stat.isFile()) {
3132531325
return path5;
3132631326
}
31327-
path5 = join19(dir, basename7(file, ext), "index" + ext);
31327+
path5 = join18(dir, basename7(file, ext), "index" + ext);
3132831328
stat = tryStat(path5);
3132931329
if (stat && stat.isFile()) {
3133031330
return path5;
@@ -34969,7 +34969,7 @@ var require_send = __commonJS({
3496934969
var Stream = __require("stream");
3497034970
var util2 = __require("util");
3497134971
var extname2 = path4.extname;
34972-
var join19 = path4.join;
34972+
var join18 = path4.join;
3497334973
var normalize = path4.normalize;
3497434974
var resolve13 = path4.resolve;
3497534975
var sep2 = path4.sep;
@@ -35141,7 +35141,7 @@ var require_send = __commonJS({
3514135141
return res;
3514235142
}
3514335143
parts = path5.split(sep2);
35144-
path5 = normalize(join19(root, path5));
35144+
path5 = normalize(join18(root, path5));
3514535145
} else {
3514635146
if (UP_PATH_REGEXP.test(path5)) {
3514735147
debug('malicious path "%s"', path5);
@@ -35274,7 +35274,7 @@ var require_send = __commonJS({
3527435274
if (err) return self.onStatError(err);
3527535275
return self.error(404);
3527635276
}
35277-
var p = join19(path5, self._index[i]);
35277+
var p = join18(path5, self._index[i]);
3527835278
debug('stat "%s"', p);
3527935279
fs3.stat(p, function(err2, stat) {
3528035280
if (err2) return next(err2);
@@ -40430,8 +40430,8 @@ var require_adm_zip = __commonJS({
4043040430
return null;
4043140431
}
4043240432
function fixPath(zipPath) {
40433-
const { join: join19, normalize, sep: sep2 } = pth.posix;
40434-
return join19(".", normalize(sep2 + zipPath.split("\\").join(sep2) + sep2));
40433+
const { join: join18, normalize, sep: sep2 } = pth.posix;
40434+
return join18(".", normalize(sep2 + zipPath.split("\\").join(sep2) + sep2));
4043540435
}
4043640436
function filenameFilter(filterfn) {
4043740437
if (filterfn instanceof RegExp) {
@@ -62921,39 +62921,30 @@ function registerCodeQLTools(server) {
6292162921
registerRegisterDatabaseTool(server);
6292262922
}
6292362923

62924+
// src/resources/getting-started.md
62925+
var getting_started_default = "# CodeQL Getting Started Guide\n\n## What is CodeQL?\n\nCodeQL is a semantic code analysis engine that allows you to write queries to find problems in source code.\n\n## Installation\n\n1. Download CodeQL CLI from GitHub releases\n2. Add to PATH\n3. Verify: `codeql version`\n\n## First Steps\n\n### 1. Create a Database\n\n```bash\ncodeql database create my-db --language=java --source-root=./src\n```\n\n### 2. Run Analysis\n\n```bash\ncodeql database analyze my-db --format=sarif --output=results.sarif\n```\n\n## Resources\n\n- [CodeQL Documentation](https://codeql.github.com/)\n- [GitHub Security Lab](https://securitylab.github.com/)\n";
62926+
62927+
// src/resources/performance-patterns.md
62928+
var performance_patterns_default = '# Performance Optimization Patterns\n\n## Efficient Joins\n\n```ql\n// Efficient - Proper join condition\nfrom Method m, MethodAccess ma\nwhere ma.getMethod() = m\nselect m, ma\n```\n\n## Early Filtering\n\n```ql\n// Filter early for better performance\nfrom Expr e\nwhere e.getEnclosingCallable().getDeclaringType().hasName("Controller")\n and e.getType().hasName("String")\n```\n';
62929+
62930+
// src/resources/query-basics.md
62931+
var query_basics_default = '# CodeQL Query Basics\n\n## Query Structure\n\n```ql\n/**\n * @name Query Name\n * @description What this query finds\n */\n\nimport language\n\nfrom Variable declarations\nwhere Conditions\nselect Results\n```\n\n## Core Clauses\n\n- **from**: Declares variables and types\n- **where**: Specifies conditions\n- **select**: Defines output\n\n## Example\n\n```ql\nfrom Method m\nwhere m.getName() = "execute"\nselect m, "Found execute method"\n```\n';
62932+
62933+
// src/resources/security-templates.md
62934+
var security_templates_default = '# Security Query Templates\n\n## SQL Injection Detection (Go)\n\nBased on the real CodeQL query from github/codeql repository:\n\n```ql\n/**\n * @name Database query built from user-controlled sources\n * @description Building a database query from user-controlled sources is vulnerable to insertion of\n * malicious code by the user.\n * @kind path-problem\n * @problem.severity error\n * @security-severity 8.8\n * @precision high\n * @id go/sql-injection\n * @tags security\n * external/cwe/cwe-089\n */\n\nimport go\nimport semmle.go.security.SqlInjection\nimport SqlInjection::Flow::PathGraph\n\nfrom SqlInjection::Flow::PathNode source, SqlInjection::Flow::PathNode sink\nwhere SqlInjection::Flow::flowPath(source, sink)\nselect sink.getNode(), source, sink, "This query depends on a $@.", source.getNode(),\n "user-provided value"\n```\n\n## Cross-Site Scripting (XSS) Template\n\n```ql\n/**\n * @name Cross-site scripting\n * @description Writing user input directly to a web page\n * allows for a cross-site scripting vulnerability.\n * @kind path-problem\n * @problem.severity error\n * @security-severity 6.1\n * @precision high\n * @id js/xss\n * @tags security\n * external/cwe/cwe-079\n */\n\nimport javascript\nimport semmle.javascript.security.dataflow.DomBasedXss\nimport DomBasedXss::Flow::PathGraph\n\nfrom DomBasedXss::Flow::PathNode source, DomBasedXss::Flow::PathNode sink\nwhere DomBasedXss::Flow::flowPath(source, sink)\nselect sink.getNode(), source, sink, "Cross-site scripting vulnerability due to $@.",\n source.getNode(), "user-provided value"\n```\n';
62935+
6292462936
// src/lib/resources.ts
62925-
import { readFileSync as readFileSync10 } from "fs";
62926-
import { join as join15, dirname as dirname8 } from "path";
62927-
import { fileURLToPath as fileURLToPath3 } from "url";
62928-
var __filename2 = fileURLToPath3(import.meta.url);
62929-
var __dirname2 = dirname8(__filename2);
6293062937
function getGettingStartedGuide() {
62931-
try {
62932-
return readFileSync10(join15(__dirname2, "../resources/getting-started.md"), "utf-8");
62933-
} catch {
62934-
return "Getting started guide not available";
62935-
}
62938+
return getting_started_default;
6293662939
}
6293762940
function getQueryBasicsGuide() {
62938-
try {
62939-
return readFileSync10(join15(__dirname2, "../resources/query-basics.md"), "utf-8");
62940-
} catch {
62941-
return "Query basics guide not available";
62942-
}
62941+
return query_basics_default;
6294362942
}
6294462943
function getSecurityTemplates() {
62945-
try {
62946-
return readFileSync10(join15(__dirname2, "../resources/security-templates.md"), "utf-8");
62947-
} catch {
62948-
return "Security templates not available";
62949-
}
62944+
return security_templates_default;
6295062945
}
6295162946
function getPerformancePatterns() {
62952-
try {
62953-
return readFileSync10(join15(__dirname2, "../resources/performance-patterns.md"), "utf-8");
62954-
} catch {
62955-
return "Performance patterns not available";
62956-
}
62947+
return performance_patterns_default;
6295762948
}
6295862949

6295962950
// src/tools/codeql-resources.ts
@@ -63039,7 +63030,7 @@ function registerCodeQLResources(server) {
6303963030
// src/tools/lsp/lsp-diagnostics.ts
6304063031
init_logger();
6304163032
init_temp_dir();
63042-
import { join as join16 } from "path";
63033+
import { join as join15 } from "path";
6304363034
import { pathToFileURL as pathToFileURL3 } from "url";
6304463035

6304563036
// src/tools/lsp/lsp-server-helper.ts
@@ -63137,7 +63128,7 @@ async function lspDiagnostics({
6313763128
serverOptions,
6313863129
workspaceUri
6313963130
});
63140-
const evalUri = pathToFileURL3(join16(getProjectTmpDir("lsp-eval"), `eval_${Date.now()}.ql`)).href;
63131+
const evalUri = pathToFileURL3(join15(getProjectTmpDir("lsp-eval"), `eval_${Date.now()}.ql`)).href;
6314163132
const diagnostics = await languageServer.evaluateQL(qlCode, evalUri);
6314263133
const summary = {
6314363134
errorCount: diagnostics.filter((d) => d.severity === 1).length,
@@ -63411,8 +63402,8 @@ function registerLSPTools(server) {
6341163402
}
6341263403

6341363404
// src/resources/language-resources.ts
63414-
import { readFileSync as readFileSync11, existsSync as existsSync12 } from "fs";
63415-
import { join as join17 } from "path";
63405+
import { readFileSync as readFileSync10, existsSync as existsSync12 } from "fs";
63406+
import { join as join16 } from "path";
6341663407

6341763408
// src/types/language-types.ts
6341863409
var LANGUAGE_RESOURCES = [
@@ -63468,16 +63459,16 @@ var LANGUAGE_RESOURCES = [
6346863459
init_package_paths();
6346963460
init_logger();
6347063461
function getQLBasePath() {
63471-
return workspaceRootDir;
63462+
return packageRootDir;
6347263463
}
6347363464
function loadResourceContent(relativePath) {
6347463465
try {
63475-
const fullPath = join17(getQLBasePath(), relativePath);
63466+
const fullPath = join16(getQLBasePath(), relativePath);
6347663467
if (!existsSync12(fullPath)) {
6347763468
logger.warn(`Resource file not found: ${fullPath}`);
6347863469
return null;
6347963470
}
63480-
return readFileSync11(fullPath, "utf-8");
63471+
return readFileSync10(fullPath, "utf-8");
6348163472
} catch (error2) {
6348263473
logger.error(`Error loading resource file ${relativePath}:`, error2);
6348363474
return null;
@@ -64169,7 +64160,7 @@ var Low = class {
6416964160
};
6417064161

6417164162
// ../node_modules/lowdb/lib/adapters/node/TextFile.js
64172-
import { readFileSync as readFileSync12, renameSync, writeFileSync as writeFileSync6 } from "node:fs";
64163+
import { readFileSync as readFileSync11, renameSync, writeFileSync as writeFileSync6 } from "node:fs";
6417364164
import path3 from "node:path";
6417464165
var TextFileSync = class {
6417564166
#tempFilename;
@@ -64182,7 +64173,7 @@ var TextFileSync = class {
6418264173
read() {
6418364174
let data;
6418464175
try {
64185-
data = readFileSync12(this.#filename, "utf-8");
64176+
data = readFileSync11(this.#filename, "utf-8");
6418664177
} catch (e) {
6418764178
if (e.code === "ENOENT") {
6418864179
return null;
@@ -64233,7 +64224,7 @@ var JSONFileSync = class extends DataFileSync {
6423364224
// src/lib/session-data-manager.ts
6423464225
init_temp_dir();
6423564226
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync7 } from "fs";
64236-
import { join as join18 } from "path";
64227+
import { join as join17 } from "path";
6423764228
import { randomUUID as randomUUID2 } from "crypto";
6423864229

6423964230
// src/types/monitoring.ts
@@ -64377,7 +64368,7 @@ var SessionDataManager = class {
6437764368
});
6437864369
this.storageDir = this.config.storageLocation;
6437964370
this.ensureStorageDirectory();
64380-
const adapter = new JSONFileSync(join18(this.storageDir, "sessions.json"));
64371+
const adapter = new JSONFileSync(join17(this.storageDir, "sessions.json"));
6438164372
this.db = new Low(adapter, {
6438264373
sessions: []
6438364374
});
@@ -64409,9 +64400,9 @@ var SessionDataManager = class {
6440964400
mkdirSync9(this.storageDir, { recursive: true });
6441064401
const subdirs = ["sessions-archive", "exports"];
6441164402
for (const subdir of subdirs) {
64412-
mkdirSync9(join18(this.storageDir, subdir), { recursive: true });
64403+
mkdirSync9(join17(this.storageDir, subdir), { recursive: true });
6441364404
}
64414-
const configPath = join18(this.storageDir, "config.json");
64405+
const configPath = join17(this.storageDir, "config.json");
6441564406
try {
6441664407
writeFileSync7(configPath, JSON.stringify(this.config, null, 2), { flag: "wx" });
6441764408
} catch (e) {
@@ -64590,9 +64581,9 @@ var SessionDataManager = class {
6459064581
if (!session) return;
6459164582
const date3 = new Date(session.endTime || session.startTime);
6459264583
const monthDir = `${date3.getFullYear()}-${String(date3.getMonth() + 1).padStart(2, "0")}`;
64593-
const archiveDir = join18(this.storageDir, "sessions-archive", monthDir);
64584+
const archiveDir = join17(this.storageDir, "sessions-archive", monthDir);
6459464585
mkdirSync9(archiveDir, { recursive: true });
64595-
const archiveFile = join18(archiveDir, `${sessionId}.json`);
64586+
const archiveFile = join17(archiveDir, `${sessionId}.json`);
6459664587
writeFileSync7(archiveFile, JSON.stringify(session, null, 2));
6459764588
await this.db.read();
6459864589
this.db.data.sessions = this.db.data.sessions.filter((s) => s.sessionId !== sessionId);
@@ -64644,7 +64635,7 @@ var SessionDataManager = class {
6464464635
...this.config,
6464564636
...configUpdate
6464664637
});
64647-
const configPath = join18(this.storageDir, "config.json");
64638+
const configPath = join17(this.storageDir, "config.json");
6464864639
writeFileSync7(configPath, JSON.stringify(this.config, null, 2));
6464964640
logger.info("Updated monitoring configuration");
6465064641
}
@@ -64654,7 +64645,7 @@ function parseBoolEnv(envVar, defaultValue) {
6465464645
return envVar.toLowerCase() === "true" || envVar === "1";
6465564646
}
6465664647
var sessionDataManager = new SessionDataManager({
64657-
storageLocation: process.env.MONITORING_STORAGE_LOCATION || join18(getProjectTmpBase(), ".ql-mcp-tracking"),
64648+
storageLocation: process.env.MONITORING_STORAGE_LOCATION || join17(getProjectTmpBase(), ".ql-mcp-tracking"),
6465864649
enableMonitoringTools: parseBoolEnv(process.env.ENABLE_MONITORING_TOOLS, false)
6465964650
});
6466064651

server/dist/codeql-development-mcp-server.js.map

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/src/lib/resources.ts

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,42 @@
11
/**
2-
* CodeQL learning resources utilities
2+
* CodeQL learning resources utilities.
3+
*
4+
* Resource files are imported as static string literals at build time via
5+
* esbuild's `loader: { '.md': 'text' }` configuration. This ensures they
6+
* are embedded in the bundled JS and available at runtime regardless of the
7+
* execution layout (monorepo source, npm install, or VSIX bundle).
38
*/
49

5-
import { readFileSync } from 'fs';
6-
import { join, dirname } from 'path';
7-
import { fileURLToPath } from 'url';
8-
9-
const __filename = fileURLToPath(import.meta.url);
10-
const __dirname = dirname(__filename);
10+
// Static imports — esbuild inlines the file contents as string literals.
11+
import gettingStartedContent from '../resources/getting-started.md';
12+
import performancePatternsContent from '../resources/performance-patterns.md';
13+
import queryBasicsContent from '../resources/query-basics.md';
14+
import securityTemplatesContent from '../resources/security-templates.md';
1115

1216
/**
1317
* Get the getting started guide content
1418
*/
1519
export function getGettingStartedGuide(): string {
16-
try {
17-
return readFileSync(join(__dirname, '../resources/getting-started.md'), 'utf-8');
18-
} catch {
19-
return 'Getting started guide not available';
20-
}
20+
return gettingStartedContent;
2121
}
2222

2323
/**
2424
* Get the query basics guide content
2525
*/
2626
export function getQueryBasicsGuide(): string {
27-
try {
28-
return readFileSync(join(__dirname, '../resources/query-basics.md'), 'utf-8');
29-
} catch {
30-
return 'Query basics guide not available';
31-
}
27+
return queryBasicsContent;
3228
}
3329

3430
/**
3531
* Get the security templates content
3632
*/
3733
export function getSecurityTemplates(): string {
38-
try {
39-
return readFileSync(join(__dirname, '../resources/security-templates.md'), 'utf-8');
40-
} catch {
41-
return 'Security templates not available';
42-
}
34+
return securityTemplatesContent;
4335
}
4436

4537
/**
4638
* Get the performance patterns content
4739
*/
4840
export function getPerformancePatterns(): string {
49-
try {
50-
return readFileSync(join(__dirname, '../resources/performance-patterns.md'), 'utf-8');
51-
} catch {
52-
return 'Performance patterns not available';
53-
}
41+
return performancePatternsContent;
5442
}

server/src/resources/language-resources.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
77
import { readFileSync, existsSync } from 'fs';
88
import { join } from 'path';
99
import { LANGUAGE_RESOURCES } from '../types/language-types';
10-
import { workspaceRootDir } from '../utils/package-paths';
10+
import { packageRootDir } from '../utils/package-paths';
1111
import { logger } from '../utils/logger';
1212

1313
/**
1414
* Get the base path for ql resources.
15-
* Uses the workspace root (monorepo root or package root) so that
16-
* resource files are found regardless of the server's process.cwd().
15+
* Uses the server package root so that resource files under `ql/` are found
16+
* regardless of the deployment layout (monorepo source, npm install, or VSIX
17+
* bundle). The `ql/` directory ships alongside the server package root in
18+
* all supported layouts.
1719
*/
1820
function getQLBasePath(): string {
19-
return workspaceRootDir;
21+
return packageRootDir;
2022
}
2123

2224
/**

server/src/types/markdown.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
* Type declarations for non-TypeScript module imports.
33
*
44
* esbuild's `loader: { '.md': 'text' }` configuration inlines `.md` files
5-
* as default-exported strings. This declaration lets TypeScript accept
6-
* `import content from './file.prompt.md'` without errors.
5+
* as default-exported strings. These declarations let TypeScript accept
6+
* `import content from './file.md'` without errors.
77
*/
8-
declare module '*.prompt.md' {
8+
declare module '*.md' {
99
const content: string;
1010
export default content;
1111
}

0 commit comments

Comments
 (0)