Skip to content

Commit 2af20c2

Browse files
Copilotdata-douser
andcommitted
feat: add stdio transport support to MCP client integration tests
- Update client to support both stdio and http transport modes (MCP_MODE env var, default: stdio) - In stdio mode, client spawns server via StdioClientTransport from MCP SDK - Update run-integration-tests.sh to skip manual server start/stop in stdio mode - Add test:integration:http and test:integration:stdio scripts to client/package.json - Update CI workflow to test both transport modes via matrix strategy - Update docs/testing.md to document stdio as default transport - Update docs/getting-started.md and docs/public.md to recommend VSIX installation Co-authored-by: data-douser <70299490+data-douser@users.noreply.github.com>
1 parent ccc9c77 commit 2af20c2

7 files changed

Lines changed: 111 additions & 42 deletions

File tree

.github/workflows/client-integration-tests.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,19 @@ permissions:
2626

2727
jobs:
2828
integration-tests:
29-
name: Integration Tests (${{ matrix.os }})
29+
name: Integration Tests (${{ matrix.os }}, ${{ matrix.mcp-mode }})
3030
runs-on: ${{ matrix.os }}
3131

3232
strategy:
3333
fail-fast: false
3434
matrix:
35+
mcp-mode: [http, stdio]
3536
os: [ubuntu-latest, windows-latest]
3637

3738
env:
3839
HTTP_HOST: 'localhost'
3940
HTTP_PORT: '3000'
41+
MCP_MODE: ${{ matrix.mcp-mode }}
4042
TIMEOUT_SECONDS: '30'
4143
URL_SCHEME: 'http'
4244

@@ -110,7 +112,7 @@ jobs:
110112
run: npm run test:integration --workspace=client
111113

112114
- name: MCP Integration Tests - Stop the background MCP server process
113-
if: always()
115+
if: always() && matrix.mcp-mode == 'http'
114116
shell: bash
115117
run: |
116118
if [ -f server.pid ]; then
@@ -141,8 +143,8 @@ jobs:
141143
- name: MCP Integration Tests - Summary
142144
shell: bash
143145
run: |
144-
echo "## Integration Tests Summary (${{ matrix.os }})" >> $GITHUB_STEP_SUMMARY
145-
echo "✅ MCP server integration tests passed on ${{ matrix.os }}" >> $GITHUB_STEP_SUMMARY
146+
echo "## Integration Tests Summary (${{ matrix.os }}, ${{ matrix.mcp-mode }})" >> $GITHUB_STEP_SUMMARY
147+
echo "✅ MCP server integration tests passed on ${{ matrix.os }} with ${{ matrix.mcp-mode }} transport" >> $GITHUB_STEP_SUMMARY
146148
147149
codeql-path-tests:
148150
name: CODEQL_PATH Tests (${{ matrix.os }})

client/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@
5555
"test:coverage": "echo 'NOOP client test:coverage'",
5656
"test:integration": "scripts/run-integration-tests.sh --no-install-packs",
5757
"test:integration:default": "ENABLE_MONITORING_TOOLS=false scripts/run-integration-tests.sh --no-install-packs",
58+
"test:integration:http": "MCP_MODE=http scripts/run-integration-tests.sh --no-install-packs",
5859
"test:integration:install-packs": "scripts/run-integration-tests.sh",
59-
"test:integration:monitoring": "ENABLE_MONITORING_TOOLS=true scripts/run-integration-tests.sh",
60+
"test:integration:monitoring": "ENABLE_MONITORING_TOOLS=true scripts/run-integration-tests.sh --no-install-packs",
61+
"test:integration:stdio": "MCP_MODE=stdio scripts/run-integration-tests.sh --no-install-packs",
6062
"tidy": "npm run lint && npm run format"
6163
}
6264
}

client/scripts/run-integration-tests.sh

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
# 2. Monitoring mode (monitoring tools enabled) - tests session_* tools
99
#
1010
# Environment Variables:
11-
# HTTP_HOST - Server host (default: localhost)
12-
# HTTP_PORT - Server port (default: 3000)
11+
# MCP_MODE - MCP transport mode (default: stdio, also: http)
12+
# HTTP_HOST - Server host for HTTP mode (default: localhost)
13+
# HTTP_PORT - Server port for HTTP mode (default: 3000)
1314
# TIMEOUT_SECONDS - Request timeout (default: 30)
1415
# ENABLE_MONITORING_TOOLS - Force a specific mode instead of running both:
1516
# "true" = only run with monitoring tools enabled
@@ -20,6 +21,7 @@
2021
# ./run-integration-tests.sh # Run in BOTH modes (recommended)
2122
# ENABLE_MONITORING_TOOLS=false ./run-integration-tests.sh # Only default mode
2223
# ENABLE_MONITORING_TOOLS=true ./run-integration-tests.sh # Only monitoring mode
24+
# MCP_MODE=http ./run-integration-tests.sh # Run using HTTP transport
2325
# ./run-integration-tests.sh --tools session_end # Filter to specific tools
2426

