# How the Open-SEO AI Search Feature Integrates DataForSEO with the MCP

> Learn how the Open-SEO AI search feature integrates DataForSEO with MCP. Discover how API calls are secured with server functions, ensuring project scoping, authentication, and billing.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: internals
- Published: 2026-09-12

---

**The Open-SEO AI search feature wraps DataForSEO API calls inside TanStack server functions that enforce project scoping, authentication, and billing guards before delegating to service modules that construct and sign HTTP requests to DataForSEO endpoints.**

The integration leverages the Managed Cloud Platform (MCP) middleware architecture to securely route brand lookups and prompt explorations through a unified pipeline. By combining `createServerFn` with project-context middleware and paid-plan validation, the system ensures that every DataForSEO request is properly authenticated, scoped, and billed according to the organization's subscription tier.

## Architectural Flow

The AI search implementation follows a layered architecture that moves from client invocation through MCP middleware to external API calls.

### From Client to Server Function

The client calls two primary endpoints: `lookupBrand` for brand intelligence and `explorePrompt` for search prompt analysis. These are implemented as TanStack "server functions" using `createServerFn` in [`src/serverFunctions/ai-search.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/ai-search.ts)【L27‑L41】. The server function acts as the entry point, automatically handling the POST request routing and context injection.

### Middleware: Project Context and Authentication

Before reaching the business logic, every request passes through `requireProjectContext` middleware. This injects the current **project** and **organization** IDs into the request context【[`ai-search.ts`](https://github.com/every-app/open-seo/blob/main/ai-search.ts#L7)‑L8】, ensuring that AI search calls are strictly scoped to specific projects and preventing cross-tenant data leakage.

### Paid-Plan Enforcement

In hosted deployments, the MCP validates the organization's subscription status via `customerHasPaidPlan`. If the check fails, the system throws an `AppError` with code `PAYMENT_REQUIRED`【[`ai-search.ts`](https://github.com/every-app/open-seo/blob/main/ai-search.ts#L14)‑L24】. Self-hosted installations bypass this guard because they manage DataForSEO billing directly rather than through the MCP's quota system.

### Service Layer Integration

Once validated, the handler forwards the request payload—augmented with `projectId`—to dedicated service modules:

- `getBrandLookup` handles brand-lookup requests【[`ai-search.ts`](https://github.com/every-app/open-seo/blob/main/ai-search.ts#L2)‑L3】
- `runExplorePrompt` handles prompt-exploration requests【[`ai-search.ts`](https://github.com/every-app/open-seo/blob/main/ai-search.ts#L3)‑L4】

Located under `src/server/features/ai-search/services/`, these modules construct the appropriate DataForSEO HTTP request, sign it with the organization's API credentials stored in the MCP's secret manager, and execute the `fetch` call to DataForSEO endpoints.

## Core Implementation Files

The integration spans several critical files that handle validation, billing, and runtime environment detection.

| File | Role |
|------|------|
| [`src/serverFunctions/ai-search.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/ai-search.ts) | Defines `lookupBrand` and `explorePrompt` server functions with paid-plan gating |
| [`src/server/features/ai-search/services/brandLookup.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/ai-search/services/brandLookup.ts) | Builds and sends DataForSEO brand-lookup requests |
| [`src/server/features/ai-search/services/promptExplorer.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/ai-search/services/promptExplorer.ts) | Builds and sends DataForSEO prompt-exploration requests |
| [`src/types/schemas/ai-search.ts`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/ai-search.ts) | Zod schemas validating incoming client data |
| [`src/lib/billing/subscription.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/billing/subscription.ts) | Helper checking whether an organization has a paid plan |
| [`src/lib/runtime-env.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/runtime-env.ts) | Determines if deployment is hosted vs. self-hosted |

The schema definitions in [`src/types/schemas/ai-search.ts`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/ai-search.ts) enforce type safety on incoming payloads before they reach the service layer, ensuring that `brandName` and `prompt` fields meet validation requirements.

## Security and Billing Controls

