From b98fa821091807b799409bb0eb4224f60fa7e5ee Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Mon, 19 Jan 2026 09:39:35 +0000 Subject: [PATCH] mcp --- .claude/settings.local.json | 6 + scripts/mcp/codeql-mcp.js | 259 ++++++++++++++++++++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100644 scripts/mcp/codeql-mcp.js diff --git a/.claude/settings.local.json b/.claude/settings.local.json index a90225e..ea50b6b 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -44,5 +44,11 @@ "Bash(for comp in GitHubBuildStatus LazyD3BarChart StorageSettings LoadingFallback NavigationItem PageHeaderContent SaveIndicator SeedDataManager TreeCard)", "Bash(do grep -r \"from ''@/components/molecules/$comp''\" src/ --files-with-matches)" ] + }, + "mcpServers": { + "codeql": { + "command": "node", + "args": ["scripts/mcp/codeql-mcp.js"] + } } } diff --git a/scripts/mcp/codeql-mcp.js b/scripts/mcp/codeql-mcp.js new file mode 100644 index 0000000..72ce51e --- /dev/null +++ b/scripts/mcp/codeql-mcp.js @@ -0,0 +1,259 @@ +#!/usr/bin/env node +const { spawn } = require('node:child_process') + +function send(message) { + process.stdout.write(`${JSON.stringify(message)}\n`) +} + +function respond(id, result) { + send({ jsonrpc: '2.0', id, result }) +} + +function respondError(id, code, message) { + send({ jsonrpc: '2.0', id, error: { code, message } }) +} + +function toolList() { + return [ + { + name: 'codeql_analyze', + description: 'Run CodeQL database analyze with a query pack.', + inputSchema: { + type: 'object', + properties: { + dbPath: { type: 'string' }, + queryPack: { type: 'string' }, + output: { type: 'string' }, + format: { type: 'string', default: 'sarifv2.1.0' }, + rerun: { type: 'boolean', default: false }, + extraArgs: { type: 'array', items: { type: 'string' } }, + }, + required: ['dbPath', 'queryPack'], + }, + }, + { + name: 'codeql_database_create', + description: 'Create a CodeQL database.', + inputSchema: { + type: 'object', + properties: { + dbPath: { type: 'string' }, + language: { type: 'string', default: 'javascript' }, + sourceRoot: { type: 'string', default: '.' }, + buildMode: { type: 'string', default: 'none' }, + extraArgs: { type: 'array', items: { type: 'string' } }, + }, + required: ['dbPath'], + }, + }, + { + name: 'codeql_pack_install', + description: 'Install dependencies for a local CodeQL pack.', + inputSchema: { + type: 'object', + properties: { + packDir: { type: 'string' }, + extraArgs: { type: 'array', items: { type: 'string' } }, + }, + required: ['packDir'], + }, + }, + { + name: 'codeql_pack_download', + description: 'Download a CodeQL pack by name.', + inputSchema: { + type: 'object', + properties: { + pack: { type: 'string' }, + extraArgs: { type: 'array', items: { type: 'string' } }, + }, + required: ['pack'], + }, + }, + { + name: 'codeql_resolve_packs', + description: 'List available CodeQL packs.', + inputSchema: { + type: 'object', + properties: { + extraArgs: { type: 'array', items: { type: 'string' } }, + }, + }, + }, + { + name: 'codeql_resolve_languages', + description: 'List available CodeQL languages.', + inputSchema: { + type: 'object', + properties: { + extraArgs: { type: 'array', items: { type: 'string' } }, + }, + }, + }, + ] +} + +function runCodeql(args, options = {}) { + return new Promise((resolve, reject) => { + const proc = spawn('codeql', args, { stdio: ['ignore', 'pipe', 'pipe'], ...options }) + let stdout = '' + let stderr = '' + + proc.stdout.on('data', (chunk) => { + stdout += chunk.toString() + }) + proc.stderr.on('data', (chunk) => { + stderr += chunk.toString() + }) + + proc.on('error', (err) => reject(err)) + proc.on('close', (code) => { + resolve({ code, stdout, stderr, args }) + }) + }) +} + +function runCodeqlAnalyze(input) { + const { + dbPath, + queryPack, + output, + format = 'sarifv2.1.0', + rerun = false, + extraArgs = [], + } = input || {} + + if (!dbPath || !queryPack) { + return Promise.reject(new Error('dbPath and queryPack are required')) + } + + const args = ['database', 'analyze', dbPath, queryPack, `--format=${format}`] + if (output) args.push(`--output=${output}`) + if (rerun) args.push('--rerun') + if (Array.isArray(extraArgs)) args.push(...extraArgs) + + return runCodeql(args) +} + +function runCodeqlDatabaseCreate(input) { + const { + dbPath, + language = 'javascript', + sourceRoot = '.', + buildMode = 'none', + extraArgs = [], + } = input || {} + + if (!dbPath) { + return Promise.reject(new Error('dbPath is required')) + } + + const args = [ + 'database', + 'create', + dbPath, + `--language=${language}`, + `--source-root=${sourceRoot}`, + `--build-mode`, + buildMode, + ] + if (Array.isArray(extraArgs)) args.push(...extraArgs) + return runCodeql(args) +} + +function runCodeqlPackInstall(input) { + const { packDir, extraArgs = [] } = input || {} + if (!packDir) return Promise.reject(new Error('packDir is required')) + const args = ['pack', 'install'] + if (Array.isArray(extraArgs)) args.push(...extraArgs) + return runCodeql(args, { cwd: packDir }) +} + +function runCodeqlPackDownload(input) { + const { pack, extraArgs = [] } = input || {} + if (!pack) return Promise.reject(new Error('pack is required')) + const args = ['pack', 'download', pack] + if (Array.isArray(extraArgs)) args.push(...extraArgs) + return runCodeql(args) +} + +function runCodeqlResolvePacks(input) { + const { extraArgs = [] } = input || {} + const args = ['resolve', 'packs'] + if (Array.isArray(extraArgs)) args.push(...extraArgs) + return runCodeql(args) +} + +function runCodeqlResolveLanguages(input) { + const { extraArgs = [] } = input || {} + const args = ['resolve', 'languages'] + if (Array.isArray(extraArgs)) args.push(...extraArgs) + return runCodeql(args) +} + +async function handleRequest(message) { + const { id, method, params } = message + + if (method === 'initialize') { + return respond(id, { + capabilities: { tools: { list: true, call: true } }, + serverInfo: { name: 'codeql-mcp', version: '0.1.0' }, + }) + } + + if (method === 'tools/list') { + return respond(id, { tools: toolList() }) + } + + if (method === 'tools/call') { + const { name, arguments: args } = params || {} + try { + let result + if (name === 'codeql_analyze') { + result = await runCodeqlAnalyze(args) + } else if (name === 'codeql_database_create') { + result = await runCodeqlDatabaseCreate(args) + } else if (name === 'codeql_pack_install') { + result = await runCodeqlPackInstall(args) + } else if (name === 'codeql_pack_download') { + result = await runCodeqlPackDownload(args) + } else if (name === 'codeql_resolve_packs') { + result = await runCodeqlResolvePacks(args) + } else if (name === 'codeql_resolve_languages') { + result = await runCodeqlResolveLanguages(args) + } else { + return respondError(id, -32601, `Unknown tool: ${name}`) + } + return respond(id, { + content: [ + { + type: 'text', + text: `exit: ${result.code}\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`, + }, + ], + }) + } catch (err) { + return respondError(id, -32603, err.message) + } + } + + return respondError(id, -32601, `Unknown method: ${method}`) +} + +let buffer = '' +process.stdin.setEncoding('utf8') +process.stdin.on('data', (chunk) => { + buffer += chunk + let idx + while ((idx = buffer.indexOf('\n')) >= 0) { + const line = buffer.slice(0, idx).trim() + buffer = buffer.slice(idx + 1) + if (!line) continue + try { + const message = JSON.parse(line) + handleRequest(message) + } catch (err) { + respondError(null, -32700, 'Invalid JSON') + } + } +})