2527
set -e
@@ -29,6 +31,7 @@ CLIENT_DIR="$(dirname "$SCRIPT_DIR")"
2931
SERVER_DIR="$(dirname "$CLIENT_DIR")/server"
3032

3133
# Set default environment variables
34+
export MCP_MODE="${MCP_MODE:-stdio}"
3235
export HTTP_HOST="${HTTP_HOST:-localhost}"
3336
export HTTP_PORT="${HTTP_PORT:-3000}"
3437
export TIMEOUT_SECONDS="${TIMEOUT_SECONDS:-30}"
@@ -65,7 +68,10 @@ for arg in "$@"; do
6568
done
6669

6770
echo "🚀 Starting CodeQL MCP Integration Tests"
68-
echo "Server URL: $URL_SCHEME://$HTTP_HOST:$HTTP_PORT/mcp"
71+
echo "MCP Mode: $MCP_MODE"
72+
if [ "$MCP_MODE" = "http" ]; then
73+
echo "Server URL: $URL_SCHEME://$HTTP_HOST:$HTTP_PORT/mcp"
74+
fi
6975

7076
# Step 1: Build and bundle the server code
7177
echo "📦 Building CodeQL MCP server bundle..."
@@ -81,43 +87,55 @@ else
8187
fi
8288

8389
cd "$CLIENT_DIR"
84-
export MCP_MODE=http
85-
export MCP_SERVER_URL="$URL_SCHEME://$HTTP_HOST:$HTTP_PORT/mcp"
90+
91+
# For HTTP mode, set the server URL for the client
92+
if [ "$MCP_MODE" = "http" ]; then
93+
export MCP_SERVER_URL="$URL_SCHEME://$HTTP_HOST:$HTTP_PORT/mcp"
94+
fi
8695

8796
# Function to run tests in a specific mode
8897
run_tests_in_mode() {
8998
local mode_name="$1"
9099
local enable_monitoring="$2"
91-
100+
92101
echo ""
93102
echo "═══════════════════════════════════════════════════════════════"
94103
echo "🧪 Running integration tests: $mode_name"
95104
echo "═══════════════════════════════════════════════════════════════"
96-
105+
97106
# Set the monitoring tools flag for this run
98107
export ENABLE_MONITORING_TOOLS="$enable_monitoring"
99-
100-
# Start MCP server with current settings
101-
echo "🚀 Starting MCP server (monitoring=$enable_monitoring)..."
102-
"$SCRIPT_DIR/start-server.sh"
103-
104-
# Wait for server startup
105-
echo "⏳ Waiting for server startup..."
106-
"$SCRIPT_DIR/wait-for-server.sh"
107-
108+
109+
if [ "$MCP_MODE" = "http" ]; then
110+
# HTTP mode: start server in background, run tests, stop server
111+
echo "🚀 Starting MCP server (monitoring=$enable_monitoring)..."
112+
"$SCRIPT_DIR/start-server.sh"
113+
114+
# Wait for server startup
115+
echo "⏳ Waiting for server startup..."
116+
"$SCRIPT_DIR/wait-for-server.sh"
117+
else
118+
# stdio mode: client spawns server directly via StdioClientTransport
119+
echo "📡 Using stdio transport (client spawns server directly)"
120+
fi
121+
108122
# Run the integration tests (skip pack installation since we already did it)
109123
echo "🧪 Running tests..."
110124
node src/ql-mcp-client.js integration-tests --no-install-packs "$@"
111-
112-
# Stop the server before next mode
113-
echo "🛑 Stopping server..."
114-
"$SCRIPT_DIR/stop-server.sh"
125+
126+
if [ "$MCP_MODE" = "http" ]; then
127+
# Stop the server before next mode
128+
echo "🛑 Stopping server..."
129+
"$SCRIPT_DIR/stop-server.sh"
130+
fi
115131
}
116132

117133
# Trap to ensure cleanup happens even if script fails
118134
cleanup() {
119135
echo "🧹 Cleaning up..."
120-
"$SCRIPT_DIR/stop-server.sh" 2>/dev/null || true
136+
if [ "$MCP_MODE" = "http" ]; then
137+
"$SCRIPT_DIR/stop-server.sh" 2>/dev/null || true
138+
fi
121139
}
122140
trap cleanup EXIT
123141

