|
| 1 | +name: 'SEO Performance Report' |
| 2 | +description: 'Pull GSC + Matomo + GA4 + Clarity + AI citations into one verdict per blog post (refresh / expand / merge / kill / double_down / hold).' |
| 3 | +author: 'automatelab.tech' |
| 4 | + |
| 5 | +branding: |
| 6 | + icon: 'bar-chart-2' |
| 7 | + color: 'blue' |
| 8 | + |
| 9 | +inputs: |
| 10 | + tool: |
| 11 | + description: | |
| 12 | + MCP tool to invoke. One of: |
| 13 | + cohort.report, posts.list, posts.snapshot, posts.decay_curve, |
| 14 | + posts.verdict, posts.refresh_brief, posts.cite_loss, gsc.quick_wins. |
| 15 | + required: true |
| 16 | + default: 'cohort.report' |
| 17 | + |
| 18 | + input: |
| 19 | + description: | |
| 20 | + JSON string passed as the tool input. Example for cohort.report: |
| 21 | + {"window": 90, "limit": 20, "min_age_days": 90} |
| 22 | + Pass {} for tool defaults. See README for per-tool schemas. |
| 23 | + required: false |
| 24 | + default: '{}' |
| 25 | + |
| 26 | + format: |
| 27 | + description: 'Output format: json or markdown. Markdown is supported for cohort.report and posts.refresh_brief; other tools fall back to fenced JSON.' |
| 28 | + required: false |
| 29 | + default: 'markdown' |
| 30 | + |
| 31 | + output-file: |
| 32 | + description: 'Path the tool result is written to. Default: $RUNNER_TEMP/seo-perf-result.<ext>.' |
| 33 | + required: false |
| 34 | + default: '' |
| 35 | + |
| 36 | + mcp-version: |
| 37 | + description: 'Pinned npm version of @automatelab/seo-performance-mcp to run. Pin this for reproducible runs.' |
| 38 | + required: false |
| 39 | + default: '0.4.0' |
| 40 | + |
| 41 | + node-version: |
| 42 | + description: 'Node.js version to set up.' |
| 43 | + required: false |
| 44 | + default: '20' |
| 45 | + |
| 46 | + # Adapter env passthrough. All optional; the verdict engine works on |
| 47 | + # whichever slices are configured. |
| 48 | + gsc-service-account-json: |
| 49 | + description: 'Base64-encoded Google service-account JSON with Search Console read access.' |
| 50 | + required: false |
| 51 | + default: '' |
| 52 | + gsc-site-url: |
| 53 | + description: 'Search Console site URL, e.g. sc-domain:example.com or https://example.com/.' |
| 54 | + required: false |
| 55 | + default: '' |
| 56 | + matomo-url: |
| 57 | + description: 'Matomo instance URL.' |
| 58 | + required: false |
| 59 | + default: '' |
| 60 | + matomo-token: |
| 61 | + description: 'Matomo auth token (view access).' |
| 62 | + required: false |
| 63 | + default: '' |
| 64 | + matomo-site-id: |
| 65 | + description: 'Matomo idSite for the site to query.' |
| 66 | + required: false |
| 67 | + default: '' |
| 68 | + ga4-property-id: |
| 69 | + description: 'GA4 property ID (numeric, no G- prefix).' |
| 70 | + required: false |
| 71 | + default: '' |
| 72 | + ga4-service-account-json: |
| 73 | + description: 'Base64-encoded Google service-account JSON with GA4 Data API access.' |
| 74 | + required: false |
| 75 | + default: '' |
| 76 | + clarity-project-id: |
| 77 | + description: 'Microsoft Clarity project ID.' |
| 78 | + required: false |
| 79 | + default: '' |
| 80 | + clarity-api-token: |
| 81 | + description: 'Microsoft Clarity Data Export API token.' |
| 82 | + required: false |
| 83 | + default: '' |
| 84 | + posts-sitemap-url: |
| 85 | + description: 'XML sitemap URL used to enumerate posts. The primary platform-agnostic discovery path.' |
| 86 | + required: false |
| 87 | + default: '' |
| 88 | + posts-list: |
| 89 | + description: 'Optional JSON array overriding sitemap discovery: [{url, title?, published_at?, tags?, word_count?}, ...].' |
| 90 | + required: false |
| 91 | + default: '' |
| 92 | + ghost-admin-api-url: |
| 93 | + description: 'Optional Ghost Admin API base URL. Pair with ghost-admin-api-key for richer metadata.' |
| 94 | + required: false |
| 95 | + default: '' |
| 96 | + ghost-admin-api-key: |
| 97 | + description: 'Optional Ghost Admin API key (id:secret).' |
| 98 | + required: false |
| 99 | + default: '' |
| 100 | + citation-intelligence-url: |
| 101 | + description: 'Optional URL of a citation-intelligence MCP server to delegate AI-citation queries to.' |
| 102 | + required: false |
| 103 | + default: '' |
| 104 | + |
| 105 | +outputs: |
| 106 | + result: |
| 107 | + description: 'Tool output (markdown or JSON, depending on `format`). Multi-line.' |
| 108 | + value: ${{ steps.run.outputs.result }} |
| 109 | + result-file: |
| 110 | + description: 'Path of the file the tool result was written to.' |
| 111 | + value: ${{ steps.run.outputs.result-file }} |
| 112 | + rows: |
| 113 | + description: 'For cohort.report: number of rows returned. Empty for other tools.' |
| 114 | + value: ${{ steps.run.outputs.rows }} |
| 115 | + |
| 116 | +runs: |
| 117 | + using: 'composite' |
| 118 | + steps: |
| 119 | + - name: Set up Node.js |
| 120 | + uses: actions/setup-node@v4 |
| 121 | + with: |
| 122 | + node-version: ${{ inputs.node-version }} |
| 123 | + |
| 124 | + - name: Run seo-performance tool |
| 125 | + id: run |
| 126 | + shell: bash |
| 127 | + env: |
| 128 | + TOOL: ${{ inputs.tool }} |
| 129 | + INPUT_JSON: ${{ inputs.input }} |
| 130 | + FORMAT: ${{ inputs.format }} |
| 131 | + OUTPUT_FILE: ${{ inputs.output-file }} |
| 132 | + MCP_VERSION: ${{ inputs.mcp-version }} |
| 133 | + GSC_SERVICE_ACCOUNT_JSON: ${{ inputs.gsc-service-account-json }} |
| 134 | + GSC_SITE_URL: ${{ inputs.gsc-site-url }} |
| 135 | + MATOMO_URL: ${{ inputs.matomo-url }} |
| 136 | + MATOMO_TOKEN: ${{ inputs.matomo-token }} |
| 137 | + MATOMO_SITE_ID: ${{ inputs.matomo-site-id }} |
| 138 | + GA4_PROPERTY_ID: ${{ inputs.ga4-property-id }} |
| 139 | + GA4_SERVICE_ACCOUNT_JSON: ${{ inputs.ga4-service-account-json }} |
| 140 | + CLARITY_PROJECT_ID: ${{ inputs.clarity-project-id }} |
| 141 | + CLARITY_API_TOKEN: ${{ inputs.clarity-api-token }} |
| 142 | + POSTS_SITEMAP_URL: ${{ inputs.posts-sitemap-url }} |
| 143 | + POSTS_LIST: ${{ inputs.posts-list }} |
| 144 | + GHOST_ADMIN_API_URL: ${{ inputs.ghost-admin-api-url }} |
| 145 | + GHOST_ADMIN_API_KEY: ${{ inputs.ghost-admin-api-key }} |
| 146 | + CITATION_INTELLIGENCE_URL: ${{ inputs.citation-intelligence-url }} |
| 147 | + run: | |
| 148 | + set -euo pipefail |
| 149 | +
|
| 150 | + # Pick output path |
| 151 | + if [ -z "${OUTPUT_FILE}" ]; then |
| 152 | + if [ "${FORMAT}" = "markdown" ]; then |
| 153 | + OUTPUT_FILE="${RUNNER_TEMP}/seo-perf-result.md" |
| 154 | + else |
| 155 | + OUTPUT_FILE="${RUNNER_TEMP}/seo-perf-result.json" |
| 156 | + fi |
| 157 | + fi |
| 158 | +
|
| 159 | + # Write tool input JSON to a file so we never interpolate untrusted |
| 160 | + # data into the shell command. |
| 161 | + INPUT_FILE="${RUNNER_TEMP}/seo-perf-input.json" |
| 162 | + printf '%s' "${INPUT_JSON}" > "${INPUT_FILE}" |
| 163 | +
|
| 164 | + npx -y -p "@automatelab/seo-performance-mcp@${MCP_VERSION}" \ |
| 165 | + seo-perf-cli "${TOOL}" \ |
| 166 | + --input-file "${INPUT_FILE}" \ |
| 167 | + --format "${FORMAT}" \ |
| 168 | + --out "${OUTPUT_FILE}" |
| 169 | +
|
| 170 | + echo "result-file=${OUTPUT_FILE}" >> "${GITHUB_OUTPUT}" |
| 171 | +
|
| 172 | + # Multi-line output: use a heredoc delimiter unlikely to clash with content. |
| 173 | + DELIM="SEO_PERF_EOF_$(date +%s%N)" |
| 174 | + { |
| 175 | + echo "result<<${DELIM}" |
| 176 | + cat "${OUTPUT_FILE}" |
| 177 | + echo "${DELIM}" |
| 178 | + } >> "${GITHUB_OUTPUT}" |
| 179 | +
|
| 180 | + # Surface row count for cohort.report (best-effort, JSON path only). |
| 181 | + if [ "${TOOL}" = "cohort.report" ] || [ "${TOOL}" = "cohort_report" ]; then |
| 182 | + if [ "${FORMAT}" = "json" ]; then |
| 183 | + ROWS=$(node -e "const d=JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')); process.stdout.write(String((d.rows||[]).length));" "${OUTPUT_FILE}") |
| 184 | + echo "rows=${ROWS}" >> "${GITHUB_OUTPUT}" |
| 185 | + fi |
| 186 | + fi |
0 commit comments