Skip to content

feat: add integration tests for AWF + MCP Gateway host access#195

Closed
Copilot wants to merge 6 commits intomainfrom
copilot/add-integration-tests-ci
Closed

feat: add integration tests for AWF + MCP Gateway host access#195
Copilot wants to merge 6 commits intomainfrom
copilot/add-integration-tests-ci

Conversation

Copy link
Contributor

Copilot AI commented Jan 9, 2026

Adds integration tests to verify AWF's host access features required for MCP Gateway integration, including --enable-host-access, environment variable passthrough, and volume mounts.

Changes

  • tests/fixtures/awf-runner.ts: Extended AwfOptions with:

    • enableHostAccess - enables --enable-host-access flag
    • envAll - enables --env-all flag
    • Added --env KEY=VALUE flag support for individual env vars
  • tests/integration/mcpg-integration.test.ts: 15 tests covering:

    • Host access configuration (host.docker.internal resolution)
    • Volume mounts for config files
    • Environment variable passthrough (--env and --env-all)
    • Combined options
    • MCP Gateway integration tests (using ghcr.io/githubnext/gh-aw-mcpg:v0.0.48)
  • .github/workflows/test-integration.yml: Added test-mcpg-integration job with:

    • MCP Gateway image pull (ghcr.io/githubnext/gh-aw-mcpg:v0.0.48)
    • Gateway container cleanup in post-test step

Usage

const result = await runner.runWithSudo('getent hosts host.docker.internal', {
  allowDomains: ['host.docker.internal'],
  enableHostAccess: true,
  envAll: true,
  env: { GITHUB_PERSONAL_ACCESS_TOKEN: token },
  volumeMounts: [`${os.tmpdir()}:${os.tmpdir()}:ro`],
});
Original prompt

Add integration tests / CI to test the integration of awf (firewall) and gh-aw-mcpg. Reference doc below:

Integrating gh-aw-mcpg with gh-aw-firewall

This guide explains how to run an MCP gateway as a Docker container on the host and connect to it from within an AWF (Agentic Workflow Firewall) container.

Tested versions:

  • ghcr.io/githubnext/gh-aw-mcpg:v0.0.10
  • gh-aw-firewall v0.8.2

Architecture

┌────────────────────────────────────────────────────────────────────┐
│                           HOST MACHINE                              │
│                                                                     │
│   ┌─────────────────────┐                                          │
│   │   gh-aw-mcpg        │◄──────────────────────┐                  │
│   │   (Docker container)│                       │                  │
│   │   Port 80:8000      │                       │                  │
│   └─────────────────────┘                       │                  │
│            │                                    │                  │
│            │ spawns (via docker socket)         │                  │
│            ▼                                    │                  │
│   ┌─────────────────────┐                       │                  │
│   │   GitHub MCP Server │                       │                  │
│   │   (Docker container)│                       │                  │
│   └─────────────────────┘                       │                  │
│                                                 │                  │
│   ┌─────────────────────────────────────────────┼────────────────┐ │
│   │                    AWF Network              │                │ │
│   │                                             │                │ │
│   │   ┌─────────────────┐    ┌─────────────────┼──┐             │ │
│   │   │   Agent         │    │   Squid Proxy      │             │ │
│   │   │   Container     │───▶│   172.30.0.10      │             │ │
│   │   │   172.30.0.20   │    │                    │             │ │
│   │   │                 │    │   CONNECT to       │─────────────┘ │
│   │   │   Copilot CLI   │    │   host.docker.     │               │
│   │   │   + MCP Client  │    │   internal:80      │               │
│   │   └─────────────────┘    └────────────────────┘               │
│   │                                                               │
│   └───────────────────────────────────────────────────────────────┘
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Prerequisites

  1. gh-aw-mcpg (MCP Gateway) - Docker image ghcr.io/githubnext/gh-aw-mcpg:v0.0.10 or later
  2. gh-aw-firewall v0.8.2 or later (includes all required fixes)
  3. Docker with access to Docker socket

Step 1: Start the MCP Gateway Container

The MCP Gateway is available as a Docker image that includes default MCP servers (github, fetch, memory).

export GITHUB_PERSONAL_ACCESS_TOKEN="<your-github-mcp-token>"

# Pull and run the gateway container
docker run -d --name mcpg-gateway \
  --restart unless-stopped \
  -p 80:8000 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -e "GITHUB_PERSONAL_ACCESS_TOKEN=${GITHUB_PERSONAL_ACCESS_TOKEN}" \
  ghcr.io/githubnext/gh-aw-mcpg:v0.0.10

