diff --git a/.github/workflows/issue-triage-llm.lock.yml b/.github/workflows/issue-triage-llm.lock.yml
index 8330fc4e95a..1fc16f19481 100644
--- a/.github/workflows/issue-triage-llm.lock.yml
+++ b/.github/workflows/issue-triage-llm.lock.yml
@@ -1210,12 +1210,14 @@ jobs:
           GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"max\":1},\"missing-tool\":{}}"
         run: |
           mkdir -p /tmp/gh-aw/mcp-config
-          cat > /tmp/gh-aw/mcp-config/mcp-servers.json << 'EOF'
+          cat > ~/.llm-tools-mcp/mcp.json << 'EOF'
           {
-            "mcpServers": {
-              "github": {
-                "command": "docker",
-                "args": [
+            "servers": [
+              {
+                "name": "github",
+                "transport": "stdio",
+                "command": [
+                  "docker",
                   "run",
                   "-i",
                   "--rm",
@@ -1227,9 +1229,13 @@ jobs:
                   "GITHUB_PERSONAL_ACCESS_TOKEN": "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}"
                 }
               },
-              "safe_outputs": {
-                "command": "node",
-                "args": ["/tmp/gh-aw/safe-outputs/mcp-server.cjs"],
+              {
+                "name": "safe-outputs",
+                "transport": "stdio",
+                "command": [
+                  "node",
+                  "/tmp/gh-aw/safe-outputs/mcp-server.cjs"
+                ],
                 "env": {
                   "GITHUB_AW_SAFE_OUTPUTS": "${{ env.GITHUB_AW_SAFE_OUTPUTS }}",
                   "GITHUB_AW_SAFE_OUTPUTS_CONFIG": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }},
@@ -1238,7 +1244,7 @@ jobs:
                   "GITHUB_AW_ASSETS_ALLOWED_EXTS": "${{ env.GITHUB_AW_ASSETS_ALLOWED_EXTS }}"
                 }
               }
-            }
+            ]
           }
           EOF
       - name: Create prompt
@@ -1416,7 +1422,7 @@ jobs:
           # Show logs database path for debugging
           echo "LLM logs database: $(llm logs path)"
         env:
-          GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json
+          GITHUB_AW_MCP_CONFIG: ~/.llm-tools-mcp/mcp.json
           GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
           GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
       - name: Configure llm with GitHub Models and MCP
@@ -1430,12 +1436,9 @@ jobs:
             mkdir -p ~/.llm-tools-mcp
             mkdir -p ~/.llm-tools-mcp/logs
 
-            # Copy MCP configuration to the expected location for llm-tools-mcp
-            cp "$GITHUB_AW_MCP_CONFIG" ~/.llm-tools-mcp/mcp.json
-
-            echo "✓ MCP configuration installed at ~/.llm-tools-mcp/mcp.json"
+            echo "✓ MCP configuration installed at $GITHUB_AW_MCP_CONFIG"
             echo "📋 MCP configuration:"
-            cat ~/.llm-tools-mcp/mcp.json
+            cat "$GITHUB_AW_MCP_CONFIG"
             echo ""
 
             # List available tools for debugging
@@ -1445,7 +1448,7 @@ jobs:
             echo "ℹ No MCP configuration available"
           fi
         env:
-          GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json
+          GITHUB_AW_MCP_CONFIG: ~/.llm-tools-mcp/mcp.json
           GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
           GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -1478,7 +1481,7 @@ jobs:
             cp /tmp/gh-aw/llm-output.txt "$GITHUB_AW_SAFE_OUTPUTS"
           fi
         env:
-          GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json
+          GITHUB_AW_MCP_CONFIG: ~/.llm-tools-mcp/mcp.json
           GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
           GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -1487,7 +1490,7 @@ jobs:
         if: always()
         uses: actions/upload-artifact@v4
         env:
-          GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json
+          GITHUB_AW_MCP_CONFIG: ~/.llm-tools-mcp/mcp.json
           GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
           GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
         with:
@@ -2312,7 +2315,7 @@ jobs:
           AGENT_OUTPUT: ${{ needs.agent.outputs.output }}
           WORKFLOW_NAME: "Issue Triage (LLM)"
           WORKFLOW_DESCRIPTION: "No description provided"
-          WORKFLOW_MARKDOWN: "<!--\nThis shared configuration sets up a custom agentic engine using simonw/llm CLI with GitHub Models.\n\n**Usage:**\nInclude this file in your workflow using frontmatter imports:\n\n```yaml\n---\nimports:\n  - shared/simonw-llm.md\n---\n```\n\n**Requirements:**\n- The workflow uses GitHub Models via the built-in GITHUB_TOKEN (no additional setup required)\n- The llm CLI will be installed via pip along with:\n  - llm-github-models: GitHub Models integration (free tier)\n  - llm-tools-mcp: MCP server support for tool access\n\n**Model:**\n- Uses `github/gpt-4o-mini` by default (free via GitHub Models)\n\n**MCP Tools:**\n- The llm-tools-mcp plugin enables MCP server integration\n- MCP configuration from GITHUB_AW_MCP_CONFIG is copied to `~/.llm-tools-mcp/mcp.json`\n- Tools from MCP servers are automatically enabled via the `-T MCP` flag when MCP config is present\n- MCP config file must be in the format expected by llm-tools-mcp (see https://github.com/VirtusLab/llm-tools-mcp)\n\n**Debugging and Logging:**\nThis configuration includes maximum debug tracing for troubleshooting MCP server issues:\n\n1. **Environment Variables:**\n   - `LLM_TOOLS_MCP_FULL_ERRORS=1` - Enables full error stack traces for MCP connection failures\n   - Set on both \"Configure llm\" and \"Run llm CLI\" steps\n\n2. **CLI Flags:**\n   - `--td` (--tools-debug) - Shows full details of tool executions\n   - `-u` (--usage) - Shows token usage information\n\n3. **MCP Server Logs:**\n   - MCP server connection logs are written to `~/.llm-tools-mcp/logs/`\n   - These logs are uploaded as workflow artifacts (artifact name: `llm-mcp-logs`)\n   - Each MCP server session creates a timestamped log file\n\n4. **Diagnostic Output:**\n   - MCP configuration is printed to stdout during setup\n   - Available MCP tools are listed with `llm tools list`\n   - LLM logs database path is displayed\n\n5. **Log Locations:**\n   - MCP server logs: `~/.llm-tools-mcp/logs/` (uploaded as artifacts)\n   - LLM conversation logs: View with `llm logs path` command\n   - Workflow output: `/tmp/gh-aw/llm-output.txt`\n\n**Troubleshooting MCP Issues:**\nIf MCP servers fail to load:\n1. Check the workflow run artifacts for `llm-mcp-logs`\n2. Review the \"Configure llm with GitHub Models and MCP\" step output for configuration details\n3. Check the \"Run llm CLI\" step output for tool execution details\n4. Look for error messages with full stack traces (enabled by LLM_TOOLS_MCP_FULL_ERRORS)\n\n**Note**: \n- This workflow requires internet access to install Python packages\n- The llm CLI stores conversations in a local SQLite database\n- Output is automatically captured for safe-outputs processing\n- You can customize the model by modifying the MODEL variable in the run step\n- GitHub Models provides free access to 30+ AI models\n-->\n\n# Issue Triage\n\nYou are an issue triage assistant. Your task is to analyze newly created issues and provide helpful triage information.\n\n## Current Issue\n\n- **Issue Number**: ${{ github.event.inputs.issue_number || github.event.issue.number }}\n- **Repository**: ${{ github.repository }}\n- **Issue Content**: \n  ```\n  ${{ needs.activation.outputs.text }}\n  ```\n\n## Triage Guidelines\n\nPlease analyze the issue and provide:\n\n1. **Issue Type Classification**: Determine if this is a:\n   - Bug report (something broken or not working)\n   - Feature request (new functionality)\n   - Documentation update\n   - Question or support request\n   - Enhancement (improvement to existing feature)\n\n2. **Priority Assessment**: Based on the content, suggest a priority level:\n   - Critical (security, data loss, complete breakage)\n   - High (major functionality affected)\n   - Medium (important but not blocking)\n   - Low (minor issue or nice-to-have)\n\n3. **Initial Analysis**: Provide:\n   - A brief summary of the issue\n   - Any missing information that would be helpful\n   - Suggested next steps or questions to ask the reporter\n   - Related components or areas of the codebase that might be affected\n\n## Your Task\n\nAnalyze the issue content above and create a triage comment that includes:\n- The issue type classification\n- The suggested priority level\n- Your initial analysis and recommendations\n\nFormat your response as a helpful comment that will be added to the issue. Use clear formatting with sections and bullet points. Be professional and constructive.\n\n**Important**: Generate a single, well-formatted comment that provides value to both the issue reporter and repository maintainers.\n"
+          WORKFLOW_MARKDOWN: "<!--\nThis shared configuration sets up a custom agentic engine using simonw/llm CLI with GitHub Models.\n\n**Usage:**\nInclude this file in your workflow using frontmatter imports:\n\n```yaml\n---\nimports:\n  - shared/simonw-llm.md\n---\n```\n\n**Requirements:**\n- The workflow uses GitHub Models via the built-in GITHUB_TOKEN (no additional setup required)\n- The llm CLI will be installed via pip along with:\n  - llm-github-models: GitHub Models integration (free tier)\n  - llm-tools-mcp: MCP server support for tool access\n\n**Model:**\n- Uses `github/gpt-4o-mini` by default (free via GitHub Models)\n\n**MCP Tools:**\n- The llm-tools-mcp plugin enables MCP server integration\n- MCP configuration is automatically generated in the llm-tools-mcp extension format using custom engine fields\n- The custom engine is configured with `mcp-config-file: ~/.llm-tools-mcp/mcp.json` and `mcp-servers-field: servers`\n- Tools from MCP servers are automatically enabled via the `-T MCP` flag when MCP config is present\n- Extension config format: `{\"servers\": [{\"name\": \"...\", \"transport\": \"stdio\", \"command\": [...], \"tools\": [...]}]}`\n- See https://github.com/VirtusLab/llm-tools-mcp for more details on the extension format\n\n**Debugging and Logging:**\nThis configuration includes maximum debug tracing for troubleshooting MCP server issues:\n\n1. **Environment Variables:**\n   - `LLM_TOOLS_MCP_FULL_ERRORS=1` - Enables full error stack traces for MCP connection failures\n   - Set on both \"Configure llm\" and \"Run llm CLI\" steps\n\n2. **CLI Flags:**\n   - `--td` (--tools-debug) - Shows full details of tool executions\n   - `-u` (--usage) - Shows token usage information\n\n3. **MCP Server Logs:**\n   - MCP server connection logs are written to `~/.llm-tools-mcp/logs/`\n   - These logs are uploaded as workflow artifacts (artifact name: `llm-mcp-logs`)\n   - Each MCP server session creates a timestamped log file\n\n4. **Diagnostic Output:**\n   - MCP configuration is printed to stdout during setup\n   - Available MCP tools are listed with `llm tools list`\n   - LLM logs database path is displayed\n\n5. **Log Locations:**\n   - MCP server logs: `~/.llm-tools-mcp/logs/` (uploaded as artifacts)\n   - LLM conversation logs: View with `llm logs path` command\n   - Workflow output: `/tmp/gh-aw/llm-output.txt`\n\n**Troubleshooting MCP Issues:**\nIf MCP servers fail to load:\n1. Check the workflow run artifacts for `llm-mcp-logs`\n2. Review the \"Configure llm with GitHub Models and MCP\" step output for configuration details\n3. Check the \"Run llm CLI\" step output for tool execution details\n4. Look for error messages with full stack traces (enabled by LLM_TOOLS_MCP_FULL_ERRORS)\n\n**Note**: \n- This workflow requires internet access to install Python packages\n- The llm CLI stores conversations in a local SQLite database\n- Output is automatically captured for safe-outputs processing\n- You can customize the model by modifying the MODEL variable in the run step\n- GitHub Models provides free access to 30+ AI models\n-->\n\n# Issue Triage\n\nYou are an issue triage assistant. Your task is to analyze newly created issues and provide helpful triage information.\n\n## Current Issue\n\n- **Issue Number**: ${{ github.event.inputs.issue_number || github.event.issue.number }}\n- **Repository**: ${{ github.repository }}\n- **Issue Content**: \n  ```\n  ${{ needs.activation.outputs.text }}\n  ```\n\n## Triage Guidelines\n\nPlease analyze the issue and provide:\n\n1. **Issue Type Classification**: Determine if this is a:\n   - Bug report (something broken or not working)\n   - Feature request (new functionality)\n   - Documentation update\n   - Question or support request\n   - Enhancement (improvement to existing feature)\n\n2. **Priority Assessment**: Based on the content, suggest a priority level:\n   - Critical (security, data loss, complete breakage)\n   - High (major functionality affected)\n   - Medium (important but not blocking)\n   - Low (minor issue or nice-to-have)\n\n3. **Initial Analysis**: Provide:\n   - A brief summary of the issue\n   - Any missing information that would be helpful\n   - Suggested next steps or questions to ask the reporter\n   - Related components or areas of the codebase that might be affected\n\n## Your Task\n\nAnalyze the issue content above and create a triage comment that includes:\n- The issue type classification\n- The suggested priority level\n- Your initial analysis and recommendations\n\nFormat your response as a helpful comment that will be added to the issue. Use clear formatting with sections and bullet points. Be professional and constructive.\n\n**Important**: Generate a single, well-formatted comment that provides value to both the issue reporter and repository maintainers.\n"
         with:
           script: |
             const fs = require('fs');
@@ -2401,7 +2404,7 @@ jobs:
           # Show logs database path for debugging
           echo "LLM logs database: $(llm logs path)"
         env:
-          GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json
+          GITHUB_AW_MCP_CONFIG: ~/.llm-tools-mcp/mcp.json
           GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
           GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
       - name: Configure llm with GitHub Models and MCP
@@ -2415,12 +2418,9 @@ jobs:
             mkdir -p ~/.llm-tools-mcp
             mkdir -p ~/.llm-tools-mcp/logs
 
-            # Copy MCP configuration to the expected location for llm-tools-mcp
-            cp "$GITHUB_AW_MCP_CONFIG" ~/.llm-tools-mcp/mcp.json
-
-            echo "✓ MCP configuration installed at ~/.llm-tools-mcp/mcp.json"
+            echo "✓ MCP configuration installed at $GITHUB_AW_MCP_CONFIG"
             echo "📋 MCP configuration:"
-            cat ~/.llm-tools-mcp/mcp.json
+            cat "$GITHUB_AW_MCP_CONFIG"
             echo ""
 
             # List available tools for debugging
@@ -2430,7 +2430,7 @@ jobs:
             echo "ℹ No MCP configuration available"
           fi
         env:
-          GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json
+          GITHUB_AW_MCP_CONFIG: ~/.llm-tools-mcp/mcp.json
           GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
           GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -2463,7 +2463,7 @@ jobs:
             cp /tmp/gh-aw/llm-output.txt "$GITHUB_AW_SAFE_OUTPUTS"
           fi
         env:
-          GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json
+          GITHUB_AW_MCP_CONFIG: ~/.llm-tools-mcp/mcp.json
           GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
           GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -2472,7 +2472,7 @@ jobs:
         if: always()
         uses: actions/upload-artifact@v4
         env:
-          GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json
+          GITHUB_AW_MCP_CONFIG: ~/.llm-tools-mcp/mcp.json
           GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
         with:
           if-no-files-found: ignore
diff --git a/.github/workflows/shared/simonw-llm.lock.yml b/.github/workflows/shared/simonw-llm.lock.yml
index 0dd0dd5460f..aa5612be9a1 100644
--- a/.github/workflows/shared/simonw-llm.lock.yml
+++ b/.github/workflows/shared/simonw-llm.lock.yml
@@ -200,12 +200,14 @@ jobs:
       - name: Setup MCPs
         run: |
           mkdir -p /tmp/gh-aw/mcp-config
-          cat > /tmp/gh-aw/mcp-config/mcp-servers.json << 'EOF'
+          cat > ~/.llm-tools-mcp/mcp.json << 'EOF'
           {
-            "mcpServers": {
-              "github": {
-                "command": "docker",
-                "args": [
+            "servers": [
+              {
+                "name": "github",
+                "transport": "stdio",
+                "command": [
+                  "docker",
                   "run",
                   "-i",
                   "--rm",
@@ -217,7 +219,7 @@ jobs:
                   "GITHUB_PERSONAL_ACCESS_TOKEN": "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}"
                 }
               }
-            }
+            ]
           }
           EOF
       - name: Create prompt
@@ -348,7 +350,7 @@ jobs:
           # Show logs database path for debugging
           echo "LLM logs database: $(llm logs path)"
         env:
-          GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json
+          GITHUB_AW_MCP_CONFIG: ~/.llm-tools-mcp/mcp.json
           GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
           GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
       - name: Configure llm with GitHub Models and MCP
@@ -362,12 +364,9 @@ jobs:
             mkdir -p ~/.llm-tools-mcp
             mkdir -p ~/.llm-tools-mcp/logs
 
-            # Copy MCP configuration to the expected location for llm-tools-mcp
-            cp "$GITHUB_AW_MCP_CONFIG" ~/.llm-tools-mcp/mcp.json
-
-            echo "✓ MCP configuration installed at ~/.llm-tools-mcp/mcp.json"
+            echo "✓ MCP configuration installed at $GITHUB_AW_MCP_CONFIG"
             echo "📋 MCP configuration:"
-            cat ~/.llm-tools-mcp/mcp.json
+            cat "$GITHUB_AW_MCP_CONFIG"
             echo ""
 
             # List available tools for debugging
@@ -377,7 +376,7 @@ jobs:
             echo "ℹ No MCP configuration available"
           fi
         env:
-          GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json
+          GITHUB_AW_MCP_CONFIG: ~/.llm-tools-mcp/mcp.json
           GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
           GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -410,7 +409,7 @@ jobs:
             cp /tmp/gh-aw/llm-output.txt "$GITHUB_AW_SAFE_OUTPUTS"
           fi
         env:
-          GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json
+          GITHUB_AW_MCP_CONFIG: ~/.llm-tools-mcp/mcp.json
           GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
           GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -419,7 +418,7 @@ jobs:
         if: always()
         uses: actions/upload-artifact@v4
         env:
-          GITHUB_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json
+          GITHUB_AW_MCP_CONFIG: ~/.llm-tools-mcp/mcp.json
           GITHUB_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
         with:
           if-no-files-found: ignore
diff --git a/.github/workflows/shared/simonw-llm.md b/.github/workflows/shared/simonw-llm.md
index 50f7fd72c6f..f8c3fd8175e 100644
--- a/.github/workflows/shared/simonw-llm.md
+++ b/.github/workflows/shared/simonw-llm.md
@@ -1,6 +1,8 @@
 ---
 engine:
   id: custom
+  mcp-config-file: ~/.llm-tools-mcp/mcp.json
+  mcp-servers-field: servers
   steps:
     - name: Install simonw/llm CLI
       run: |
@@ -25,12 +27,9 @@ engine:
           mkdir -p ~/.llm-tools-mcp
           mkdir -p ~/.llm-tools-mcp/logs
           
-          # Copy MCP configuration to the expected location for llm-tools-mcp
-          cp "$GITHUB_AW_MCP_CONFIG" ~/.llm-tools-mcp/mcp.json
-          
-          echo "✓ MCP configuration installed at ~/.llm-tools-mcp/mcp.json"
+          echo "✓ MCP configuration installed at $GITHUB_AW_MCP_CONFIG"
           echo "📋 MCP configuration:"
-          cat ~/.llm-tools-mcp/mcp.json
+          cat "$GITHUB_AW_MCP_CONFIG"
           echo ""
           
           # List available tools for debugging
@@ -113,9 +112,11 @@ imports:
 
 **MCP Tools:**
 - The llm-tools-mcp plugin enables MCP server integration
-- MCP configuration from GITHUB_AW_MCP_CONFIG is copied to `~/.llm-tools-mcp/mcp.json`
+- MCP configuration is automatically generated in the llm-tools-mcp extension format using custom engine fields
+- The custom engine is configured with `mcp-config-file: ~/.llm-tools-mcp/mcp.json` and `mcp-servers-field: servers`
 - Tools from MCP servers are automatically enabled via the `-T MCP` flag when MCP config is present
-- MCP config file must be in the format expected by llm-tools-mcp (see https://github.com/VirtusLab/llm-tools-mcp)
+- Extension config format: `{"servers": [{"name": "...", "transport": "stdio", "command": [...], "tools": [...]}]}`
+- See https://github.com/VirtusLab/llm-tools-mcp for more details on the extension format
 
 **Debugging and Logging:**
 This configuration includes maximum debug tracing for troubleshooting MCP server issues:
diff --git a/pkg/parser/schemas/included_file_schema.json b/pkg/parser/schemas/included_file_schema.json
index 9be98cd69d4..144cc6e80cf 100644
--- a/pkg/parser/schemas/included_file_schema.json
+++ b/pkg/parser/schemas/included_file_schema.json
@@ -230,6 +230,19 @@
               "type": "object",
               "additionalProperties": true
             }
+          },
+          "mcp-config-file": {
+            "type": "string",
+            "description": "Path to the MCP configuration file (custom engine only). Overrides the default path (/tmp/gh-aw/mcp-config/mcp-servers.json)."
+          },
+          "mcp-servers-field": {
+            "type": "string",
+            "description": "Field name for the MCP servers in the configuration file (custom engine only). Common values: 'mcpServers' (default), 'servers'. Used to determine the structure of the generated MCP configuration. Takes precedence over mcp-servers-shape if both are provided."
+          },
+          "mcp-servers-shape": {
+            "type": "string",
+            "enum": ["array", "map"],
+            "description": "Shape of MCP servers in the configuration file (custom engine only). 'array' generates {\"servers\": [{...}]}, 'map' generates {\"mcpServers\": {\"name\": {...}}}. Default: 'map'. Alternative to mcp-servers-field."
           }
         },
         "required": [
diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json
index f0b5e81fe24..0380bbee41d 100644
--- a/pkg/parser/schemas/main_workflow_schema.json
+++ b/pkg/parser/schemas/main_workflow_schema.json
@@ -2452,6 +2452,19 @@
             "config": {
               "type": "string",
               "description": "Additional TOML configuration text that will be appended to the generated config.toml in the action (codex engine only)"
+            },
+            "mcp-config-file": {
+              "type": "string",
+              "description": "Path to the MCP configuration file (custom engine only). Overrides the default path (/tmp/gh-aw/mcp-config/mcp-servers.json)."
+            },
+            "mcp-servers-field": {
+              "type": "string",
+              "description": "Field name for the MCP servers in the configuration file (custom engine only). Common values: 'mcpServers' (default), 'servers'. Used to determine the structure of the generated MCP configuration. Takes precedence over mcp-servers-shape if both are provided."
+            },
+            "mcp-servers-shape": {
+              "type": "string",
+              "enum": ["array", "map"],
+              "description": "Shape of MCP servers in the configuration file (custom engine only). 'array' generates {\"servers\": [{...}]}, 'map' generates {\"mcpServers\": {\"name\": {...}}}. Default: 'map'. Alternative to mcp-servers-field."
             }
           },
           "required": [
diff --git a/pkg/workflow/custom_engine.go b/pkg/workflow/custom_engine.go
index da0991bdd4b..7a7f901c235 100644
--- a/pkg/workflow/custom_engine.go
+++ b/pkg/workflow/custom_engine.go
@@ -2,6 +2,7 @@ package workflow
 
 import (
 	"fmt"
+	"sort"
 	"strings"
 )
 
@@ -53,7 +54,12 @@ func (e *CustomEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str
 			envVars["GITHUB_AW_PROMPT"] = "/tmp/gh-aw/aw-prompts/prompt.txt"
 
 			// Add GITHUB_AW_MCP_CONFIG for MCP server configuration
-			envVars["GITHUB_AW_MCP_CONFIG"] = "/tmp/gh-aw/mcp-config/mcp-servers.json"
+			// Use custom path if specified, otherwise use default
+			mcpConfigPath := "/tmp/gh-aw/mcp-config/mcp-servers.json"
+			if workflowData.EngineConfig != nil && workflowData.EngineConfig.MCPConfigFile != "" {
+				mcpConfigPath = workflowData.EngineConfig.MCPConfigFile
+			}
+			envVars["GITHUB_AW_MCP_CONFIG"] = mcpConfigPath
 
 			// Add GITHUB_AW_SAFE_OUTPUTS if safe-outputs feature is used
 			if workflowData.SafeOutputs != nil {
@@ -143,47 +149,117 @@ func (e *CustomEngine) convertStepToYAML(stepMap map[string]any) (string, error)
 
 // RenderMCPConfig renders MCP configuration using shared logic with Claude engine
 func (e *CustomEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]any, mcpTools []string, workflowData *WorkflowData) {
+	// Determine MCP config file path and servers field name
+	mcpConfigPath := "/tmp/gh-aw/mcp-config/mcp-servers.json"
+	mcpServersField := "mcpServers" // Default to mcpServers format
+	
+	if workflowData != nil && workflowData.EngineConfig != nil {
+		if workflowData.EngineConfig.MCPConfigFile != "" {
+			mcpConfigPath = workflowData.EngineConfig.MCPConfigFile
+		}
+		
+		// Support both mcp-servers-field and mcp-servers-shape
+		// mcp-servers-field takes precedence if both are provided
+		if workflowData.EngineConfig.MCPServersField != "" {
+			mcpServersField = workflowData.EngineConfig.MCPServersField
+		} else if workflowData.EngineConfig.MCPServersShape != "" {
+			// Convert shape to field name
+			if workflowData.EngineConfig.MCPServersShape == "array" {
+				mcpServersField = "servers"
+			} else {
+				mcpServersField = "mcpServers"
+			}
+		}
+	}
+
 	// Custom engine uses the same MCP configuration generation as Claude
-	yaml.WriteString("          cat > /tmp/gh-aw/mcp-config/mcp-servers.json << 'EOF'\n")
-	yaml.WriteString("          {\n")
-	yaml.WriteString("            \"mcpServers\": {\n")
-
-	// Add safe-outputs MCP server if safe-outputs are configured
-	totalServers := len(mcpTools)
-	serverCount := 0
-
-	// Generate configuration for each MCP tool using shared logic
-	for _, toolName := range mcpTools {
-		serverCount++
-		isLast := serverCount == totalServers
-
-		switch toolName {
-		case "github":
-			githubTool := tools["github"]
-			e.renderGitHubMCPConfig(yaml, githubTool, isLast)
-		case "playwright":
-			playwrightTool := tools["playwright"]
-			e.renderPlaywrightMCPConfig(yaml, playwrightTool, isLast)
-		case "cache-memory":
-			e.renderCacheMemoryMCPConfig(yaml, isLast, workflowData)
-		case "safe-outputs":
-			e.renderSafeOutputsMCPConfig(yaml, isLast)
-		case "web-fetch":
-			renderMCPFetchServerConfig(yaml, "json", "              ", isLast, false)
-		default:
-			// Handle custom MCP tools (those with MCP-compatible type)
-			if toolConfig, ok := tools[toolName].(map[string]any); ok {
-				if hasMcp, _ := hasMCPConfig(toolConfig); hasMcp {
-					if err := e.renderCustomMCPConfig(yaml, toolName, toolConfig, isLast); err != nil {
-						fmt.Printf("Error generating custom MCP configuration for %s: %v\n", toolName, err)
+	yaml.WriteString(fmt.Sprintf("          cat > %s << 'EOF'\n", mcpConfigPath))
+	
+	// Handle different server field formats
+	if mcpServersField == "servers" {
+		// Extension format: { "servers": [ {...}, {...} ] }
+		yaml.WriteString("          {\n")
+		yaml.WriteString(fmt.Sprintf("            \"%s\": [\n", mcpServersField))
+		
+		// Add safe-outputs MCP server if safe-outputs are configured
+		totalServers := len(mcpTools)
+		serverCount := 0
+		
+		// Generate configuration for each MCP tool using array format
+		for _, toolName := range mcpTools {
+			serverCount++
+			isLast := serverCount == totalServers
+			
+			switch toolName {
+			case "github":
+				githubTool := tools["github"]
+				e.renderGitHubMCPConfigArrayFormat(yaml, toolName, githubTool, isLast)
+			case "playwright":
+				playwrightTool := tools["playwright"]
+				e.renderPlaywrightMCPConfigArrayFormat(yaml, toolName, playwrightTool, isLast)
+			case "cache-memory":
+				e.renderCacheMemoryMCPConfigArrayFormat(yaml, toolName, isLast, workflowData)
+			case "safe-outputs":
+				e.renderSafeOutputsMCPConfigArrayFormat(yaml, toolName, isLast)
+			case "web-fetch":
+				e.renderWebFetchMCPConfigArrayFormat(yaml, toolName, isLast)
+			default:
+				// Handle custom MCP tools (those with MCP-compatible type)
+				if toolConfig, ok := tools[toolName].(map[string]any); ok {
+					if hasMcp, _ := hasMCPConfig(toolConfig); hasMcp {
+						if err := e.renderCustomMCPConfigArrayFormat(yaml, toolName, toolConfig, isLast); err != nil {
+							fmt.Printf("Error generating custom MCP configuration for %s: %v\n", toolName, err)
+						}
 					}
 				}
 			}
 		}
+		
+		yaml.WriteString("            ]\n")
+		yaml.WriteString("          }\n")
+	} else {
+		// Default mcpServers format: { "mcpServers": { "name": {...}, ... } }
+		yaml.WriteString("          {\n")
+		yaml.WriteString(fmt.Sprintf("            \"%s\": {\n", mcpServersField))
+		
+		// Add safe-outputs MCP server if safe-outputs are configured
+		totalServers := len(mcpTools)
+		serverCount := 0
+		
+		// Generate configuration for each MCP tool using shared logic
+		for _, toolName := range mcpTools {
+			serverCount++
+			isLast := serverCount == totalServers
+			
+			switch toolName {
+			case "github":
+				githubTool := tools["github"]
+				e.renderGitHubMCPConfig(yaml, githubTool, isLast)
+			case "playwright":
+				playwrightTool := tools["playwright"]
+				e.renderPlaywrightMCPConfig(yaml, playwrightTool, isLast)
+			case "cache-memory":
+				e.renderCacheMemoryMCPConfig(yaml, isLast, workflowData)
+			case "safe-outputs":
+				e.renderSafeOutputsMCPConfig(yaml, isLast)
+			case "web-fetch":
+				renderMCPFetchServerConfig(yaml, "json", "              ", isLast, false)
+			default:
+				// Handle custom MCP tools (those with MCP-compatible type)
+				if toolConfig, ok := tools[toolName].(map[string]any); ok {
+					if hasMcp, _ := hasMCPConfig(toolConfig); hasMcp {
+						if err := e.renderCustomMCPConfig(yaml, toolName, toolConfig, isLast); err != nil {
+							fmt.Printf("Error generating custom MCP configuration for %s: %v\n", toolName, err)
+						}
+					}
+				}
+			}
+		}
+		
+		yaml.WriteString("            }\n")
+		yaml.WriteString("          }\n")
 	}
-
-	yaml.WriteString("            }\n")
-	yaml.WriteString("          }\n")
+	
 	yaml.WriteString("          EOF\n")
 }
 
@@ -367,3 +443,204 @@ func (e *CustomEngine) ParseLogMetrics(logContent string, verbose bool) LogMetri
 func (e *CustomEngine) GetLogParserScriptId() string {
 	return "parse_custom_log"
 }
+
+// Array format rendering functions for servers array MCP configuration
+
+// renderGitHubMCPConfigArrayFormat generates GitHub MCP server configuration in servers array format
+func (e *CustomEngine) renderGitHubMCPConfigArrayFormat(yaml *strings.Builder, toolName string, githubTool any, isLast bool) {
+	githubDockerImageVersion := getGitHubDockerImageVersion(githubTool)
+	customArgs := getGitHubCustomArgs(githubTool)
+	readOnly := getGitHubReadOnly(githubTool)
+
+	yaml.WriteString("              {\n")
+	yaml.WriteString(fmt.Sprintf("                \"name\": \"%s\",\n", toolName))
+	yaml.WriteString("                \"transport\": \"stdio\",\n")
+	yaml.WriteString("                \"command\": [\n")
+	yaml.WriteString("                  \"docker\",\n")
+	yaml.WriteString("                  \"run\",\n")
+	yaml.WriteString("                  \"-i\",\n")
+	yaml.WriteString("                  \"--rm\",\n")
+	yaml.WriteString("                  \"-e\",\n")
+	yaml.WriteString("                  \"GITHUB_PERSONAL_ACCESS_TOKEN\",\n")
+	if readOnly {
+		yaml.WriteString("                  \"-e\",\n")
+		yaml.WriteString("                  \"GITHUB_READ_ONLY=1\",\n")
+	}
+	yaml.WriteString("                  \"ghcr.io/github/github-mcp-server:" + githubDockerImageVersion + "\"")
+
+	// Append custom args if present
+	if len(customArgs) > 0 {
+		for _, arg := range customArgs {
+			yaml.WriteString(",\n")
+			yaml.WriteString(fmt.Sprintf("                  \"%s\"", arg))
+		}
+	}
+
+	yaml.WriteString("\n")
+	yaml.WriteString("                ],\n")
+	yaml.WriteString("                \"env\": {\n")
+	yaml.WriteString("                  \"GITHUB_PERSONAL_ACCESS_TOKEN\": \"${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}\"\n")
+	yaml.WriteString("                }\n")
+
+	if isLast {
+		yaml.WriteString("              }\n")
+	} else {
+		yaml.WriteString("              },\n")
+	}
+}
+
+// renderPlaywrightMCPConfigArrayFormat generates Playwright MCP server configuration in servers array format
+func (e *CustomEngine) renderPlaywrightMCPConfigArrayFormat(yaml *strings.Builder, toolName string, playwrightTool any, isLast bool) {
+	args := generatePlaywrightDockerArgs(playwrightTool)
+	customArgs := getPlaywrightCustomArgs(playwrightTool)
+
+	yaml.WriteString("              {\n")
+	yaml.WriteString(fmt.Sprintf("                \"name\": \"%s\",\n", toolName))
+	yaml.WriteString("                \"transport\": \"stdio\",\n")
+	yaml.WriteString("                \"command\": [\n")
+	yaml.WriteString("                  \"npx\",\n")
+	yaml.WriteString("                  \"@playwright/mcp@latest\",\n")
+	yaml.WriteString("                  \"--output-dir\",\n")
+	yaml.WriteString("                  \"/tmp/gh-aw/mcp-logs/playwright\"")
+	if len(args.AllowedDomains) > 0 {
+		yaml.WriteString(",\n")
+		yaml.WriteString("                  \"--allowed-origins\",\n")
+		yaml.WriteString("                  \"" + strings.Join(args.AllowedDomains, ";") + "\"")
+	}
+
+	// Append custom args if present
+	if len(customArgs) > 0 {
+		for _, arg := range customArgs {
+			yaml.WriteString(",\n")
+			yaml.WriteString(fmt.Sprintf("                  \"%s\"", arg))
+		}
+	}
+
+	yaml.WriteString("\n")
+	yaml.WriteString("                ]\n")
+
+	if isLast {
+		yaml.WriteString("              }\n")
+	} else {
+		yaml.WriteString("              },\n")
+	}
+}
+
+// renderCacheMemoryMCPConfigArrayFormat generates cache-memory MCP configuration in servers array format
+// (currently no-op as cache-memory is a simple file share)
+func (e *CustomEngine) renderCacheMemoryMCPConfigArrayFormat(yaml *strings.Builder, toolName string, isLast bool, workflowData *WorkflowData) {
+	// Cache-memory no longer uses MCP server mounting
+}
+
+// renderSafeOutputsMCPConfigArrayFormat generates Safe Outputs MCP server configuration in servers array format
+func (e *CustomEngine) renderSafeOutputsMCPConfigArrayFormat(yaml *strings.Builder, toolName string, isLast bool) {
+	yaml.WriteString("              {\n")
+	yaml.WriteString(fmt.Sprintf("                \"name\": \"%s\",\n", toolName))
+	yaml.WriteString("                \"transport\": \"stdio\",\n")
+	yaml.WriteString("                \"command\": [\n")
+	yaml.WriteString("                  \"node\",\n")
+	yaml.WriteString("                  \"/tmp/gh-aw/safe-outputs/mcp-server.cjs\"\n")
+	yaml.WriteString("                ],\n")
+	yaml.WriteString("                \"env\": {\n")
+	yaml.WriteString("                  \"GITHUB_AW_SAFE_OUTPUTS\": \"${{ env.GITHUB_AW_SAFE_OUTPUTS }}\",\n")
+	yaml.WriteString("                  \"GITHUB_AW_SAFE_OUTPUTS_CONFIG\": ${{ toJSON(env.GITHUB_AW_SAFE_OUTPUTS_CONFIG) }},\n")
+	yaml.WriteString("                  \"GITHUB_AW_ASSETS_BRANCH\": \"${{ env.GITHUB_AW_ASSETS_BRANCH }}\",\n")
+	yaml.WriteString("                  \"GITHUB_AW_ASSETS_MAX_SIZE_KB\": \"${{ env.GITHUB_AW_ASSETS_MAX_SIZE_KB }}\",\n")
+	yaml.WriteString("                  \"GITHUB_AW_ASSETS_ALLOWED_EXTS\": \"${{ env.GITHUB_AW_ASSETS_ALLOWED_EXTS }}\"\n")
+	yaml.WriteString("                }\n")
+
+	if isLast {
+		yaml.WriteString("              }\n")
+	} else {
+		yaml.WriteString("              },\n")
+	}
+}
+
+// renderWebFetchMCPConfigArrayFormat generates web-fetch MCP server configuration in servers array format
+func (e *CustomEngine) renderWebFetchMCPConfigArrayFormat(yaml *strings.Builder, toolName string, isLast bool) {
+	yaml.WriteString("              {\n")
+	yaml.WriteString(fmt.Sprintf("                \"name\": \"%s\",\n", toolName))
+	yaml.WriteString("                \"transport\": \"stdio\",\n")
+	yaml.WriteString("                \"command\": [\n")
+	yaml.WriteString("                  \"npx\",\n")
+	yaml.WriteString("                  \"-y\",\n")
+	yaml.WriteString("                  \"@modelcontextprotocol/server-fetch\"\n")
+	yaml.WriteString("                ]\n")
+
+	if isLast {
+		yaml.WriteString("              }\n")
+	} else {
+		yaml.WriteString("              },\n")
+	}
+}
+
+// renderCustomMCPConfigArrayFormat generates custom MCP server configuration in servers array format
+func (e *CustomEngine) renderCustomMCPConfigArrayFormat(yaml *strings.Builder, toolName string, toolConfig map[string]any, isLast bool) error {
+	// Get MCP configuration
+	mcpConfig, err := getMCPConfig(toolConfig, toolName)
+	if err != nil {
+		return fmt.Errorf("failed to parse MCP config for tool '%s': %w", toolName, err)
+	}
+
+	yaml.WriteString("              {\n")
+	yaml.WriteString(fmt.Sprintf("                \"name\": \"%s\",\n", toolName))
+	yaml.WriteString("                \"transport\": \"stdio\",\n")
+
+	// Build command array from command + args
+	if mcpConfig.Command != "" {
+		yaml.WriteString("                \"command\": [\n")
+		yaml.WriteString(fmt.Sprintf("                  \"%s\"", mcpConfig.Command))
+		if len(mcpConfig.Args) > 0 {
+			for _, arg := range mcpConfig.Args {
+				yaml.WriteString(",\n")
+				yaml.WriteString(fmt.Sprintf("                  \"%s\"", arg))
+			}
+		}
+		yaml.WriteString("\n")
+		yaml.WriteString("                ]")
+	}
+
+	// Add environment variables if present
+	if len(mcpConfig.Env) > 0 {
+		yaml.WriteString(",\n")
+		yaml.WriteString("                \"env\": {\n")
+		
+		envKeys := make([]string, 0, len(mcpConfig.Env))
+		for key := range mcpConfig.Env {
+			envKeys = append(envKeys, key)
+		}
+		sort.Strings(envKeys)
+		
+		for i, key := range envKeys {
+			comma := ","
+			if i == len(envKeys)-1 {
+				comma = ""
+			}
+			yaml.WriteString(fmt.Sprintf("                  \"%s\": \"%s\"%s\n", key, mcpConfig.Env[key], comma))
+		}
+		yaml.WriteString("                }")
+	}
+
+	// Add allowed tools if specified
+	if len(mcpConfig.Allowed) > 0 {
+		yaml.WriteString(",\n")
+		yaml.WriteString("                \"tools\": [\n")
+		for i, tool := range mcpConfig.Allowed {
+			comma := ","
+			if i == len(mcpConfig.Allowed)-1 {
+				comma = ""
+			}
+			yaml.WriteString(fmt.Sprintf("                  \"%s\"%s\n", tool, comma))
+		}
+		yaml.WriteString("                ]")
+	}
+
+	yaml.WriteString("\n")
+	if isLast {
+		yaml.WriteString("              }\n")
+	} else {
+		yaml.WriteString("              },\n")
+	}
+
+	return nil
+}
diff --git a/pkg/workflow/engine.go b/pkg/workflow/engine.go
index b311fa811da..7524b7ea6e6 100644
--- a/pkg/workflow/engine.go
+++ b/pkg/workflow/engine.go
@@ -7,16 +7,19 @@ import (
 
 // EngineConfig represents the parsed engine configuration
 type EngineConfig struct {
-	ID            string
-	Version       string
-	Model         string
-	MaxTurns      string
-	Concurrency   string // Agent job-level concurrency configuration (YAML format)
-	UserAgent     string
-	Env           map[string]string
-	Steps         []map[string]any
-	ErrorPatterns []ErrorPattern
-	Config        string
+	ID               string
+	Version          string
+	Model            string
+	MaxTurns         string
+	Concurrency      string // Agent job-level concurrency configuration (YAML format)
+	UserAgent        string
+	Env              map[string]string
+	Steps            []map[string]any
+	ErrorPatterns    []ErrorPattern
+	Config           string
+	MCPConfigFile    string // Path to MCP config file (custom engine only)
+	MCPServersField  string // Field name for MCP servers in config file (custom engine only)
+	MCPServersShape  string // Shape of MCP servers: "array" or "map" (custom engine only, alternative to MCPServersField)
 }
 
 // NetworkPermissions represents network access permissions
@@ -183,6 +186,27 @@ func (c *Compiler) ExtractEngineConfig(frontmatter map[string]any) (string, *Eng
 				}
 			}
 
+			// Extract optional 'mcp-config-file' field (custom engine only)
+			if mcpConfigFile, hasMCPConfigFile := engineObj["mcp-config-file"]; hasMCPConfigFile {
+				if mcpConfigFileStr, ok := mcpConfigFile.(string); ok {
+					config.MCPConfigFile = mcpConfigFileStr
+				}
+			}
+
+			// Extract optional 'mcp-servers-field' field (custom engine only)
+			if mcpServersField, hasMCPServersField := engineObj["mcp-servers-field"]; hasMCPServersField {
+				if mcpServersFieldStr, ok := mcpServersField.(string); ok {
+					config.MCPServersField = mcpServersFieldStr
+				}
+			}
+
+			// Extract optional 'mcp-servers-shape' field (custom engine only)
+			if mcpServersShape, hasMCPServersShape := engineObj["mcp-servers-shape"]; hasMCPServersShape {
+				if mcpServersShapeStr, ok := mcpServersShape.(string); ok {
+					config.MCPServersShape = mcpServersShapeStr
+				}
+			}
+
 			// Return the ID as the engineSetting for backwards compatibility
 			return config.ID, config
 		}