The integration implements multiple security layers to protect both MCP infrastructure and DataForSEO quotas:

**Authentication to DataForSEO.** Each organization stores its DataForSEO API key and secret in the MCP's secret manager (typically via environment variables). Service modules read these secrets server-side and inject them into outgoing HTTP headers, never exposing credentials to the client.

**Project-Level Scoping.** The `requireProjectContext` middleware binds every request to a specific project ID, preventing unauthorized access to other organizations' search data.

**Billing Protection.** The `assertPaidPlan` logic (lines 18‑24) blocks unauthorized usage in hosted mode by checking [`src/lib/billing/subscription.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/billing/subscription.ts), ensuring that only paying customers consume DataForSEO credits through the platform.

**Error Normalization.** All DataForSEO errors are caught and re-thrown as `AppError` instances, preserving the MCP's consistent API error format across the application.

## Calling the AI Search Functions

Below is a minimal client-side example demonstrating how to invoke the AI search endpoints using the TanStack server function client:

```typescript
import { lookupBrand, explorePrompt } from '@/serverFunctions/ai-search';

// 1️⃣ Brand lookup – e.g. “Apple”
async function doBrandLookup() {
  const result = await lookupBrand({
    brandName: 'Apple',
    // other validated fields defined in brandLookupInputSchema
  });
  console.log('DataForSEO brand lookup result:', result);
}

// 2️⃣ Prompt exploration – e.g. “best SEO tools”
async function doPromptExplore() {
  const result = await explorePrompt({
    prompt: 'best SEO tools for small businesses',
    // other validated fields defined in promptExplorerInputSchema
  });
  console.log('DataForSEO prompt exploration result:', result);
}

// Invoke the functions
doBrandLookup();
doPromptExplore();

```

The client sends POST requests to `/api/ai-search/lookupBrand` or `/api/ai-search/explorePrompt`, with the MCP automatically appending `projectId` and `organizationId` to the context. The server function validates the payload against Zod schemas, executes the paid-plan check (if hosted), and delegates to the DataForSEO service modules.

## Summary

- **Server Functions.** The AI search feature uses `createServerFn` in [`src/serverFunctions/ai-search.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/ai-search.ts) to expose `lookupBrand` and `explorePrompt` as type-safe endpoints.
- **Middleware Protection.** `requireProjectContext` enforces project-level isolation, while `customerHasPaidPlan` prevents unpaid usage in hosted environments.
- **Service Abstraction.** Modules in `src/server/features/ai-search/services/` handle DataForSEO HTTP request construction, signing, and execution using securely stored credentials.
- **Consistent Error Handling.** All external errors are normalized to `AppError` instances to maintain API consistency across the MCP.

## Frequently Asked Questions

### What is the MCP server function framework?

The MCP server function framework is a TanStack-based system that allows developers to define server-side logic using `createServerFn` while automatically handling routing, context injection, and middleware chaining. In Open-SEO, this framework enables the AI search endpoints to enforce authentication and billing before processing DataForSEO requests.

### How does the paid-plan guard differ between hosted and self-hosted deployments?

In hosted deployments, the MCP checks the organization's subscription status via [`src/lib/billing/subscription.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/billing/subscription.ts) and throws a `PAYMENT_REQUIRED` error if unpaid. Self-hosted installations skip this check because they manage their own DataForSEO API credentials and billing relationships directly, as determined by [`src/lib/runtime-env.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/runtime-env.ts).

### How are DataForSEO API credentials managed securely?

Credentials are stored in the MCP's secret manager (accessed through environment variables) and read server-side by the service modules in `src/server/features/ai-search/services/`. The client never handles these secrets; all signing occurs within the MCP's secure environment before the HTTP request is dispatched to DataForSEO.

### What happens when DataForSEO returns an error?

DataForSEO errors are caught by the service modules and wrapped in `AppError` objects with consistent error codes. This normalization ensures that clients receive predictable error payloads regardless of whether the failure originated from DataForSEO, billing validation, or schema validation in [`src/types/schemas/ai-search.ts`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/ai-search.ts).