# OpenSEO Architecture Explained: A Full-Stack TypeScript App on Cloudflare Workers

> Explore OpenSEO architecture, a full-stack TypeScript app on Cloudflare Workers. Learn about its modular components: React front-end, API, Durable Objects, MCP server, and database. See the code on GitHub.

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

---

**OpenSEO is built as a modern, full‑stack TypeScript application running on Cloudflare Workers, separating the React front‑end, API layer, Durable Object agents, MCP server, and database into modular, maintainable components.**

This deep dive breaks down how every‑app/open‑seo structures its codebase, routes requests, and integrates AI agents through the Model Context Protocol (MCP). All file paths and implementation details are drawn directly from the source code.

## Front‑End Layer: TanStack React‑Start with Tailwind CSS

The user interface is a **server‑side rendered React application** built on TanStack React‑Start.

- **TanStack React‑Start** handles routing and SSR. The generated router tree lives in [`src/routeTree.gen.ts`](https://github.com/every-app/open-seo/blob/main/src/routeTree.gen.ts), while [`src/app.tsx`](https://github.com/every-app/open-seo/blob/main/src/app.tsx) serves as the entry point.
- **TanStack Query** manages data fetching and caching.
- **Tailwind CSS** provides utility‑first styling via [`tailwind.config.ts`](https://github.com/every-app/open-seo/blob/main/tailwind.config.ts).

Front‑end requests hit the Worker at `/api/*` routes, which dispatch to server functions in `src/serverFunctions/`.

## Worker Entrypoint: Central Request Dispatch

Every HTTP request flows through **[`src/server.ts`](https://github.com/every-app/open-seo/blob/main/src/server.ts)**, the single Cloudflare Worker script. This file defines:

- The `fetch` handler for incoming requests
- Scheduled cron jobs (OAuth KV purge, rank‑check loops, audit reconciliation, telemetry)
- Exports for **Durable Object** classes (`OnboardingChatAgent`, `SamChatAgent`)

The Worker inspects the request path and routes accordingly:

| Path Prefix | Handler | Purpose |
|-------------|---------|---------|
| `/agents/*` | `routeChatAgents` | Real‑time AI chat agents |
| `/mcp` | MCP transport | Machine‑readable API for AI agents |
| `/autumn-webhook` | `handleAutumnWebhookRequest` | Billing events |
| `/api/*` or other | `appFetch` (TanStack router) | Standard API and UI routes |

## API Layer: Server Functions and Feature Modules

Business logic is organized by feature under **`src/serverFunctions/`**. Each module exports functions that the Worker imports and exposes as API endpoints:

- [`src/serverFunctions/dashboard.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/dashboard.ts) – Dashboard data aggregation
- [`src/serverFunctions/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/rank-tracking.ts) – Rank monitoring operations
- [`src/serverFunctions/seo.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/seo.ts) – Core SEO analysis functions
- [`src/serverFunctions/projects.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/projects.ts) – Project CRUD operations

These functions rely on repository classes (e.g., `ProjectRepository` in [`src/server/features/projects/repositories/ProjectRepository.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/projects/repositories/ProjectRepository.ts)) for database access.

## Durable Object Agents: Real‑Time AI Chat

OpenSEO deploys **stateful chat agents** using Cloudflare Durable Objects and the Agents SDK:

- **`OnboardingChatAgent`** ([`src/server/features/onboarding/OnboardingChatAgent.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/onboarding/OnboardingChatAgent.ts)) – Guides new users through setup
- **`SamChatAgent`** – General SEO assistant

Agent routes are protected by `authorizeChatAgent` middleware. These objects maintain websocket connections and conversation state across requests.

## MCP Server: AI Agent Integration

The **Model Context Protocol (MCP)** exposes a structured API that external AI agents (Claude, OpenClaw, Hermes) can call to retrieve SEO data and trigger workflows.

Implementation lives in **`src/server/mcp/`**:

- [`transport.ts`](https://github.com/every-app/open-seo/blob/main/transport.ts) – Handles inbound MCP requests, authorization, and routing to server functions
- [`public-origin.ts`](https://github.com/every-app/open-seo/blob/main/public-origin.ts) – Origin validation for public MCP endpoints

The Worker injects the MCP pipeline when `env.AUTH_MODE` is `hosted`. For self‑hosted deployments, `handleSelfHostedOpenSeoMcpRequest` manages MCP access without OAuth.

## Authentication: Hosted vs. Self‑Hosted

OpenSEO supports two deployment modes, determined by **[`src/lib/auth-mode.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth-mode.ts)**:

| Mode | Auth Method | Use Case |
|------|-------------|----------|
| **Hosted** | Cloudflare Access + OAuth KV store | Managed `openseo.so` deployments |
| **Self‑hosted** | No authentication | Local development or private infrastructure |

Middleware in [`src/middleware/ensure-user/resolve.ts`](https://github.com/every-app/open-seo/blob/main/src/middleware/ensure-user/resolve.ts) enforces the appropriate guard based on `getAuthMode()`.

## Database Layer: Drizzle ORM with D1 and Postgres

Data persistence uses **Drizzle ORM** with dual backend support:

- **Cloudflare D1** (SQLite) – Default for hosted deployments
- **Postgres** – Optional for self‑hosted or high‑volume scenarios

Key files:
- [`src/db/schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema.ts) – Shared table definitions
- `src/db/pg/*` – Postgres‑specific extensions
- `withPgClient` helper – Supplies per‑request Postgres clients (noop for D1)

## Scheduled Jobs and Background Work

The Worker's `scheduled` method (lines 90‑140 of [`src/server.ts`](https://github.com/every-app/open-seo/blob/main/src/server.ts)) triggers:

1. **Daily OAuth KV purge** – Removes expired tokens
2. **Rank‑check loop** – Executes `RankCheckWorkflow` for active projects
3. **Stale‑audit reconciliation** – Cleans abandoned audit sessions
4. **Self‑host telemetry** – Anonymous usage metrics

## Billing and Compliance

- **[`src/server/billing/autumn-webhook.ts`](https://github.com/every-app/open-seo/blob/main/src/server/billing/autumn-webhook.ts)** – Processes subscription events from Autumn
- **[`src/gdpr/storage-erasure.ts`](https://github.com/every-app/open-seo/blob/main/src/gdpr/storage-erasure.ts)** – Handles GDPR Article 17 data deletion requests

## Code Examples: Working with OpenSEO

### Fetching Data from the Front‑End

```typescript
import { createQuery } from '@tanstack/react-query';
import { fetch } from '@/lib/fetch';

export const useProjects = () =>
  createQuery(['projects'], async () => {
    const resp = await fetch('/api/projects');
    if (!resp.ok) throw new Error('Failed to load projects');
    return resp.json();
  });

```

This TanStack Query hook calls `/api/projects`, routed by the Worker to [`src/serverFunctions/projects.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/projects.ts).

### Calling OpenSEO from an AI Agent via MCP

```typescript
import { createMcpClient } from '@modelcontextprotocol/client';

const client = createMcpClient({ baseUrl: 'https://openseo.so/mcp' });

async function getKeywordIdeas(domain: string) {
  const result = await client.call({
    service: 'keywordResearch',
    method: 'generateIdeas',
    params: { domain },
  });
  return result.ideas;
}

```

The MCP transport in [`src/server/mcp/transport.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/transport.ts) validates the request and forwards it to the appropriate server function.

### Triggering a Rank Check Programmatically

```typescript
import { RankCheckWorkflow } from '@/server/workflows/RankCheckWorkflow';

async function runRankCheck(projectId: string) {
  const workflow = new RankCheckWorkflow({ projectId });
  await workflow.start();
}

```

`RankCheckWorkflow` ([`src/server/workflows/RankCheckWorkflow.ts`](https://github.com/every-app/open-seo/blob/main/src/server/workflows/RankCheckWorkflow.ts)) orchestrates external API calls and database updates. The same class is invoked by the nightly cron job.

## Summary

- OpenSEO runs as a **single Cloudflare Worker** ([`src/server.ts`](https://github.com/every-app/open-seo/blob/main/src/server.ts)) that routes requests to React‑Start, API functions, Durable Objects, or MCP handlers.
- The **front‑end** uses TanStack React‑Start with TanStack Query and Tailwind CSS.
- **Server functions** in `src/serverFunctions/*` encapsulate business logic and use Drizzle repositories for database access.
- **Durable Object agents** (`OnboardingChatAgent`, `SamChatAgent`) enable stateful, real‑time AI conversations.
- The **MCP server** (`src/server/mcp/`) exposes machine‑readable endpoints for external AI agents.
- **Dual auth modes** (hosted/self‑hosted) and **dual database backends** (D1/Postgres) provide deployment flexibility.
- **Scheduled jobs** handle background work like rank tracking and data cleanup.

## Frequently Asked Questions

### What framework does OpenSEO use for the front‑end?

OpenSEO uses **TanStack React‑Start** for server‑side rendering, file‑based routing, and API endpoints. TanStack Query handles client‑side data fetching, and Tailwind CSS provides styling. The entry point is [`src/app.tsx`](https://github.com/every-app/open-seo/blob/main/src/app.tsx), with generated routes in [`src/routeTree.gen.ts`](https://github.com/every-app/open-seo/blob/main/src/routeTree.gen.ts).

### How does OpenSEO handle authentication?

Authentication depends on the deployment mode. **Hosted** deployments use Cloudflare Access with an OAuth KV store for session management. **Self‑hosted** deployments run without authentication. The mode is determined at runtime via [`src/lib/auth-mode.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth-mode.ts) and enforced by middleware in [`src/middleware/ensure-user/resolve.ts`](https://github.com/every-app/open-seo/blob/main/src/middleware/ensure-user/resolve.ts).

### What is the MCP server in OpenSEO?

The **Model Context Protocol (MCP)** server exposes a structured API at `/mcp` that AI agents can call to access SEO data and trigger workflows. It is implemented in [`src/server/mcp/transport.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/transport.ts) and supports both hosted (OAuth‑protected) and self‑hosted (unauthenticated) modes. Agents like Claude use this to perform keyword research, run audits, and retrieve rankings programmatically.

### Can OpenSEO run without Cloudflare D1?

Yes. OpenSEO supports **Postgres** as an alternative database backend via the `withPgClient` helper. The Drizzle ORM schema in [`src/db/schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema.ts) works with both D1 (SQLite) and Postgres implementations. Postgres‑specific code lives in `src/db/pg/*`.