Note: The container runs on port 8000 internally, mapped to port 80 on the host. It needs Docker socket access to spawn MCP server containers.

Verify it's running:

curl http://127.0.0.1:80/health
# Should return: OK

Optional: Custom Gateway Configuration

If you need to customize the MCP servers, create /tmp/mcpg-config.json:

{
  "mcpServers": {
    "github": {
      "type": "local",
      "container": "ghcr.io/github/github-mcp-server:latest",
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": ""
      }
    }
  }
}

Then mount it when starting the container:

docker run -d --name mcpg-gateway \
  --restart unless-stopped \
  -p 80:8000 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /tmp/mcpg-config.json:/config.json:ro \
  -e "GITHUB_PERSONAL_ACCESS_TOKEN=${GITHUB_PERSONAL_ACCESS_TOKEN}" \
  -e "CONFIG_FILE=/config.json" \
  ghcr.io/githubnext/gh-aw-mcpg:v0.0.10

Step 2: Create MCP Client Configuration for Copilot

Create /tmp/mcp-gateway-config.json:

{
  "mcpServers": {
    "github-gateway": {
      "type": "http",
      "url": "http://host.docker.internal/mcp/github",
      "headers": {
        "Authorization": "Bearer my-session-token"
      },
      "tools": ["*"]
    }
  }
}

Key points:

  • type: "http" - Connect via HTTP instead of spawning a local process
  • url uses host.docker.internal - Docker's special hostname to reach the host
  • headers - Pass Bearer token for gateway authentication (can be any string for session identification)

Step 3: Run AWF with Copilot CLI

export GITHUB_TOKEN="<your-copilot-token>"
e...

</details>



<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

Copilot AI and others added 3 commits January 9, 2026 17:44
Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com>
Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com>
Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com>
Copilot AI changed the title [WIP] Add integration tests for awf and gh-aw-mcpg feat: add integration tests for AWF + MCP Gateway host access Jan 9, 2026
Copilot AI requested a review from Mossaka January 9, 2026 18:32
@Mossaka Mossaka marked this pull request as ready for review January 9, 2026 19:24
@github-actions
Copy link
Contributor

github-actions bot commented Jan 9, 2026

✅ Coverage Check Passed

Overall Coverage

Metric Base PR Delta
Lines 77.19% 77.19% ➡️ +0.00%
Statements 77.27% 77.27% ➡️ +0.00%
Functions 77.17% 77.17% ➡️ +0.00%
Branches 69.76% 69.76% ➡️ +0.00%

Coverage comparison generated by scripts/ci/compare-coverage.ts

@Mossaka
Copy link
Collaborator

Mossaka commented Jan 13, 2026

@copilot refresh the gateway with the latest release and newest behaviour

Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 13, 2026

@copilot refresh the gateway with the latest release and newest behaviour

Updated MCP Gateway to v0.0.48 (latest stable) and added gateway integration tests in 8d7927f. The tests now include:

  • Gateway health endpoint test
  • Host access resolution test for gateway connectivity
  • MCP config mounting test

The workflow also now pulls the latest gateway image before running tests.

…on-tests-ci

# Conflicts:
#	.github/workflows/test-integration.yml
@github-actions
Copy link
Contributor

github-actions bot commented Jan 17, 2026

🎬 THE ENDSmoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨

@github-actions
Copy link
Contributor

github-actions bot commented Jan 17, 2026

📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤

@github-actions
Copy link
Contributor

github-actions bot commented Jan 17, 2026

🌑 The shadows whisper... Smoke Codex failed. The oracle requires further meditation...

},
},
};
fs.writeFileSync(testConfigPath, JSON.stringify(testConfig, null, 2));

Check failure

Code scanning / CodeQL

Insecure temporary file High test

Insecure creation of file in
the os temp dir
.

Copilot Autofix

AI 19 days ago

In general, the fix is to stop constructing temp file paths manually from os.tmpdir() and instead use a library that securely creates a unique file with appropriate permissions, such as tmp. That library creates the file atomically in a safe way and returns its path, eliminating the race condition and permission-issues associated with hand-rolled temp file names.

Concretely here, in tests/integration/mcpg-integration.test.ts, we should:

  • Import the tmp library.
  • Replace the manual construction of testConfigPath using os.tmpdir() and Date.now() with a call to tmp.fileSync, letting it create the file securely under the hood.
  • Keep the rest of the logic (writing JSON to that file and later deleting it) unchanged.

Specifically:

  • Add import * as tmp from 'tmp'; alongside the other imports at the top of tests/integration/mcpg-integration.test.ts.
  • Replace the line:
    • testConfigPath = path.join(tmpDir, \awf-test-config-${Date.now()}.json`);with a secure creation usingtmp.fileSync`, e.g.:
    • const tmpFile = tmp.fileSync({ prefix: 'awf-test-config-', postfix: '.json' });
    • testConfigPath = tmpFile.name;
      We keep tmpDir defined, but no longer use it for the config file path. The rest of the setup and cleanup logic stays as-is.
Suggested changeset 2
tests/integration/mcpg-integration.test.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/tests/integration/mcpg-integration.test.ts b/tests/integration/mcpg-integration.test.ts
--- a/tests/integration/mcpg-integration.test.ts
+++ b/tests/integration/mcpg-integration.test.ts
@@ -20,6 +20,7 @@
 import * as fs from 'fs';
 import * as os from 'os';
 import * as path from 'path';
+import * as tmp from 'tmp';
 import execa = require('execa');
 
 // MCP Gateway configuration - use latest stable version
@@ -40,8 +41,9 @@
     runner = createRunner();
     docker = createDockerHelper();
 
-    // Create a test config file for volume mount tests
-    testConfigPath = path.join(tmpDir, `awf-test-config-${Date.now()}.json`);
+    // Create a test config file for volume mount tests using a secure temp file
+    const tmpFile = tmp.fileSync({ prefix: 'awf-test-config-', postfix: '.json' });
+    testConfigPath = tmpFile.name;
     const testConfig = {
       testKey: 'testValue',
       servers: {
EOF
@@ -20,6 +20,7 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as tmp from 'tmp';
import execa = require('execa');

// MCP Gateway configuration - use latest stable version
@@ -40,8 +41,9 @@
runner = createRunner();
docker = createDockerHelper();

// Create a test config file for volume mount tests
testConfigPath = path.join(tmpDir, `awf-test-config-${Date.now()}.json`);
// Create a test config file for volume mount tests using a secure temp file
const tmpFile = tmp.fileSync({ prefix: 'awf-test-config-', postfix: '.json' });
testConfigPath = tmpFile.name;
const testConfig = {
testKey: 'testValue',
servers: {
package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -42,7 +42,8 @@
     "chalk": "^4.1.2",
     "commander": "^12.0.0",
     "execa": "^5.1.1",
-    "js-yaml": "^4.1.1"
+    "js-yaml": "^4.1.1",
+    "tmp": "^0.2.5"
   },
   "devDependencies": {
     "@commitlint/cli": "^20.1.0",
EOF
@@ -42,7 +42,8 @@
"chalk": "^4.1.2",
"commander": "^12.0.0",
"execa": "^5.1.1",
"js-yaml": "^4.1.1"
"js-yaml": "^4.1.1",
"tmp": "^0.2.5"
},
"devDependencies": {
"@commitlint/cli": "^20.1.0",
This fix introduces these dependencies
Package Version Security advisories
tmp (npm) 0.2.5 None
Copilot is powered by AI and may make mistakes. Always verify output.
},
},
};
fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));

Check failure

Code scanning / CodeQL

Insecure temporary file High test

Insecure creation of file in
the os temp dir
.

Copilot Autofix

AI 19 days ago

In general, to fix insecure temporary file creation in Node.js, avoid manually building paths under os.tmpdir() and instead delegate to a library that creates unique, non-world-readable files atomically. The recommended library is tmp, which handles secure file creation and cleanup.

For this specific test in tests/integration/mcpg-integration.test.ts, the best fix is to replace the manual construction of mcpConfigPath (path.join(tmpDir, ...) based on os.tmpdir()) with a secure temporary file created by tmp. We will:

  1. Add an import for the tmp library near the other imports.
  2. Replace the const mcpConfigPath = path.join(tmpDir, ...) line with a call to tmp.fileSync that:
    • uses a prefix like 'mcp-gateway-config-'
    • uses a .json postfix
    • sets dir: tmpDir to keep the file in the same temp directory (so the existing volumeMounts still work).
  3. Store the tmp file handle (tmpFile) so we can both access .name (the path) and call .removeCallback() in finally for cleanup.
  4. Retain the existing fs.writeFileSync(mcpConfigPath, ...) and the Docker volumeMounts logic, but in the finally block, instead of only unlinking the path, prefer tmpFile.removeCallback() (with a fallback unlinkSync if needed).

These changes are all within the shown test file and do not change the behavior visible to the rest of the code: the test still mounts the same temp directory and reads the config file, but the file is now created securely.

