How to Save Keywords Using the OpenSEO MCP: A Complete Developer Guide
Use the saveKeywords RPC method on the MCP endpoint at https://app.openseo.so/mcp with a valid Bearer token and JSON payload containing your keyword array to persist data to the saved_keyword table.
The every-app/open-seo repository exposes a Multi-Client Protocol (MCP) that enables AI agents and external applications to programmatically manage SEO data. When you need to save keywords using the OpenSEO MCP, you interact with an RPC-style endpoint that handles authentication, validation via Zod, and database persistence through a layered service architecture.
MCP Architecture and Authentication Flow
Before invoking any write operations, your client must obtain a short-lived MCP token. According to the authentication flow documented in web/content/docs/mcp.md, this token encodes both user identity and projectId scope. Without the projectId scope, the server rejects calls with a 401 Unauthorized error.
The Complete Workflow to Save Keywords
The implementation follows a three-tier architecture defined in the codebase:
- Request Handling – The MCP router in
src/server/routes/mcp.tsreceives POST requests and routessaveKeywordscalls to the service layer. - Business Logic –
src/server/services/keyword.service.tscontains theKeywordService.saveMany()method, which validates payloads against the Zod schema and coordinates with the repository. - Data Persistence –
src/server/repositories/savedKeywordRepository.tsexecutes anINSERT … ON CONFLICT … DO UPDATEoperation against thesaved_keywordtable defined indrizzle/0015_huge_celestials.sql.
Required Payload Schema
The request body must conform to the Zod schema implemented in the service layer:
const SaveKeywordSchema = z.object({
keyword: z.string(),
tags: z.array(z.string()).optional(),
notes: z.string().optional(),
});
const SaveKeywordsPayload = z.object({
keywords: z.array(SaveKeywordSchema),
});
Implementation Examples
Node.js and Fetch
import fetch from "node-fetch";
const MCP_ENDPOINT = "https://app.openseo.so/mcp";
const MCP_TOKEN = "<YOUR_MCP_TOKEN>";
const PROJECT_ID = "<PROJECT_ID>";
async function saveKeywords(keywords) {
const body = {
method: "saveKeywords",
params: { keywords },
};
const resp = await fetch(`${MCP_ENDPOINT}?projectId=${PROJECT_ID}`, {
method: "POST",
headers: {
Authorization: `Bearer ${MCP_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!resp.ok) throw new Error(`MCP error ${resp.status}`);
const data = await resp.json();
console.log("Saved IDs:", data);
}
saveKeywords([
{ keyword: "air-conditioner repair", tags: ["service"], notes: "high intent" },
{ keyword: "best HVAC contractors", tags: ["competitor"], notes: "" },
]);
AI Agent Integration
When configuring an AI agent (Claude, Codex, etc.) with MCP capabilities, send the following JSON structure:
{
"method": "saveKeywords",
"params": {
"keywords": [
{ "keyword": "solar panel installers", "tags": ["lead"], "notes": "seasonal" },
{ "keyword": "energy rebate programs", "tags": ["research"] }
]
}
}
The agent must include the MCP token in the Authorization header as configured in its client settings.
Retrieving Saved Keywords
To verify persistence or populate UI components, call listSavedKeywords:
async function listSavedKeywords() {
const resp = await fetch(`${MCP_ENDPOINT}?projectId=${PROJECT_ID}`, {
method: "POST",
headers: {
Authorization: `Bearer ${MCP_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ method: "listSavedKeywords", params: {} }),
});
const { keywords } = await resp.json();
console.table(keywords);
}
Database Schema and Idempotency
The saved_keyword table schema in drizzle/0015_huge_celestials.sql stores tags as JSON arrays and supports upsert semantics. When you save keywords using the OpenSEO MCP with a keyword that already exists, the repository updates the existing record's tags and notes rather than creating duplicates. This ensures idempotent operations suitable for automated workflows.
UI Synchronization
Saved keywords remain accessible through the web interface. The UI route web/src/routes/_marketing/features/saved-keywords.tsx fetches data via the same listSavedKeywords MCP tool, guaranteeing consistency between API operations and the visual interface.
Summary
- Authentication: Obtain an MCP token with
projectIdscope fromweb/content/docs/mcp.mdOAuth flows. - Endpoint: POST to
https://app.openseo.so/mcpwith methodsaveKeywords. - Validation: The service layer validates input using Zod schemas in
src/server/services/keyword.service.ts. - Persistence: Data writes to PostgreSQL via
savedKeywordRepository.tsusing upsert logic indrizzle/0015_huge_celestials.sql. - Idempotency: Repeated saves update existing records rather than creating duplicates.
- Integration: UI components consume the same MCP tools for real-time synchronization.
Frequently Asked Questions
What authentication method does the OpenSEO MCP require?
The MCP requires a Bearer token obtained through OAuth or API key authentication. The token must include the projectId scope to authorize write operations to keyword data. Requests without this scope receive a 401 Unauthorized response.
Can I update existing keywords instead of creating duplicates?
Yes. The saveKeywords method is idempotent. The repository layer in src/server/repositories/savedKeywordRepository.ts executes an INSERT … ON CONFLICT … DO UPDATE query, allowing you to modify tags and notes on existing keywords without creating duplicate entries.
How are tags stored in the database?
Tags are stored as JSON arrays in the tags column of the saved_keyword table. This structure, defined in drizzle/0015_huge_celestials.sql, enables efficient filtering and clustering operations in downstream SEO workflows.
Where can I find the UI implementation for viewing saved keywords?
The React route at web/src/routes/_marketing/features/saved-keywords.tsx implements the Saved Keyword Lists page. This component calls listSavedKeywords via the MCP to ensure the UI reflects the same data persisted through your API calls.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →