pSEO Engine
Server Details
Programmatic SEO as callable tools: research, generate, audit and publish landing pages.
- Status
- Healthy
- Uptime
- 100.0% over 21 days
- Last Tested
- Transport
- Streamable HTTP · MCP 2025-11-25
- URL
TDQS
Scored across 10 tools
Each tool maps cleanly to one action/resource: project lifecycle, AI connection, research, generation, audit, status polling, publishing, and row listing. The two async starters differ in domain (research vs content) and their descriptions state their roles clearly, while seo_job_status is a shared poller rather than an overlapping action.
All tools share the seo_ prefix and use snake_case, with mostly resource_action ordering such as project_create, rows_list, and audit_start. Minor deviations like seo_publish being verb-only and seo_job_status being a noun query are readable and do not undermine the overall pattern.
Ten tools is well-scoped for a programmatic-SEO pipeline covering setup, research, generation, quality checks, status polling, and publishing. No tool feels redundant, and the count is comfortably within the ideal range for a focused MCP server.
The core workflow is covered: project create/list/get, AI provider connection, research, content generation, audit, row listing, and publishing reviewed pages. Gaps like project update/delete, row export, audit detail retrieval, and job cancellation are minor or intentionally delegated to the dashboard, so agents can complete the main flow without dead ends.
Available Tools
10 toolsseo_audit_startAudit every generated page for defectsAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| projectId | Yes | 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
| Name | Required | Description |
|---|---|---|
| jobId | Yes | Poll seo_job_status. |
| nextStep | Yes | |
| projectId | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already include readOnlyHint=false, idempotentHint=false, but the description adds critical context: it is ASYNCHRONOUS, returns a jobId, records findings, and costs 30c per call. It also explicitly limits scope to reporting only, which is beyond the annotations and informs the agent's expectations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences with no redundancy. The first sentence front-loads the action and scope, the second gives the async pattern and follow-up step, and the third clarifies reporting-only and cost. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an async tool with an output schema, the description covers what it does, the defect types it finds, the async pattern and polling target, cost, and non-fixing behavior. Nothing essential is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the projectId parameter description is already extremely detailed (explaining what it is, how to get it, and that it's not a slug or name). The tool description adds no parameter-specific information, so a baseline of 3 is appropriate since schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (scans all generated pages) and resource (all generated pages), listing concrete defect types (broken text, unfilled placeholders, truncated copy, missing images). Clearly distinguishes from siblings like seo_content_generate and seo_publish by focusing on auditing and explicitly stating it only reports problems.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs to poll seo_job_status after receiving a jobId, giving a clear follow-up pattern. Clarifies that it does not fix issues, so the agent knows not to expect repairs. Does not explicitly state when to avoid using it or compare to alternative audit tools, but the async usage pattern is well defined.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
seo_content_generateGenerate page content for pending rowsAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| projectId | Yes | 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
| Name | Required | Description |
|---|---|---|
| jobId | No | Poll seo_job_status until COMPLETED. |
| total | No | Rows queued for generation. |
| nextStep | Yes | |
| projectId | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and idempotentHint=false, leaving behavioral disclosure to the description. The description adds critical facts: cost ('spends real AI budget per generated page', 'Costs 75c per call'), asynchronous return ('Returns a jobId immediately'), and the lifecycle ('NOT live until approved and published'). No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Each sentence carries operational value: async marker, queue scope, cost, prerequisite, return contract, and final status. The cost is mentioned twice in different units (per page vs per call), which is mild redundancy but not harmful. Well structured and front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a queue-starting, cost-bearing, asynchronous mutation, the description covers all required context: what it does, prerequisite check, return contract, follow-up polling, and resulting page status. The output schema exists to document the jobId shape, and the description explicitly references it. Complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers the sole parameter at 100% and explains it in detail (cuid, not slug/display name, call seo_project_list if missing). The main description adds no additional parameter semantics beyond the schema, so the baseline 3 applies as the schema carries the full burden.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('starts'), resource ('content-generation queue'), and scope ('every PENDING row in the project'). The leading 'ASYNCHRONOUS' and the mention of 'jobId' differentiate it from synchronous tools, and the sibling 'start' tools (seo_audit_start, seo_research_start) are clearly distinct by naming the generation queue.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit prerequisites: 'Requires the project to be configured (check readyToGenerate via seo_project_get first)' and describes the follow-up: 'poll seo_job_status'. This tells the agent when it is safe to call and what to do after, but it does not explicitly name alternatives when the project is not configured or when only a subset of rows is desired. Clear context without an explicit exclusion.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
seo_job_statusCheck the latest background jobARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| projectId | Yes | 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
| Name | Required | Description |
|---|---|---|
| job | Yes | Null when no job has ever run for this project. |
| note | No | |
| projectId | Yes | |
| stillRunning | No | Keep polling while true. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnlyHint, idempotentHint, and destructiveHint, so the description's additional details about being FREE and the refusal behavior when re-calling start during RUNNING add genuine context beyond the structured fields. It does not contradict any annotation. A 4 reflects that it is highly transparent but doesn't describe every edge case.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences: the return value, the polling guidance, and the cost/refusal warning. Each sentence earns its place, the most important information is front-loaded, and there is zero redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple status-check tool, the description covers return fields, polling cadence, a critical guardrail, and cost. The output schema exists to document the response format. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema provides a thorough description of projectId, including format, examples, and guidance to call seo_project_list if needed. Since schema coverage is 100%, the description itself adds no additional parameter semantics. Baseline 3 is appropriate because the schema carries the full burden.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'Returns' and the specific resource 'the most recent background job for a project', then enumerates the returned fields (type, status, progress counters, last log line). It implicitly distinguishes itself from sibling start tools (seo_audit_start, seo_content_generate, etc.) by describing a polling operation rather than a job initiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly tells the agent when to use this tool ('Poll this every few seconds after any tool that starts a job') and provides a critical negative instruction ('Do NOT re-call the start tool while status is RUNNING'). It also clarifies the consequence (refusal), leaving no ambiguity about the correct invocation pattern.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
seo_project_connect_aiConnect (or replace) a project's AI keysAIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| aiModel | No | Optional model id; defaults to gemini-2.5-flash on the Gemini endpoint. | |
| aiApiKey | No | The AI provider key to store (replaces any stored one). Omit to leave unchanged. | |
| aiBaseUrl | No | Optional OpenAI-compatible base URL; defaults to Google's Gemini endpoint. | |
| projectId | Yes | 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 | No | Pass true to REMOVE the stored AI key — generation stops until a new one is connected. | |
| perplexityApiKey | No | Optional Perplexity API key for visibility checks; replaces any stored one. Omit to leave unchanged. | |
| clearPerplexityKey | No | Pass true to REMOVE the stored Perplexity key. |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | Yes | |
| projectId | Yes | |
| aiKeyConnected | Yes | |
| perplexityKeyConnected | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses key behaviors beyond the annotations: keys are stored encrypted and can never be read back, only replaced or cleared; tokens bill the caller's provider account (BYOK); and the call is free. These are substantive behavioral traits that the annotations (readOnlyHint, idempotentHint, destructiveHint) do not capture.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact—three sentences—and front-loads the core purpose before layering billing, key recommendation, and security context. Each sentence adds a distinct piece of information with minimal filler, though the 'FREE' note is slightly redundant with the BYOK billing point.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With seven parameters, an output schema, and these annotations, the description still covers the essential context: purpose, recommended key type, optional Perplexity key, encryption/no-read-back, and cost. It omits error/edge-case behavior, but the rich input/output schemas cover remaining details, making this adequate for an agent to call correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds high-level context (BYOK, encrypted storage, what the keys power) but does not detail individual parameter semantics beyond the schema. Those details are already well covered in the input schema, so no additional credit is warranted.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Stores') and a precise resource (the AI provider key that a project's generation, audits, and research run on), and the title makes the connect-or-replace function explicit. The optional Perplexity key adds a second distinct purpose (citation-bearing visibility checks), which clearly separates it from the sibling action tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives actionable usage context: it recommends a Google Gemini API key, notes the free tier needs no card, and specifies when the optional Perplexity key is needed (citation-bearing AI visibility checks). It does not explicitly name alternatives or state when not to use it, but the purpose is so distinct from the siblings that the context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
seo_project_createCreate a projectAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Display name, e.g. 'Plumbers by city'. The URL slug is derived from it automatically. | |
| domain | No | Optional custom domain the pages will be published on, e.g. 'example.com'. Omit to serve them under the platform's own domain. | |
| aiModel | No | Optional model id. Defaults to gemini-2.5-flash on the Gemini endpoint. | |
| aiApiKey | No | 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 | No | Page template. Defaults to 'location'. | |
| aiBaseUrl | No | Optional OpenAI-compatible base URL for the key. Defaults to Google's Gemini endpoint. Example for OpenRouter: https://openrouter.ai/api/v1 | |
| perplexityApiKey | No | Optional Perplexity API key (api.perplexity.ai) to enable citation-bearing AI visibility checks for this project. Stored encrypted. |
Output Schema
| Name | Required | Description |
|---|---|---|
| name | Yes | |
| slug | Yes | |
| domain | Yes | |
| nextStep | Yes | |
| template | Yes | |
| projectId | Yes | Pass this to every other tool. |
| readyToGenerate | Yes | Always false here — a new project has no data source yet. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnlyHint=false, idempotentHint=false, destructiveHint=false, leaving the safety profile mostly inferred. The description adds genuinely valuable behavior beyond that: the project is 'created EMPTY' and still needs a data source and content spec before seo_content_generate runs, the agent should fetch readinessNote afterward, and 'FREE — this call costs nothing' discloses billing behavior the annotations never cover. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences, each earning its place: what it does, when to call it, what state it leaves the project in and the follow-up step, and cost. The core purpose is front-loaded in the first clause, and no sentence repeats schema or annotation content. Dense but not bloated.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter creation tool with a 100%-documented schema list hubs and an output schema present, the description covers the non-obvious operational context: empty-list trigger, projectId dependency of sibling tools, empty creation state, readinessNote follow-up, and cost. An agent has everything needed to decide whether and how to invoke this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline of 3 applies. All seven parameters already carry rich descriptions in the schema (e.g., 'BYOK: the AI provider key... stored encrypted'). The description adds context about the projectId output and empty-state lifecycle but does not enhance meaning of any individual parameter, which is acceptable given the schema bears that burden.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Creates a new programmatic-SEO project') and names the concrete outcome ('returns its projectId'). The sentence 'every other tool needs a projectId and a new account has none' differentiates it from the sibling toolset by positioning it as the entry-point creation operation. No ambiguity with nearby siblings like seo_project_connect_ai or seo_project_get.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit trigger condition: 'Call this when seo_project_list comes back empty.' It also explains the rationale ('every other tool needs a projectId') and prescribes the follow-up sequence ('call seo_project_get afterwards and read readinessNote'). It stops short of an explicit when-not-to-use statement or naming alternatives, but the context is clear enough for an agent to route correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
seo_project_getGet one project's configuration and readinessARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| projectId | Yes | 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
| Name | Required | Description |
|---|---|---|
| name | Yes | |
| slug | Yes | |
| domain | Yes | |
| template | Yes | |
| indexable | Yes | False while the project is in staging mode. |
| projectId | Yes | |
| readinessNote | Yes | Plain-language reason, whichever way readyToGenerate went. |
| researchBrief | Yes | |
| readyToGenerate | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the description doesn't repeat those. It adds new behavioral context: the readiness criteria (needs both a field mapping and a content spec) and the cost ('FREE — this call costs nothing'). No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, zero filler. The primary purpose is front-loaded, followed by a usage directive, then the cost note. Every word earns its place; there is no redundancy with the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is a simple read-only getter with a single parameter and an output schema. The description covers purpose, when to call it, and cost. There is nothing an agent needs to invoke it correctly that is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the parameter already has a rich description (cuid format, exact source from seo_project_list, and explicit disambiguation from slug/name). The tool description adds no additional parameter guidance, so it stays at the baseline 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Returns'), a clear resource ('one project's settings') and an explicit condition ('whether it is configured enough to generate content'). It also names the sibling it is not (seo_content_generate) and explains why the readiness check matters, so an agent can instantly distinguish it from related tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs when to use the tool: 'Use this before seo_content_generate to avoid starting a run that will immediately fail.' This gives a concrete workflow position and a decision rule. It also notes the call is free, which is a practical selection factor.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
seo_project_listList projectsARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| hint | Yes | What to do next, given whether the list was empty. |
| projects | Yes | Every project this key can act on. Empty on a new account. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnlyHint=true, idempotentHint=true, destructiveHint=false, openWorldHint=false), so the description rightly does not repeat it. It adds genuine extra context: the cost behavior ('FREE — this call costs nothing') and the scope constraint that ids are account-specific and cannot be guessed. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place: purpose+output, usage-ordering rule, and cost note. The core purpose is front-loaded in the first sentence. The 'FREE' note is a minor addition but is genuinely useful behavioral information rather than padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a parameterless list tool with a full output schema and safety annotations, the description covers everything an agent needs: what is returned, that it must be called first, that ids come from here, and that it costs nothing. Nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the baseline is 4 and there is nothing to document beyond it. The description does mention the output fields (id, name, slug, custom domain, page count), which adds value for interpreting the result even though it is arguably output-schema territory.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Lists') and resource ('every programmatic-SEO project this API key can act on'), plus enumerates the returned fields (id, name, slug, custom domain, page count). The scope qualifier 'can act on' and the collection semantics clearly distinguish it from seo_project_get (single project) and seo_project_create (creation) among siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit, imperative routing: 'Call this FIRST in any session — every other tool needs a projectId from here.' It also states the negative case — ids cannot be guessed or carried over from another account — which tells the agent why this tool is mandatory rather than optional. This is exemplary usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
seo_publishPublish approved pagesAIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| projectId | Yes | 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
| Name | Required | Description |
|---|---|---|
| note | No | |
| projectId | Yes | |
| publishedCount | No | Rows taken live by this call. |
| skippedNeedsReview | No | GENERATED drafts skipped: approval is a human gate. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations indicate this is a write operation (readOnlyHint=false), non-destructive, idempotent, and open-world. The description adds critical behavioral details beyond these: the call is synchronous, costs 15c per call, skips GENERATED drafts due to a human approval gate, and reports skip counts. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact yet information-dense. It front-loads the synchronous nature, then covers the key behavior (status filtering), the rationale, the response interpretation, and the cost. Every sentence adds value without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the presence of an output schema, the description does not need to explain return values. It covers all essential context for an agent to call the tool correctly: the status gate, the skip behavior, cost, and diagnostic guidance. No significant gaps for a single-parameter tool with a well-documented schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides 100% coverage of the single parameter (projectId), including its exact source (seo_project_list) and the caution against using slug/display name. The description does not add additional meaning about the parameter itself, so a baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: publishing approved pages ('takes approved pages live'). It distinguishes itself from content generation by explicitly noting that GENERATED drafts are skipped, which differentiates it from sibling seo_content_generate. The verb 'takes' is slightly informal but the resource and action are unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit conditions for use: only rows in REVIEWED status are published, and GENERATED drafts are skipped. It also explains how to interpret the response (if publishedCount is 0 and skippedNeedsReview is high, approval is needed first). This gives clear guidance on when to call the tool and what to expect.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
seo_research_startStart AI keyword researchAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| brief | Yes | 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 | Yes | 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 | No | How many keywords to mine, 10-2000. Higher costs more AI time. Start around 200 unless told otherwise. |
Output Schema
| Name | Required | Description |
|---|---|---|
| jobId | Yes | Poll seo_job_status; the keywords do not exist yet. |
| started | Yes | |
| nextStep | Yes | |
| projectId | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond the annotations by disclosing that the tool returns immediately with a jobId but keywords do not yet exist, spends real AI budget, costs 750c, fails when another run is in progress, and has human/trial account restrictions. These behavioral traits are not implied by readOnlyHint, openWorldHint, idempotentHint, or destructiveHint.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than average but every sentence carries operational weight: async behavior, budget, polling, immediate return semantics, concurrency failure, trial account rules, and cost. It is front-loaded with the most critical fact ('ASYNCHRONOUS') and proceeds logically from invocation to completion behavior to edge cases.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an async, cost-incurring start-tool with an output schema, the description covers everything needed to call it correctly: polling workflow, jobId return, non-existence of results on return, failure modes, retry guidance, and cost. The existing output schema and annotations cover the remaining structured details.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description itself does not add parameter-specific semantics, but schema description coverage is 100% and the schema already provides rich detail: examples, exact source instructions like 'exactly as returned by seo_project_list', and numeric constraints. Baseline 3 is appropriate because the description does not need to compensate for schema gaps.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'ASYNCHRONOUS' and states a specific action and resource: 'Starts a keyword-research run that mines and qualifies keywords into PENDING rows.' This clearly distinguishes it from async siblings like seo_audit_start and seo_content_generate by naming the resource type and its output state.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit operational guidance: 'call it once and then poll seo_job_status until status is COMPLETED.' It also warns against retrying by telling the agent to 'stop and report instead of retrying' on card_required, and documents the in-progress failure condition.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
seo_rows_listList pages in a projectARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| sort | No | 'risk' returns pages ordered by review risk (highest first), each with a score, band and human-readable reasons. Omit for creation order. | |
| limit | No | How many rows to return, 1-200. Defaults to 50. Values above 200 are rejected — page through instead of asking for everything. | |
| status | No | Optional exact status filter. Omit for all statuses. Must be one of the listed values, uppercase. | |
| projectId | Yes | 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
| Name | Required | Description |
|---|---|---|
| rows | Yes | |
| limit | Yes | |
| returned | Yes | How many rows came back; compare with limit to detect more pages. |
| projectId | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the tool as read-only, idempotent, and non-destructive; the description adds the cost of 3c per call, which is not present in the annotations, and clarifies that content bodies are excluded. It also explains status semantics and the risk sort behavior, all beyond structured fields. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences, front-loads the core function, and each sentence adds distinct value – outcome, status semantics, exclusion of content, the risk-sort workflow, and cost. No filler or repetition; structure is efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only listing tool with an output schema and full parameter descriptions, the description is remarkably complete: it covers cost, status domain, sort behavior, and what is not returned. It even gives a practical review workflow. No important operational aspect an agent needs is left unexplained.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
All four parameters are already described in the schema (100% coverage), so the baseline is 3. The description adds meaning by interpreting the status enum values ('PENDING (no content yet), GENERATING, GENERATED (draft)...') and by explaining the purpose of sort:'risk' as a review queue. This goes beyond the schema's bare enum listing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Returns a page of rows (individual generated pages) with slug, target keyword and status', naming the specific resource and output fields. It further distinguishes itself from an export by saying 'Content bodies are NOT included — this is a listing, not an export.' This clearly differentiates it from sibling tools like seo_content_generate or seo_publish.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives concrete guidance for its main niche: 'Use sort:''risk'' to get the review queue ordered by risk score... so a human reviews the riskiest pages before bulk-approving the safe ones.' It also states what it does not do ('not an export'), but it never names a specific alternative tool for content export or other actions, so the routing is clear but not exhaustive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
- Changed
seo_rows_list2 fields changed- added
Input schema / properties / sortAdded value: +{ + "description": "'risk' returns pages ordered by review risk (highest first), each with a score, band and human-readable reasons. Omit for creation order.", + "enum": [ + "createdAt", + "risk" + ], + "type": "string" +} - added
Output schema / properties / rows / items / properties / riskAdded value: +{ + "additionalProperties": false, + "description": "Present only when sort:'risk'.", + "properties": { + "band": { + "enum": [ + "high", + "medium", + "low" + ], + "type": "string" + }, + "reasons": { + "items": { + "type": "string" + }, + "type": "array" + }, + "score": { + "maximum": 100, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "score", + "band", + "reasons" + ], + "type": "object" +}
2 tool updates
- Added
seo_project_connect_ai - Changed
seo_project_create4 fields changed- added
Input schema / properties / aiApiKeyAdded value: +{ + "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.", + "type": "string" +} - added
Input schema / properties / aiBaseUrlAdded value: +{ + "description": "Optional OpenAI-compatible base URL for the key. Defaults to Google's Gemini endpoint. Example for OpenRouter: https://openrouter.ai/api/v1", + "type": "string" +} - added
Input schema / properties / aiModelAdded value: +{ + "description": "Optional model id. Defaults to gemini-2.5-flash on the Gemini endpoint.", + "type": "string" +} - added
Input schema / properties / perplexityApiKeyAdded value: +{ + "description": "Optional Perplexity API key (api.perplexity.ai) to enable citation-bearing AI visibility checks for this project. Stored encrypted.", + "type": "string" +}
18 tool updates
- Added
seo_audit_start - Added
seo_content_generate - Added
seo_job_status - Added
seo_project_create - Added
seo_project_get - Added
seo_project_list - Added
seo_publish - Added
seo_research_start - Added
seo_rows_list - Removed
seo.audit.start - Removed
seo.content.generate - Removed
seo.job.status - Removed
seo.project.create - Removed
seo.project.get - Removed
seo.project.list - Removed
seo.publish - Removed
seo.research.start - Removed
seo.rows.list
9 tool updates
- Changed
seo.audit.start1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "jobId": { + "description": "Poll seo.job.status.", + "type": "string" + }, + "nextStep": { + "type": "string" + }, + "projectId": { + "type": "string" + } + }, + "required": [ + "jobId", + "projectId", + "nextStep" + ], + "type": "object" +}
- Changed
seo.content.generate1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "jobId": { + "description": "Poll seo.job.status until COMPLETED.", + "type": "string" + }, + "nextStep": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "total": { + "description": "Rows queued for generation.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "projectId", + "nextStep" + ], + "type": "object" +}
- Changed
seo.job.status1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "job": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "completed": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "failed": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "lastLog": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": "string" + }, + "total": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string" + } + }, + "required": [ + "id", + "type", + "status", + "total", + "completed", + "failed", + "lastLog" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "description": "Null when no job has ever run for this project." + }, + "note": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "stillRunning": { + "description": "Keep polling while true.", + "type": "boolean" + } + }, + "required": [ + "projectId", + "job" + ], + "type": "object" +}
- Changed
seo.project.create1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "domain": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "nextStep": { + "type": "string" + }, + "projectId": { + "description": "Pass this to every other tool.", + "type": "string" + }, + "readyToGenerate": { + "description": "Always false here — a new project has no data source yet.", + "type": "boolean" + }, + "slug": { + "type": "string" + }, + "template": { + "type": "string" + } + }, + "required": [ + "projectId", + "name", + "slug", + "domain", + "template", + "readyToGenerate", + "nextStep" + ], + "type": "object" +}
- Changed
seo.project.get1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "domain": { + "type": [ + "string", + "null" + ] + }, + "indexable": { + "description": "False while the project is in staging mode.", + "type": "boolean" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "readinessNote": { + "description": "Plain-language reason, whichever way readyToGenerate went.", + "type": "string" + }, + "readyToGenerate": { + "type": "boolean" + }, + "researchBrief": { + "type": [ + "string", + "null" + ] + }, + "slug": { + "type": "string" + }, + "template": { + "type": "string" + } + }, + "required": [ + "projectId", + "name", + "slug", + "domain", + "template", + "indexable", + "researchBrief", + "readyToGenerate", + "readinessNote" + ], + "type": "object" +}
- Changed
seo.project.list1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "hint": { + "description": "What to do next, given whether the list was empty.", + "type": "string" + }, + "projects": { + "description": "Every project this key can act on. Empty on a new account.", + "items": { + "additionalProperties": false, + "properties": { + "createdAt": { + "type": "string" + }, + "domain": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "totalPages": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "projectId", + "name", + "slug", + "domain", + "totalPages", + "createdAt" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "projects", + "hint" + ], + "type": "object" +}
- Changed
seo.publish1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "note": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "publishedCount": { + "description": "Rows taken live by this call.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "skippedNeedsReview": { + "description": "GENERATED drafts skipped: approval is a human gate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "projectId" + ], + "type": "object" +}
- Changed
seo.research.start1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "jobId": { + "description": "Poll seo.job.status; the keywords do not exist yet.", + "type": "string" + }, + "nextStep": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "started": { + "type": "boolean" + } + }, + "required": [ + "started", + "jobId", + "projectId", + "nextStep" + ], + "type": "object" +}
- Changed
seo.rows.list1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "limit": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "projectId": { + "type": "string" + }, + "returned": { + "description": "How many rows came back; compare with limit to detect more pages.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "rows": { + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "keyword": { + "type": [ + "string", + "null" + ] + }, + "slug": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": "string" + } + }, + "required": [ + "id", + "slug", + "keyword", + "status" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "projectId", + "returned", + "limit", + "rows" + ], + "type": "object" +}
9 tool updates
- First observed
seo.audit.start - First observed
seo.content.generate - First observed
seo.job.status - First observed
seo.project.create - First observed
seo.project.get - First observed
seo.project.list - First observed
seo.publish - First observed
seo.research.start - First observed
seo.rows.list
Related MCP Connectors
Live SEO workflow tools for Claude Code, Codex, and AI agents.
SEO, GEO & AI Visibility — research, write, optimize, publish & monitor content. 121 tools.
SEO research SaaS exposed as 30+ MCP tools. Forge niche analysis, plans, and writer-ready briefs.
Full-cycle SEO automation for AI agents: technical audits, SEO articles, machine-readable pricing.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceTurn Claude Code into your SEO manager with keyword research, content pipeline that ships pull requests, rank tracking, and a dashboard.7 npm77AGPL 3.0
- AlicenseNot gradedqualityBmaintenanceEnables semantic keyword clustering, search intent classification, cannibalization detection, and topical authority mapping for SEO workflows.MIT
- AlicenseNot gradedqualityBmaintenanceProvides 23 bounded MCP tools for AI agents to perform technical SEO audits, including crawl setup, page analysis, issue detection, and report exports, all while keeping data local.7MIT
- AlicenseAqualityCmaintenanceEnables running SEO and AI/GEO visibility audits on websites, reading audit results, competitor analyses and expert reports, and importing or retrieving editorial calendars, all from any MCP client such as Claude Desktop, Claude Code or Cursor.1129 npmMIT
Glama MCP Gateway
Add one secure layer between your agents and this server.