Skip to content

Add configurable stdio container runtime (Docker/Podman) with runtime command overrides - #10003

Merged
lpcox merged 9 commits into
mainfrom
copilot/add-config-option-launch-mcp-servers-podman
Jul 24, 2026
Merged

Add configurable stdio container runtime (Docker/Podman) with runtime command overrides#10003
lpcox merged 9 commits into
mainfrom
copilot/add-config-option-launch-mcp-servers-podman

Conversation

Copilot AI commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

This change removes the hard Docker-daemon assumption for stdio MCP server launches by introducing runtime selection (docker/podman) and runtime-command overrides, while preserving the existing stdio parent/child transport model. Default behavior remains Docker-compatible for existing deployments.

  • Runtime selection for stdio container launches

    • Added gateway config for runtime resolution:
      • gateway.containerRuntime (docker default, podman supported)
      • gateway.containerRuntimeCommand (override binary/path, e.g. nerdctl, /usr/bin/podman)
      • gateway.containerRuntimeArgs (prepended runtime-level args before run)
    • Added env override: MCP_GATEWAY_CONTAINER_RUNTIME (validated; invalid values are ignored in favor of configured/default runtime).
  • JSON stdin conversion path (container servers)

    • Stdio server conversion now uses the resolved runtime command instead of hardcoding docker.
    • Launch shape remains <runtime> [runtimeArgs...] run --rm -i ... <image> ..., preserving stdio-only MCP transport and non-networked child process semantics.
  • TOML stdio containerization validation

    • Validation now enforces that stdio server command matches the selected effective runtime command (instead of always requiring "docker").
    • Maintains the containerization invariant while allowing Podman/custom runtime command adoption.
  • Launcher direct-command classification

    • Updated direct-command detection to avoid false positives for container runtimes beyond Docker:
      • recognizes docker / podman / nerdctl
      • recognizes runtime binaries by basename (e.g., /usr/local/bin/podman)
      • treats custom runtime binaries as containerized when first arg is run.
  • Documentation updates

    • Updated configuration and env-var docs to describe runtime selection/override behavior and rootless Podman prerequisites.
    • Updated examples to reflect runtime-agnostic stdio container launch expectations.
{
  "gateway": {
    "containerRuntime": "podman",
    "containerRuntimeCommand": "/usr/bin/podman",
    "containerRuntimeArgs": ["--events-backend=file"]
  },
  "mcpServers": {
    "github": {
      "type": "stdio",
      "container": "ghcr.io/github/github-mcp-server:latest"
    }
  }
}

Copilot AI changed the title [WIP] Add config option to launch MCP servers via Podman Add configurable stdio container runtime (Docker/Podman) with runtime command overrides Jul 24, 2026
Copilot finished work on behalf of lpcox July 24, 2026 17:24
Copilot AI requested a review from lpcox July 24, 2026 17:24
@lpcox
lpcox marked this pull request as ready for review July 24, 2026 18:26
Copilot AI review requested due to automatic review settings July 24, 2026 18:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds configurable Docker/Podman runtime support while preserving stdio transport.

Changes:

  • Adds runtime selection, command overrides, and runtime arguments.
  • Updates TOML validation and launcher classification.
  • Adds tests, schema fields, and operator documentation.
Show a summary per file
File Description
README.md Documents runtime environment override.
internal/launcher/launcher.go Classifies container runtime launches.
internal/launcher/launcher_runtime_test.go Tests runtime classification.
internal/config/validation_test.go Tests Podman TOML validation.
internal/config/validation_gateway.go Validates selected runtime commands.
internal/config/schema/mcp-gateway-config.schema.json Defines JSON runtime settings.
internal/config/container_runtime.go Resolves runtime configuration.
internal/config/config_stdin.go Builds runtime-specific stdio commands.
internal/config/config_stdin_test.go Tests runtime selection and overrides.
internal/config/config_core.go Adds core runtime fields and defaults.
docs/ENVIRONMENT_VARIABLES.md Documents the environment override.
docs/CONFIGURATION.md Documents configuration and Podman prerequisites.
config.example.toml Adds runtime configuration examples.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 13/13 changed files
  • Comments generated: 4
  • Review effort level: Medium

