Skip to content

Commit 3ec6716

Browse files
committed
v0.4.0: seo-perf-cli + GitHub Action
Adds two surfaces on top of the existing MCP server: - src/cli.ts (seo-perf-cli bin): runs any tool as a one-shot CLI with --input JSON and --format json|markdown. Wired into package.json bin. - action.yml: composite GitHub Action that wraps the CLI for CI workflows. Inputs for tool, JSON args, format, and all adapter env vars. - examples/weekly-cohort-report.yml: reference workflow that posts the cohort.report markdown into a GitHub Issue every Monday. - README: 'Use as a GitHub Action' + 'Use as a one-shot CLI' sections.
1 parent cda59f2 commit 3ec6716

6 files changed

Lines changed: 495 additions & 4 deletions

File tree

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,43 @@ To add a brand-new platform: nothing to build - just point `POSTS_SITEMAP_URL` a
107107
| `posts.cite_loss` | LLM citations that dropped off for a given URL. Needs `CITATION_INTELLIGENCE_URL`. |
108108
| `gsc.quick_wins` | `(page, query)` pairs at positions 5-15 with low CTR - fastest title-rewrite wins. |
109109

110+
## Use as a GitHub Action
111+
112+
Run any of the tools on a cron from CI and post the output to a GitHub Issue, Discussion, or PR. The action is published on the GitHub Marketplace.
113+
114+
```yaml
115+
- uses: AutomateLab-tech/seo-performance-mcp@v1
116+
with:
117+
tool: cohort.report
118+
format: markdown
119+
input: '{"window": 90, "min_age_days": 90, "limit": 20}'
120+
gsc-service-account-json: ${{ secrets.GSC_SERVICE_ACCOUNT_JSON }}
121+
gsc-site-url: ${{ secrets.GSC_SITE_URL }}
122+
posts-sitemap-url: ${{ secrets.POSTS_SITEMAP_URL }}
123+
```
124+
125+
Outputs:
126+
127+
| Output | Description |
128+
|---|---|
129+
| `result` | Tool output as a multi-line string (markdown or JSON, per `format`). |
130+
| `result-file` | Path of the file the tool output was written to. Hand to `peter-evans/create-issue-from-file` etc. |
131+
| `rows` | For `cohort.report` with `format: json` only: number of rows returned. |
132+
133+
A complete weekly-audit workflow that opens a GitHub Issue with the cohort report is in [examples/weekly-cohort-report.yml](./examples/weekly-cohort-report.yml).
134+
135+
## Use as a one-shot CLI
136+
137+
The package also ships a `seo-perf-cli` bin so you can run a single tool without an MCP client:
138+
139+
```bash
140+
npx -p @automatelab/seo-performance-mcp seo-perf-cli cohort.report \
141+
--input '{"window": 90, "limit": 20}' \
142+
--format markdown
143+
```
144+
145+
Same env vars as the MCP server. `--format markdown` is supported for `cohort.report` and `posts.refresh_brief`; other tools fall back to fenced JSON.
146+
110147
## Companion skills + Cursor rule
111148

112149
Three thin routing files ship in the repo so the LLM in your client knows *when* to reach for these tools:

action.yml

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
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

examples/weekly-cohort-report.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Example workflow: run a weekly seo-performance cohort report and open a
2+
# GitHub Issue with the resulting markdown table. Copy into your repo at
3+
# .github/workflows/seo-performance.yml and fill in the secrets.
4+
5+
name: Weekly SEO performance cohort
6+
7+
on:
8+
schedule:
9+
# Mondays at 08:00 UTC. Adjust to your editorial cadence.
10+
- cron: '0 8 * * 1'
11+
workflow_dispatch:
12+
13+
jobs:
14+
cohort:
15+
runs-on: ubuntu-latest
16+
permissions:
17+
issues: write
18+
steps:
19+
- name: Run cohort report
20+
id: report
21+
uses: AutomateLab-tech/seo-performance-mcp@v1
22+
with:
23+
tool: cohort.report
24+
format: markdown
25+
input: |
26+
{"window": 90, "min_age_days": 90, "limit": 20}
27+
gsc-service-account-json: ${{ secrets.GSC_SERVICE_ACCOUNT_JSON }}
28+
gsc-site-url: ${{ secrets.GSC_SITE_URL }}
29+
posts-sitemap-url: ${{ secrets.POSTS_SITEMAP_URL }}
30+
# Optional extra slices. Leave unset to skip.
31+
ga4-property-id: ${{ secrets.GA4_PROPERTY_ID }}
32+
ga4-service-account-json: ${{ secrets.GA4_SERVICE_ACCOUNT_JSON }}
33+
matomo-url: ${{ secrets.MATOMO_URL }}
34+
matomo-token: ${{ secrets.MATOMO_TOKEN }}
35+
matomo-site-id: ${{ secrets.MATOMO_SITE_ID }}
36+
37+
- name: Open issue with the cohort report
38+
uses: peter-evans/create-issue-from-file@v5
39+
with:
40+
title: "Weekly SEO cohort report ${{ github.run_started_at }}"
41+
content-filepath: ${{ steps.report.outputs.result-file }}
42+
labels: |
43+
seo
44+
automated

package-lock.json

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@automatelab/seo-performance-mcp",
3-
"version": "0.3.0",
3+
"version": "0.4.0",
44
"mcpName": "io.github.AutomateLab-tech/seo-performance-mcp",
55
"description": "Post-publish SEO performance MCP. Unifies Google Search Console, Matomo, GA4, Clarity, and AI-citation signals per URL and emits a verdict (refresh / expand / merge / kill / double_down / hold) per post with reason codes.",
66
"license": "MIT",
@@ -18,7 +18,8 @@
1818
},
1919
"type": "module",
2020
"bin": {
21-
"seo-performance-mcp": "dist/index.js"
21+
"seo-performance-mcp": "dist/index.js",
22+
"seo-perf-cli": "dist/cli.js"
2223
},
2324
"main": "dist/index.js",
2425
"scripts": {

0 commit comments

Comments
 (0)