Skip to content

Claude Code + Recce MCP #252

Claude Code + Recce MCP

Claude Code + Recce MCP #252

Workflow file for this run

name: Claude Code + Recce MCP
on:
issue_comment:
types: [created]
jobs:
claude:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
actions: read # Required for Claude to read CI results on PRs
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
# For PR events, checkout PR head; for issue_comment, will be updated in Get PR information step
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Set up Python
uses: actions/setup-python@v4
with:
# Python 3.10 is used for compatibility with Recce and dbt-core
# Recce requires Python 3.9+ per its setup.py
python-version: '3.12'
- name: Cache Python dependencies
uses: actions/cache@v4
with:
path: |
~/.cache/pip
~/.local/lib/python3.12/site-packages
key: pip-recce-nightly-${{ runner.os }}-py3.12-${{ hashFiles('requirements.txt') }}
restore-keys: |
pip-recce-nightly-${{ runner.os }}-py3.12-
pip-recce-nightly-
- name: Cache dbt packages
uses: actions/cache@v4
with:
path: |
dbt_packages/
~/.dbt/
key: dbt-packages-${{ hashFiles('packages.yml') }}
restore-keys: |
dbt-packages-
- name: Install Python dependencies
run: |
pip install -r requirements.txt
- name: Get PR information for artifacts
if: github.event.pull_request != null || github.event.issue.pull_request != null
id: pr-info-artifacts
run: |
if [ "${{ github.event_name }}" == "issue_comment" ]; then
PR_NUMBER=${{ github.event.issue.number }}
else
PR_NUMBER="${{ github.event.pull_request.number }}"
fi
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
echo "::notice::PR Number: $PR_NUMBER"
env:
GH_TOKEN: ${{ github.token }}
- name: Download Current Artifacts
if: |
(github.event.pull_request != null || github.event.issue.pull_request != null) &&
(contains(github.event.comment.body, '/ms2') || contains(github.event.comment.body, '/ms3') || contains(github.event.comment.body, '/one-summary'))
id: download-current
continue-on-error: false
uses: dawidd6/action-download-artifact@v11
with:
name: dbt-artifacts-current-${{ steps.pr-info-artifacts.outputs.pr_number }}
path: target/
github_token: ${{ github.token }}
workflow: dbt-build-pr.yml
pr: ${{ steps.pr-info-artifacts.outputs.pr_number }}
check_artifacts: true
search_artifacts: true
- name: Download Base Artifacts
if: |
(github.event.pull_request != null || github.event.issue.pull_request != null) &&
(contains(github.event.comment.body, '/ms2') || contains(github.event.comment.body, '/ms3') || contains(github.event.comment.body, '/one-summary'))
id: download-base
continue-on-error: false
uses: dawidd6/action-download-artifact@v11
with:
name: dbt-artifacts-base-main
path: target-base/
github_token: ${{ github.token }}
workflow: dbt-build-base.yml
branch: main
check_artifacts: true
search_artifacts: true
- name: Install Recce with MCP support
if: contains(github.event.comment.body, '/ms2') || contains(github.event.comment.body, '/ms3') || contains(github.event.comment.body, '/one-summary')
run: |
echo "::group::Installing Recce Nightly with MCP support"
# Upgrade pip, setuptools, and wheel to ensure proper dependency resolution
echo "Upgrading pip, setuptools, and wheel..."
pip install --upgrade pip setuptools wheel
# Install Recce Nightly with MCP extras
echo "Installing recce-nightly[mcp]..."
pip install "git+https://github.com/DataRecce/recce.git@main#egg=recce[mcp]"
# Verify installation
if command -v recce >/dev/null 2>&1; then
echo "✓ Recce installed successfully: $(which recce)"
echo "✓ Recce version: $(recce version)"
else
echo "::error::Recce installation failed - command not found"
exit 1
fi
# Verify critical dependencies are installed
echo "Verifying critical dependencies..."
python -c "import mcp; import fastapi; import click; print('✓ All critical dependencies verified')" || {
echo "::error::Some critical dependencies are missing"
echo "Installed packages:"
pip list
exit 1
}
echo "::endgroup::"
- name: Validate Artifacts
if: |
(github.event.pull_request != null || github.event.issue.pull_request != null) &&
(contains(github.event.comment.body, '/ms2') || contains(github.event.comment.body, '/ms3') || contains(github.event.comment.body, '/one-summary'))
id: validate-artifacts
run: |
echo "::group::Validating downloaded artifacts"
# Check current artifacts (from dbt-build-artifacts.yml)
if [ -f "target/manifest.json" ] && [ -f "target/catalog.json" ]; then
echo "✓ Current artifacts found (from PR build)"
echo "current_exists=true" >> $GITHUB_OUTPUT
else
echo "::error::Current artifacts not found. Cannot proceed with Recce analysis."
echo "current_exists=false" >> $GITHUB_OUTPUT
exit 1
fi
# Check base artifacts (from dbt-build-base.yml)
if [ -f "target-base/manifest.json" ] && [ -f "target-base/catalog.json" ]; then
echo "✓ Base artifacts found (from main branch build)"
echo "base_exists=true" >> $GITHUB_OUTPUT
else
echo "::error::Base artifacts not found. Please ensure 'dbt Base Environment' workflow has run successfully."
echo "base_exists=false" >> $GITHUB_OUTPUT
exit 1
fi
echo "::endgroup::"
- name: Prepare System Prompt
if: github.event.pull_request != null || github.event.issue.pull_request != null
id: system-prompt
run: |
echo "::group::Composing system prompt based on milestone"
COMMENT_BODY="${{ github.event.comment.body }}"
# Store comment body for system prompt (will be injected into prompt to provide context)
# Note: We don't use the 'prompt' parameter to preserve Claude action's default MCP and PR comment behavior
echo "comment_body=$COMMENT_BODY" >> $GITHUB_OUTPUT
# Detect milestone command
if echo "$COMMENT_BODY" | grep -q "/ms1"; then
MILESTONE="ms1"
SYSTEM_PROMPT_FILE=".github/prompts/ms1-system-prompt.md"
RESPONSE_TEMPLATE=".github/prompts/ms1-response-format.md"
echo "📋 Milestone: MS1 (PR/Git only)"
elif echo "$COMMENT_BODY" | grep -q "/ms2"; then
MILESTONE="ms2"
SYSTEM_PROMPT_FILE=".github/prompts/ms2-system-prompt.md"
RESPONSE_TEMPLATE=".github/prompts/ms2-response-format.md"
echo "📋 Milestone: MS2 (+ dbt metadata)"
elif echo "$COMMENT_BODY" | grep -q "/ms3"; then
MILESTONE="ms3"
SYSTEM_PROMPT_FILE=".github/prompts/ms3-system-prompt.md"
RESPONSE_TEMPLATE=".github/prompts/ms3-response-format.md"
echo "📋 Milestone: MS3 (+ data diffs)"
elif echo "$COMMENT_BODY" | grep -q "/one-summary"; then
# Backward compatibility: /one-summary uses MS3 with compact format
MILESTONE="ms3"
SYSTEM_PROMPT_FILE=".github/prompts/ms3-system-prompt.md"
RESPONSE_TEMPLATE=".github/prompts/one-summary.md"
echo "📋 Milestone: MS3 (one-summary format)"
else
# Default: no system prompt, general PR review
echo "📋 Default: General PR review (no system prompt)"
echo "milestone=default" >> $GITHUB_OUTPUT
echo "has_system_prompt=false" >> $GITHUB_OUTPUT
echo "::endgroup::"
exit 0
fi
# Validate required prompt files exist
REQUIRED_FILES=(
".github/prompts/system-prompt-base.md"
"$SYSTEM_PROMPT_FILE"
"$RESPONSE_TEMPLATE"
".github/prompts/execution-notes.md"
)
for file in "${REQUIRED_FILES[@]}"; do
if [ ! -f "$file" ]; then
echo "::error::Required prompt file not found: $file"
exit 1
fi
echo "✓ Found: $file"
done
# Compose system prompt for MS1/MS2/MS3
# Inject current comment context at the beginning
echo "## Current User Request" > /tmp/system_prompt.txt
echo "" >> /tmp/system_prompt.txt
echo "The user mentioned @claude with the following comment:" >> /tmp/system_prompt.txt
echo '```' >> /tmp/system_prompt.txt
echo "$COMMENT_BODY" >> /tmp/system_prompt.txt
echo '```' >> /tmp/system_prompt.txt
echo "" >> /tmp/system_prompt.txt
echo "**IMPORTANT**: Focus ONLY on this current comment. Ignore any previous @claude mentions or historical discussions in the PR thread." >> /tmp/system_prompt.txt
echo "" >> /tmp/system_prompt.txt
echo "---" >> /tmp/system_prompt.txt
echo "" >> /tmp/system_prompt.txt
# Add base system prompt
cat .github/prompts/system-prompt-base.md >> /tmp/system_prompt.txt
echo "" >> /tmp/system_prompt.txt
# Add milestone-specific prompt
cat "$SYSTEM_PROMPT_FILE" >> /tmp/system_prompt.txt
echo "" >> /tmp/system_prompt.txt
# Add response format
cat "$RESPONSE_TEMPLATE" >> /tmp/system_prompt.txt
echo "" >> /tmp/system_prompt.txt
# Add execution notes
cat .github/prompts/execution-notes.md >> /tmp/system_prompt.txt
echo "milestone=$MILESTONE" >> $GITHUB_OUTPUT
echo "has_system_prompt=true" >> $GITHUB_OUTPUT
echo "prompt_file=/tmp/system_prompt.txt" >> $GITHUB_OUTPUT
# Display prompt preview for debugging
echo "System prompt composed successfully ($(wc -l < /tmp/system_prompt.txt) lines)"
echo "::group::System Prompt Preview (first 50 lines)"
head -50 /tmp/system_prompt.txt
echo "::endgroup::"
echo "::endgroup::"
- name: Validate MCP Configuration
if: |
(github.event.pull_request != null || github.event.issue.pull_request != null) &&
(contains(github.event.comment.body, '/ms2') || contains(github.event.comment.body, '/ms3') || contains(github.event.comment.body, '/one-summary'))
run: |
# Show current working directory and files for debugging
echo "Current directory: $(pwd)"
echo "MCP config path: $(pwd)/.github/mcp_config.json"
ls -la .github/ || echo "No .github directory"
# Validate MCP config file exists and is valid JSON
if [ -f ".github/mcp_config.json" ]; then
echo "✓ MCP configuration file found"
if jq empty .github/mcp_config.json 2>/dev/null; then
echo "✓ MCP configuration is valid JSON"
cat .github/mcp_config.json
else
echo "::error::MCP configuration is invalid JSON"
exit 1
fi
else
echo "::error::MCP configuration file not found at .github/mcp_config.json"
exit 1
fi
# Check if Recce CLI is installed and has mcp-server command
if command -v recce >/dev/null 2>&1; then
echo "✓ Recce CLI is installed: $(which recce)"
# Test if mcp-server command is available
if recce mcp-server --help >/dev/null 2>&1; then
echo "✓ Recce MCP server command is available"
else
echo "::error::Recce CLI found but mcp-server command not available"
echo "This may indicate the feature branch is not installed correctly"
exit 1
fi
else
echo "::error::Recce CLI not found in PATH"
exit 1
fi
- name: Test Recce MCP Server
if: |
(github.event.pull_request != null || github.event.issue.pull_request != null) &&
(contains(github.event.comment.body, '/ms2') || contains(github.event.comment.body, '/ms3') || contains(github.event.comment.body, '/one-summary'))
run: |
echo "::group::Testing Recce MCP server startup"
CMD="recce mcp-server"
LOG_FILE="recce_mcp_startup.log"
echo "Command to test: $CMD"
echo "Log file: $LOG_FILE"
echo ""
# 啟動 Recce MCP server 並收集輸出
echo "Starting Recce MCP server (with 5s timeout)..."
timeout 5s bash -c "$CMD" >"$LOG_FILE" 2>&1 &
MCP_PID=$!
echo "Process started with PID: $MCP_PID"
# 等待 server 初始化
sleep 2
# 檢查 process 狀態
echo ""
echo "Checking process status..."
if ! kill -0 "$MCP_PID" 2>/dev/null; then
echo "::error::Recce MCP server process is not running"
echo "::group::MCP Server Output (Failed Start)"
cat "$LOG_FILE" || echo "No log output captured"
echo "::endgroup::"
exit 1
else
echo "✓ Process is running (PID: $MCP_PID)"
fi
# 檢查 log 內容是否有錯誤
echo ""
echo "Checking server logs for errors..."
if grep -qi "error\|exception\|too few parameters\|failed" "$LOG_FILE"; then
echo "::error::Recce MCP server started but errors detected in logs"
echo "::group::MCP Server Output (With Errors)"
cat "$LOG_FILE"
echo "::endgroup::"
kill "$MCP_PID" 2>/dev/null || true
exit 1
else
echo "✓ No critical errors found in logs"
fi
# 顯示完整 log
echo ""
echo "::group::MCP Server Output (Full Log)"
cat "$LOG_FILE" || echo "No log output captured"
echo "::endgroup::"
# 清理 process
echo ""
echo "Stopping MCP server process..."
kill "$MCP_PID" 2>/dev/null || true
sleep 1
# 確認 process 已結束
if kill -0 "$MCP_PID" 2>/dev/null; then
echo "::warning::Process still running, sending SIGKILL..."
kill -9 "$MCP_PID" 2>/dev/null || true
fi
echo "✓ Recce MCP server test completed successfully"
echo "::endgroup::"
- name: Run Claude Code with Recce MCP
id: claude
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
# NOTE: We do NOT set 'prompt' parameter here to preserve Claude action's default behavior
# The comment context is injected into system prompt instead
# This is an optional setting that allows Claude to read CI results on PRs
additional_permissions: |
actions: read
# Configure Recce MCP tools via claude_args
#
# Milestone-Based Tool Access:
# - Default (@claude): General PR review, uses Claude action's default behavior (no custom args)
# - MS1 (/ms1): Git/PR context only - GitHub CLI, Git commands
# - MS2 (/ms2): + dbt metadata - lineage_diff MCP tool only
# - MS3 (/ms3): + data diffs - all Recce MCP tools
# - /one-summary: MS3 with compact format
#
# Available MCP tools (5 total for MS3):
# 1. get_lineage_diff: Get lineage diff (added/removed/modified models)
# 2. row_count_diff: Compare row counts between base and current
# 3. query: Execute SQL query on specified environment (supports Jinja templates)
# 4. query_diff: Compare SQL query results between base and current
# 5. profile_diff: Compare statistical profiles (min/max/avg/distinct count)
#
# For default @claude: don't pass any custom args to preserve default behavior
claude_args: |
${{ steps.system-prompt.outputs.milestone == 'default' && '' || '--mcp-config .github/mcp_config.json' }}
${{ steps.system-prompt.outputs.milestone == 'default' && '' || '--max-turns 50' }}
${{ steps.system-prompt.outputs.has_system_prompt == 'true' && format('--system-prompt-file {0}', steps.system-prompt.outputs.prompt_file) || '' }}
${{ steps.system-prompt.outputs.milestone == 'ms1' && '--allowedTools "Read(*),Bash(gh pr *),Bash(git diff *),Bash(git log *),Bash(git show *)"' || '' }}
${{ steps.system-prompt.outputs.milestone == 'ms2' && '--allowedTools "Read(recce.yml),Read(*.yml),Read(*.sql),Bash(gh pr *),Bash(recce mcp-server),Bash(recce version),mcp__recce__get_lineage_diff"' || '' }}
${{ steps.system-prompt.outputs.milestone == 'ms3' && '--allowedTools "Read(recce.yml),Read(*.yml),Read(*.sql),Bash(recce),Bash(recce mcp-server),Bash(gh pr *),Bash(jq *),mcp__recce__get_lineage_diff,mcp__recce__row_count_diff,mcp__recce__query,mcp__recce__query_diff,mcp__recce__profile_diff"' || '' }}
${{ steps.system-prompt.outputs.milestone == 'default' && '' || '--debug' }}