Skip to content

Commit de5f12d

Browse files
authored
Merge pull request #98 from githubnext/copilot/add-execution-environment-validation
Add execution environment validation for containerized MCP Gateway
2 parents d875eb9 + 2230a2c commit de5f12d

9 files changed

Lines changed: 1402 additions & 57 deletions

File tree

Dockerfile

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ WORKDIR /app
2525
# Copy binary from builder
2626
COPY --from=builder /app/awmg .
2727

28-
# Copy run.sh script
28+
# Copy run scripts
2929
COPY run.sh .
30-
RUN chmod +x run.sh
30+
COPY run_containerized.sh .
31+
RUN chmod +x run.sh run_containerized.sh
3132

3233
# Expose default HTTP port
3334
EXPOSE 8000
3435

35-
# Use run.sh as entrypoint
36-
ENTRYPOINT ["/app/run.sh"]
36+
# Use run_containerized.sh as entrypoint for container deployments
37+
# This script requires stdin (-i flag) for JSON configuration
38+
ENTRYPOINT ["/app/run_containerized.sh"]

README.md

Lines changed: 131 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,41 @@ For detailed setup instructions, building from source, and local development, se
3131
docker pull ghcr.io/githubnext/gh-aw-mcpg:latest
3232
```
3333

34-
2. **Run the container**:
34+
2. **Create a configuration file** (`config.json`):
35+
```json
36+
{
37+
"mcpServers": {
38+
"github": {
39+
"type": "stdio",
40+
"container": "ghcr.io/github/github-mcp-server:latest",
41+
"env": {
42+
"GITHUB_PERSONAL_ACCESS_TOKEN": ""
43+
}
44+
}
45+
}
46+
}
47+
```
48+
49+
3. **Run the container**:
3550
```bash
36-
docker run --rm -v $(pwd)/.env:/app/.env \
51+
docker run --rm -i \
52+
-e MCP_GATEWAY_PORT=8000 \
53+
-e MCP_GATEWAY_DOMAIN=localhost \
54+
-e MCP_GATEWAY_API_KEY=your-secret-key \
3755
-v /var/run/docker.sock:/var/run/docker.sock \
56+
-v /path/to/logs:/tmp/gh-aw/sandbox/mcp \
3857
-p 8000:8000 \
39-
ghcr.io/githubnext/gh-aw-mcpg:latest
58+
ghcr.io/githubnext/gh-aw-mcpg:latest < config.json
4059
```
4160

42-
MCPG will start in routed mode on `http://127.0.0.1:8000`, proxying MCP requests to your configured backend servers.
61+
**Required flags:**
62+
- `-i`: Enables stdin for passing JSON configuration
63+
- `-e MCP_GATEWAY_*`: Required environment variables
64+
- `-v /var/run/docker.sock`: Required for spawning backend MCP servers
65+
- `-v /path/to/logs:/tmp/gh-aw/sandbox/mcp`: Mount for persistent gateway logs
66+
- `-p 8000:8000`: Port mapping must match `MCP_GATEWAY_PORT`
67+
68+
MCPG will start in routed mode on `http://0.0.0.0:8000` (using `MCP_GATEWAY_PORT`), proxying MCP requests to your configured backend servers.
4369

4470
## Configuration
4571