client/src/ql-mcp-client.js

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
/* global URL, setTimeout */
99

1010
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
11+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
1112
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
1213
import { execSync } from "child_process";
1314
import dotenv from "dotenv";
@@ -36,6 +37,7 @@ class CodeQLMCPClient {
3637
constructor(options = {}) {
3738
this.client = null;
3839
this.transport = null;
40+
this.mcpMode = process.env.MCP_MODE || "stdio";
3941
this.serverUrl = process.env.MCP_SERVER_URL || DEFAULT_SERVER_URL;
4042
this.timeout = parseInt(options.timeout || process.env.TIMEOUT_SECONDS || "30") * 1000;
4143
this.logger = new TestLogger();
@@ -113,14 +115,34 @@ class CodeQLMCPClient {
113115
*/
114116
async connect() {
115117
try {
116-
this.logger.log(`Connecting to MCP server at ${this.serverUrl}`);
118+
this.logger.log(`Connecting to MCP server (mode: ${this.mcpMode})`);
117119

118120
this.client = new Client({
119121
name: "codeql-development-mcp-client",
120122
version: "1.0.0"
121123
});
122124

123-
this.transport = new StreamableHTTPClientTransport(new URL(this.serverUrl));
125+
if (this.mcpMode === "stdio") {
126+
const serverPath = path.join(
127+
__dirname,
128+
"..",
129+
"server",
130+
"dist",
131+
"codeql-development-mcp-server.js"
132+
);
133+
this.transport = new StdioClientTransport({
134+
command: "node",
135+
args: [serverPath],
136+
env: {
137+
...process.env,
138+
TRANSPORT_MODE: "stdio"
139+
},
140+
stderr: "pipe"
141+
});
142+
} else {
143+
this.logger.log(`Server URL: ${this.serverUrl}`);
144+
this.transport = new StreamableHTTPClientTransport(new URL(this.serverUrl));
145+
}
124146

125147
// Set up timeout
126148
const connectPromise = this.client.connect(this.transport);
@@ -169,7 +191,10 @@ class CodeQLMCPClient {
169191
*/
170192
async runTests() {
171193
this.logger.log("Starting CodeQL MCP Client Integration Tests");
172-
this.logger.log(`Server URL: ${this.serverUrl}`);
194+
this.logger.log(`MCP Mode: ${this.mcpMode}`);
195+
if (this.mcpMode === "http") {
196+
this.logger.log(`Server URL: ${this.serverUrl}`);
197+
}
173198
this.logger.log(`Timeout: ${this.timeout}ms`);
174199

175200
// Check CodeQL CLI availability first
@@ -212,7 +237,7 @@ class CodeQLMCPClient {
212237
*/
213238
async runMonitoringDemo() {
214239
this.logger.log("🚀 Starting MCP Server Monitoring Demo");
215-
this.logger.log(`Server URL: ${this.serverUrl}`);
240+
this.logger.log(`MCP Mode: ${this.mcpMode}`);
216241

217242
let connected = false;
218243

@@ -315,7 +340,7 @@ class CodeQLMCPClient {
315340
*/
316341
async runWorkflowTests() {
317342
this.logger.log("🔄 Starting Workflow Integration Tests");
318-
this.logger.log(`Server URL: ${this.serverUrl}`);
343+
this.logger.log(`MCP Mode: ${this.mcpMode}`);
319344

320345
let connected = false;
321346

@@ -347,7 +372,7 @@ class CodeQLMCPClient {
347372
*/
348373
async runMonitoringIntegrationTests() {
349374
this.logger.log("📊 Starting Monitoring Integration Tests");
350-
this.logger.log(`Server URL: ${this.serverUrl}`);
375+
this.logger.log(`MCP Mode: ${this.mcpMode}`);
351376

352377
let connected = false;
353378

docs/getting-started.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ This guide covers installation, configuration, and usage of the CodeQL Developme
1212

1313
### VS Code Extension (recommended)
1414

15-
The easiest way to get started is the **VS Code extension**, which automates
16-
installation, configuration, and CodeQL CLI discovery.
17-
See the [VS Code Extension guide](./vscode/extension.md) for details.
18-
19-
The `.vsix` can be downloaded from
15+
The easiest and recommended way to get started is the **VS Code extension** (VSIX
16+
archive bundle), which automates installation, configuration, and CodeQL CLI
17+
discovery. Download the `.vsix` from
2018
[GitHub Releases](https://github.com/advanced-security/codeql-development-mcp-server/releases)
21-
or built from source (`npm run package:vsix` at the repository root).
19+
and install it in VS Code via `Extensions: Install from VSIX…` in the Command
20+
Palette, or build from source (`npm run package:vsix` at the repository root).
21+
See the [VS Code Extension guide](./vscode/extension.md) for details.
2222

2323
### From npm
2424

docs/public.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,22 @@ codeql --version # any recent release
2525

2626
## Installation
2727

28-
### 1. Install the MCP Server (npm)
28+
### Recommended: VS Code Extension (VSIX archive bundle)
29+
30+
The easiest and recommended way to get started is the **VS Code extension**,
31+
distributed as a `.vsix` archive bundle. It automates installation,
32+
configuration, and CodeQL CLI discovery — no manual `mcp.json` editing required.
33+
34+
1. Download the latest `.vsix` from
35+
[GitHub Releases](https://github.com/advanced-security/codeql-development-mcp-server/releases).
36+
2. In VS Code, open the Command Palette (`Ctrl+Shift+P` / `Cmd+Shift+P`) and
37+
run **Extensions: Install from VSIX…**.
38+
3. Select the downloaded `.vsix` file.
39+
40+
The extension bundles the MCP server and will register it automatically on
41+
activation. See the [VS Code Extension guide](./vscode/extension.md) for details.
42+
43+
### Alternative: Install the MCP Server (npm)
2944

3045
The server is published as a public package on [npmjs.org](https://www.npmjs.com/package/codeql-development-mcp-server). No authentication or special configuration is needed:
3146

@@ -39,7 +54,7 @@ npx -y codeql-development-mcp-server
3954

4055
The package ships the bundled server (`dist/codeql-development-mcp-server.js`), production dependencies, and the CodeQL tool query source packs (`ql/*/tools/src/`).
4156

42-
### 2. Install CodeQL Tool Query Packs (optional)
57+
### Alternative: Install CodeQL Tool Query Packs (optional)
4358

4459
The server ships with embedded copies of its tool query packs. If you prefer to manage the packs independently, or want to pin a specific version, download them from GHCR:
4560

docs/testing.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@ Unit tests verify the VS Code extension's TypeScript code outside of the Extensi
4040

4141
Integration tests exercise individual MCP tools against a live server instance using the custom MCP client.
4242

43-
- **Client**: `client/src/ql-mcp-client.js` — starts the MCP server, invokes tools, and validates results.
43+
- **Client**: `client/src/ql-mcp-client.js` — connects to the MCP server, invokes tools, and validates results.
44+
- **Transport modes**: The client supports both `stdio` (default) and `http` transport modes, controlled by the `MCP_MODE` environment variable. In `stdio` mode the client spawns the server as a child process via `StdioClientTransport`; in `http` mode it connects to a separately started HTTP server via `StreamableHTTPClientTransport`.
4445
- **Test data**: `client/integration-tests/primitives/tools/` — each test has `before/` and `after/` directories that define the initial fixture state and, for file-based tests, the expected final state.
4546
- **Run command**: `npm run test:integration:default -w client` (or `npm run test:client` from the repo root).
4647
- **Key properties**:
4748
- Tests are deterministic and repeatable.
4849
- No mocks — tests use real CodeQL databases and queries bundled under `server/ql/`.
50+
- The default transport is `stdio`, matching the primary user experience.
4951
- The `before/monitoring-state.json` file supplies tool arguments. For file-based tests, the integration-test runner diffs filesystem state from `before/` to `after/`; for monitoring-based tests, `after/` artifacts are generally not diffed and are only interpreted for specific validations (for example, `codeql_query_run` interpreted output).
5052

5153
### 2b — Extension integration tests
@@ -81,6 +83,7 @@ From the repository root:
8183

8284
```bash
8385
# Build everything and run all layers (1a + 1b + 2a + 2b)
86+
# Integration tests use stdio transport by default
8487
npm run build-and-test
8588

8689
# Run only server unit tests (1a)
@@ -89,9 +92,13 @@ npm run test:server
8992
# Run extension unit tests + integration tests (1b + 2b)
9093
npm run test:vscode
9194

92-
# Run only MCP tool integration tests (2a)
95+
# Run only MCP tool integration tests (2a) - stdio mode (default)
9396
npm run test:client
9497

98+
# Run MCP tool integration tests with explicit transport mode
99+
MCP_MODE=stdio npm run test:client # stdio transport (default)
100+
MCP_MODE=http npm run test:client # HTTP transport
101+
95102
# Run only extension integration tests (2b)
96103
npm run test:integration -w extensions/vscode
97104
```

0 commit comments

Comments
 (0)