diff --git a/.changeset/patch-fix-gemini-mcp-config.md b/.changeset/patch-fix-gemini-mcp-config.md new file mode 100644 index 0000000000..2ab887442c --- /dev/null +++ b/.changeset/patch-fix-gemini-mcp-config.md @@ -0,0 +1,5 @@ +--- +"gh-aw": patch +--- + +Fix the Gemini engine to write its MCP config into .gemini/settings.json instead of using the unsupported `--mcp-config` flag. diff --git a/.github/workflows/smoke-gemini.lock.yml b/.github/workflows/smoke-gemini.lock.yml index ad3ce797c4..aafbc93941 100644 --- a/.github/workflows/smoke-gemini.lock.yml +++ b/.github/workflows/smoke-gemini.lock.yml @@ -901,10 +901,10 @@ jobs: run: | set -o pipefail sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains '*.githubusercontent.com,*.googleapis.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,generativelanguage.googleapis.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.20.2 --skip-pull \ - -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && gemini --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --output-format json --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && gemini --output-format json --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} - GH_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json + GH_AW_MCP_CONFIG: ${{ github.workspace }}/.gemini/settings.json GH_AW_MODEL_AGENT_GEMINI: ${{ vars.GH_AW_MODEL_AGENT_GEMINI || '' }} GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} diff --git a/actions/setup/sh/convert_gateway_config_gemini.sh b/actions/setup/sh/convert_gateway_config_gemini.sh new file mode 100644 index 0000000000..1fc86ce58a --- /dev/null +++ b/actions/setup/sh/convert_gateway_config_gemini.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash +# Convert MCP Gateway Configuration to Gemini Format +# This script converts the gateway's standard HTTP-based MCP configuration +# to the JSON format expected by Gemini CLI (.gemini/settings.json) +# +# Gemini CLI reads MCP server configuration from settings.json files: +# - Global: ~/.gemini/settings.json +# - Project: .gemini/settings.json (used here) +# +# See: https://geminicli.com/docs/tools/mcp-server/ + +set -e + +# Required environment variables: +# - MCP_GATEWAY_OUTPUT: Path to gateway output configuration file +# - MCP_GATEWAY_DOMAIN: Domain to use for MCP server URLs (e.g., host.docker.internal) +# - MCP_GATEWAY_PORT: Port for MCP gateway (e.g., 80) +# - GITHUB_WORKSPACE: Workspace directory for project-level settings + +if [ -z "$MCP_GATEWAY_OUTPUT" ]; then + echo "ERROR: MCP_GATEWAY_OUTPUT environment variable is required" + exit 1 +fi + +if [ ! -f "$MCP_GATEWAY_OUTPUT" ]; then + echo "ERROR: Gateway output file not found: $MCP_GATEWAY_OUTPUT" + exit 1 +fi + +if [ -z "$MCP_GATEWAY_DOMAIN" ]; then + echo "ERROR: MCP_GATEWAY_DOMAIN environment variable is required" + exit 1 +fi + +if [ -z "$MCP_GATEWAY_PORT" ]; then + echo "ERROR: MCP_GATEWAY_PORT environment variable is required" + exit 1 +fi + +if [ -z "$GITHUB_WORKSPACE" ]; then + echo "ERROR: GITHUB_WORKSPACE environment variable is required" + exit 1 +fi + +echo "Converting gateway configuration to Gemini format..." +echo "Input: $MCP_GATEWAY_OUTPUT" +echo "Target domain: $MCP_GATEWAY_DOMAIN:$MCP_GATEWAY_PORT" + +# Convert gateway output to Gemini settings.json format +# Gateway format: +# { +# "mcpServers": { +# "server-name": { +# "type": "http", +# "url": "http://domain:port/mcp/server-name", +# "headers": { +# "Authorization": "apiKey" +# } +# } +# } +# } +# +# Gemini settings.json format: +# { +# "mcpServers": { +# "server-name": { +# "url": "http://domain:port/mcp/server-name", +# "headers": { +# "Authorization": "apiKey" +# } +# } +# } +# } +# +# The main differences: +# 1. Remove "type" field (Gemini uses transport auto-detection from url/httpUrl) +# 2. Remove "tools" field (Copilot-specific) +# 3. URLs must use the correct domain (host.docker.internal) for container access + +# Build the correct URL prefix using the configured domain and port +URL_PREFIX="http://${MCP_GATEWAY_DOMAIN}:${MCP_GATEWAY_PORT}" + +# Create .gemini directory in the workspace (project-level settings) +GEMINI_SETTINGS_DIR="${GITHUB_WORKSPACE}/.gemini" +GEMINI_SETTINGS_FILE="${GEMINI_SETTINGS_DIR}/settings.json" + +mkdir -p "$GEMINI_SETTINGS_DIR" + +jq --arg urlPrefix "$URL_PREFIX" ' + .mcpServers |= with_entries( + .value |= ( + (del(.type)) | + (del(.tools)) | + # Fix the URL to use the correct domain + .url |= (. | sub("^http://[^/]+/mcp/"; $urlPrefix + "/mcp/")) + ) + ) +' "$MCP_GATEWAY_OUTPUT" > "$GEMINI_SETTINGS_FILE" + +echo "Gemini configuration written to $GEMINI_SETTINGS_FILE" +echo "" +echo "Converted configuration:" +cat "$GEMINI_SETTINGS_FILE" diff --git a/actions/setup/sh/start_mcp_gateway.sh b/actions/setup/sh/start_mcp_gateway.sh index 168c420af4..c60bc632c3 100755 --- a/actions/setup/sh/start_mcp_gateway.sh +++ b/actions/setup/sh/start_mcp_gateway.sh @@ -365,6 +365,10 @@ case "$ENGINE_TYPE" in echo "Using Claude converter..." bash /opt/gh-aw/actions/convert_gateway_config_claude.sh ;; + gemini) + echo "Using Gemini converter..." + bash /opt/gh-aw/actions/convert_gateway_config_gemini.sh + ;; *) echo "No agent-specific converter found for engine: $ENGINE_TYPE" echo "Using gateway output directly" diff --git a/pkg/workflow/gemini_engine.go b/pkg/workflow/gemini_engine.go index 7b05e9a651..347631f416 100644 --- a/pkg/workflow/gemini_engine.go +++ b/pkg/workflow/gemini_engine.go @@ -172,10 +172,9 @@ func (e *GeminiEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str geminiArgs = append(geminiArgs, "--model", workflowData.EngineConfig.Model) } - // Add MCP config if servers are present - if HasMCPServers(workflowData) { - geminiArgs = append(geminiArgs, "--mcp-config", "/tmp/gh-aw/mcp-config/mcp-servers.json") - } + // Gemini CLI reads MCP config from .gemini/settings.json (project-level) + // The conversion script (convert_gateway_config_gemini.sh) writes settings.json + // during the MCP setup step, so no --mcp-config flag is needed here. // Add headless mode with JSON output geminiArgs = append(geminiArgs, "--output-format", "json") @@ -225,9 +224,9 @@ func (e *GeminiEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str "GITHUB_WORKSPACE": "${{ github.workspace }}", } - // Add MCP config env var if needed + // Add MCP config env var if needed (points to .gemini/settings.json for Gemini) if HasMCPServers(workflowData) { - env["GH_AW_MCP_CONFIG"] = "/tmp/gh-aw/mcp-config/mcp-servers.json" + env["GH_AW_MCP_CONFIG"] = "${{ github.workspace }}/.gemini/settings.json" } // Add safe outputs env diff --git a/pkg/workflow/gemini_engine_test.go b/pkg/workflow/gemini_engine_test.go index 2eb271a01e..e2c68b51d8 100644 --- a/pkg/workflow/gemini_engine_test.go +++ b/pkg/workflow/gemini_engine_test.go @@ -189,8 +189,9 @@ func TestGeminiEngineExecution(t *testing.T) { stepContent := strings.Join(steps[0], "\n") - assert.Contains(t, stepContent, "--mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json", "Should include MCP config") - assert.Contains(t, stepContent, "GH_AW_MCP_CONFIG: /tmp/gh-aw/mcp-config/mcp-servers.json", "Should set MCP config env var") + // Gemini CLI reads MCP config from .gemini/settings.json, not --mcp-config flag + assert.NotContains(t, stepContent, "--mcp-config", "Should NOT include --mcp-config flag (Gemini CLI does not support it)") + assert.Contains(t, stepContent, "GH_AW_MCP_CONFIG: ${{ github.workspace }}/.gemini/settings.json", "Should set MCP config env var to Gemini settings.json path") }) t.Run("with custom command", func(t *testing.T) { diff --git a/tmp-smoke-test-22204310776.txt b/tmp-smoke-test-22204310776.txt new file mode 100644 index 0000000000..40dfa53670 --- /dev/null +++ b/tmp-smoke-test-22204310776.txt @@ -0,0 +1 @@ +Smoke test file for PR push test - Run 22204310776