@@ -164,12 +190,107 @@ Usage:
164190
Flags:
165191
-c, --config string Path to config file (default "config.toml")
166192
--config-stdin Read MCP server configuration from stdin (JSON format). When enabled, overrides --config
193+
--enable-difc Enable DIFC enforcement and session requirement (requires sys___init call before tool access)
167194
--env string Path to .env file to load environment variables
168195
-h, --help help for awmg
169196
-l, --listen string HTTP server listen address (default "127.0.0.1:3000")
170197
--log-dir string Directory for log files (falls back to stdout if directory cannot be created) (default "/tmp/gh-aw/sandbox/mcp")
171198
--routed Run in routed mode (each backend at /mcp/<server>)
172199
--unified Run in unified mode (all backends at /mcp)
200+
--validate-env Validate execution environment (Docker, env vars) before starting
201+
```
202+
203+
## Environment Variables
204+
205+
The following environment variables are used by the MCP Gateway:
206+
207+
### Required for Production (Containerized Mode)
208+
209+
When running in a container (`run_containerized.sh`), these variables **must** be set:
210+
211+
| Variable | Description | Example |
212+
|----------|-------------|---------|
213+
| `MCP_GATEWAY_PORT` | The port the gateway listens on (used for `--listen` address) | `8080` |
214+
| `MCP_GATEWAY_DOMAIN` | The domain name for the gateway | `localhost` |
215+
| `MCP_GATEWAY_API_KEY` | API key for authentication | `your-secret-key` |
216+
217+
### Optional (Non-Containerized Mode)
218+
219+
When running locally (`run.sh`), these variables are optional (warnings shown if missing):
220+
221+
| Variable | Description | Default |
222+
|----------|-------------|---------|
223+
| `MCP_GATEWAY_PORT` | Gateway listening port | `8000` |
224+
| `MCP_GATEWAY_DOMAIN` | Gateway domain | `localhost` |
225+
| `MCP_GATEWAY_API_KEY` | API authentication key | (disabled) |
226+
| `MCP_GATEWAY_HOST` | Gateway bind address | `0.0.0.0` |
227+
| `MCP_GATEWAY_MODE` | Gateway mode | `--routed` |
228+
| `MCP_GATEWAY_LOG_DIR` | Log file directory | `/tmp/gh-aw/sandbox/mcp` |
229+
230+
### Docker Configuration
231+
232+
| Variable | Description | Default |
233+
|----------|-------------|---------|
234+
| `DOCKER_HOST` | Docker daemon socket path | `/var/run/docker.sock` |
235+
| `DOCKER_API_VERSION` | Docker API version | Auto-detected (1.43 for arm64, 1.44 for amd64) |
236+
237+
## Containerized Mode
238+
239+
### Running in Docker
240+
241+
For production deployments in Docker containers, use `run_containerized.sh` which:
242+
243+
1. **Validates the container environment** before starting
244+
2. **Requires** all essential environment variables
245+
3. **Requires** stdin input (`-i` flag) for JSON configuration
246+
4. **Validates** Docker socket accessibility
247+
5. **Validates** port mapping configuration
248+
249+
```bash
250+
# Correct way to run the gateway in a container:
251+
docker run -i \
252+
-e MCP_GATEWAY_PORT=8080 \
253+
-e MCP_GATEWAY_DOMAIN=localhost \
254+
-e MCP_GATEWAY_API_KEY=your-key \
255+
-v /var/run/docker.sock:/var/run/docker.sock \
256+
-p 8080:8080 \
257+
ghcr.io/githubnext/gh-aw-mcpg:latest < config.json
258+
```
259+
260+
**Important flags:**
261+
- `-i`: Required for passing configuration via stdin
262+
- `-v /var/run/docker.sock:/var/run/docker.sock`: Required for spawning backend MCP servers
263+
- `-p <host>:<container>`: Port mapping must match `MCP_GATEWAY_PORT`
264+
265+
### Validation Checks
266+
267+
The containerized startup script performs these validations:
268+
269+
| Check | Description | Action on Failure |
270+
|-------|-------------|-------------------|
271+
| Docker Socket | Verifies Docker daemon is accessible | Exit with error |
272+
| Environment Variables | Checks required env vars are set | Exit with error |
273+
| Port Mapping | Verifies container port is mapped to host | Exit with error |
274+
| Stdin Interactive | Ensures `-i` flag was used | Exit with error |
275+
| Log Directory Mount | Verifies log directory is mounted to host | Warning (logs won't persist) |
276+
277+
### Non-Containerized Mode
278+
279+
For local development, use `run.sh` which:
280+
281+
1. **Warns** about missing environment variables (but continues)
282+
2. **Provides** default configuration if no config file specified
283+
3. **Auto-detects** containerized environments and redirects to `run_containerized.sh`
284+
285+
```bash
286+
# Run locally with defaults:
287+
./run.sh
288+
289+
# Run with custom config:
290+
CONFIG=my-config.toml ./run.sh
291+
292+
# Run with environment variables:
293+
MCP_GATEWAY_PORT=3000 ./run.sh
173294
```
174295

175296
## Logging
@@ -178,12 +299,17 @@ MCPG provides comprehensive logging of all gateway operations to help diagnose i
178299

179300
### Log File Location
180301

181-
By default, logs are written to `/tmp/gh-aw/sandbox/mcp/mcp-gateway.log`. This location can be configured using the `--log-dir` flag:
302+
By default, logs are written to `/tmp/gh-aw/sandbox/mcp/mcp-gateway.log`. This location can be configured using the `--log-dir` flag or `MCP_GATEWAY_LOG_DIR` environment variable:
182303

183304
```bash
184305
./awmg --config config.toml --log-dir /var/log/mcp-gateway
185306
```
186307

308+
**Important for containerized mode:** Mount the log directory to persist logs outside the container:
309+
```bash
310+
docker run -v /path/on/host:/tmp/gh-aw/sandbox/mcp ...
311+
```
312+
187313
If the log directory cannot be created or accessed, MCPG automatically falls back to logging to stdout.
188314

189315
### What Gets Logged

internal/cmd/root.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ var (
4545
envFile string
4646
enableDIFC bool
4747
logDir string
48+
validateEnv bool
4849
debugLog = logger.New("cmd:root")
4950
version = "dev" // Default version, overridden by SetVersion
5051
)
@@ -67,6 +68,7 @@ func init() {
6768
rootCmd.Flags().StringVar(&envFile, "env", defaultEnvFile, "Path to .env file to load environment variables")
6869
rootCmd.Flags().BoolVar(&enableDIFC, "enable-difc", defaultEnableDIFC, "Enable DIFC enforcement and session requirement (requires sys___init call before tool access)")
6970
rootCmd.Flags().StringVar(&logDir, "log-dir", defaultLogDir, "Directory for log files (falls back to stdout if directory cannot be created)")
71+
rootCmd.Flags().BoolVar(&validateEnv, "validate-env", false, "Validate execution environment (Docker, env vars) before starting")
7072

7173
// Mark mutually exclusive flags
7274
rootCmd.MarkFlagsMutuallyExclusive("routed", "unified")
@@ -96,6 +98,18 @@ func run(cmd *cobra.Command, args []string) error {
9698
}
9799
}
98100

101+
// Validate execution environment if requested
102+
if validateEnv {
103+
debugLog.Printf("Validating execution environment...")
104+
result := config.ValidateExecutionEnvironment()
105+
if !result.IsValid() {
106+
logger.LogError("startup", "Environment validation failed: %s", result.Error())
107+
return fmt.Errorf("environment validation failed: %s", result.Error())
108+
}
109+
logger.LogInfo("startup", "Environment validation passed")
110+
log.Println("Environment validation passed")
111+
}
112+
99113
// Load configuration
100114
var cfg *config.Config
101115
var err error

0 commit comments

Comments
 (0)