MCP Server

pSEO Engine

net.quantumcx/pseo-engine
Business & Operations Marketing & Advertising Public & reachable MCP 2025-11-25

What this MCP does

Runs programmatic SEO projects covering keyword research, AI page generation, audits, review queues, and publishing approved landing pages.

seo_audit_start
Audit every generated page for defects
ASYNCHRONOUS. Scans all generated pages for broken text, unfilled placeholders, truncated copy and missing images, recording findings per page. Returns a jobId; poll seo_job_status. This only REPORTS problems — it does not fix them. Costs 30c per call.
Open world
Input schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projectId'], 'properties': {'projectId': {'type': 'string', 'minLength': 1, 'description': "The project id, exactly as returned by seo_project_list (a cuid such as 'cmtjyi1q20000l204octn48ai'). Not the slug, not the display name. If you do not have one, call seo_project_list first."}}}
Output schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['jobId', 'projectId', 'nextStep'], 'properties': {'jobId': {'type': 'string', 'description': 'Poll seo_job_status.'}, 'nextStep': {'type': 'string'}, 'projectId': {'type': 'string'}}, 'additionalProperties': False}
seo_content_generate
Generate page content for pending rows
ASYNCHRONOUS. Starts the content-generation queue over every PENDING row in the project. It also spends real AI budget per generated page. Requires the project to be configured (check readyToGenerate via seo_project_get first). Returns a jobId immediately; poll seo_job_status. Generated pages land in GENERATED status and are NOT live until approved and published. Costs 75c per call.
Open world
Input schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projectId'], 'properties': {'projectId': {'type': 'string', 'minLength': 1, 'description': "The project id, exactly as returned by seo_project_list (a cuid such as 'cmtjyi1q20000l204octn48ai'). Not the slug, not the display name. If you do not have one, call seo_project_list first."}}}
Output schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projectId', 'nextStep'], 'properties': {'jobId': {'type': 'string', 'description': 'Poll seo_job_status until COMPLETED.'}, 'total': {'type': 'integer', 'maximum': 9007199254740991, 'minimum': -9007199254740991, 'description': 'Rows queued for generation.'}, 'nextStep': {'type': 'string'}, 'projectId': {'type': 'string'}}, 'additionalProperties': False}
seo_job_status
Check the latest background job
Returns the most recent background job for a project: type, status (QUEUED/RUNNING/COMPLETED/FAILED/CANCELLED), progress counters and the last log line. Poll this every few seconds after any tool that starts a job. Do NOT re-call the start tool while status is RUNNING; it will be refused. FREE — this call costs nothing.
Read only Idempotent
Input schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projectId'], 'properties': {'projectId': {'type': 'string', 'minLength': 1, 'description': "The project id, exactly as returned by seo_project_list (a cuid such as 'cmtjyi1q20000l204octn48ai'). Not the slug, not the display name. If you do not have one, call seo_project_list first."}}}
Output schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projectId', 'job'], 'properties': {'job': {'anyOf': [{'type': 'object', 'required': ['id', 'type', 'status', 'total', 'completed', 'failed', 'lastLog'], 'properties': {'id': {'type': 'string'}, 'type': {'type': 'string'}, 'total': {'anyOf': [{'type': 'integer', 'maximum': 9007199254740991, 'minimum': -9007199254740991}, {'type': 'null'}]}, 'failed': {'anyOf': [{'type': 'integer', 'maximum': 9007199254740991, 'minimum': -9007199254740991}, {'type': 'null'}]}, 'status': {'type': 'string'}, 'lastLog': {'type': ['string', 'null']}, 'completed': {'anyOf': [{'type': 'integer', 'maximum': 9007199254740991, 'minimum': -9007199254740991}, {'type': 'null'}]}}, 'additionalProperties': False}, {'type': 'null'}], 'description': 'Null when no job has ever run for this project.'}, 'note': {'type': 'string'}, 'projectId': {'type': 'string'}, 'stillRunning': {'type': 'boolean', 'description': 'Keep polling while true.'}}, 'additionalProperties': False}
seo_project_connect_ai
Connect (or replace) a project's AI keys
Stores the AI provider key this project's generation, audits and research run on — BYOK: the tokens bill YOUR provider account, not ours. Recommended: a Google Gemini API key (aistudio.google.com/apikey); its free tier works with no card. Optionally stores a Perplexity API key to enable citation-bearing AI visibility checks. Keys are stored encrypted and can never be read back — only replaced or cleared. FREE — this call costs nothing.
Idempotent
Input schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projectId'], 'properties': {'aiModel': {'type': 'string', 'description': 'Optional model id; defaults to gemini-2.5-flash on the Gemini endpoint.'}, 'aiApiKey': {'type': 'string', 'description': 'The AI provider key to store (replaces any stored one). Omit to leave unchanged.'}, 'aiBaseUrl': {'type': 'string', 'description': "Optional OpenAI-compatible base URL; defaults to Google's Gemini endpoint."}, 'projectId': {'type': 'string', 'minLength': 1, 'description': "The project id, exactly as returned by seo_project_list (a cuid such as 'cmtjyi1q20000l204octn48ai'). Not the slug, not the display name. If you do not have one, call seo_project_list first."}, 'clearAiKey': {'type': 'boolean', 'description': 'Pass true to REMOVE the stored AI key — generation stops until a new one is connected.'}, 'perplexityApiKey': {'type': 'string', 'description': 'Optional Perplexity API key for visibility checks; replaces any stored one. Omit to leave unchanged.'}, 'clearPerplexityKey': {'type': 'boolean', 'description': 'Pass true to REMOVE the stored Perplexity key.'}}}
Output schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projectId', 'aiKeyConnected', 'perplexityKeyConnected', 'note'], 'properties': {'note': {'type': 'string'}, 'projectId': {'type': 'string'}, 'aiKeyConnected': {'type': 'boolean'}, 'perplexityKeyConnected': {'type': 'boolean'}}, 'additionalProperties': False}
seo_project_create
Create a project
Creates a new programmatic-SEO project and returns its projectId. Call this when seo_project_list comes back empty — every other tool needs a projectId and a new account has none. The project is created EMPTY: it still needs a data source and a content spec before seo_content_generate will run, so call seo_project_get afterwards and read readinessNote. FREE — this call costs nothing.
Input schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['name'], 'properties': {'name': {'type': 'string', 'maxLength': 120, 'minLength': 1, 'description': "Display name, e.g. 'Plumbers by city'. The URL slug is derived from it automatically."}, 'domain': {'type': 'string', 'description': "Optional custom domain the pages will be published on, e.g. 'example.com'. Omit to serve them under the platform's own domain."}, 'aiModel': {'type': 'string', 'description': 'Optional model id. Defaults to gemini-2.5-flash on the Gemini endpoint.'}, 'aiApiKey': {'type': 'string', 'description': 'BYOK: the AI provider key this project runs on (stored encrypted). Recommended: a Google Gemini key from aistudio.google.com/apikey — its free tier works with no card. Omit to connect later via seo_project_connect_ai.'}, 'template': {'enum': ['location', 'industry', 'comparison', 'all'], 'type': 'string', 'description': "Page template. Defaults to 'location'."}, 'aiBaseUrl': {'type': 'string', 'description': "Optional OpenAI-compatible base URL for the key. Defaults to Google's Gemini endpoint. Example for OpenRouter: https://openrouter.ai/api/v1"}, 'perplexityApiKey': {'type': 'string', 'description': 'Optional Perplexity API key (api.perplexity.ai) to enable citation-bearing AI visibility checks for this project. Stored encrypted.'}}}
Output schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projectId', 'name', 'slug', 'domain', 'template', 'readyToGenerate', 'nextStep'], 'properties': {'name': {'type': 'string'}, 'slug': {'type': 'string'}, 'domain': {'type': ['string', 'null']}, 'nextStep': {'type': 'string'}, 'template': {'type': 'string'}, 'projectId': {'type': 'string', 'description': 'Pass this to every other tool.'}, 'readyToGenerate': {'type': 'boolean', 'description': 'Always false here — a new project has no data source yet.'}}, 'additionalProperties': False}
seo_project_get
Get one project's configuration and readiness
Returns one project's settings and whether it is configured enough to generate content (needs both a field mapping and a content spec). Use this before seo_content_generate to avoid starting a run that will immediately fail. FREE — this call costs nothing.
Read only Idempotent
Input schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projectId'], 'properties': {'projectId': {'type': 'string', 'minLength': 1, 'description': "The project id, exactly as returned by seo_project_list (a cuid such as 'cmtjyi1q20000l204octn48ai'). Not the slug, not the display name. If you do not have one, call seo_project_list first."}}}
Output schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projectId', 'name', 'slug', 'domain', 'template', 'indexable', 'researchBrief', 'readyToGenerate', 'readinessNote'], 'properties': {'name': {'type': 'string'}, 'slug': {'type': 'string'}, 'domain': {'type': ['string', 'null']}, 'template': {'type': 'string'}, 'indexable': {'type': 'boolean', 'description': 'False while the project is in staging mode.'}, 'projectId': {'type': 'string'}, 'readinessNote': {'type': 'string', 'description': 'Plain-language reason, whichever way readyToGenerate went.'}, 'researchBrief': {'type': ['string', 'null']}, 'readyToGenerate': {'type': 'boolean'}}, 'additionalProperties': False}
seo_project_list
List projects
Lists every programmatic-SEO project this API key can act on, with id, name, slug, custom domain and page count. Call this FIRST in any session — every other tool needs a projectId from here, and ids cannot be guessed or carried over from another account. FREE — this call costs nothing.
Read only Idempotent
Input schema
{'type': 'object', 'properties': {}}
Output schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projects', 'hint'], 'properties': {'hint': {'type': 'string', 'description': 'What to do next, given whether the list was empty.'}, 'projects': {'type': 'array', 'items': {'type': 'object', 'required': ['projectId', 'name', 'slug', 'domain', 'totalPages', 'createdAt'], 'properties': {'name': {'type': 'string'}, 'slug': {'type': 'string'}, 'domain': {'type': ['string', 'null']}, 'createdAt': {'type': 'string'}, 'projectId': {'type': 'string'}, 'totalPages': {'type': 'integer', 'maximum': 9007199254740991, 'minimum': -9007199254740991}}, 'additionalProperties': False}, 'description': 'Every project this key can act on. Empty on a new account.'}}, 'additionalProperties': False}
seo_publish
Publish approved pages
SYNCHRONOUS. Takes approved pages live. IMPORTANT: only rows in REVIEWED status are published — GENERATED drafts are deliberately skipped, because approval is a human gate in this product. The response reports how many were skipped and why; if publishedCount is 0 and skippedNeedsReview is high, the pages need approving in the dashboard first. Costs 15c per call.
Open world Idempotent
Input schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projectId'], 'properties': {'projectId': {'type': 'string', 'minLength': 1, 'description': "The project id, exactly as returned by seo_project_list (a cuid such as 'cmtjyi1q20000l204octn48ai'). Not the slug, not the display name. If you do not have one, call seo_project_list first."}}}
Output schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projectId'], 'properties': {'note': {'type': 'string'}, 'projectId': {'type': 'string'}, 'publishedCount': {'type': 'integer', 'maximum': 9007199254740991, 'minimum': -9007199254740991, 'description': 'Rows taken live by this call.'}, 'skippedNeedsReview': {'type': 'integer', 'maximum': 9007199254740991, 'minimum': -9007199254740991, 'description': 'GENERATED drafts skipped: approval is a human gate.'}}, 'additionalProperties': False}
seo_research_start
Start AI keyword research
ASYNCHRONOUS. Starts a keyword-research run that mines and qualifies keywords into PENDING rows. It spends real AI budget, so call it once and then poll seo_job_status until status is COMPLETED. Returns immediately with a jobId — the keywords do NOT exist yet when this returns. Fails if a research run is already in progress. HUMAN trial accounts get one research run free; a second run returns code card_required — only a human on the account can start a plan, so stop and report instead of retrying. Costs 750c per call.
Open world
Input schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projectId', 'brief'], 'properties': {'brief': {'type': 'string', 'minLength': 10, 'description': "One or two sentences describing the niche and the customer, e.g. 'Plumbing lead generation for independent plumbers across UK cities'. Must be at least 10 characters — a bare keyword is not enough context to mine from."}, 'projectId': {'type': 'string', 'minLength': 1, 'description': "The project id, exactly as returned by seo_project_list (a cuid such as 'cmtjyi1q20000l204octn48ai'). Not the slug, not the display name. If you do not have one, call seo_project_list first."}, 'targetCount': {'type': 'integer', 'default': 200, 'maximum': 2000, 'minimum': 10, 'description': 'How many keywords to mine, 10-2000. Higher costs more AI time. Start around 200 unless told otherwise.'}}}
Output schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['started', 'jobId', 'projectId', 'nextStep'], 'properties': {'jobId': {'type': 'string', 'description': 'Poll seo_job_status; the keywords do not exist yet.'}, 'started': {'type': 'boolean'}, 'nextStep': {'type': 'string'}, 'projectId': {'type': 'string'}}, 'additionalProperties': False}
seo_rows_list
List pages in a project
Returns a page of rows (individual generated pages) with slug, target keyword and status. Statuses are PENDING (no content yet), GENERATING, GENERATED (draft), REVIEWED (approved), PUBLISHED (live), FAILED, FLAGGED_DUPLICATE. Content bodies are NOT included — this is a listing, not an export. Use sort:'risk' to get the review queue ordered by risk score (duplicates and pages with QA findings first) so a human reviews the riskiest pages before bulk-approving the safe ones. Costs 3c per call.
Read only Idempotent
Input schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projectId'], 'properties': {'sort': {'enum': ['createdAt', 'risk'], 'type': 'string', 'description': "'risk' returns pages ordered by review risk (highest first), each with a score, band and human-readable reasons. Omit for creation order."}, 'limit': {'type': 'integer', 'default': 50, 'maximum': 200, 'minimum': 1, 'description': 'How many rows to return, 1-200. Defaults to 50. Values above 200 are rejected — page through instead of asking for everything.'}, 'status': {'enum': ['PENDING', 'GENERATING', 'GENERATED', 'REVIEWED', 'PUBLISHED', 'FAILED', 'FLAGGED_DUPLICATE'], 'type': 'string', 'description': 'Optional exact status filter. Omit for all statuses. Must be one of the listed values, uppercase.'}, 'projectId': {'type': 'string', 'minLength': 1, 'description': "The project id, exactly as returned by seo_project_list (a cuid such as 'cmtjyi1q20000l204octn48ai'). Not the slug, not the display name. If you do not have one, call seo_project_list first."}}}
Output schema
{'type': 'object', '$schema': 'http://json-schema.org/draft-07/schema#', 'required': ['projectId', 'returned', 'limit', 'rows'], 'properties': {'rows': {'type': 'array', 'items': {'type': 'object', 'required': ['id', 'slug', 'keyword', 'status'], 'properties': {'id': {'type': 'string'}, 'risk': {'type': 'object', 'required': ['score', 'band', 'reasons'], 'properties': {'band': {'enum': ['high', 'medium', 'low'], 'type': 'string'}, 'score': {'type': 'integer', 'maximum': 100, 'minimum': 0}, 'reasons': {'type': 'array', 'items': {'type': 'string'}}}, 'description': "Present only when sort:'risk'.", 'additionalProperties': False}, 'slug': {'type': ['string', 'null']}, 'status': {'type': 'string'}, 'keyword': {'type': ['string', 'null']}}, 'additionalProperties': False}}, 'limit': {'type': 'integer', 'maximum': 9007199254740991, 'minimum': -9007199254740991}, 'returned': {'type': 'integer', 'maximum': 9007199254740991, 'minimum': -9007199254740991, 'description': 'How many rows came back; compare with limit to detect more pages.'}, 'projectId': {'type': 'string'}}, 'additionalProperties': False}
Changed
seo_research_start
Sept. 21, 2026, 2:57 a.m.
Changed
seo_rows_list
Sept. 19, 2026, 2:48 a.m.
Added
seo_project_connect_ai
Sept. 19, 2026, 2:48 a.m.
Changed
seo_project_create
Sept. 19, 2026, 2:48 a.m.
Added
seo_publish
Sept. 17, 2026, 12:54 p.m.
Added
seo_audit_start
Sept. 17, 2026, 12:54 p.m.
Added
seo_content_generate
Sept. 17, 2026, 12:54 p.m.
Added
seo_research_start
Sept. 17, 2026, 12:54 p.m.
Added
seo_job_status
Sept. 17, 2026, 12:54 p.m.
Added
seo_rows_list
Sept. 17, 2026, 12:54 p.m.
Added
seo_project_get
Sept. 17, 2026, 12:54 p.m.
Added
seo_project_create
Sept. 17, 2026, 12:54 p.m.
Added
seo_project_list
Sept. 17, 2026, 12:54 p.m.