Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ Each language directory follows a standardized structure that enables automatic
- ALWAYS place query implementation files in `tools/src/<query-name>/` subdirectories.
- ALWAYS place corresponding test files in `tools/test/<query-name>/` subdirectories.
- ALWAYS include proper CodeQL query metadata using `@name`, `@description`, `@id`, `@kind`, and `@tags` annotations.
- ALWAYS create a `.md` query documentation file alongside every `.ql` query in `tools/src/<query-name>/` (e.g., `PrintAST.md` next to `PrintAST.ql`). This is enforced by the `query-documentation.test.ts` unit test.
- ALWAYS use the existing `server/ql/*/tools/src/PrintCFG/PrintCFG.md` files as the canonical style reference for `@kind graph` query documentation. These docs describe the structural output (nodes/edges) rather than flagging problems, so code examples should illustrate what structure the query visualizes — not whether code is compliant or non-compliant.
- ALWAYS create `.qlref` files that reference the correct query path relative to the tools directory.
- ALWAYS create `.expected` files with the expected output for each test case.
- ALWAYS implement test code source files that test both the query's ability to ignore `COMPLIANT` code patterns AND to detect `NON_COMPLIANT` code patterns.
- ALWAYS comment test cases as either `COMPLIANT` (i.e. query should not match) or `NON-COMPLIANT` (i.e. query should match).
- ALWAYS implement test code source files that test both the query's ability to ignore `COMPLIANT` code patterns AND to detect `NON_COMPLIANT` code patterns for detection-style queries (`@kind problem` / `@kind path-problem`).
- ALWAYS comment test cases as either `COMPLIANT` (i.e. query should not match) or `NON_COMPLIANT` (i.e. query should match) for detection-style queries.
- ALWAYS omit `COMPLIANT` and `NON_COMPLIANT` annotations from `@kind graph` query documentation and test code, because these queries produce structural output (ASTs, CFGs, call graphs) rather than detecting problems.
- ALWAYS use the `server/scripts/install-packs.sh` script to install dependencies for CodeQL packs defined under the `server/ql/*/language/tools/` directories.
- ALWAYS use explicit version numbers in `codeql-pack.yml` files; never use wildcards (`*`).
- ALWAYS set `ql-mcp-*` pack versions to match the CodeQL CLI version from `.codeql-version` (without the `v` prefix).
Expand All @@ -51,4 +54,5 @@ Each language directory follows a standardized structure that enables automatic
- NEVER create `.qlref` files with incorrect paths or missing target queries.
- NEVER mix different query purposes within a single query file.
- NEVER omit required CodeQL query metadata annotations.
- NEVER omit query documentation (`.md`) for any query published in a `tools/src/` pack directory.
- NEVER create test cases that don't actually exercise the query logic being tested.
4 changes: 3 additions & 1 deletion .github/workflows/build-and-test-extension.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ jobs:

- name: Verify VSIX packaging
working-directory: extensions/vscode
run: npx @vscode/vsce package --no-dependencies --out codeql-development-mcp-server.vsix
run: |
VERSION=$(node -e "console.log(require('./package.json').version)")
npx @vscode/vsce package --no-dependencies --out "codeql-development-mcp-server-v${VERSION}.vsix"
- name: Verify VSIX contents
working-directory: extensions/vscode
Expand Down
14 changes: 12 additions & 2 deletions .github/workflows/release-codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,22 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
RELEASE_NAME="${{ steps.version.outputs.release_name }}"
LANGUAGES="actions cpp csharp go java javascript python ruby swift"

# Prerelease versions (containing a hyphen) require --allow-prerelease
PRERELEASE_FLAG=""
if [[ "${RELEASE_NAME}" == *-* ]]; then
PRERELEASE_FLAG="--allow-prerelease"
echo "Detected prerelease version — using ${PRERELEASE_FLAG}"
fi

echo "Publishing CodeQL tool query packs..."
for lang in ${LANGUAGES}; do
PACK_DIR="server/ql/${lang}/tools/src"
if [ -d "${PACK_DIR}" ]; then
echo "📦 Publishing ${PACK_DIR}..."
codeql pack publish --threads=-1 -- "${PACK_DIR}"
codeql pack publish --threads=-1 ${PRERELEASE_FLAG} -- "${PACK_DIR}"
echo "✅ Published ${lang} tool query pack"
else
echo "⚠️ Skipping ${lang}: ${PACK_DIR} not found"
Expand All @@ -106,7 +115,8 @@ jobs:
for lang in ${LANGUAGES}; do
PACK_DIR="server/ql/${lang}/tools/src"
if [ -d "${PACK_DIR}" ]; then
PACK_NAME="ql-mcp-${lang}-tools-src"
VERSION="${{ steps.version.outputs.version }}"
PACK_NAME="ql-mcp-${lang}-tools-src-${VERSION}"
OUTPUT="dist-packs/${PACK_NAME}.tar.gz"
echo "📦 Bundling ${PACK_DIR} -> ${OUTPUT}..."
codeql pack bundle --threads=-1 --output="${OUTPUT}" -- "${PACK_DIR}"
Expand Down
24 changes: 20 additions & 4 deletions .github/workflows/release-tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,27 @@ jobs:
id: check-tag
run: |
TAG="${{ steps.version.outputs.version }}"
RELEASE_NAME="${{ steps.version.outputs.release_name }}"
if git rev-parse "refs/tags/${TAG}" >/dev/null 2>&1; then
TAG_SHA=$(git rev-parse "refs/tags/${TAG}^{commit}" 2>/dev/null || git rev-parse "refs/tags/${TAG}")
echo "tag_exists=true" >> $GITHUB_OUTPUT
echo "tag_sha=${TAG_SHA}" >> $GITHUB_OUTPUT
echo "ℹ️ Tag ${TAG} already exists at commit ${TAG_SHA:0:8}"

