How to Contribute to Open-SEO: A Complete Guide for Developers
Contributing to the every-app/open-seo repository requires setting up a local TanStack React Start environment with Cloudflare Workers, running database migrations, and submitting single-purpose pull requests that pass lint, type-check, and build validation.
The Open-SEO platform is an open-source, pay-as-you-go SEO tool built by every-app using modern edge-native architecture. This guide walks through cloning the repo, configuring local development, understanding the codebase structure, and successfully submitting your first contribution.
Prerequisites and Initial Setup
Before you can contribute to Open-SEO, ensure you have Node.js 18+, pnpm (via corepack), and Git installed.
Clone the repository and install dependencies:
git clone https://github.com/every-app/open-seo.git
cd open-seo
corepack enable
pnpm install --frozen-lockfile
The --frozen-lockfile flag guarantees you use the exact dependency versions specified in the project's lockfile, preventing environment drift.
Configure Your Local Development Environment
Open-SEO supports multiple database backends and authentication modes. For local development, you'll use SQLite via D1 with authentication disabled.
Create your environment file:
cp .env.example .env.local
Add your DataForSEO API credentials (required for rank tracking and keyword research features):
printf '%s' 'YOUR_LOGIN:YOUR_PASSWORD' | base64
Paste the base64 output into .env.local as DATAFORSEO_API_KEY, then set the auth mode:
echo "AUTH_MODE=local_noauth" >> .env.local
This bypasses Cloudflare Access for local testing, routing all requests through the local_noauth handler defined in src/lib/auth-mode.ts.
Database Setup and Development Server
Initialize the database schema:
pnpm run db:migrate:local
Migrations are managed via Drizzle ORM and configured in drizzle.config.ts for both D1 (SQLite) and Postgres backends.
Start the development environment:
pnpm dev:agents
This launches the app on http://open-seo.localhost:1355 using the portless Cloudflare Workers local simulation. Alternatively, run pnpm dev for a simpler Vite-based dev loop without agent features.
Understanding the Open-SEO Architecture
To contribute effectively, you need to know how requests flow through the system:
| Component | Location | Purpose |
|---|---|---|
| React Start entry point | src/start.ts |
Initializes server-function middleware and CSRF protection |
| Worker request handler | src/server.ts |
Routes HTTP traffic, authenticates agents, executes scheduled tasks |
| MCP transport | src/server/mcp/transport.ts |
Exposes JSON-RPC API for AI agents (Claude Code, OpenClaw, Hermes) |
| Durable Objects | src/server/features/onboarding/OnboardingChatAgent.ts |
Real-time chat channels for onboarding and SAM agents |
| Database layer | src/db/index.ts |
Provides scoped Postgres clients via withPgClient wrapper |
| Scheduled rank checks | src/server/features/rank-tracking/services/scheduledRankChecks.ts |
Daily cron job for updating tracked rankings |
The MCP (Managed-Code-Protocol) in src/server/mcp/* is the primary extension point for new AI-accessible functionality. Each method follows a standardized schema-validation pattern using Zod.
Contribution Workflow: Single-Purpose PRs
Open-SEO enforces a single-purpose PR philosophy: one logical change per pull request. This keeps reviews focused and history clean.
Follow this workflow when you contribute to Open-SEO:
- Open an issue describing your proposed change (optional but recommended for significant features)
- Create a feature branch from
main:git checkout -b feat/descriptive-name - Implement your change with appropriate tests
- Run local CI checks before pushing:
pnpm ci:check # Linting and type-checking
pnpm test:ci # Test suite
pnpm vite build # Production build verification
If you modified files under web/, also run the web-specific checks documented in docs/CONTRIBUTING.md.
- Push and open a pull request. The CI pipeline automatically re-runs these checks; passing status is required for merge.
Code Example: Adding a New MCP Method
Here's how to extend the platform with a new AI-accessible endpoint. This example adds a searchKeywords method that wraps the DataForSEO keyword research API.
Create the method implementation:
// src/server/mcp/methods/searchKeywords.ts
import { z } from "zod";
import { fetchDataForSeo } from "@/server/dataforseo/client";
export const searchKeywords = {
input: z.object({
query: z.string(),
language: z.string().optional(),
location: z.string().optional(),
}),
async resolve({ query, language = "en", location = "us" }) {
const response = await fetchDataForSeo("keyword_research", {
keyword: query,
language,
location,
});
return response;
},
};
Register the method in the transport layer:
// src/server/mcp/transport.ts
import { searchKeywords } from "./methods/searchKeywords";
export const mcpMethods = {
// existing methods...
searchKeywords,
};
Add corresponding tests in src/server/mcp/__tests__/searchKeywords.test.ts to verify schema validation and client invocation. Run pnpm test:ci to confirm coverage.
Key Files Every Contributor Should Know
| File | Why it matters for contributions |
|---|---|
README.md |
Project overview, live demo, community links |
docs/CONTRIBUTING.md |
Official guidelines, CI commands, PR etiquette |
docs/LOCAL_DEVELOPMENT.md |
Detailed environment setup and auth mode reference |
src/start.ts |
Server middleware configuration entry point |
src/server.ts |
Core request routing and Worker orchestration |
package.json |
Runtime dependencies (@tanstack/react-start, zod) and npm scripts |
drizzle.config.ts |
Database schema management for D1 and Postgres |
scripts/* |
Data seeding, migration utilities, release tools |
These files provide the context needed to understand how Open-SEO boots, how requests are processed, and where to place new functionality.
Summary
- Clone and install: Use
corepack enableandpnpm install --frozen-lockfilefor reproducible builds - Configure locally: Set
AUTH_MODE=local_noauthand base64-encode your DataForSEO credentials - Understand the flow: Requests enter through
src/server.ts, MCP methods extend functionality viasrc/server/mcp/transport.ts - Follow the workflow: Single-purpose PRs, local CI checks (
pnpm ci:check,pnpm test:ci,pnpm vite build), then push - Extend via MCP: New AI-accessible features follow the Zod schema + async resolve pattern shown in the
searchKeywordsexample
Frequently Asked Questions
What authentication mode should I use for local development?
Use AUTH_MODE=local_noauth in your .env.local file. This mode, implemented in src/lib/auth-mode.ts, skips Cloudflare Access validation and allows direct API testing without enterprise identity provider setup.
How do I add a new API endpoint that AI agents can call?
Implement a new method in src/server/mcp/methods/ following the { input: z.Schema, resolve: async fn } pattern, then register it in src/server/mcp/transport.ts. The MCP transport automatically exposes registered methods as JSON-RPC endpoints that Claude Code and other agents can discover and invoke.
What checks must pass before my PR can merge?
The CI pipeline enforces three validations: pnpm ci:check (ESLint and TypeScript), pnpm test:ci (unit tests), and pnpm vite build (production bundle). Web-specific checks from docs/CONTRIBUTING.md apply if you modified files under web/.
Can I run Open-SEO with Postgres instead of D1?
Yes. The database layer in src/db/index.ts uses a withPgClient wrapper that provides scoped Postgres clients when configured. The same Drizzle migrations work for both D1 (SQLite) and Postgres backends—adjust your connection string in environment variables to switch.
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 →