# How to Perform Keyword Research with OpenSEO MCP: Complete Technical Guide

> Learn to perform keyword research using OpenSEO MCP. This technical guide details the research_keywords tool and its integration for efficient AI-driven data analysis and markdown table generation.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: how-to-guide
- Published: 2026-09-05

---

**OpenSEO MCP delivers keyword research capabilities through the `research_keywords` tool defined in [`src/server/mcp/tools/research-keywords.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/research-keywords.ts), which orchestrates data retrieval via the `KeywordResearchService.research` method and formats results as markdown tables for AI agents.**

OpenSEO MCP serves as the multi-channel planning backend for the `every-app/open-seo` repository, providing a Model Context Protocol (MCP) interface for SEO automation. Performing keyword research with this system requires understanding its layered architecture, from tool definitions that validate inputs to service layers that handle DataForSEO API integration and credit billing.

## Architecture of the OpenSEO MCP Keyword System

The keyword research functionality is implemented across four distinct architectural layers that ensure type safety, data integrity, and consistent formatting.

### MCP Tool Definition Layer

The entry point is the `researchKeywordsTool` in [`src/server/mcp/tools/research-keywords.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/research-keywords.ts). This layer declares the tool name, input schema, and output formatting logic. It validates incoming requests using Zod schemas, resolves market parameters via `resolveMarket`, and builds user-friendly text responses by delegating to the service layer.

### Service Layer Implementation

Heavy data retrieval occurs in `KeywordResearchService.research`, exposed through [`src/server/features/keywords/services/KeywordResearchService.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/keywords/services/KeywordResearchService.ts). This façade forwards requests to the concrete implementation in [`src/server/features/keywords/services/research.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/keywords/services/research.ts), which manages DataForSEO provider connections, clickstream data options, and credit consumption accounting.

### Response Formatting

Raw API rows are transformed into markdown tables using `formatMcpTable` from [`src/server/mcp/table.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/table.ts). This utility standardizes column alignment and adds meta information including project links and credit usage summaries.

### Skill-Based Workflow Definitions

Human-readable workflow guidance lives in [`plugins/openseo/skills/keyword-research/SKILL.md`](https://github.com/every-app/open-seo/blob/main/plugins/openseo/skills/keyword-research/SKILL.md). This file defines the 9-step research process, required inputs, and strict guardrails: never invent metrics, never save without explicit user consent, and always prioritize business fit over raw search volume.

## The 9-Step Keyword Research Workflow

According to the skill definition in [`SKILL.md`](https://github.com/every-app/open-seo/blob/main/SKILL.md), the complete workflow for performing keyword research with OpenSEO MCP follows this sequence:

1. **Project Context Retrieval** – Call `get_project_context` to ground research in the project's business overview and current goals.

2. **Search Console Pre-flight** – For projects with Google Search Console linked, fetch "striking-distance" queries using `get_search_console_performance`, filter positions 5-20 client-side, and hydrate them with `get_keyword_metrics` for high-signal opportunities.

3. **Local SEO Branch** – For location-specific queries, invoke `search_local_businesses`, `get_local_serp_results`, and `get_google_business_questions` to obtain geographically targeted keywords.

4. **Exploratory Discovery** – Execute `research_keywords` with 1-5 seed topics. The tool returns up to 150 results per seed (configurable to 300 or 500 via `resultLimit`).

5. **Metrics Hydration** – Pass the keyword list to `get_keyword_metrics` to append volume, keyword difficulty (KD), CPC, and search intent classifications.

6. **Filtering and Prioritization** – Remove duplicates, branded-only terms, and off-intent keywords. Prioritize by business fit, search intent, reasonable difficulty, volume/CPC ratios, and SERP competitiveness.

7. **SERP Validation** – Optionally invoke `get_serp_results` for high-potential or ambiguous keywords to verify actual search intent before final selection.

8. **Presentation** – Return a "top-opportunity" summary followed by a full markdown table with columns: Keyword | Intent | Volume | KD | CPC | Priority | Notes.

9. **Persisting Results** – Request explicit user confirmation before calling `save_keywords`. Apply structured tags such as `topic:<topic>`, `intent:<intent>`, and `page:<slug>` for organization.

## Code Implementation Examples

### Invoking the research_keywords Tool via JSON-RPC

Client applications communicate with OpenSEO MCP through JSON-RPC requests to the MCP endpoint. The `research_keywords` tool accepts seed keywords, location codes, and result limits:

```typescript
await fetch("/api/mcp", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    tool: "research_keywords",
    args: {
      projectId: "proj_12345",
      seeds: [{ 
        seed: "organic coffee", 
        locationCode: "US", 
        languageCode: "en" 
      }],
      resultLimit: 150,
      includeClickstreamData: false,
    },
  }),
});

```

The handler in [`research-keywords.ts`](https://github.com/every-app/open-seo/blob/main/research-keywords.ts) validates these arguments against the input schema before delegating to the service layer.

### Direct Service Layer Integration

For server-side automation, import the `KeywordResearchService` façade to bypass HTTP overhead and access billing context directly:

```typescript
import { KeywordResearchService } from "@/server/features/keywords/services/KeywordResearchService";

const result = await KeywordResearchService.research(
  {
    projectId: "proj_12345",
    keywords: ["organic coffee"],
    locationCode: "US",
    languageCode: "en",
    resultLimit: 150,
    mode: "auto",
    clickstream: false,
  },
  billingContext,
);

```

This method located in [`src/server/features/keywords/services/research.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/keywords/services/research.ts) handles all external API calls to DataForSEO, market resolution, and credit accounting.

### Persisting Keywords with save_keywords

After user confirmation, persist selected keywords using the `save_keywords` tool defined in [`src/server/mcp/tools/save-keywords.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/save-keywords.ts):

```typescript
await fetch("/api/mcp", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    tool: "save_keywords",
    args: {
      projectId: "proj_12345",
      keywords: [
        {
          keyword: "organic coffee beans",
          tags: ["topic:coffee", "intent:navigational"],
        },
      ],
    },
  }),
});

```

The `save_keywords` handler calls `KeywordResearchService.saveKeywords` to write records to the database with proper tagging.

## Summary

- **Primary Entry Point**: The `research_keywords` tool in [`src/server/mcp/tools/research-keywords.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/research-keywords.ts) validates inputs and formats responses.
- **Data Layer**: `KeywordResearchService.research` in [`src/server/features/keywords/services/KeywordResearchService.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/keywords/services/KeywordResearchService.ts) manages DataForSEO integration and credit billing.
- **Workflow**: The 9-step process defined in [`plugins/openseo/skills/keyword-research/SKILL.md`](https://github.com/every-app/open-seo/blob/main/plugins/openseo/skills/keyword-research/SKILL.md) ensures systematic research from context gathering to final persistence.
- **Guardrails**: Never invent metrics, require explicit consent for saving, and prioritize business fit over volume.
- **Output**: Results are rendered via `formatMcpTable` in [`src/server/mcp/table.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/table.ts) as standardized markdown tables.

## Frequently Asked Questions

### What is the maximum number of keywords returned by the research_keywords tool?

The `research_keywords` tool returns up to 150 results per seed keyword by default, configurable to 300 or 500 via the `resultLimit` parameter. This limit is enforced in the service layer implementation to manage DataForSEO API credit consumption and response payload sizes.

### How does OpenSEO MCP handle keyword metrics like volume and difficulty?

Metrics are hydrated through the `get_keyword_metrics` tool, which appends search volume, keyword difficulty (KD), CPC, and intent classifications to raw keyword lists. The system sources this data from DataForSEO providers and never invents or estimates metrics beyond what the API returns, as enforced by the skill guardrails in [`SKILL.md`](https://github.com/every-app/open-seo/blob/main/SKILL.md).

### Can I perform local keyword research with OpenSEO MCP?

Yes, the workflow includes a dedicated Local SEO branch using `search_local_businesses`, `get_local_serp_results`, and `get_google_business_questions` to obtain location-specific keywords. These tools fetch geographically targeted data that you can then hydrate with standard metrics using `get_keyword_metrics`.

### What are the required parameters for calling the research_keywords tool?

The tool requires `projectId` and an array of `seeds` containing objects with `seed` (the keyword string), `locationCode` (e.g., "US"), and `languageCode` (e.g., "en"). Optional parameters include `resultLimit` (default 150), `includeClickstreamData` (boolean), and market resolution settings validated by `resolveMarket` in the handler.