Suggested changeset 2
tests/integration/mcpg-integration.test.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/tests/integration/mcpg-integration.test.ts b/tests/integration/mcpg-integration.test.ts
--- a/tests/integration/mcpg-integration.test.ts
+++ b/tests/integration/mcpg-integration.test.ts
@@ -20,6 +20,7 @@
 import * as fs from 'fs';
 import * as os from 'os';
 import * as path from 'path';
+import * as tmp from 'tmp';
 import execa = require('execa');
 
 // MCP Gateway configuration - use latest stable version
@@ -418,8 +419,14 @@
   test('MCP Gateway config can be mounted into AWF container', async () => {
     await startMcpGateway();
 
-    // Create MCP client config file
-    const mcpConfigPath = path.join(tmpDir, `mcp-gateway-config-${Date.now()}.json`);
+    // Create MCP client config file securely in the temp directory
+    const tmpFile = tmp.fileSync({
+      dir: tmpDir,
+      prefix: 'mcp-gateway-config-',
+      postfix: '.json',
+      discardDescriptor: true,
+    });
+    const mcpConfigPath = tmpFile.name;
     const mcpConfig = {
       mcpServers: {
         'github-gateway': {
@@ -450,8 +457,12 @@
       expect(result.stdout).toContain('github-gateway');
       expect(result.stdout).toContain(`host.docker.internal:${MCPG_HOST_PORT}`);
     } finally {
-      if (fs.existsSync(mcpConfigPath)) {
-        fs.unlinkSync(mcpConfigPath);
+      try {
+        tmpFile.removeCallback();
+      } catch {
+        if (fs.existsSync(mcpConfigPath)) {
+          fs.unlinkSync(mcpConfigPath);
+        }
       }
     }
   }, 180000);
EOF
@@ -20,6 +20,7 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as tmp from 'tmp';
import execa = require('execa');

// MCP Gateway configuration - use latest stable version
@@ -418,8 +419,14 @@
test('MCP Gateway config can be mounted into AWF container', async () => {
await startMcpGateway();

// Create MCP client config file
const mcpConfigPath = path.join(tmpDir, `mcp-gateway-config-${Date.now()}.json`);
// Create MCP client config file securely in the temp directory
const tmpFile = tmp.fileSync({
dir: tmpDir,
prefix: 'mcp-gateway-config-',
postfix: '.json',
discardDescriptor: true,
});
const mcpConfigPath = tmpFile.name;
const mcpConfig = {
mcpServers: {
'github-gateway': {
@@ -450,8 +457,12 @@
expect(result.stdout).toContain('github-gateway');
expect(result.stdout).toContain(`host.docker.internal:${MCPG_HOST_PORT}`);
} finally {
if (fs.existsSync(mcpConfigPath)) {
fs.unlinkSync(mcpConfigPath);
try {
tmpFile.removeCallback();
} catch {
if (fs.existsSync(mcpConfigPath)) {
fs.unlinkSync(mcpConfigPath);
}
}
}
}, 180000);
package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -42,7 +42,8 @@
     "chalk": "^4.1.2",
     "commander": "^12.0.0",
     "execa": "^5.1.1",
-    "js-yaml": "^4.1.1"
+    "js-yaml": "^4.1.1",
+    "tmp": "^0.2.5"
   },
   "devDependencies": {
     "@commitlint/cli": "^20.1.0",
EOF
@@ -42,7 +42,8 @@
"chalk": "^4.1.2",
"commander": "^12.0.0",
"execa": "^5.1.1",
"js-yaml": "^4.1.1"
"js-yaml": "^4.1.1",
"tmp": "^0.2.5"
},
"devDependencies": {
"@commitlint/cli": "^20.1.0",
This fix introduces these dependencies
Package Version Security advisories
tmp (npm) 0.2.5 None
Copilot is powered by AI and may make mistakes. Always verify output.
@github-actions
Copy link
Contributor

Smoke Test Results (Claude)

Last 2 Merged PRs:

  • ci: pin GitHub Actions to commit SHAs for supply chain security
  • (title extracted from JSON data)

Test Results:

  • ✅ GitHub MCP: Retrieved PRs successfully
  • ✅ Playwright: Page title contains "GitHub"
  • ✅ File Write: Created /tmp/gh-aw/agent/smoke-test-claude-21092110662.txt
  • ✅ Bash: Verified file contents

Overall Status: PASS

AI generated by Smoke Claude

@github-actions
Copy link
Contributor

Smoke Test Results

Last 2 Merged PRs:

Test Results:

  • ✅ GitHub MCP: Retrieved last 2 PRs
  • ✅ Playwright: GitHub page title verified
  • ✅ File Write: Created smoke test file
  • ✅ Bash: File content verified

Status: PASS

cc @Mossaka (assignee)

AI generated by Smoke Copilot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants