How to Configure MCP Servers in Workflow Frontmatter with Version Pinning
Pin MCP server versions in GitHub Agentic Workflows by adding a version field to the tool configuration in your frontmatter, ensuring reproducible builds across stdio, container, and http server types.
GitHub Agentic Workflows (repository github/gh-aw) enable developers to integrate custom Model Context Protocol (MCP) servers directly into automated workflows. When you configure MCP servers in workflow frontmatter, version pinning is essential to guarantee that the same server binary or container image is used across every execution, preventing silent breaking changes from upstream updates.
Understanding MCP Server Configuration in Frontmatter
Workflow frontmatter in github/gh-aw uses a structured tools: section to declare external capabilities. The system parses these declarations into internal configuration objects that drive the MCP server lifecycle.
The ToolsConfig Structure
In pkg/workflow/tools_types.go, the ToolsConfig struct defines how custom MCP servers are stored. The Custom map holds per-tool configurations using MCPServerConfig values:
// pkg/workflow/tools_types.go L81-L84
type ToolsConfig struct {
// ... other fields ...
Custom map[string]MCPServerConfig `yaml:"custom,omitempty"`
}
This structure captures the version field alongside connection parameters, ensuring the pinned value travels through the entire compilation pipeline.
Where Version Pinning is Defined
The frontmatter parser in pkg/workflow/tools_parser.go extracts the version key from the raw YAML map and normalizes it into the configuration object. Lines 190-299 and 365-404 handle the extraction logic, supporting string, integer, and float representations of version numbers before converting them to canonical string form for the workflow compiler.
How to Pin MCP Server Versions in Workflow Frontmatter
Version pinning syntax varies slightly depending on the MCP server transport type. All configurations reside under the tools: key in your workflow's frontmatter block.
STDIO-Based MCP Servers
For servers that communicate via standard input/output, specify the version alongside the command and args:
---
tools:
my-analysis-server:
type: stdio
command: "node"
args: ["/usr/local/lib/mcp-server/index.js"]
version: "v1.41.0" # Pin to exact release
---
The version field accepts semantic version strings, commit SHAs, or any identifier your deployment system recognizes.
Container-Based MCP Servers
Container-type servers require version pinning according to the validation logic in pkg/workflow/mcp_config_validation.go (lines 270-282). The compiler enforces that container type tools include a version field to prevent accidental "latest" tag usage:
---
tools:
secure-container-tool:
type: container
container: "ghcr.io/example/mcp-security-scanner"
version: "sha-09deac4" # Immutable digest preferred
env:
SCAN_DEPTH: "deep"
---
If you omit version for a container tool, the validator returns an error: tool 'X' mcp configuration must specify a 'version' field.
HTTP-Based MCP Servers
HTTP transport optionally supports version pinning for documentation purposes, though the remote server controls the actual implementation:
---
tools:
remote-api-bridge:
type: http
url: "https://api.example.com/mcp/v1"
version: "v2.0" # Informational; verify remote compatibility
---
Validation and Enforcement
The github/gh-aw compiler implements strict validation to ensure version pinning compliance. In pkg/workflow/mcp_config_validation.go, the validation logic specifically checks container-based configurations:
// pkg/workflow/mcp_config_validation.go L270-L282
if config.Type == "container" && config.Version == "" {
return fmt.Errorf("tool '%s' mcp configuration must specify a 'version' field", toolName)
}
This enforcement guarantees that containerized MCP servers cannot execute with floating tags like latest, preventing supply-chain attacks and breaking changes from upstream image updates.
Complete Workflow Frontmatter Example
Here is a production-ready frontmatter block demonstrating multiple MCP server types with strict version pinning:
---
engine: copilot
tools:
# Built-in GitHub toolset (managed by GitHub, version controlled separately)
github:
toolsets: [default]
# Custom stdio server for data analysis
data-processor:
type: stdio
command: "python3"
args: ["/opt/mcp-servers/data-processor/main.py"]
version: "v3.2.1"
# Container-based security scanner with immutable digest
security-audit:
type: container
container: "ghcr.io/company/mcp-security-scanner"
version: "sha-256:09deac4f2b3c8e5d6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9"
env:
SEVERITY_THRESHOLD: "high"
OUTPUT_FORMAT: "sarif"
---
Summary
- Version pinning in
github/gh-awworkflow frontmatter ensures MCP server reproducibility by locking the specific binary or container image used during execution. - Configuration location: The
ToolsConfigstruct inpkg/workflow/tools_types.gostores versions in theCustommap, parsed bypkg/workflow/tools_parser.go. - Container enforcement:
pkg/workflow/mcp_config_validation.gomandates version fields for container-type servers to prevent floating tag usage. - Syntax flexibility: Versions accept semantic tags, Git SHAs, or Docker digests depending on the server type (
stdio,container, orhttp).
Frequently Asked Questions
Why is version pinning required for container-based MCP servers?
Container-based MCP servers in github/gh-aw require version pinning to prevent supply-chain attacks and breaking changes from mutable image tags. The validator in pkg/workflow/mcp_config_validation.go (lines 270-282) explicitly rejects container configurations lacking a version field, ensuring workflows use immutable references like SHA digests rather than floating latest tags.
Can I use semantic versioning or Docker digests for version pinning?
Yes, the version field accepts any string identifier your deployment infrastructure recognizes. For stdio servers, use semantic versions like v1.41.0 or Git commit SHAs. For container servers, Docker image digests (e.g., sha-09deac4 or full SHA-256 hashes) provide the strongest immutability guarantees, though semantic tags are also valid if your registry supports immutable tagging.
What happens if I omit the version field for a stdio MCP server?
Unlike container servers, stdio MCP servers do not enforce version presence in the current github/gh-aw implementation. However, omitting the version risks executing different binary versions across workflow runs if the underlying command path points to a mutable installation. Best practice dictates always specifying a version field for documentation and reproducibility, even when not strictly enforced by pkg/workflow/mcp_config_validation.go.
How does the frontmatter parser handle version formats?
The frontmatter parser in pkg/workflow/tools_parser.go (lines 190-299 and 365-404) normalizes the version value into a canonical string representation. It accepts YAML strings, integers, and floats, converting numeric values to strings (e.g., 1.41 becomes "1.41"). This normalized string is then stored in the MCPServerConfig.Version field defined in pkg/workflow/tools_types.go, ensuring consistent handling regardless of how the user formats the frontmatter value.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →