| name | add-mcp-support-for-new-language |
|---|---|
| description | Add first-class support for a new CodeQL-supported language to the CodeQL Development MCP Server. Use this skill when CodeQL adds support for a new language (e.g., Rust) and the MCP server needs to provide tool queries, prompts, and resources for that language. |
This skill guides you through adding first-class support for a new CodeQL language to the CodeQL Development MCP Server.
- CodeQL releases support for a new language (e.g., Rust)
- The MCP server needs to provide PrintAST, PrintCFG, and CallGraph tool queries for the language
- Language-specific skills and documentation need to be created
- CodeQL CLI with the new language extractor available
- The
codeql/{language}-allstandard library pack published - Knowledge of the language's CodeQL library structure (PrintAst module, CFG library, etc.)
This skill includes automation scripts to accelerate implementation:
| File | Description |
|---|---|
scaffold-new-language.sh |
Creates directory structure and boilerplate query files |
workflow-template.yml |
CI workflow template for OS-specific languages |
Quick Start:
# Create scaffold for a new language (e.g., Rust)
./.github/skills/add-mcp-support-for-new-language/scaffold-new-language.sh rust rs| Category | File Path | Change |
|---|---|---|
| TypeScript | server/src/prompts/workflow-prompts.ts |
Add to SUPPORTED_LANGUAGES |
server/src/lib/query-file-finder.ts |
Add to LANGUAGE_EXTENSIONS |
|
server/src/lib/cli-tool-registry.ts |
Update error message strings | |
| Query Packs | server/ql/{language}/tools/src/ |
Create 4 tool queries |
server/ql/{language}/tools/test/ |
Create test cases | |
| npm Package | server/package.json |
Add ql/{language}/tools/src/ to files array |
| Scripts | server/scripts/install-packs.sh |
Add to VALID_LANGUAGES + install call |
server/scripts/extract-test-databases.sh |
Add to VALID_LANGUAGES |
|
server/scripts/run-query-unit-tests.sh |
Add to VALID_LANGUAGES |
|
| Documentation | server/ql/README.md |
Add language to list |
server/src/prompts/tools-query-workflow.prompt.md |
Add to supported languages | |
server/src/prompts/explain-codeql-query.prompt.md |
Add to language list | |
server/src/prompts/document-codeql-query.prompt.md |
Add to language list | |
docs/public.md |
Add to Supported Languages table | |
| Skills | .github/skills/create-codeql-query-tdd-generic/SKILL.md |
Add to supported languages |
.github/skills/validate-ql-mcp-server-tools-queries/SKILL.md |
Add to language table | |
| CI/CD | .github/workflows/query-unit-tests.yml |
Add to matrix (standard) |
.github/workflows/query-unit-tests-{lang}.yml |
Create if special OS needed | |
.github/workflows/release.yml |
Add to LANGUAGES list in pack publish step |
server/ql/{language}/tools/
├── src/
│ ├── codeql-pack.yml
│ ├── PrintAST/
│ │ └── PrintAST.ql
│ ├── PrintCFG/
│ │ └── PrintCFG.ql
│ ├── CallGraphFrom/
│ │ └── CallGraphFrom.ql
│ └── CallGraphTo/
│ └── CallGraphTo.ql
└── test/
├── codeql-pack.yml
├── PrintAST/
│ ├── Example1.{ext}
│ ├── PrintAST.qlref
│ └── PrintAST.expected
├── PrintCFG/
│ ├── Example1.{ext}
│ ├── PrintCFG.qlref
│ └── PrintCFG.expected
├── CallGraphFrom/
│ ├── Example1.{ext}
│ ├── CallGraphFrom.qlref
│ └── CallGraphFrom.expected
└── CallGraphTo/
├── Example1.{ext}
├── CallGraphTo.qlref
└── CallGraphTo.expected
Create server/ql/{language}/tools/src/codeql-pack.yml:
name: advanced-security/ql-mcp-{language}-tools-src
version: 0.0.1
library: false
dependencies:
codeql/{language}-all: '*'Create server/ql/{language}/tools/test/codeql-pack.yml:
name: advanced-security/ql-mcp-{language}-tools-test
version: 0.0.1
dependencies:
codeql/{language}-queries: '*'
advanced-security/ql-mcp-{language}-tools-src: ${workspace}
extractor: { language }Each tool query must:
- Use an external predicate for source file selection
- Fall back to
Example1.{ext}for unit tests - Follow the language's CodeQL library conventions
See templates/ section for query templates.
In server/src/prompts/workflow-prompts.ts, add the language alphabetically:
export const SUPPORTED_LANGUAGES = [
'actions',
'cpp',
'csharp',
'go',
'java',
'javascript',
'python',
'ruby',
'{language}' // Add here alphabetically
] as const;In server/src/lib/query-file-finder.ts, add the language with its file extension:
const LANGUAGE_EXTENSIONS: Record<string, string> = {
'actions': 'yml',
'cpp': 'cpp',
'csharp': 'cs',
'go': 'go',
'java': 'java',
'javascript': 'js',
'python': 'py',
'ruby': 'rb',
'{language}': '{ext}', // Add here alphabetically
'typescript': 'ts'
};In server/src/lib/cli-tool-registry.ts, search for supported languages error messages and add the new language:
// Find and update these strings (around line 568-569):
'Supported languages: actions, cpp, csharp, go, java, javascript, python, ruby, {language}';In server/scripts/install-packs.sh:
- Add to
VALID_LANGUAGESarray:
VALID_LANGUAGES=("actions" "cpp" "csharp" "go" "java" "javascript" "python" "ruby" "{language}")- Add install call at the end of the "all languages" block:
install_packs "server/ql/{language}/tools"In server/scripts/extract-test-databases.sh, add to VALID_LANGUAGES:
VALID_LANGUAGES=("actions" "cpp" "csharp" "go" "java" "javascript" "python" "ruby" "{language}")In server/scripts/run-query-unit-tests.sh, add to VALID_LANGUAGES:
VALID_LANGUAGES=("actions" "cpp" "csharp" "go" "java" "javascript" "python" "ruby" "{language}")Update language lists in these files:
server/ql/README.md- Add to supported languages listserver/src/prompts/tools-query-workflow.prompt.md- Add to supported languagesserver/src/prompts/explain-codeql-query.prompt.md- Add to language listserver/src/prompts/document-codeql-query.prompt.md- Add to language list
Update these existing skills to include the new language:
.github/skills/create-codeql-query-tdd-generic/SKILL.md- Update description and supported languages list.github/skills/validate-ql-mcp-server-tools-queries/SKILL.md- Add row to language table
Consider creating:
.github/skills/create-codeql-query-unit-test-{language}/SKILL.md.github/skills/update-codeql-query-dataflow-{language}/SKILL.md
Add the language to the matrix in .github/workflows/query-unit-tests.yml:
strategy:
matrix:
language:
['actions', 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', '{language}']If the language requires a specific OS (like Swift requires macOS), create a dedicated workflow:
# .github/workflows/query-unit-tests-{language}.yml
name: Query Unit Tests - {Language} ({os})
on:
pull_request:
branches: ['main']
paths:
- '.codeql-version'
- '.github/actions/setup-codeql-environment/**'
- '.github/workflows/query-unit-tests-{language}.yml'
- '.node-version'
- 'server/ql/{language}/**'
- 'server/scripts/install-packs.sh'
- 'server/scripts/run-query-unit-tests.sh'
push:
branches: ['main']
paths:
# Same as above
permissions:
contents: read
jobs:
query-unit-tests-{language}:
name: Query Unit Tests - {language}
runs-on: {os}-latest # e.g., macos-latest, windows-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
cache: 'npm'
node-version-file: '.node-version'
- run: npm ci --workspaces --ignore-scripts
- uses: ./.github/actions/setup-codeql-environment
with:
install-language-runtimes: false
- run: ./server/scripts/install-packs.sh --language {language}
- run: ./server/scripts/run-query-unit-tests.sh --language {language}# Install packs to generate lock files
codeql pack install --no-strict-mode \
--additional-packs=server/ql/{language}/tools \
-- server/ql/{language}/tools/src
codeql pack install --no-strict-mode \
--additional-packs=server/ql/{language}/tools \
-- server/ql/{language}/tools/test# Run tests for the new language
./server/scripts/run-query-unit-tests.sh --language {language}# Rebuild the MCP server bundle
npm run build --workspace=servernpm run build-and-test/**
* @name Print AST for {language}
* @description Outputs a representation of the Abstract Syntax Tree for specified source files.
* @id {language}/tools/print-ast
* @kind graph
* @tags ast
*/
import {language}
import {printast-module} // e.g., semmle.code.java.PrintAst or codeql.swift.printast.PrintAst
/**
* Gets the source files to generate AST from.
*/
external string selectedSourceFiles();
string getSelectedSourceFile() { result = selectedSourceFiles().splitAt(",").trim() }
File getSelectedFile() {
exists(string selectedFile |
selectedFile = getSelectedSourceFile() and
(
result.getRelativePath() = selectedFile
or
not selectedFile.matches("%/%") and result.getBaseName() = selectedFile
or
result.getAbsolutePath().suffix(result.getAbsolutePath().length() - selectedFile.length()) = selectedFile
)
)
}
class Cfg extends PrintAstConfiguration {
override predicate shouldPrint({element-type} e, {location-type} l) {
super.shouldPrint(e, l) and
(
l.getFile() = getSelectedFile()
or
not exists(getSelectedFile()) and
l.getFile().getBaseName() = "Example1.{ext}"
)
}
}/**
* @name Print CFG for {language}
* @description Produces a representation of a file's Control Flow Graph.
* @id {language}/tools/print-cfg
* @kind graph
* @tags cfg
*/
import {language}
import {cfg-module}
external string selectedSourceFiles();
string getSelectedSourceFile() { result = selectedSourceFiles().splitAt(",").trim() }
File getSelectedFile() {
exists(string selectedFile |
selectedFile = getSelectedSourceFile() and
(
result.getRelativePath() = selectedFile or
not selectedFile.matches("%/%") and result.getBaseName() = selectedFile or
result.getAbsolutePath().suffix(result.getAbsolutePath().length() - selectedFile.length()) = selectedFile
)
)
}
predicate shouldPrintNode(ControlFlowNode node) {
node.getLocation().getFile() = getSelectedFile()
or
not exists(getSelectedFile()) and
node.getLocation().getFile().getBaseName() = "Example1.{ext}"
}
query predicate nodes(ControlFlowNode node, string property, string value) {
shouldPrintNode(node) and
property = "semmle.label" and
value = node.toString()
}
query predicate edges(ControlFlowNode pred, ControlFlowNode succ) {
shouldPrintNode(pred) and shouldPrintNode(succ) and
pred.getASuccessor() = succ
}/**
* @name Call Graph From for {language}
* @description Displays calls made from a specified function.
* @id {language}/tools/call-graph-from
* @kind problem
* @problem.severity recommendation
* @tags call-graph
*/
import {language}
external string sourceFunction();
string getSourceFunctionName() { result = sourceFunction().splitAt(",").trim() }
{function-type} getSourceFunction() {
exists(string selectedFunc |
selectedFunc = getSourceFunctionName() and
result.getName() = selectedFunc
)
}
string getCalleeName({call-type} call) {
if exists(call.{get-target}())
then result = call.{get-target}().getName()
else result = call.toString()
}
from {call-type} call, {function-type} source
where
call.{get-enclosing}() = source and
(
source = getSourceFunction()
or
not exists(getSourceFunction()) and
source.getFile().getBaseName() = "Example1.{ext}"
)
select call, "Call from `" + source.getName() + "` to `" + getCalleeName(call) + "`"/**
* @name Call Graph To for {language}
* @description Displays calls made to a specified function.
* @id {language}/tools/call-graph-to
* @kind problem
* @problem.severity recommendation
* @tags call-graph
*/
import {language}
external string targetFunction();
string getTargetFunctionName() { result = targetFunction().splitAt(",").trim() }
string getCallerName({call-type} call) {
if exists(call.{get-enclosing}())
then result = call.{get-enclosing}().getName()
else result = "Top-level"
}
string getCalleeName({call-type} call) {
if exists(call.{get-target}())
then result = call.{get-target}().getName()
else result = call.toString()
}
from {call-type} call
where
call.{get-target}().getName() = getTargetFunctionName()
or
not exists(getTargetFunctionName()) and
call.getLocation().getFile().getBaseName() = "Example1.{ext}"
select call, "Call to `" + getCalleeName(call) + "` from `" + getCallerName(call) + "`"- cpp, csharp, go, java, javascript, python, ruby, actions
- Add to the matrix in
query-unit-tests.yml
- Swift: Requires Xcode and macOS SDK
- Create dedicated
query-unit-tests-{language}.ymlwithruns-on: macos-latest
- Create dedicated workflow with
runs-on: windows-latest - May need PowerShell script variants
- Some languages may require specific environment setup
- Check CodeQL documentation for extractor requirements
Before submitting the PR:
- All 4 tool queries created (PrintAST, PrintCFG, CallGraphFrom, CallGraphTo)
- Test cases with
.expectedfiles populated (run on appropriate OS) - Pack lock files generated (
codeql-pack.lock.yml) - TypeScript source updated and builds successfully
- All scripts updated with new language
-
server/package.jsonfilesarray includesql/{language}/tools/src/ -
.github/workflows/release.ymlLANGUAGESlist includes new language -
docs/public.mdSupported Languages table updated - Documentation updated (4+ files)
- Skills updated (2+ files)
- CI workflow configured (matrix or dedicated)
- Server bundle rebuilt (
server/dist/codeql-development-mcp-server.js) - All existing tests still pass
- New language tests pass
-
npm pack --dry-run(fromserver/) includes the new language's.ql,.md, andcodeql-pack.ymlfiles
The new language is successfully integrated when:
- ✅
codeql_query_runtool accepts the language for tool queries - ✅ PrintAST returns valid AST for source files
- ✅ PrintCFG returns valid CFG for source files
- ✅ CallGraph queries work with source/target functions
- ✅ All CI tests pass (including new language)
- ✅ Server responds to prompts mentioning the new language