Comment on lines +26 to +33
func validateContainerRuntimeValue(value, fieldName string) error {
switch normalizeContainerRuntime(value) {
case "", containerRuntimeDocker, containerRuntimePodman:
return nil
default:
return fmt.Errorf("%s must be one of: docker, podman (got: %q)", fieldName, value)
}
}
Comment thread internal/launcher/launcher.go Outdated
Comment on lines +309 to +310
if len(args) > 0 && strings.EqualFold(args[0], "run") {
return false
Comment thread internal/config/validation_gateway.go Outdated
func validateTOMLStdioContainerization(servers map[string]*ServerConfig) error {
func validateTOMLStdioContainerization(servers map[string]*ServerConfig, gateway *GatewayConfig) error {
logValidation.Print("Validating TOML stdio server containerization requirement")
expectedCommand := effectiveContainerRuntimeCommand(gateway)
Comment thread config.example.toml Outdated
Comment on lines +43 to +44
# Optional: Runtime-level args inserted before "run" for stdio container launches
# container_runtime_args = []
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@lpcox

lpcox commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

@copilot address review feedback and also make sure that podman is installed in the mcpg container image

Copilot AI commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

@copilot address review feedback and also make sure that podman is installed in the mcpg container image

Addressed in this PR update (latest commit 903c812, with related follow-ups in 4ab3a52..0760959). This includes the runtime/validation/launcher review fixes and Podman installation in the mcpg container image (Dockerfile now installs podman alongside docker-cli).

Copilot finished work on behalf of lpcox July 24, 2026 19:45
@github-actions

Copy link
Copy Markdown
Contributor

🔒 mcpg Read-Only Stress — gVisor

Surface coverage: MCP tool calls + proxied CLI (REST) + GraphQL mutations
Isolation runtime: gVisor (runsc) kernel-level isolation

Part Surface Op Result Expected Status
A MCP reads (list_issues/PRs/commits, get_file_contents) data returned ALLOWED
B MCP writes (reaction/star/issue/comment/branch/file/PR) gateway refused (unknown tool) BLOCKED
C CLI reads (list_issues, get_file_contents) data returned ALLOWED
D CLI REST writes (reaction/star/issue/comment/file) refused (gh unauthenticated) BLOCKED
E CLI GraphQL mutations (addReaction/addStar/createIssue) refused (gh unauthenticated) BLOCKED

Overall: PASS

References: §30121486918

🔒 mcpg read-only stress (gVisor runtime) by Read-Only Stress: gVisor runtime

@github-actions

Copy link
Copy Markdown
Contributor

🔒 mcpg Read-Only Stress — default AWF

Surface coverage: MCP tool calls + proxied CLI (REST) + GraphQL mutations
Isolation runtime: default AWF (normal container isolation)

Part Surface Op Result Expected Status
A MCP reads (issues/PRs/file/commits) data returned ALLOWED
B MCP writes (reaction/star/issue/comment/branch/file/PR) all refused BLOCKED
C CLI reads (list_issues, get_file_contents) data returned ALLOWED
D CLI REST writes (reaction/star/issue/comment/file) all refused BLOCKED
E CLI GraphQL mutations (addReaction/addStar/createIssue) all refused BLOCKED

Overall: PASS

Full Report

Part A — MCP Reads (ALLOWED ✅)

  • list_issues: 3 issues returned
  • list_pull_requests: 3 PRs returned
  • get_file_contents README.md: content returned
  • list_commits: 3 commits returned

Part B — MCP Writes (BLOCKED ✅)

Gateway exposes 22 read-only tools only. Write tools are filtered at the gateway level. All write attempts returned Error [-32602]: unknown tool (MCP-level gateway enforcement via tool-list filtering).

  • add_issue_reactionunknown tool (gateway filtered)
  • star_repositoryunknown tool (gateway filtered)
  • create_issueunknown tool (gateway filtered)
  • add_issue_commentunknown tool (gateway filtered)
  • create_branchunknown tool (gateway filtered)
  • create_or_update_fileunknown tool (gateway filtered)
  • create_pull_requestunknown tool (gateway filtered)

MCP error -32602 is the gateway enforcement mechanism: write tools are stripped from the exposed tool list and are not callable.

Part C — CLI Reads (ALLOWED ✅)

  • github list_issues: data returned via MCP bridge
  • github get_file_contents: data returned via MCP bridge

Part D — CLI REST Writes (BLOCKED ✅)

All REST write calls rejected — gh has no GH_TOKEN in this environment (unauthenticated). Combined boundary: mcpg read-only MCP + unauthenticated gh CLI.

Part E — GraphQL Mutations (BLOCKED ✅)

All GraphQL mutation attempts rejected — gh unauthenticated (GH_TOKEN not set).

Result: RESULT=PASS RUNTIME=default RUNID=30121487008

References: §30121487008

🔒 mcpg read-only stress (default AWF runtime) by Read-Only Stress: default runtime

@github-actions

Copy link
Copy Markdown
Contributor

🔒 mcpg Read-Only Stress — docker-sbx

Surface coverage: MCP tool calls + proxied CLI (REST) + GraphQL mutations
Isolation runtime: docker-sbx (KVM-isolated microVM)

Part Surface Op Result Expected Status
A MCP reads (list_issues, list_prs, get_file, list_commits) data returned ALLOWED
B MCP writes (reaction/star/issue/comment/branch/file/PR) unknown tool [-32602] gateway error BLOCKED
C CLI reads (list_issues, get_file_contents) data returned ALLOWED
D CLI REST writes (reaction/star/issue/comment/file) gh unauthenticated (no GH_TOKEN) BLOCKED
E CLI GraphQL mutations (addReaction/addStar/createIssue) gh unauthenticated (no GH_TOKEN) BLOCKED

Overall: PASS

Part B note: Gateway enforces via MCP error [-32602]: unknown tool "<write_tool>" — write tools are absent from the gateway tool registry, a gateway-level enforcement boundary distinct from backend read-only config.
Part D/E note: gh CLI has no GH_TOKEN in this docker-sbx runtime; all REST/GraphQL write attempts fail at auth.

References: §30121486968

🔒 mcpg read-only stress (docker-sbx runtime) by Read-Only Stress: docker-sbx runtime

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.

Add config option to launch MCP servers via Podman (remove Docker socket dependency)

3 participants