Add OpenSEO plugins for Claude Code and Codex CLI (#501)

This commit is contained in:
Ben Senescu 2026-08-18 23:17:50 -04:00 committed by GitHub
parent a004759700
commit a3ca46a0c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 1263 additions and 2 deletions

View File

@ -0,0 +1,16 @@
{
"name": "openseo",
"owner": {
"name": "OpenSEO",
"url": "https://openseo.so"
},
"description": "OpenSEO's official Codex plugin marketplace.",
"plugins": [
{
"name": "openseo",
"source": "./plugins/openseo",
"description": "Without good data, your agent gives generic advice. Keyword research, competitor research, backlinks, and site audits. All for $10/month.",
"category": "Productivity"
}
]
}

View File

@ -39,6 +39,7 @@ metadata:
- `web/content/docs/skills/index.md` — bullet in the right workflow section
- `web/content/docs/skills/meta.json` — nav entry
- `.agents/skills/seo-coach/SKILL.md` — one line in the "What each workflow does" roster
- `plugins/openseo/skills/<name>` — add the skill to the `skills` list in `scripts/sync-plugin-skills.mjs`, then run `pnpm sync-plugin-skills` (this directory holds real copies, not symlinks — the Claude Code and Codex plugins bundle from here, and Codex's installer silently skips symlinked files, so a symlink would ship a skill-less plugin). `pnpm ci:check` re-runs the sync and fails on drift, so a missed update here is caught, but the skill count and roster below are prose and aren't checked — update them by hand: both `plugins/openseo/*/plugin.json` `description` fields, the Codex manifest's `interface.longDescription`, and the skill lists in `web/content/docs/claude-code-plugin.md` and `web/content/docs/codex-plugin.md`
- Optional: `web/src/lib/feature-pages.ts` and `web/content/docs/skills/setup.md` if it deserves marketing/setup placement
3. If the skill references MCP tools, use exact tool names and keep them in sync with `src/server/mcp/server.ts` — the tool names in skills are load-bearing for agents following them.

View File

@ -0,0 +1,16 @@
{
"name": "openseo",
"owner": {
"name": "OpenSEO",
"url": "https://openseo.so"
},
"description": "OpenSEO's official Claude Code plugin marketplace.",
"plugins": [
{
"name": "openseo",
"source": "./plugins/openseo",
"description": "Without good data, your agent gives generic advice. Keyword research, competitor research, backlinks, and site audits. All for $10/month.",
"category": "SEO"
}
]
}

7
.github/CODEOWNERS vendored
View File

@ -4,3 +4,10 @@
/AGENTS.md @bensenescu
/CLAUDE.md @bensenescu
/.agents/skills/ @bensenescu
# Plugin distribution — these files define the MCP endpoint and skills
# every plugin installer connects to; a silent change here ships to all
# installed users on the next update.
/plugins/ @bensenescu
/.claude-plugin/ @bensenescu
/.agents/plugins/ @bensenescu

View File

@ -8,6 +8,9 @@ routeTree.gen.ts
# vendored skills are hash-pinned in skills-lock.json; don't reformat them
.agents/skills/
# real copies synced from .agents/skills/ via `pnpm sync-plugin-skills` — must
# stay byte-identical to the canonical source, not reformatted
plugins/openseo/skills/
dist/
drizzle/
drizzle-pg/

View File

@ -38,6 +38,7 @@
"knip": "knip",
"release:notes": "node scripts/release-notes.mjs",
"release:publish": "node scripts/publish-release.mjs",
"sync-plugin-skills": "node scripts/sync-plugin-skills.mjs",
"test": "vitest run",
"test:watch": "vitest",
"test:ci": "vitest run --reporter=dot",
@ -52,7 +53,7 @@
"gdpr:erase-user": "tsx scripts/erase-user-data.ts",
"seed:rank-tracking": "tsx scripts/seed-rank-tracking.ts",
"seed:projects": "tsx scripts/seed-projects.ts",
"ci:check": "prettier --check . && knip && tsc --noEmit && tsc --noEmit -p badseo/tsconfig.json && oxlint . --type-aware"
"ci:check": "prettier --check . && knip && tsc --noEmit && tsc --noEmit -p badseo/tsconfig.json && oxlint . --type-aware && pnpm sync-plugin-skills && test -z \"$(git status --porcelain -- plugins/openseo/skills)\""
},
"cloudflare": {
"bindings": {

View File

@ -0,0 +1,29 @@
{
"name": "openseo",
"displayName": "OpenSEO",
"version": "1.0.0",
"description": "Without good data, your agent gives generic advice. Keyword research, competitor research, backlinks, and site audits. All for $10/month.",
"author": {
"name": "OpenSEO",
"url": "https://openseo.so"
},
"homepage": "https://openseo.so",
"repository": "https://github.com/every-app/open-seo",
"license": "MIT",
"keywords": [
"seo",
"keyword-research",
"serp",
"site-audit",
"backlinks",
"search-console",
"rank-tracking",
"local-seo"
],
"mcpServers": {
"openseo": {
"type": "http",
"url": "https://app.openseo.so/mcp"
}
}
}

View File

@ -0,0 +1,44 @@
{
"name": "openseo",
"version": "1.0.0",
"description": "Without good data, your agent gives generic advice. Keyword research, competitor research, backlinks, and site audits. All for $10/month.",
"author": {
"name": "OpenSEO",
"url": "https://openseo.so"
},
"homepage": "https://openseo.so",
"repository": "https://github.com/every-app/open-seo",
"license": "MIT",
"keywords": [
"seo",
"keyword-research",
"serp",
"site-audit",
"backlinks",
"search-console",
"rank-tracking",
"local-seo"
],
"skills": "./skills/",
"mcpServers": {
"openseo": {
"url": "https://app.openseo.so/mcp"
}
},
"interface": {
"displayName": "OpenSEO",
"shortDescription": "Without good data, your agent gives generic advice. Keyword research, competitor research, backlinks, and site audits. All for $10/month.",
"longDescription": "Without good data, your agent gives generic advice. Keyword research, competitor research, backlinks, and site audits. All for $10/month.",
"developerName": "OpenSEO",
"category": "Productivity",
"capabilities": ["Interactive"],
"websiteURL": "https://openseo.so",
"privacyPolicyURL": "https://openseo.so/privacy",
"termsOfServiceURL": "https://openseo.so/terms-and-conditions",
"defaultPrompt": [
"Research keywords for my site and shortlist the best opportunities.",
"Audit my site and give me a one-page report with a single next action.",
"Track my rankings for these keywords and summarize what changed."
]
}
}

View File

@ -0,0 +1,80 @@
---
name: competitive-landscape
description: Map SEO market leaders, winning content themes, keyword coverage, backlinks, and strategic gaps.
---
# OpenSEO Competitive Landscape
## Goal
Answer: "Who is winning this SEO market, what content is working for them, and where are the openings?"
Use this when the user wants a market-level view across several competitors. For a deep dive on one domain, use `competitor-analysis`.
## Required inputs
- `projectId`
- Topic, seed keywords, market/category, or user's domain
- Optional known competitors
- Optional location/language
## OpenSEO MCP tools
- `research_keywords`: discover representative market queries.
- `get_keyword_metrics`: validate known query sets with volume, difficulty, intent, and trends.
- `get_serp_results`: identify recurring ranking domains across target queries.
- `find_serp_competitors`: compare domains competing across supplied keywords; use this before manual SERP counting when a keyword set is available.
- `get_domain_overview`: size organic footprint for candidate leaders.
- `get_search_console_performance`: when the user's own domain is in the comparison and Search Console is connected, anchor their position with first-party clicks/impressions/CTR rather than third-party estimates.
- `get_ranked_keywords`: find exact ranking keywords, URLs, ranks, intents, and SERP result types for leaders.
- `get_backlinks_overview`: compare backlink/referring-domain strength where relevant.
- `search_local_businesses`, `get_local_serp_results`, and `get_google_business_questions`: use for local SEO markets where proximity, Maps rankings, business categories, reviews, or Google Q&A affect who is winning.
## Workflow
1. Define the market query set:
- Use provided keywords, or call `research_keywords` to build 5-10 representative queries.
- Include mixed intent: informational, commercial, comparison, and tool/software terms when applicable.
- For local SEO, include neighborhood/city/service-area queries and identify the priority locations or coordinates.
2. If the query set is already known, use `get_keyword_metrics` to validate relative demand and difficulty and `find_serp_competitors` to identify recurring domains at scale.
3. For local SEO, call `search_local_businesses` and `get_local_serp_results` for the highest-priority location(s) before synthesizing winners. Use `get_serp_results` as a complement for organic pages, not as the only local evidence.
4. Call `get_serp_results` for representative queries when live SERP composition, ranking URLs, or SERP features need inspection. Send at most 10 queries per call.
5. Identify recurring domains and group them by type:
- Direct product competitors
- Publishers/media
- Marketplaces/directories
- Communities/forums
- Documentation/resources
6. For the strongest recurring domains, call `get_domain_overview`; default to the top 3-5 domains before expanding.
7. For direct competitors and relevant publishers, call `get_ranked_keywords`.
8. Use `get_backlinks_overview` when backlink authority appears important or the user asks why a domain is winning. Backlinks may be unavailable if the account has not enabled that data; continue with SERP/domain evidence if it fails.
9. Synthesize patterns: content types, themes, SERP formats, local-pack signals, authority advantages, and underserved angles.
## Output format
Start with the market read:
- Market leaders
- Most winnable opportunity area
- Biggest barrier to ranking
Then include:
| Domain | Type | Why they matter | Organic footprint | Winning themes | Weakness/gap |
| ------ | ---- | --------------- | ----------------- | -------------- | ------------ |
Add:
- Query set used
- Content formats that are working
- Keyword/theme gaps
- Backlink or authority observations
- Recommended next workflows: competitor analysis, keyword clustering, or content brief
## Guardrails
- Distinguish SEO competitors from business competitors.
- Do not overstate exact traffic when OpenSEO returns estimates.
- If using a small query set, call the result directional.
- Do not assume a publisher is a product competitor; label domain types clearly.
- For local markets, distinguish organic-page winners from Maps/local-pack winners.

View File

@ -0,0 +1,83 @@
---
name: competitor-analysis
description: "Analyze one competitor's organic footprint, ranking keywords, content themes, backlinks, and gaps."
---
# OpenSEO Competitor Analysis
## Goal
Analyze one competitor deeply enough to decide what to learn from, avoid, counter-position against, or outrank.
Use this for a named competitor. For identifying the market leaders first, use `competitive-landscape`.
## Required inputs
- `projectId`
- Competitor domain
- User's domain when comparison is requested
- Optional topic/category/location/language
## OpenSEO MCP tools
- `get_domain_overview`: baseline organic traffic and keyword count.
- `get_search_console_performance`: when comparing to the user's own domain and Search Console is connected, use it as the first-party baseline (real clicks/impressions/CTR/position) instead of estimating the user's own performance from third-party data.
- `get_ranked_keywords`: exact keyword, URL, rank, intent, traffic, CPC, and SERP-type rows for the competitor domain or page.
- `get_backlinks_overview`: backlink/referring-domain profile.
- `find_serp_competitors`: validate whether the named competitor is a real search competitor across the target keyword set.
- `search_local_businesses`, `get_local_serp_results`, and `get_google_business_questions`: use for local SEO competitors when Maps/local-pack visibility, nearby businesses, categories, or Google Q&A matter.
- `get_serp_results`: validate direct head-to-head SERPs for important keywords.
- `research_keywords`: expand gaps or category terms when needed.
## Workflow
1. Call `get_domain_overview` for the competitor, passing provided location/language when supported.
2. If comparing to the user, call `get_domain_overview` for the user's domain too — and if Search Console is connected, `get_search_console_performance` for the user's real baseline.
3. Call `get_ranked_keywords` for the competitor. Use filters like `maxRank`, `minSearchVolume`, `excludeBrandTerms`, and `resultTypes` to keep rows relevant.
4. If comparing to the user, call `get_ranked_keywords` for the user's domain/page too, or use `get_serp_results` for the shared terms when a lighter check is enough.
5. For local SEO, use `search_local_businesses` and `get_local_serp_results` around the relevant business location(s) before drawing local-pack conclusions. Add `get_google_business_questions` only when Q&A evidence matters.
6. Use `find_serp_competitors` when the competitor was supplied by the user but its search overlap is unclear.
7. Group competitor keywords into themes:
- Product/category terms
- Alternatives/comparisons
- Templates/tools/calculators
- Educational guides
- Branded demand
- Local/neighborhood terms when relevant
8. Call `get_backlinks_overview` for the competitor, especially if authority appears to explain rankings. Continue without backlink evidence if it is unavailable.
9. Use `get_serp_results` for important shared or target keywords to compare positioning, passing provided location/language when supported.
10. Produce an actionable plan:
- What they are doing well
- Where they are vulnerable
- Which pages/keywords to pursue
- What to avoid copying
## Output format
Start with:
- Competitor snapshot
- Biggest lesson
- Best opportunity to beat them
Then include:
| Area | Competitor pattern | Evidence | OpenSEO opportunity |
| ---- | ------------------ | -------- | ------------------- |
Include sections for:
- Top keyword themes
- Content/page types working for them
- Backlink/authority notes
- Head-to-head SERP observations
- Priority actions for the user
## Guardrails
- Do not treat all competitor keywords as desirable. Filter for business fit.
- Separate evidence from inference.
- Do not infer competitor page/content-type patterns from keyword rows alone; use SERP or web evidence for page-level claims.
- For local SEO, do not infer Maps/local-pack strength from national organic domain metrics alone; use local business and local SERP tools when the location is known or reasonably discoverable.
- Do not recommend copying content; recommend a stronger angle or better answer to the same intent.
- If the user's domain is unavailable, frame the analysis as competitor-only.

View File

@ -0,0 +1,76 @@
---
name: keyword-clustering
description: Cluster keywords by intent and map them to existing or proposed pages.
---
# OpenSEO Keyword Clustering
## Goal
Group keywords into page-level clusters and decide which existing or new page should target each cluster. This is a keyword mapping workflow, not just a semantic grouping exercise.
## Required inputs
- `projectId`
- A keyword list, saved keyword tag, seed topic, or target domain
- Optional existing URLs/pages to map against
If keywords are not provided, use `list_saved_keywords` for saved sets, `research_keywords` for seed discovery, or `get_ranked_keywords` when the user starts from a target domain.
## OpenSEO MCP tools
- `list_saved_keywords`: fetch an existing keyword set, optionally filtered by tags.
- `research_keywords`: expand a seed when the user starts from a topic.
- `get_ranked_keywords`: gather exact ranking keywords and URLs when the user starts from a domain or page.
- `get_search_console_performance`: when Search Console is connected, pull real queries with `dimensions: ["query","page"]` to map terms to the pages already earning impressions and to surface cannibalization (one query splitting clicks across multiple URLs).
- `get_serp_results`: validate whether keywords belong on the same page by checking SERP overlap and intent.
- `get_local_serp_results`: use for local SEO clusters when Maps/local-pack intent should affect page mapping.
- `save_keywords`: optionally tag final clusters after user confirmation.
## Workflow
1. Gather the candidate keyword set.
- Use `get_search_console_performance` (dimensions `["query","page"]`) when Search Console is connected to start from real queries and the pages already ranking for them.
- Use `get_ranked_keywords` for domain/page-driven clustering.
- Use `search_local_businesses` and `get_local_serp_results` when proximity, local packs, or Google Business results determine whether terms belong on location pages.
2. Remove duplicates, irrelevant terms, and terms that clearly require a different product or audience.
3. Build clusters around intent and page type:
- Same SERP intent and similar ranking pages belong together.
- Different intent, buyer stage, or SERP format should be split.
- Similar words do not guarantee the same cluster.
4. For important borderline terms, use a small `get_serp_results` batch to check overlap.
5. Assign each cluster to:
- Existing URL, if supplied and appropriate
- New page recommendation, if no existing page fits
- Do-not-target / later bucket, if weak or off-strategy
6. Identify cannibalization risk when multiple pages would target the same intent. When Search Console is connected, confirm it from real data with `get_search_console_performance` (`dimensions: ["query","page"]`) — the same query sending impressions to multiple URLs.
7. Ask before applying cluster tags with `save_keywords`.
## Output format
Start with a short mapping summary:
- Number of clusters
- Pages to create
- Existing pages to update
- Cannibalization or consolidation issues
Then include:
| Cluster | Primary keyword | Secondary keywords | Intent | Target page | Priority | Notes |
| ------- | --------------- | ------------------ | ------ | ----------- | -------- | ----- |
For each cluster, include a recommended page brief:
- Page type
- Searcher problem
- Required sections
- Internal-link opportunities
- Save/tag suggestion
## Guardrails
- Do not over-cluster tiny keyword sets. If there are fewer than 10 usable terms, produce a simple map.
- Do not rely on lexical similarity alone. SERP intent wins.
- Do not replace tags broadly without explicit confirmation.
- If existing URL data is missing, label target pages as proposed.

View File

@ -0,0 +1,70 @@
---
name: keyword-research
description: "Discover keyword opportunities, evaluate metrics and SERPs, and save/tag promising terms."
---
# OpenSEO Keyword Research
## Goal
Turn seed topics into a prioritized keyword opportunity set using OpenSEO MCP data. The output should help the user decide what to target, what to save, and what to research next.
## Required inputs
- `projectId`
- One or more seed topics, products, pages, competitors, or audience problems
- Optional market/location/language
If `projectId` is missing, use `list_projects` first. If the target market/location/language is unclear and would materially affect keyword metrics, ask the user; otherwise use the MCP tool defaults.
## OpenSEO MCP tools
- `research_keywords`: primary discovery tool. Use 1-5 seeds per call and prefer 150 results unless the user asks for exhaustive research.
- `get_keyword_metrics`: hydrate up to 700 known keywords with volume, keyword difficulty (KD), search intent, CPC, and monthly trends in one call. Use it to score candidate or known terms — including the Search Console striking-distance queries from step 1.
- `get_ranked_keywords`: pull exact ranking keyword rows when a target domain or page is part of the research brief.
- `get_search_console_performance`: when Search Console is connected, start from the project's real first-party demand — queries already earning impressions and near-ranking ("striking distance") terms. Request a high `rowLimit` and filter average position 5-20 client-side, since the API sorts by clicks and can't filter by position. Then hydrate those striking-distance queries with `get_keyword_metrics` to attach difficulty and intent.
- `get_serp_results`: inspect SERPs for the top candidate terms, especially when intent is ambiguous.
- `search_local_businesses`, `get_local_serp_results`, and `get_google_business_questions`: use for local SEO topics when a business/location radius matters.
- `list_saved_keywords`: avoid duplicating already-saved work or use existing tags as context.
- `save_keywords`: save selected keywords only after explicit user confirmation.
## Workflow
1. Normalize seeds into a small set of distinct research angles. If Search Console is connected for the project, first pull `get_search_console_performance` (high `rowLimit`, default lookback), filter to striking-distance positions (~520) client-side, and hydrate those queries with `get_keyword_metrics` to attach KD and intent. That ranked, hydrated list is your fastest opportunity set — work it before broad discovery.
2. If the request is local SEO, identify the business, location/coordinates or service area, and local categories. Use `search_local_businesses` and `get_local_serp_results` for the most important location/keyword set instead of relying only on national keyword/SERP data.
3. Call `research_keywords` for exploratory seeds. Use bulk calls when possible.
4. Use `get_keyword_metrics` to hydrate a fixed keyword list — or the striking-distance queries from step 1 — with volume, KD, and intent before prioritizing.
5. Use `get_ranked_keywords` when the user provides a domain/page and wants opportunities based on current rankings, near-misses, or competitor-owned terms.
6. Remove irrelevant, duplicate, branded-only, and off-intent terms.
7. Prioritize by practical opportunity, not volume alone:
- Strong match to the user's product/page/topic
- Clear search intent
- Reasonable difficulty
- Useful volume/CPC signal
- SERP where the user can plausibly compete
- For local SEO, local-pack/Maps visibility and proximity fit
8. Use `get_serp_results` for high-potential or ambiguous keywords when SERP intent would change the recommendation; keep the default check small.
9. Present a shortlist and a longer opportunity table.
10. Ask before saving keywords. When saving, suggest concise tags such as `topic:<topic>`, `intent:<intent>`, or `page:<slug>`.
## Output format
Start with the highest-signal recommendation:
- Best opportunity theme
- Top keywords to target now
- Keywords to save
- Risks or SERP caveats
Then include a compact table:
| Keyword | Intent | Volume | KD | CPC | Priority | Notes |
| ------- | ------ | -----: | --: | --: | -------- | ----- |
End with next actions, including whether to run keyword clustering, create a content brief, or save the chosen keywords.
## Guardrails
- Do not invent metrics. If OpenSEO does not return a value, write `unknown`.
- Do not call `save_keywords` without explicit confirmation.
- Prefer business-fit and intent-fit over chasing the largest volume term.

View File

@ -0,0 +1,107 @@
---
name: link-prospecting
description: Find link prospects, discover contact paths, and draft outreach from SERPs and backlink signals.
---
# OpenSEO Link Prospecting
## Goal
Find realistic pages, sites, and authors that might reference the user's page, product, study, guide, or tool. Use OpenSEO for prospect discovery, then use available web/search/browser tools for contact discovery.
## Required inputs
- `projectId`
- User domain or target URL
- Linkable asset, page, product, study, tool, or topic
- Optional competitors
- Optional market/location/language
## OpenSEO MCP tools
- `get_serp_results`: find ranking articles, listicles, resource pages, comparisons, and topical publishers.
- `get_backlinks_overview`: inspect competitor domain or page backlink/referring-domain patterns.
- `get_domain_overview`: qualify important prospect domains.
- `get_ranked_keywords`: understand what a prospect or competitor ranks for when topical fit matters.
- `search_local_businesses` and `get_local_serp_results`: use for local SEO link prospecting when nearby businesses, local competitors, or Maps/category signals can reveal partnership targets.
- `research_keywords`: expand prospecting queries.
## Contact discovery tools
After OpenSEO identifies good prospects, use available non-OpenSEO browsing or search tools for public contact discovery. Depending on the client, this may be web search, page fetches, browser automation, or a search API.
Look for:
- Author byline pages
- Contact pages
- Editorial guidelines
- About/team pages
- LinkedIn, X, Bluesky, or other professional profiles
- Newsletter or publication masthead pages
- Public email addresses in page HTML or visible page text
- Structured data such as `Person`, `Organization`, `sameAs`, or `email`
Only record contact details that were actually found. Include the source URL for any email, profile, or contact form.
## Prospecting query patterns
Build queries from the asset/topic:
- `<topic> resources`
- `best <category> tools`
- `<competitor> alternatives`
- `<topic> statistics`
- `<topic> guide`
- `<topic> examples`
- `<topic> templates`
- `<topic> software`
- `<topic> for <audience>`
Use `get_serp_results` in batches for the most relevant patterns. Send at most 10 queries per call.
## Workflow
1. Clarify the linkable asset and the reason someone would reference it.
2. Build 5-10 prospecting queries by default.
3. Call `get_serp_results` for those queries.
4. If competitors are provided, call `get_backlinks_overview` for the strongest competitor domains or pages first. Continue without backlink evidence if it is unavailable.
5. For local SEO, use `search_local_businesses` and `get_local_serp_results` around priority locations to identify nearby competitors, categories, and local SERP evidence before searching for local chambers, associations, campus resources, community pages, and directories.
6. Filter prospects:
- Keep topical relevance and editorial pages.
- Prioritize articles, directories, resource pages, comparisons, statistics pages, templates, and curated lists.
- Deprioritize homepages, login pages, thin affiliate pages, spam, unrelated forums, and direct competitors unless a comparison angle is valid.
7. For each good prospect, define the outreach angle:
- Broken/missing resource
- Better current data
- Useful tool/template
- Alternative or comparison inclusion
- Expert quote or supporting reference
8. For the strongest prospects, visit or search the prospect site to find the best contact path.
9. Draft outreach messages. If contact details were found, include the source. If not, list the next best contact-discovery path.
## Output format
Start with:
- Best outreach angle
- Highest-priority prospect type
- Any data limitations
Then include:
| Prospect URL | Site/domain | Source | Relevance | Suggested angle | Contact path | Priority |
| ------------ | ----------- | ------ | --------- | --------------- | ------------ | -------- |
Then provide 2-3 reusable outreach drafts:
- Resource/list inclusion
- Article update/reference suggestion
- Competitor alternative/comparison angle
## Guardrails
- Do not invent email addresses, social handles, or contact names.
- Do not say OpenSEO found contact details unless an OpenSEO tool returned them. Attribute contact discovery to the web/search/browser source used.
- If contact details are not available after a reasonable search, recommend specific discovery steps such as checking the author page, contact page, LinkedIn, X, or a reputable contact-enrichment tool.
- Avoid spammy mass outreach. Personalize by page and reason.
- Flag prospects that are direct competitors or likely paid placements.

View File

@ -0,0 +1,72 @@
---
name: local-seo
description: "Audit a Google Business Profile, compare it to local competitors, and map Maps visibility around a location."
---
# OpenSEO Local SEO
## Goal
Work out why a business does or does not show up in Google Maps and the local pack near its customers, and what to fix first.
Use this when rankings depend on a physical location or service area. For national organic work, use `competitor-analysis` or `keyword-research`.
## Required inputs
- `projectId`
- The business: name, or a `cid`/`placeId` (most reliable)
- Its coordinate (latitude/longitude) — derive it from a `search_local_businesses` / `get_local_serp_results` row; only ask the user when derivation is ambiguous
- One to three keywords customers actually search (e.g. "emergency plumber", not the brand name)
## OpenSEO MCP tools
- `search_local_businesses`: nearby listings, filterable by `minRating`, `minReviews`, and `isClaimed` — use `isClaimed: false` to find unclaimed listings when prospecting. One call with the brand name as `query` and a wide radius returns category, rating, review count, claimed status, coordinates, and `cid` for every location of a chain — usually enough that per-location `get_business_profile` calls are unnecessary.
- `get_local_serp_results`: the Maps/Local Finder result set near a coordinate. The rows carry `cid` and `place_id` — collect them once and reuse them everywhere below.
- `get_business_profile`: the full profile for one business (hours, rating breakdown) when the `search_local_businesses` row isn't enough.
- `get_business_reviews`: reviews with ratings, text, and whether the owner replied. Queued: a `processing` response returns a `taskId` — call again with it after 30-60 seconds, at no extra cost.
- `get_local_rank_grid`: rank at every point of a grid around a coordinate, with each point's result count and #1 business. 3x3 is nine searches; only go to 5x5 when the service area is genuinely wide.
- `get_google_business_questions`: Q&A on the profile (accepts `cid`/`placeId`).
- `get_business_updates`: posts published on the profile, with dates.
- `list_business_categories`: valid category slugs for `search_local_businesses`.
## Workflow
1. Find the business. Given only a name or website, `search_local_businesses` (name as `query`, wide radius) locates the listing and yields its `cid` and coordinate. If it returns several locations, the business is a chain — see multi-location below.
2. Run `get_local_serp_results` for the main keyword near the business coordinate. Record the top 3-5 competitors' `cid`/`place_id` and the user's own row.
3. Compare the user's listing against the top two competitors: primary category, additional categories, review count, hours completeness, photo count, claimed status. `search_local_businesses` rows usually carry all of this; use `get_business_profile` for what they lack.
4. Sanity-check each listing's website link (`url`/`contact_url` in the rows): it should deep-link to that location's page on the project domain, not a homepage or a stale domain. For broader on-page work, hand off to `run_site_audit`.
5. Call `get_business_reviews` for the user and the strongest competitor. Look at review volume, recency, average rating, and how many reviews got an owner reply.
6. Run `get_local_rank_grid` for the main keyword. Use the grid to separate "ranks at the storefront only" from "ranks across the service area", and each point's `topResult` to name who wins where the target doesn't.
7. Add `get_google_business_questions` and `get_business_updates` when the profile basics are already competitive and the gap is engagement rather than setup.
8. Turn the evidence into a prioritized list. Category and claim problems outrank posting cadence every time.
### Multi-location businesses
Always build the profile snapshot table for the whole chain — one `search_local_businesses` call covers it. The per-location deep-dives (reviews, grid, posts, Q&A) are where cost scales:
- 5 locations or fewer: deep-dive them all.
- More than 5: present the snapshot table, then ask the user (AskUserQuestion) which 1-3 locations to deep-dive. Pick sensible defaults to recommend — e.g. the weakest profile in the densest market.
## Output format
Start with:
- Profile snapshot (category, rating, reviews, claimed) — one row per location for chains
- Where visibility drops off, per the grid
- The one fix to do this week
Then include:
| Signal | This business | Best competitor | Gap | Action |
| ------ | ------------- | --------------- | --- | ------ |
Cover: categories, reviews (count, recency, owner replies), hours and profile completeness, listing website links, Maps coverage from the grid, Q&A and posting hygiene.
## Guardrails
- Do not run a 5x5 grid, or grids for several keywords, without telling the user the cost first — every point is a paid SERP call.
- Match businesses by `cid` or `place_id` when you have one. Name matching collides with chains and similarly named businesses.
- A missing rank at a grid point means the business wasn't among the results returned there. Read it with that point's `resultsCount`: a full result set means outranked; a near-empty one means a sparse SERP, not proof of invisibility.
- Do not infer local-pack strength from national organic metrics.
- Never recommend review gating, fake reviews, or keyword-stuffed business names.
- A grid centered on the wrong place is worse than no grid — confirm the coordinate matches the storefront before spending grid credits.

View File

@ -0,0 +1,63 @@
---
name: seo-audit
description: "Audit a website and deliver a one-page, plain-language SEO report anyone can act on, centered on a single do-this-week action."
---
# OpenSEO SEO Audit
## Goal
Audit a domain and produce a one-page HTML report that anyone, including a complete SEO beginner, can read once and act on. The whole report exists to support ONE action the owner can take this week; everything else is supporting detail.
Use this when asked for an SEO audit or review of a domain, especially when the output is a shareable report for a non-expert. For expert-facing analysis of a competitor or market, use `competitor-analysis` or `competitive-landscape` instead.
## Required inputs
- Domain to audit
- `projectId` (use `list_projects`; if no project matches the domain, create one with `create_project`)
## OpenSEO MCP tools
- `whoami`: confirm connection and remaining credits before spending anything. If OpenSEO is not connected, stop and ask the user to connect it.
- `list_projects` / `create_project`: resolve the `projectId`.
- `run_site_audit`: start the crawl (default page budget), then poll `get_audit_status` and read `get_audit_issues`. Use `get_audit_pages` when per-page evidence helps.
- `get_backlinks_overview`: backlink and referring-domain picture; usually the deciding data for the "one thing".
- `get_domain_overview`: estimated organic traffic and organic keyword count. Skip when the site is clearly dead.
- `research_keywords`: keyword ideas with volume and difficulty, used to propose a starting focus area. One call with 1-3 seeds taken from what the site is actually about. Skip when the site is down.
Keep total spend modest: one audit, one backlinks overview, at most one domain overview, and at most one keyword-research call. Only the overview and keyword lookups spend credits.
## Workflow
1. `whoami`, then resolve the `projectId`.
2. `run_site_audit` for the domain. While it crawls, fetch `get_backlinks_overview`.
3. When the crawl finishes, read `get_audit_issues` (and `get_domain_overview` if the site is alive).
4. If the audit comes back broken or nearly empty (certificate errors, 5xx, one page crawled): investigate before writing. Check the certificate and redirect variants yourself, and search the web for the business. A dead domain often has a live successor site, which flips the whole recommendation to "redirect the old domain".
5. Verify every finding you plan to report against the live page HTML by fetching pages yourself. Report nothing you have not seen evidence for.
6. Decide the one thing. Derive it from the data, never from generic advice. Common patterns:
- Clean site, no backlinks: outreach to guests, partners, or directories, with a ready-to-send message.
- Dead domain, live successor site: permanent redirect via hosting support, with the exact sentence to send them.
- Blocked or noindexed pages: remove the block.
It must be doable this week by a non-technical person, with copy-paste-ready mechanics included.
7. When the site is healthy, propose a starting focus area: run one `research_keywords` call seeded from the site's actual topic, then pick one theme and 3 to 5 specific, low-difficulty keywords the site can realistically rank for, each with the page or post to make. This is a starting direction, not a keyword strategy; point the user at the `keyword-research` skill for the full workflow. Skip this step entirely when the site is down — the one thing is all that matters there.
8. Write the report using `template.html` in this skill directory (see Output format).
9. Review before delivering: run an adversarial pass with a second agent or model if your environment has one, otherwise do a fresh self-review. Give the reviewer the verified facts and have it attack four things: claims beyond the facts, unglossed jargon, anything overwhelming for a beginner, and dramatic language. The reviewer may also flag true facts it was not given; check those against your evidence instead of "fixing" them.
10. Deliver the report: if your environment can publish or preview HTML (for example as an artifact), do that; otherwise save the HTML file and tell the user to open it in their browser.
## Output format
Use `template.html` next to this file. Fill in content; keep the CSS and structure as they are (light palette only, no dark mode).
- Header: domain as the title, the review date on its own line under it, then a 2-3 sentence summary of the whole report (overall state; the main gap and the one thing; what the report covers).
- Section order: verdict, the one thing, small fixes (5 to 10 max, ordered by impact), where to focus first (healthy sites only), already working, method footer.
- Each fix row shows the exact evidence (a quoted tag or number) and concrete steps a non-technical person can follow.
- "Where to focus first" names one topic area and 3 to 5 keywords, each with its search volume in plain words and the page or post to make. Omit the section when the site is down.
## Guardrails
- Tone: calm and plain. No exclamation points, no drama words, no em dashes, no "Not X. Y." contrasts, no filler. Severity words only where literally true (a down site is critical; a long title is not).
- Gloss every term of art in plain English on first use: canonical, meta description, alt text, crawler, 301, structured data.
- Skip nitpicks that do not matter for the specific site. A beginner report with twenty findings has failed.
- Missing backlink or ranking data means "no recorded data", not a penalty; say so rather than dramatizing it.
- Favor keywords the site can win now: specific intent, low difficulty. Do not list head terms a new site cannot rank for yet.
- Separate what the tools reported from what you verified yourself, and note both in the method footer.

View File

@ -0,0 +1,136 @@
<!-- SEO audit report template.
Replace ALL-CAPS placeholders and the example rows/items. Keep the CSS and structure.
If your agent publishes HTML through a pipeline that adds its own document wrapper
(for example an artifact viewer), strip this comment and the doctype/head/body tags;
as a standalone file opened in a browser, keep everything. -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>DOMAIN SEO review</title>
<style>
:root {
--bg: #e2e9f3;
--ink: #122650;
--muted: #4f5f79;
--rule: #c2cddd;
--rule-strong: #122650;
}
html { background: var(--bg); color-scheme: light; }
body {
background: var(--bg); color: var(--ink);
font-family: Charter, Georgia, "Times New Roman", serif;
line-height: 1.55; margin: 0; padding: 3.5rem 1.5rem 5rem;
}
.page { max-width: 860px; margin: 0 auto; }
.mono {
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-size: 0.72rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted);
}
h1 {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight: 700; font-size: 1.6rem; letter-spacing: -0.01em;
margin: 0.75rem 0 0.25rem; text-wrap: balance;
}
.subtitle { color: var(--muted); margin: 0 0 2.5rem; font-size: 1.02rem; max-width: 46rem; }
.dateline { color: var(--muted); font-size: 0.92rem; margin: 0 0 1.1rem; }
h2 {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight: 700; font-size: 1.25rem; margin: 3rem 0 0.75rem;
}
.lede { font-size: 1.05rem; max-width: 46rem; }
section > p { max-width: 46rem; }
hr.heavy { border: 0; border-top: 2px solid var(--rule-strong); margin: 3rem 0 0; }
section .cols:first-of-type { border-top: 1px solid var(--rule); }
.cols {
display: grid; grid-template-columns: minmax(180px, 5fr) 8fr;
gap: 1.25rem; padding: 0.6rem 0; border-bottom: 1px solid var(--rule);
}
.row-title { font-size: 1.12rem; margin: 0; }
.row-title .tag { display: block; margin-top: 0.35rem; }
.cols > div p { margin: 0 0 0.6rem; color: var(--muted); font-size: 0.95rem; }
.cols > div p:last-child { margin-bottom: 0; }
.cols > div p.fix { color: var(--ink); }
.fix b { font-weight: 600; }
code {
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-size: 0.85em; word-break: break-word;
}
ol { padding-left: 1.3rem; max-width: 44rem; }
ol li { margin-bottom: 0.5rem; }
ul.plain { list-style: none; padding: 0; max-width: 46rem; }
ul.plain li { padding: 0.5rem 0; border-bottom: 1px solid var(--rule); font-size: 0.98rem; }
ul.plain li:first-child { border-top: 1px solid var(--rule); }
ul.plain .why { color: var(--muted); }
footer { margin-top: 4rem; color: var(--muted); font-size: 0.88rem; max-width: 46rem; }
@media (max-width: 620px) {
.cols { grid-template-columns: 1fr; gap: 0.4rem; }
}
</style>
</head>
<body>
<div class="page">
<header>
<h1>DOMAIN</h1>
<p class="dateline">DATE</p>
<p class="subtitle">SUMMARY: 2-3 sentences covering the whole report. Sentence 1: overall state of the site. Sentence 2: the main gap and the one thing. Sentence 3: what the report covers.</p>
</header>
<section>
<h2>Where the site stands</h2>
<p class="lede">VERDICT-PARAGRAPH: what is working, in plain words.</p>
<p class="lede">VERDICT-PARAGRAPH-2: the main gap the one thing addresses.</p>
</section>
<hr class="heavy">
<section>
<h2>If you only do one thing</h2>
<p class="lede">THE-ONE-THING, one sentence.</p>
<p>WHY-IT-MATTERS, one or two sentences.</p>
<ol>
<li>STEP-1 (concrete, doable today).</li>
<li>STEP-2 (include any copy-paste message in <i>italics</i>).</li>
<li>STEP-3 (how to check it worked).</li>
</ol>
</section>
<section>
<h2>Small fixes, in order</h2>
<!-- one .cols block per finding; 5-10 max; tag = severity word only where literally true -->
<div class="cols">
<div><p class="row-title">FINDING-TITLE <span class="mono tag">low</span></p></div>
<div>
<p>WHAT-WE-FOUND with the exact evidence, e.g. a quoted tag in <code>code</code>.</p>
<p class="fix"><b>Fix:</b> CONCRETE-STEPS a non-technical person can follow.</p>
</div>
</div>
</section>
<section>
<h2>Where to focus first</h2>
<!-- healthy sites only: omit this whole section when the site is down -->
<p>FOCUS-INTRO: the one topic area to build toward and why it fits this site.</p>
<div class="cols">
<div><p class="row-title">KEYWORD <span class="mono tag">N searches/mo</span></p></div>
<div>
<p>WHY-THIS-KEYWORD: who searches it and how winnable it is, in plain words.</p>
<p class="fix"><b>Make:</b> WHAT-TO-CREATE, one page or post and its angle.</p>
</div>
</div>
</section>
<section>
<h2>Already working</h2>
<ul class="plain">
<li>GOOD-THING. <span class="why">Why it matters, one clause.</span></li>
</ul>
</section>
<footer>
<p>Reviewed DATE. DATA-SOURCES, e.g.: crawl and backlink data from OpenSEO; every page also fetched and reviewed individually.</p>
</footer>
</div>
</body>
</html>

View File

@ -0,0 +1,111 @@
---
name: seo-coach
description: Enter a friendly OpenSEO coach mode that explains workflows, recommends next steps, and helps users use agents, web search, scraping, and MCP data effectively.
---
# OpenSEO Coach
## Goal
Act as a friendly SEO coach for users working with OpenSEO and an AI agent. Help them understand what the workflows do, choose the right next action, and use the agent's full toolset effectively.
## Tone
Be warm, direct, and beginner-friendly. Ask whether the user is new to SEO and adapt the explanation depth. Avoid sounding like a course or a consultant deck. Make SEO feel doable.
## First response
When this mode starts, orient the user:
- Ask whether they are new to SEO, experienced, or somewhere in between.
- Ask what site or project they are working on.
- Ask whether they want strategy, execution help, or explanation of the tools.
- Offer 2-4 concrete next options, not a long menu.
Example:
```text
I can coach you through this. Are you new to SEO, or do you mostly want help using OpenSEO faster?
Good starting points:
- Set up SEO project context
- Get a one-page audit of your site
- Find keyword opportunities
- Map keywords to pages
- Study a competitor
- Build link prospects for a page
```
## What each workflow does
- `seo-project-setup`: sets up the workspace, verifies MCP, captures goals and positioning, and connects Google Search Console (or imports GSC exports).
- `seo-audit`: audits a site and produces a one-page, plain-language report built around a single next action. The right first workflow for anyone with an existing site, especially beginners.
- `keyword-research`: finds search opportunities from seed topics and evaluates volume, difficulty, CPC, intent, and SERPs.
- `keyword-clustering`: groups keywords by intent and maps clusters to existing or proposed pages.
- `competitive-landscape`: identifies who wins across a market and what content/backlink patterns are working.
- `competitor-analysis`: studies one competitor's keywords, content themes, backlink profile, and gaps.
- `local-seo`: audits a Google Business Profile against local competitors and maps Maps visibility around a location.
- `link-prospecting`: finds likely link opportunities, discovers contact paths, and drafts outreach.
## Tool coaching
Explain the difference between data sources:
- OpenSEO MCP tools provide SEO data such as keyword research, exact ranked keywords, search volume, SERPs, SERP competitors, local business and Maps data, domain overviews, backlinks, saved keywords, projects, and rank trackers.
- Google Search Console (when connected on the project's Integrations page) is the user's own first-party data — real clicks, impressions, CTR, and position. Read it live with `get_search_console_performance` instead of asking for CSV exports. It's free (no credits) and the best starting point for "what already ranks" and near-ranking opportunities.
- Web search can find current market context, recent pages, reviews, docs, social profiles, and contact paths outside OpenSEO.
- Browser/page scraping can extract page copy, headings, author names, contact links, schema, and content structure.
- Local files can preserve strategy, GSC CSVs, content briefs, crawls, prospect lists, and prior decisions over time.
Encourage the user to put project files in one SEO folder so the agent can reuse context.
## Coaching patterns
When the user is unsure what to do:
1. Clarify their goal.
2. Identify what data they already have.
3. Pick one workflow.
4. Explain what the agent will do.
5. Ask for only the next needed input.
When the user asks for education:
- Explain the concept plainly.
- Show how it maps to an OpenSEO workflow.
- Give a concrete example.
- Offer to run the next step.
When the user asks for strategy:
- Anchor on business goals and positioning before keywords.
- Separate SEO competitors from business competitors.
- Prioritize pages and topics that can plausibly create business value.
- Use SERPs to understand intent instead of guessing.
- For local SEO, use local visibility/Maps evidence instead of relying only on national keyword and organic-domain metrics.
When the user asks for execution:
- Move quickly into the relevant workflow.
- Use OpenSEO MCP data where available.
- Use web/search/browser tools for context that OpenSEO does not provide.
- Save or tag data only after confirmation.
## Suggested next actions
Offer concise options based on context:
- "Let's set up project context first."
- "Let's audit your site and find the one thing to do first."
- "Let's research keywords from your seed topics."
- "Let's cluster your GSC/query export into page targets."
- "Let's map the competitive landscape before choosing pages."
- "Let's study one competitor."
- "Let's find link prospects for your best linkable asset."
## Guardrails
- Do not overload beginners with every SEO concept at once.
- Do not pretend OpenSEO MCP can browse arbitrary pages or discover contacts by itself.
- Distinguish live SEO data, web evidence, local-file evidence, and coaching judgment.
- Keep recommendations actionable: one next step is usually better than ten.

View File

@ -0,0 +1,166 @@
---
name: seo-project-setup
description: Set up a durable local SEO workspace with project context, notes, goals, positioning, preferences, MCP checks, and Search Console data intake.
---
# OpenSEO SEO Project Setup
## Goal
Help the user set up a local SEO workspace for one website or SEO project. The folder is where the agent saves notes, goals, exports, briefs, reports, preferences, and project context over time. This is a workspace and context setup workflow, not a full audit.
## Tone
Be friendly, practical, and structured. Ask questions in small batches. Explain why each item matters only when useful. Do not overwhelm a beginner with jargon.
## Checklist
### 1. Pick a working folder
Suggest that the user choose or create a local folder for SEO work, for example:
- `~/SEO/<company-or-site>/`
- `~/Documents/SEO/<company-or-site>/`
- A repo or workspace folder if SEO work should live beside website/content files
Explain that keeping notes, exports, briefs, scraped pages, reports, and preferences in one folder helps the agent build context over time. Future SEO workflows can use that folder rather than starting from a blank conversation.
Recommended starter structure:
```text
seo-workspace/
README.md
gsc/
keywords/
competitors/
content/
outreach/
reports/
```
Do not create folders unless the user asks. If file tools are available and the user asks, create a simple structure and a short `README.md` with the current goals, known sites, and user preferences for how the agent should approach SEO for this project.
### 2. Collect website scope
Ask for:
- Primary website/domain
- Additional domains or subdomains
- Important products, services, categories, or pages
- Target countries/languages
- Whether the site is new, established, migrating, or recovering from a drop
- CMS or publishing workflow, if relevant
### 3. Capture goals
Ask the user what they want from SEO:
- More qualified leads
- More signups/trials
- More ecommerce revenue
- More newsletter/audience growth
- More brand/category awareness
- Recovery from traffic loss
- Better ranking for specific pages
Ask for success metrics and timeframe. If goals are vague, help turn them into measurable goals such as "increase non-branded organic signups" or "rank top 10 for 20 buying-intent terms."
### 4. Capture positioning and strategy context
Ask what research they have already done about the company, product, audience, and competitors. Request any notes, docs, customer interviews, positioning docs, pitch decks, landing pages, or strategy memos they can share.
Probe for:
- Who the product or site is for
- What pain it solves
- Why users choose it over alternatives
- Competitors and substitutes
- Strong opinions or positioning claims
- Best customers and bad-fit customers
- Existing content that already converts
- Topics they do not want to target
If the user has not done this yet, offer to help research positioning using the company website, competitor pages, reviews, forums, and web search.
### 5. Verify OpenSEO MCP
After the user has described the company, website, goals, and positioning, check that OpenSEO MCP is configured and mapped to the right project:
1. Use `whoami` if available.
2. Use `list_projects` to confirm the user can access projects.
3. Match the project to the website/domain they want to rank for.
4. If the project list is ambiguous, ask the user which project should be used.
5. If the MCP is unavailable, tell the user to connect OpenSEO MCP before continuing with live OpenSEO data.
Do not run research tools just to test connectivity; `whoami` and `list_projects` are enough.
### 6. Connect Google Search Console
GSC is the richest first-party signal: existing impressions, near-ranking terms, cannibalization, and pages that already have search demand.
**Preferred (hosted): connect it natively.** On the project's Integrations page, connect Google Search Console and pull live data with `get_search_console_performance`. Once connected, the agent reads it directly in `keyword-research` and `keyword-clustering` — no manual files to maintain.
**Fallback (self-hosted, or if the user prefers files):** ask the user to export CSVs from Search Console into the SEO working folder.
Recommended exports:
- Queries: last 3 months and last 16 months if available
- Pages: last 3 months and last 16 months if available
- Query + page combinations when possible
- Countries/devices if relevant
Ask them to drop files into `gsc/` and use names like:
```text
gsc/queries-last-3-months.csv
gsc/pages-last-3-months.csv
gsc/queries-last-16-months.csv
gsc/pages-last-16-months.csv
```
### 7. Inventory existing assets
Ask for or discover:
- Sitemap or important URL list
- Current blog/resources/content library
- Product/category/feature pages
- Existing keyword lists
- Current rank trackers
- Backlink or PR assets
- Linkable assets such as studies, templates, tools, datasets, calculators, or original opinions
### 8. Recommend first workflow
After intake, recommend one next OpenSEO workflow:
- `seo-audit`: when the site already exists and the user wants to know what to fix or do first, especially if they are new to SEO
- `keyword-research`: when the user needs ideas from seed topics
- `keyword-clustering`: when they have keywords or GSC data to map to pages
- `competitive-landscape`: when the market is unclear
- `competitor-analysis`: when they know a competitor to study
- `link-prospecting`: when they have a linkable asset or target page
## Output format
Use a checklist with statuses:
| Step | Status | Notes | Next action |
| ---- | ------ | ----- | ----------- |
Then summarize:
- Working folder
- OpenSEO MCP/project status
- Sites in scope
- Goals
- Known positioning
- Uploaded data/files
- Recommended next workflow
## Guardrails
- Keep setup lightweight. The user should feel oriented, not assigned homework.
- Do not pretend a GSC CSV has been uploaded unless you can see it, and do not claim Search Console is connected unless `get_search_console_performance` confirms it (it returns a "not connected" message otherwise).
- Keep project setup focused on setup and context unless the user asks for live research.
- If web search or scraping is used for positioning research, distinguish source evidence from inference.

View File

@ -0,0 +1,36 @@
#!/usr/bin/env node
// Codex plugin installs copy the plugin directory and skip symlinks, so
// plugins/openseo/skills/* must be real files, not symlinks to .agents/skills/*.
// Run this after editing any of the skills listed below. `pnpm ci:check` runs
// this and diffs the result, so a stale copy fails CI instead of shipping.
import { cpSync, rmSync } from "node:fs";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
const repoRoot = fileURLToPath(new URL("..", import.meta.url));
const sourceDir = join(repoRoot, ".agents/skills");
const targetDir = join(repoRoot, "plugins/openseo/skills");
const skills = [
"competitive-landscape",
"competitor-analysis",
"keyword-clustering",
"keyword-research",
"link-prospecting",
"local-seo",
"seo-audit",
"seo-coach",
"seo-project-setup",
];
// Wipe and rebuild so a skill removed from the list above doesn't leave a
// stale copy behind.
rmSync(targetDir, { recursive: true, force: true });
for (const skill of skills) {
cpSync(join(sourceDir, skill), join(targetDir, skill), {
recursive: true,
dereference: true,
});
}
console.log(`Synced ${skills.length} skills into plugins/openseo/skills/`);

View File

@ -0,0 +1,78 @@
---
title: "Install the OpenSEO plugin for Claude Code"
description: "Add OpenSEO MCP and Agent Skills to Claude Code with one marketplace and one install command."
---
The OpenSEO plugin bundles OpenSEO MCP and all nine SEO Agent Skills into one install. This is the preferred way to set up OpenSEO in Claude Code.
## Install
Run these two commands in Claude Code:
```bash
/plugin marketplace add every-app/open-seo
/plugin install openseo@openseo
```
If the install summary says `Run /reload-plugins to activate.`, run that command.
Claude Code connects OpenSEO MCP at `https://app.openseo.so/mcp` and enables nine skills:
- SEO Project Setup
- SEO Coach
- SEO Audit
- Keyword Research
- Keyword Clustering
- Competitive Landscape
- Competitor Analysis
- Local SEO
- Link Prospecting
## Finish the login
Claude Code should prompt you to log in to OpenSEO right after install. If it doesn't, run `/mcp` and approve the OpenSEO connection from there.
## Run a skill
Plugin skills are namespaced by the plugin name:
```
/openseo:seo-project-setup
/openseo:seo-coach
/openseo:seo-audit
/openseo:keyword-research
/openseo:keyword-clustering
/openseo:competitive-landscape
/openseo:competitor-analysis
/openseo:local-seo
/openseo:link-prospecting
```
## Claude Desktop
Claude Desktop doesn't support this plugin format — plugins are a Claude Code feature. For Claude Desktop, [add OpenSEO as an MCP connector](/docs/mcp#claude-desktop) instead.
## Update or remove
```bash
/plugin marketplace update openseo
/plugin uninstall openseo@openseo
```
Updates land in the cache immediately, but the running session keeps the old version until you run `/reload-plugins` or restart Claude Code.
## Troubleshooting
To check what's actually installed, run `/plugin list` rather than bare `/plugin``/plugin` alone opens an interactive panel that doesn't show plain text.
If `/reload-plugins` reports `0 skills`, that's normal, not a failure — its summary only counts a plugin's `commands/` directory, not `skills/`. Confirm the skills loaded by running one directly, for example `/openseo:seo-audit`.
If `/plugin uninstall openseo@openseo` reports "not installed in this project," you likely installed to a different scope than the one being checked (User, Project, or Local). Run `/plugin list` to see the actual scope, or sidestep the picker entirely with the shell form: `claude plugin uninstall openseo@openseo --scope user`.
If plugin skills don't appear, clear the plugin cache with `rm -rf ~/.claude/plugins/cache` — this clears every installed plugin's cache, not just OpenSEO's, so reinstall anything else you have after — then restart Claude Code and reinstall the plugin.
If the OpenSEO connection doesn't show as authenticated, run `/mcp`, select OpenSEO, and complete the login.
## Other clients
This plugin is for Claude Code. For Codex CLI, use the [OpenSEO plugin for Codex](/docs/codex-plugin) instead. For Cursor, Codex Desktop, Claude Desktop, or an API key setup, see [Set up OpenSEO MCP](/docs/mcp) and [Set up OpenSEO Agent Skills](/docs/skills/setup).

View File

@ -0,0 +1,58 @@
---
title: "Install the OpenSEO plugin for Codex"
description: "Add OpenSEO MCP and Agent Skills to Codex with one marketplace and one install command."
---
The OpenSEO plugin bundles OpenSEO MCP and all nine SEO Agent Skills into one install. This is the preferred way to set up OpenSEO in Codex CLI.
## Install
Run these commands in your terminal:
```bash
codex plugin marketplace add every-app/open-seo
codex plugin add openseo@openseo
codex mcp login openseo
```
`codex mcp login` opens a browser to approve the OpenSEO connection. If it reports that `openseo` isn't found, restart Codex first — bundled MCP servers only register after a restart, not immediately after install — then run `codex mcp login openseo` again.
Codex connects OpenSEO MCP at `https://app.openseo.so/mcp` and enables nine skills:
- SEO Project Setup
- SEO Coach
- SEO Audit
- Keyword Research
- Keyword Clustering
- Competitive Landscape
- Competitor Analysis
- Local SEO
- Link Prospecting
## Run a skill
Type `$` in Codex to see available skills, or ask Codex to run one by name, for example "run seo-project-setup" or "run seo-audit on example.com".
## Update or remove
```bash
codex plugin marketplace upgrade openseo
codex plugin remove openseo@openseo
```
## Troubleshooting
If the OpenSEO MCP server doesn't appear after restart, run `/mcp` in the Codex TUI to check its status, then run `codex mcp login openseo` again.
If it still doesn't authenticate, log out first and retry:
```bash
codex mcp logout openseo
codex mcp login openseo
```
If a `codex plugin` command reports "unrecognized subcommand," run `codex plugin --help` to see the subcommands your installed version actually supports — they've changed across versions (for example, `add`/`remove`, not `install`/`uninstall`).
## Other clients
This plugin is for Codex CLI. For Claude Code, use the [OpenSEO plugin for Claude Code](/docs/claude-code-plugin) instead. For Claude Desktop, Cursor, Codex Desktop, or an API key setup, see [Set up OpenSEO MCP](/docs/mcp) and [Set up OpenSEO Agent Skills](/docs/skills/setup).

View File

@ -17,6 +17,8 @@ For the most current setup UI and a copyable endpoint, open [AI & MCP in OpenSEO
## Claude Code
The [OpenSEO plugin](/docs/claude-code-plugin) is the preferred way to connect Claude Code — one install adds MCP and all nine Agent Skills together. Use the steps below only if you want MCP on its own.
Use user scope to make OpenSEO available across projects. Use local scope for the current repository.
```bash
@ -54,6 +56,8 @@ Claude Desktop custom connectors require a Claude plan that supports custom conn
## Codex CLI
The [OpenSEO plugin](/docs/codex-plugin) is the preferred way to connect Codex CLI — one install adds MCP and all nine Agent Skills together. Use the steps below only if you want MCP on its own.
Run this in your terminal:
```bash

View File

@ -1,3 +1,3 @@
{
"pages": ["mcp", "skills", "self-hosting"]
"pages": ["mcp", "claude-code-plugin", "codex-plugin", "skills", "self-hosting"]
}

View File

@ -11,6 +11,8 @@ You stay in charge of strategy. Your agent uses OpenSEO data and the skill instr
## Set up OpenSEO Agent Skills
On Claude Code, the [OpenSEO plugin](/docs/claude-code-plugin) installs MCP and every skill below in one step. On Codex CLI, the [OpenSEO plugin](/docs/codex-plugin) does the same. Use the manual steps here for other agents, or if you want to pick individual skills.
1. [Set up OpenSEO MCP](/docs/mcp).
2. [Set up OpenSEO Agent Skills](/docs/skills/setup).

View File

@ -5,6 +5,8 @@ description: "Add OpenSEO skill files to your AI agent after connecting OpenSEO
OpenSEO Agent Skills are separate files from OpenSEO MCP.
On Claude Code, skip the steps below and use the [OpenSEO plugin](/docs/claude-code-plugin) instead — it installs MCP and every skill in one step. On Codex CLI, use the [OpenSEO plugin](/docs/codex-plugin) the same way.
First, [set up OpenSEO MCP](/docs/mcp). MCP gives your agent access to OpenSEO data.
Then add the OpenSEO `SKILL.md` files you want your agent to use. Each skill gives your agent one SEO workflow.