# Verify version-bearing files at the tagged commit match the release
TAG_SERVER_VERSION=$(git show "${TAG_SHA}:server/package.json" \
| grep -m1 '"version"' \
| sed 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
if [[ "${TAG_SERVER_VERSION}" == "${RELEASE_NAME}" ]]; then
echo "tag_exists=true" >> $GITHUB_OUTPUT
echo "tag_sha=${TAG_SHA}" >> $GITHUB_OUTPUT
echo "✅ Existing tag ${TAG} has correct version (${RELEASE_NAME})"
else
echo "⚠️ Version mismatch at tag ${TAG}: found ${TAG_SERVER_VERSION}, expected ${RELEASE_NAME}"
echo " Removing stale tag to recreate with correct versions..."
git tag -d "${TAG}" 2>/dev/null || true
git push origin ":refs/tags/${TAG}" 2>/dev/null || true
echo "tag_exists=false" >> $GITHUB_OUTPUT
echo "ℹ️ Stale tag ${TAG} removed — will recreate with updated versions"
fi
else
echo "tag_exists=false" >> $GITHUB_OUTPUT
echo "ℹ️ Tag ${TAG} does not exist yet"
Expand Down Expand Up @@ -128,8 +144,8 @@ jobs:
# Stage version-bearing files and lockfile changes
git add -A
# Ensure CodeQL-generated artifacts are not staged for commit
git restore --staged .codeql || true
git restore --staged '*.qlx' || true
git restore --staged .codeql 2>/dev/null || true
git restore --staged '*.qlx' 2>/dev/null || true

# Check if there are changes to commit
if git diff --cached --quiet; then
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/release-vsix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ on:
description: 'The full version string with "v" prefix (e.g., vX.Y.Z)'
value: ${{ jobs.publish-vsix.outputs.version }}
vsix_name:
description: 'The VSIX filename (e.g., codeql-development-mcp-server.vsix)'
description: 'The VSIX filename (e.g., codeql-development-mcp-server-vX.Y.Z.vsix)'
value: ${{ jobs.publish-vsix.outputs.vsix_name }}

# Note: This workflow is called exclusively via workflow_call from release.yml.
Expand Down Expand Up @@ -85,7 +85,8 @@ jobs:
id: package
working-directory: extensions/vscode
run: |
VSIX_NAME="codeql-development-mcp-server.vsix"
VERSION="${{ steps.version.outputs.version }}"
VSIX_NAME="codeql-development-mcp-server-${VERSION}.vsix"
npx @vscode/vsce package --no-dependencies --out "${VSIX_NAME}"
echo "vsix_name=${VSIX_NAME}" >> $GITHUB_OUTPUT
echo "✅ Packaged ${VSIX_NAME}"
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ on:
permissions:
contents: read

concurrency:
group: release-${{ github.event.inputs.version || github.ref_name }}
cancel-in-progress: true

jobs:
# ─────────────────────────────────────────────────────────────────────────────
# Step 1: Determine the release version
Expand Down Expand Up @@ -239,7 +243,7 @@ jobs:
files: |
codeql-development-mcp-server-${{ needs.resolve-version.outputs.version }}.tar.gz
dist-packs/*.tar.gz
dist-vsix/codeql-development-mcp-server.vsix
dist-vsix/codeql-development-mcp-server-${{ needs.resolve-version.outputs.version }}.vsix
generate_release_notes: true
tag_name: ${{ needs.resolve-version.outputs.version }}

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ codeql-development-mcp-server.code-workspace
*.swo
*.tgz
*.tar.gz
*.vsix
*~

.vscode/mcp.json
Expand Down
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codeql-development-mcp-server_client",
"version": "2.24.2",
"version": "2.24.2-rc3",
"description": "MCP client for integration testing of the CodeQL development MCP server",
"main": "src/ql-mcp-client.js",
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ npx -y codeql-development-mcp-server
### From GitHub Releases

1. Download the latest release from [Releases](https://github.com/advanced-security/codeql-development-mcp-server/releases)
2. Extract: `tar -xzf codeql-development-mcp-server-vX.X.X.tar.gz -C /path/to/destination`
2. Extract: `tar -xzf codeql-development-mcp-server-vX.Y.Z.tar.gz -C /path/to/destination`

### From Source

Expand Down
6 changes: 3 additions & 3 deletions docs/vscode/extension.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ to do by hand.

### From `.vsix` (GitHub Releases)

Download `codeql-development-mcp-server.vsix` from the latest
Download `codeql-development-mcp-server-vX.Y.Z.vsix` from the latest
[GitHub Release](https://github.com/advanced-security/codeql-development-mcp-server/releases),
then install:

```bash
code --install-extension codeql-development-mcp-server.vsix
code --install-extension codeql-development-mcp-server-vX.Y.Z.vsix
```

Or in VS Code: **Extensions** sidebar → `⋯` menu → **Install from VSIX…** → select the file.
Expand All @@ -40,7 +40,7 @@ From the repository root:

```bash
npm run package:vsix
code --install-extension extensions/vscode/codeql-development-mcp-server.vsix
code --install-extension extensions/vscode/codeql-development-mcp-server-vX.Y.Z.vsix
```

The extension requires the [CodeQL extension](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-codeql) (`GitHub.vscode-codeql`) and will prompt you to install it if missing.
Expand Down
4 changes: 2 additions & 2 deletions extensions/vscode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ A VS Code extension that automatically installs, configures, and manages the [Co
### From `.vsix`

```bash
code --install-extension codeql-development-mcp-server.vsix
code --install-extension codeql-development-mcp-server-vX.Y.Z.vsix
```

Or in VS Code: **Extensions** sidebar → `` menu → **Install from VSIX…** → select the file.
Expand All @@ -23,7 +23,7 @@ Or in VS Code: **Extensions** sidebar → `⋯` menu → **Install from VSIX…*
```bash
cd extensions/vscode
npm run package
code --install-extension codeql-development-mcp-server.vsix
code --install-extension codeql-development-mcp-server-vX.Y.Z.vsix
```

## What It Does
Expand Down
4 changes: 2 additions & 2 deletions extensions/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "vscode-codeql-development-mcp-server",
"displayName": "CodeQL Development MCP Server",
"description": "LLM-assisted development of CodeQL queries, libraries, and tests via #ql-mcp prompts, resources, and tools.",
"version": "2.24.2",
"version": "2.24.2-rc3",
"publisher": "advanced-security",
"license": "SEE LICENSE IN LICENSE",
"icon": "media/codeql-icon.png",
Expand Down Expand Up @@ -140,7 +140,7 @@
"clean": "rm -rf dist server *.vsix",
"lint": "eslint src/ test/",
"lint:fix": "eslint src/ test/ --fix",
"package": "vsce package --no-dependencies --out codeql-development-mcp-server.vsix",
"package": "vsce package --no-dependencies --out codeql-development-mcp-server-v$(node -e 'process.stdout.write(require(`./package.json`).version)').vsix",
"test": "npm run test:coverage && npm run test:integration",
"test:coverage": "vitest --run --coverage",
"test:integration": "vscode-test",
Expand Down
7 changes: 1 addition & 6 deletions extensions/vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,7 @@ export async function activate(
// Run in background — don't block activation
void (async () => {
try {
const freshInstall = await serverManager.ensureInstalled();
if (freshInstall) {
logger.info('Fresh npm install completed — running CodeQL pack setup...');
} else {
logger.info('npm package already present — checking CodeQL packs...');
}
await serverManager.ensureInstalled();
await packInstaller.installAll();
mcpProvider.fireDidChange();
logger.info('✅ MCP server setup complete. Server is ready to be started.');
Expand Down
42 changes: 30 additions & 12 deletions extensions/vscode/src/server/pack-installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,19 @@ export interface PackInstallOptions {

/**
* Installs CodeQL pack dependencies for the bundled tool query packs
* shipped with the `codeql-development-mcp-server` npm package.
* shipped inside the VSIX (or, as fallback, in the locally-installed
* `codeql-development-mcp-server` npm package).
*
* The npm package bundles the qlpack source files (`.ql` + lock files),
* but their CodeQL library dependencies (e.g. `codeql/javascript-all`)
* must be fetched from GHCR via `codeql pack install`. This class
* automates the `codeql-development-mcp-server-setup-packs` step
* documented in the getting-started guide.
* The VSIX bundles the qlpack source files (`.ql` + lock files) at
* `<extensionRoot>/server/ql/<lang>/tools/src/`, and the npm install
* mirrors them at `globalStorage/mcp-server/node_modules/.../ql/...`.
* The bundled copy is always preferred so that the packs used by
* `codeql pack install` match the server code running from the VSIX.
*
* CodeQL library dependencies (e.g. `codeql/javascript-all`) must be
* fetched from GHCR via `codeql pack install`. This class automates
* the `codeql-development-mcp-server-setup-packs` step documented in
* the getting-started guide.
*/
export class PackInstaller extends DisposableObject {
static readonly SUPPORTED_LANGUAGES = [
Expand All @@ -46,13 +52,25 @@ export class PackInstaller extends DisposableObject {
}

/**
* Get the qlpack source directories for all languages under the
* locally installed npm package.
* Get the root directory for qlpack resolution.
*
* Prefers the bundled `server/` directory inside the VSIX so that the
* packs installed match the server version. Falls back to the
* npm-installed package root in `globalStorage` (for local dev or when
* the VSIX bundle is missing).
*/
private getQlpackRoot(): string {
return this.serverManager.getBundledQlRoot()
?? this.serverManager.getPackageRoot();
}

/**
* Get the qlpack source directories for all languages.
*/
getQlpackPaths(): string[] {
const packageRoot = this.serverManager.getPackageRoot();
const root = this.getQlpackRoot();
return PackInstaller.SUPPORTED_LANGUAGES.map((lang) =>
join(packageRoot, 'ql', lang, 'tools', 'src'),
join(root, 'ql', lang, 'tools', 'src'),
);
}

Expand All @@ -69,12 +87,12 @@ export class PackInstaller extends DisposableObject {
return;
}

const packageRoot = this.serverManager.getPackageRoot();
const qlRoot = this.getQlpackRoot();
const languages =
options?.languages ?? [...PackInstaller.SUPPORTED_LANGUAGES];

for (const lang of languages) {
const packDir = join(packageRoot, 'ql', lang, 'tools', 'src');
const packDir = join(qlRoot, 'ql', lang, 'tools', 'src');

// Check if the pack directory exists
try {
Expand Down
Loading