Sign In

@agentseo/mcp-server

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@agentseo/mcp-server - npm Package Compare versions

Comparing version
0.1.1
to
0.1.2
dist/server.js

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

+9
-301
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import axios from "axios";
import dotenv from "dotenv";
import { createAgentSeoMcpServer } from "./server.js";
dotenv.config();
const API_BASE_URL = process.env.AGENTSEO_API_URL || "http://localhost:3000/api/v1";
const API_BASE_URL = process.env.AGENTSEO_API_URL || "https://www.agentseo.dev/api/v1";
const API_KEY = process.env.AGENTSEO_API_KEY;

@@ -16,300 +14,10 @@ const PROJECT_ID = process.env.AGENTSEO_PROJECT_ID;

}
const server = new McpServer({
name: "AgentSEO MCP Server",
version: "0.2.0",
});
function isAxiosLikeError(error) {
return typeof error === "object" && error !== null && "isAxiosError" in error;
}
async function callAgentSeoApi(endpoint, options = {}) {
const method = options.method ?? "POST";
try {
const response = await axios.request({
url: `${API_BASE_URL}${endpoint}`,
method,
data: options.data,
params: options.params,
headers: {
"x-api-key": API_KEY,
"Content-Type": "application/json",
...(PROJECT_ID ? { "x-project-id": PROJECT_ID } : {}),
...(WORKFLOW_ID ? { "x-workflow-id": WORKFLOW_ID } : {}),
},
});
return response.data;
}
catch (error) {
if (isAxiosLikeError(error)) {
const errorData = error.response?.data?.error || error.message;
throw new Error(`AgentSEO API Error: ${JSON.stringify(errorData)}`);
}
throw error;
}
}
function jsonContent(payload) {
return {
content: [
{ type: "text", text: JSON.stringify(payload, null, 2) },
],
};
}
server.tool("agentseo_search", "Perform a Google search with optional domain filters.", {
query: z.string().describe("The search query"),
limit: z
.number()
.int()
.min(1)
.max(50)
.default(10)
.describe("Number of results"),
include_domains: z
.array(z.string())
.optional()
.describe("Restrict results to these domains"),
exclude_domains: z
.array(z.string())
.optional()
.describe("Exclude results from these domains"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
}, async ({ query, limit, include_domains, exclude_domains, sync }) => {
const result = await callAgentSeoApi("/search", {
data: { query, limit, include_domains, exclude_domains },
params: { sync },
const apiKey = API_KEY;
async function main() {
const server = createAgentSeoMcpServer({
apiBaseUrl: API_BASE_URL,
apiKey,
projectId: PROJECT_ID,
workflowId: WORKFLOW_ID,
});
return jsonContent(result);
});
server.tool("agentseo_extract", "Extract content from a URL and convert to Markdown.", {
url: z.string().url().describe("The URL to scrape"),
include_images: z
.boolean()
.default(false)
.describe("Include image links in markdown"),
}, async ({ url, include_images }) => {
const result = await callAgentSeoApi("/extract", {
data: { url, format: "markdown", include_images },
});
const parsed = z
.object({ content: z.string() })
.passthrough()
.parse(result);
return {
content: [{ type: "text", text: parsed.content }],
};
});
server.tool("agentseo_analyze_serp", "Deep analysis of Search Engine Results Page (SERP) features and competitors.", {
keyword: z.string().describe("Target keyword"),
location: z
.string()
.default("United States")
.describe("Geographic location"),
device: z
.enum(["desktop", "mobile"])
.default("desktop")
.describe("Device type"),
language: z.string().default("en").describe("Language code"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
}, async ({ keyword, location, device, language, sync }) => {
const result = await callAgentSeoApi("/analyze/serp", {
data: { keyword, location, device, language },
params: { sync },
});
return jsonContent(result);
});
server.tool("agentseo_local_audit", "Run a local SEO audit for a domain and location.", {
domain: z.string().describe("Domain or business name"),
location: z.string().describe("Target location"),
language: z.string().default("en").describe("Language code"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
}, async ({ domain, location, language, sync }) => {
const result = await callAgentSeoApi("/audit/local", {
data: { domain, location, language },
params: { sync },
});
return jsonContent(result);
});
server.tool("agentseo_local_audit_batch", "Queue up to 10 local audits in one request.", {
items: z
.array(z.object({
domain: z.string(),
location: z.string(),
language: z.string().optional(),
}))
.max(10)
.describe("Audit items"),
}, async ({ items }) => {
const result = await callAgentSeoApi("/audit/local/batch", {
data: { items },
});
return jsonContent(result);
});
server.tool("agentseo_content_gap", "Run content gap analysis for a URL and keyword.", {
url: z.string().url().describe("Your URL"),
keyword: z.string().describe("Target keyword"),
location: z
.string()
.default("United States")
.describe("Geographic location"),
language: z.string().default("en").describe("Language code"),
scrape_depth: z
.enum(["h1", "h2", "h3"])
.default("h3")
.describe("How deep to analyze headings"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
}, async ({ url, keyword, location, language, scrape_depth, sync }) => {
const result = await callAgentSeoApi("/content/gap", {
data: { url, keyword, location, language, scrape_depth },
params: { sync },
});
return jsonContent(result);
});
server.tool("agentseo_social_listen", "Find web mentions for a query (web-wide discovery; not platform-native monitoring).", {
query: z.string().describe("Search query"),
platform: z
.enum(["reddit", "twitter", "all"])
.default("all")
.describe("Platform filter"),
limit: z.number().int().min(1).max(50).default(10).describe("Result limit"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
}, async ({ query, platform, limit, sync }) => {
const result = await callAgentSeoApi("/social/listen", {
data: { query, platform, limit },
params: { sync },
});
return jsonContent(result);
});
// Preferred name; keep agentseo_social_listen for backward compatibility.
server.tool("agentseo_web_mentions", "Find web mentions for a query (web-wide discovery; not platform-native monitoring).", {
query: z.string().describe("Search query"),
platform: z
.enum(["reddit", "twitter", "all"])
.default("all")
.describe("Platform filter"),
limit: z.number().int().min(1).max(50).default(10).describe("Result limit"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
}, async ({ query, platform, limit, sync }) => {
const result = await callAgentSeoApi("/social/listen", {
data: { query, platform, limit },
params: { sync },
});
return jsonContent(result);
});
server.tool("agentseo_ai_overview_extract", "Extract AI Overview insights for a target keyword.", {
keyword: z.string().describe("Target keyword"),
location: z
.string()
.default("United States")
.describe("Geographic location"),
language: z.string().default("en").describe("Language code"),
target_domain: z
.string()
.optional()
.describe("Optional domain to check against the sampled citation candidates"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
}, async ({ keyword, location, language, target_domain, sync }) => {
const result = await callAgentSeoApi("/ai-overview/extract", {
data: { keyword, location, language, target_domain },
params: { sync },
});
return jsonContent(result);
});
server.tool("agentseo_local_visibility_track", "Track local visibility for a domain across keywords and locations.", {
domain: z.string().describe("Domain to track"),
keywords: z.array(z.string()).min(1).max(5).describe("Keywords"),
locations: z.array(z.string()).min(1).max(3).describe("Locations"),
language: z.string().default("en").describe("Language code"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
}, async ({ domain, keywords, locations, language, sync }) => {
const result = await callAgentSeoApi("/local-visibility/track", {
data: { domain, keywords, locations, language },
params: { sync },
});
return jsonContent(result);
});
server.tool("agentseo_llm_mentions_track", "Track brand mentions across LLM/search-style results.", {
brand: z.string().describe("Brand name"),
queries: z.array(z.string()).min(1).max(10).describe("Queries to check"),
limit_per_query: z
.number()
.int()
.min(1)
.max(20)
.default(8)
.describe("Per-query result limit"),
platform: z
.enum(["reddit", "twitter", "all"])
.default("all")
.describe("Platform filter"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
}, async ({ brand, queries, limit_per_query, platform, sync }) => {
const result = await callAgentSeoApi("/llm-mentions/track", {
data: { brand, queries, limit_per_query, platform },
params: { sync },
});
return jsonContent(result);
});
server.tool("agentseo_content_decay_detect", "Detect potential content decay for a URL and keyword.", {
url: z.string().url().describe("Target URL"),
keyword: z.string().describe("Target keyword"),
lookback_days: z
.number()
.int()
.min(3)
.max(180)
.default(30)
.describe("Lookback window"),
threshold: z
.number()
.min(1)
.max(20)
.default(3)
.describe("Rank drop threshold"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
}, async ({ url, keyword, lookback_days, threshold, sync }) => {
const result = await callAgentSeoApi("/content-decay/detect", {
data: { url, keyword, lookback_days, threshold },
params: { sync },
});
return jsonContent(result);
});
server.tool("agentseo_keyword_cluster_build", "Build keyword clusters from an input keyword list.", {
keywords: z.array(z.string()).min(1).max(200).describe("Keywords"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
}, async ({ keywords, sync }) => {
const result = await callAgentSeoApi("/keyword-cluster/build", {
data: { keywords },
params: { sync },
});
return jsonContent(result);
});
server.tool("agentseo_rank_track", "Queue a rank tracking check for a URL and keyword.", {
keyword: z.string().describe("Keyword to track"),
url: z.string().url().describe("URL to track"),
location: z.string().default("United States").describe("Location"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
}, async ({ keyword, url, location, sync }) => {
const result = await callAgentSeoApi("/rank/track", {
data: { keyword, url, location },
params: { sync },
});
return jsonContent(result);
});
server.tool("agentseo_rank_history", "Fetch historical rank data for a keyword and URL.", {
keyword: z.string().describe("Keyword"),
url: z.string().url().describe("URL"),
}, async ({ keyword, url }) => {
const result = await callAgentSeoApi("/rank/track", {
method: "GET",
params: { keyword, url },
});
return jsonContent(result);
});
server.tool("agentseo_job_status", "Get status/result for a previously queued job.", {
job_id: z.string().describe("Job ID"),
}, async ({ job_id }) => {
const result = await callAgentSeoApi(`/jobs/${job_id}`, {
method: "GET",
});
return jsonContent(result);
});
async function main() {
const transport = new StdioServerTransport();

@@ -316,0 +24,0 @@ await server.connect(transport);

{
"name": "@agentseo/mcp-server",
"version": "0.1.1",
"version": "0.1.2",
"type": "module",

@@ -9,2 +9,7 @@ "license": "MIT",

},
"repository": {
"type": "git",
"url": "git+https://github.com/AgentSEO-dev/agentseo-clients.git",
"directory": "packages/mcp-server"
},
"scripts": {

@@ -16,3 +21,3 @@ "build": "tsc",

"dependencies": {
"@modelcontextprotocol/sdk": "latest",
"@modelcontextprotocol/sdk": "^1.26.0",
"zod": "^3.23.8",

@@ -19,0 +24,0 @@ "axios": "^1.7.7",

@@ -78,4 +78,4 @@ # @agentseo/mcp-server

- `AGENTSEO_API_KEY` (required): Your workspace API key.
- `AGENTSEO_API_URL` (optional): Defaults to `http://localhost:3000/api/v1`.
- For hosted API use `https://www.agentseo.dev/api/v1`.
- `AGENTSEO_API_URL` (optional): Defaults to `https://www.agentseo.dev/api/v1`.
- For local development use `http://localhost:3000/api/v1`.
- `AGENTSEO_PROJECT_ID` (optional): Default project attribution for all tool calls.

@@ -90,12 +90,42 @@ - `AGENTSEO_WORKFLOW_ID` (optional): Default workflow attribution for all tool calls.

- `agentseo_local_audit`
- `agentseo_local_audit_batch`
- `agentseo_content_gap`
- `agentseo_web_mentions` (preferred; alias: `agentseo_social_listen`)
- `agentseo_opportunities_find`
- `agentseo_opportunities_brief`
- `agentseo_content_refresh_brief`
- `agentseo_content_serp_outline`
- `agentseo_content_brief`
- `agentseo_content_draft_qa`
- `agentseo_content_schema_plan`
- `agentseo_content_internal_links`
- `agentseo_content_title_meta`
- `agentseo_content_keyword_map`
- `agentseo_content_technical_qa`
- `agentseo_content_action_plan`
- `agentseo_site_sitemap_audit`
- `agentseo_serp_volatility`
- `agentseo_page_cro_qa`
- `agentseo_ai_visibility_prompt_set`
- `agentseo_content_programmatic_template`
- `agentseo_content_cannibalization`
- `agentseo_content_competitor_gap_matrix`
- `agentseo_keyword_ideas_suggest`
- `agentseo_keyword_metrics_overview`
- `agentseo_domain_ranked_keywords`
- `agentseo_domain_relevant_pages`
- `agentseo_domain_competitors`
- `agentseo_domain_intersection`
- `agentseo_domain_traffic_estimate`
- `agentseo_backlinks_summary`
- `agentseo_backlinks_list`
- `agentseo_backlinks_anchors`
- `agentseo_backlinks_referring_domains`
- `agentseo_backlinks_domain_pages`
- `agentseo_backlinks_competitors`
- `agentseo_backlinks_new_lost_timeseries`
- `agentseo_backlinks_page_intersection`
- `agentseo_backlinks_opportunity_finder`
- `agentseo_page_intersection`
- `agentseo_ai_overview_extract`
- `agentseo_local_visibility_track`
- `agentseo_llm_mentions_track`
- `agentseo_content_decay_detect`
- `agentseo_keyword_cluster_build`
- `agentseo_rank_track`
- `agentseo_rank_history`
- `agentseo_job_status`

@@ -105,2 +135,4 @@

`agentseo_content_internal_links` requires `target_pages`; pass 1-50 candidate internal pages from your sitemap, CMS, crawler, Search Console, or `agentseo_site_sitemap_audit`.
## Local Run (Without Claude)

@@ -107,0 +139,0 @@

#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import axios from "axios";
import dotenv from "dotenv";
import { createAgentSeoMcpServer } from "./server.js";

@@ -12,3 +10,3 @@ dotenv.config();

const API_BASE_URL =
process.env.AGENTSEO_API_URL || "http://localhost:3000/api/v1";
process.env.AGENTSEO_API_URL || "https://www.agentseo.dev/api/v1";
const API_KEY = process.env.AGENTSEO_API_KEY;

@@ -23,422 +21,11 @@ const PROJECT_ID = process.env.AGENTSEO_PROJECT_ID;

const server = new McpServer({
name: "AgentSEO MCP Server",
version: "0.2.0",
});
const apiKey = API_KEY;
interface AxiosLikeError {
isAxiosError?: boolean;
message?: string;
response?: {
data?: {
error?: unknown;
};
};
}
function isAxiosLikeError(error: unknown): error is AxiosLikeError {
return typeof error === "object" && error !== null && "isAxiosError" in error;
}
type ApiMethod = "GET" | "POST";
async function callAgentSeoApi<T = unknown>(
endpoint: string,
options: {
method?: ApiMethod;
data?: unknown;
params?: Record<string, string | number | boolean | undefined>;
} = {},
): Promise<T> {
const method = options.method ?? "POST";
try {
const response = await axios.request({
url: `${API_BASE_URL}${endpoint}`,
method,
data: options.data,
params: options.params,
headers: {
"x-api-key": API_KEY,
"Content-Type": "application/json",
...(PROJECT_ID ? { "x-project-id": PROJECT_ID } : {}),
...(WORKFLOW_ID ? { "x-workflow-id": WORKFLOW_ID } : {}),
},
});
return response.data as T;
} catch (error: unknown) {
if (isAxiosLikeError(error)) {
const errorData = error.response?.data?.error || error.message;
throw new Error(`AgentSEO API Error: ${JSON.stringify(errorData)}`);
}
throw error;
}
}
function jsonContent(payload: unknown) {
return {
content: [
{ type: "text" as const, text: JSON.stringify(payload, null, 2) },
],
};
}
server.tool(
"agentseo_search",
"Perform a Google search with optional domain filters.",
{
query: z.string().describe("The search query"),
limit: z
.number()
.int()
.min(1)
.max(50)
.default(10)
.describe("Number of results"),
include_domains: z
.array(z.string())
.optional()
.describe("Restrict results to these domains"),
exclude_domains: z
.array(z.string())
.optional()
.describe("Exclude results from these domains"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
},
async ({ query, limit, include_domains, exclude_domains, sync }) => {
const result = await callAgentSeoApi("/search", {
data: { query, limit, include_domains, exclude_domains },
params: { sync },
});
return jsonContent(result);
},
);
server.tool(
"agentseo_extract",
"Extract content from a URL and convert to Markdown.",
{
url: z.string().url().describe("The URL to scrape"),
include_images: z
.boolean()
.default(false)
.describe("Include image links in markdown"),
},
async ({ url, include_images }) => {
const result = await callAgentSeoApi("/extract", {
data: { url, format: "markdown", include_images },
});
const parsed = z
.object({ content: z.string() })
.passthrough()
.parse(result);
return {
content: [{ type: "text", text: parsed.content }],
};
},
);
server.tool(
"agentseo_analyze_serp",
"Deep analysis of Search Engine Results Page (SERP) features and competitors.",
{
keyword: z.string().describe("Target keyword"),
location: z
.string()
.default("United States")
.describe("Geographic location"),
device: z
.enum(["desktop", "mobile"])
.default("desktop")
.describe("Device type"),
language: z.string().default("en").describe("Language code"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
},
async ({ keyword, location, device, language, sync }) => {
const result = await callAgentSeoApi("/analyze/serp", {
data: { keyword, location, device, language },
params: { sync },
});
return jsonContent(result);
},
);
server.tool(
"agentseo_local_audit",
"Run a local SEO audit for a domain and location.",
{
domain: z.string().describe("Domain or business name"),
location: z.string().describe("Target location"),
language: z.string().default("en").describe("Language code"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
},
async ({ domain, location, language, sync }) => {
const result = await callAgentSeoApi("/audit/local", {
data: { domain, location, language },
params: { sync },
});
return jsonContent(result);
},
);
server.tool(
"agentseo_local_audit_batch",
"Queue up to 10 local audits in one request.",
{
items: z
.array(
z.object({
domain: z.string(),
location: z.string(),
language: z.string().optional(),
}),
)
.max(10)
.describe("Audit items"),
},
async ({ items }) => {
const result = await callAgentSeoApi("/audit/local/batch", {
data: { items },
});
return jsonContent(result);
},
);
server.tool(
"agentseo_content_gap",
"Run content gap analysis for a URL and keyword.",
{
url: z.string().url().describe("Your URL"),
keyword: z.string().describe("Target keyword"),
location: z
.string()
.default("United States")
.describe("Geographic location"),
language: z.string().default("en").describe("Language code"),
scrape_depth: z
.enum(["h1", "h2", "h3"])
.default("h3")
.describe("How deep to analyze headings"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
},
async ({ url, keyword, location, language, scrape_depth, sync }) => {
const result = await callAgentSeoApi("/content/gap", {
data: { url, keyword, location, language, scrape_depth },
params: { sync },
});
return jsonContent(result);
},
);
server.tool(
"agentseo_social_listen",
"Find web mentions for a query (web-wide discovery; not platform-native monitoring).",
{
query: z.string().describe("Search query"),
platform: z
.enum(["reddit", "twitter", "all"])
.default("all")
.describe("Platform filter"),
limit: z.number().int().min(1).max(50).default(10).describe("Result limit"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
},
async ({ query, platform, limit, sync }) => {
const result = await callAgentSeoApi("/social/listen", {
data: { query, platform, limit },
params: { sync },
});
return jsonContent(result);
},
);
// Preferred name; keep agentseo_social_listen for backward compatibility.
server.tool(
"agentseo_web_mentions",
"Find web mentions for a query (web-wide discovery; not platform-native monitoring).",
{
query: z.string().describe("Search query"),
platform: z
.enum(["reddit", "twitter", "all"])
.default("all")
.describe("Platform filter"),
limit: z.number().int().min(1).max(50).default(10).describe("Result limit"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
},
async ({ query, platform, limit, sync }) => {
const result = await callAgentSeoApi("/social/listen", {
data: { query, platform, limit },
params: { sync },
});
return jsonContent(result);
},
);
server.tool(
"agentseo_ai_overview_extract",
"Extract AI Overview insights for a target keyword.",
{
keyword: z.string().describe("Target keyword"),
location: z
.string()
.default("United States")
.describe("Geographic location"),
language: z.string().default("en").describe("Language code"),
target_domain: z
.string()
.optional()
.describe(
"Optional domain to check against the sampled citation candidates",
),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
},
async ({ keyword, location, language, target_domain, sync }) => {
const result = await callAgentSeoApi("/ai-overview/extract", {
data: { keyword, location, language, target_domain },
params: { sync },
});
return jsonContent(result);
},
);
server.tool(
"agentseo_local_visibility_track",
"Track local visibility for a domain across keywords and locations.",
{
domain: z.string().describe("Domain to track"),
keywords: z.array(z.string()).min(1).max(5).describe("Keywords"),
locations: z.array(z.string()).min(1).max(3).describe("Locations"),
language: z.string().default("en").describe("Language code"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
},
async ({ domain, keywords, locations, language, sync }) => {
const result = await callAgentSeoApi("/local-visibility/track", {
data: { domain, keywords, locations, language },
params: { sync },
});
return jsonContent(result);
},
);
server.tool(
"agentseo_llm_mentions_track",
"Track brand mentions across LLM/search-style results.",
{
brand: z.string().describe("Brand name"),
queries: z.array(z.string()).min(1).max(10).describe("Queries to check"),
limit_per_query: z
.number()
.int()
.min(1)
.max(20)
.default(8)
.describe("Per-query result limit"),
platform: z
.enum(["reddit", "twitter", "all"])
.default("all")
.describe("Platform filter"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
},
async ({ brand, queries, limit_per_query, platform, sync }) => {
const result = await callAgentSeoApi("/llm-mentions/track", {
data: { brand, queries, limit_per_query, platform },
params: { sync },
});
return jsonContent(result);
},
);
server.tool(
"agentseo_content_decay_detect",
"Detect potential content decay for a URL and keyword.",
{
url: z.string().url().describe("Target URL"),
keyword: z.string().describe("Target keyword"),
lookback_days: z
.number()
.int()
.min(3)
.max(180)
.default(30)
.describe("Lookback window"),
threshold: z
.number()
.min(1)
.max(20)
.default(3)
.describe("Rank drop threshold"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
},
async ({ url, keyword, lookback_days, threshold, sync }) => {
const result = await callAgentSeoApi("/content-decay/detect", {
data: { url, keyword, lookback_days, threshold },
params: { sync },
});
return jsonContent(result);
},
);
server.tool(
"agentseo_keyword_cluster_build",
"Build keyword clusters from an input keyword list.",
{
keywords: z.array(z.string()).min(1).max(200).describe("Keywords"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
},
async ({ keywords, sync }) => {
const result = await callAgentSeoApi("/keyword-cluster/build", {
data: { keywords },
params: { sync },
});
return jsonContent(result);
},
);
server.tool(
"agentseo_rank_track",
"Queue a rank tracking check for a URL and keyword.",
{
keyword: z.string().describe("Keyword to track"),
url: z.string().url().describe("URL to track"),
location: z.string().default("United States").describe("Location"),
sync: z.boolean().default(true).describe("Whether to run synchronously"),
},
async ({ keyword, url, location, sync }) => {
const result = await callAgentSeoApi("/rank/track", {
data: { keyword, url, location },
params: { sync },
});
return jsonContent(result);
},
);
server.tool(
"agentseo_rank_history",
"Fetch historical rank data for a keyword and URL.",
{
keyword: z.string().describe("Keyword"),
url: z.string().url().describe("URL"),
},
async ({ keyword, url }) => {
const result = await callAgentSeoApi("/rank/track", {
method: "GET",
params: { keyword, url },
});
return jsonContent(result);
},
);
server.tool(
"agentseo_job_status",
"Get status/result for a previously queued job.",
{
job_id: z.string().describe("Job ID"),
},
async ({ job_id }) => {
const result = await callAgentSeoApi(`/jobs/${job_id}`, {
method: "GET",
});
return jsonContent(result);
},
);
async function main() {
const server = createAgentSeoMcpServer({
apiBaseUrl: API_BASE_URL,
apiKey,
projectId: PROJECT_ID,
workflowId: WORKFLOW_ID,
});
const transport = new StdioServerTransport();

@@ -445,0 +32,0 @@ await server.connect(transport);