# How to Connect Google Search Console to OpenSEO: Complete Integration Guide

> Easily connect Google Search Console to OpenSEO using OAuth. Select your property and unlock powerful Search Performance and URL Inspection tools for better SEO insights.

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

---

**Connect Google Search Console to OpenSEO via OAuth, select a property, and unlock the Search Performance and URL Inspection MCP tools.**

OpenSEO integrates Google Search Console (GSC) as an optional data source that powers the **Search Performance** and **URL Inspection** tools. This guide explains how to connect Google Search Console to OpenSEO based on the source code in `every-app/open-seo`, covering both hosted deployments and self-hosted installations.

## Architecture Overview

The GSC integration consists of several coordinated components defined in the OpenSEO source code:

| Component | Responsibility | Source Location |
|-----------|--------------|---------------|
| **OAuth Provider** | Identifies the GSC OAuth client and required scopes (`openid`, `email`, `profile`, `https://www.googleapis.com/auth/webmasters.readonly`) | [`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts) |
| **Self-hosted OAuth Config** | Reads `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, and `BETTER_AUTH_SECRET` from environment variables | `src/server/features/google/oauth-config` |
| **GscService** | Handles all GSC API calls including token management, request building, and error translation | [`src/server/features/gsc/services/GscService.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/gsc/services/GscService.ts) |
| **Server Functions** | TanStack Server Functions exposed to the frontend for connection management | [`src/serverFunctions/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/gsc.ts) |
| **MCP Tools** | Implements `get_search_console_performance` and `inspect_urls` commands for the OpenSEO command palette | [`src/server/mcp/tools/search-console-tools.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/search-console-tools.ts) |

When any GSC-related MCP tool is invoked, the handler first checks `missingSelfHostedGoogleClientResponse`. For self-hosted deployments missing OAuth configuration, the user receives a message containing `GSC_SELF_HOSTED_SETUP_DOCS_URL` from [`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts).

## Connection Methods: Hosted vs. Self-Hosted

### Hosted Version (Every App Managed)

Users on the hosted version require **no environment configuration**. The OAuth provider is pre-configured by Every App.

To connect Google Search Console to OpenSEO:

1. Navigate to your project's **Integrations** settings
2. Click **Connect Search Console**
3. Authenticate with Google and authorize the **Webmasters Read-only** scope
4. Select a property from your available GSC sites

### Self-Hosted Version

Self-hosted deployments require manual OAuth configuration before users can connect.

#### Step 1: Configure Environment Variables

Set these variables in your OpenSEO instance:

```bash
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
BETTER_AUTH_SECRET=your-better-auth-secret

```

The `hasSelfHostedGoogleOAuthConfig` helper in `src/server/features/google/oauth-config` validates these values. If missing, the UI displays setup nudges pointing to `GSC_SELF_HOSTED_SETUP_DOCS_URL`.

#### Step 2: Initiate OAuth Flow

From a client component, call `startSelfHostedGscLink`:

```typescript
// Called when the user clicks "Connect Search Console"
await startSelfHostedGscLink({
  callbackURL: `${window.location.origin}/auth/callback`,
}).then((resp) => {
  // Redirect the user to the Google consent screen
  window.location.href = resp.url;
});

```

#### Step 3: Complete Authentication

After Google redirects to your callback URL, the server stores the access and refresh tokens linked to the user's Better Auth account. The `GscService` class manages token refresh automatically.

## Selecting and Binding a GSC Property

Once authenticated, users must bind a specific GSC property to their OpenSEO project.

### List Available Sites

Call `listGscSites` to retrieve accessible properties:

```typescript
const { accounts } = await listGscSites({ projectId });

accounts.forEach((grant) => {
  console.log(`Account ${grant.email} – sites:`);
  grant.sites.forEach((site) => {
    const status = site.isSelected ? '[selected]' : '';
    console.log(` • ${site.siteUrl} (${site.permissionLevel}) ${status}`);
  });
});

```

### Bind a Site to the Project

Use `setGscSite` to create the connection:

```typescript
await setGscSite({
  projectId,
  accountId: "<selected-account-id>",
  siteUrl: "https://example.com",
});

```

This creates a row in the `gsc` table defined in [`src/db/gsc.schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/gsc.schema.ts), storing the `accountId`, `siteUrl`, and linking metadata.

### Verify Connection Status

Call `getGscConnection` to confirm the link:

```typescript
const connection = await getGscConnection({ projectId });
// Returns: { connected: true, siteUrl, connectedByEmail, createdAt, updatedAt }

```

The Integrations page displays this status to users.

## Using GSC Data: MCP Tools

With an active connection, two MCP tools become available in the OpenSEO command palette.

### Search Performance Tool

The `get_search_console_performance` tool queries GSC analytics data:

```typescript
await getSearchConsolePerformanceTool.handler({
  projectId,
  dimensions: ["query", "page"],
  dateRange: "last_28_days",
});

```

**Response includes:**
- `rows` — array of performance data with clicks, impressions, CTR, and position
- `hasMore` — pagination flag
- `text` — human-readable summary for UI display

Valid dimensions: `query`, `page`, `country`, `device`, `searchAppearance`. Valid date ranges: `last_7_days`, `last_28_days`, `last_3_months`, `last_6_months`, `last_year`, `last_12_months`.

### URL Inspection Tool

The `inspect_urls` tool checks indexing status for specific URLs:

```typescript
await inspectUrlsTool.handler({
  projectId,
  urls: ["https://example.com/blog/post"],
});

```

**Response includes per URL:**
- `verdict` — overall status
- `coverageState` — indexing state
- `canonical` — canonical URL information

## Error Handling

The `describeGscError` function in [`GscService.ts`](https://github.com/every-app/open-seo/blob/main/GscService.ts) normalizes errors into user-friendly messages:

| Error Type | Cause | User Action |
|------------|-------|-------------|
| `GscNotConnectedError` | No property bound to project | Visit `/p/:projectId/search-performance` to connect |
| `GscTokenError` | Expired or revoked token | Re-authenticate via OAuth flow |
| `GscApiError` | GSC API failure | Retry or check Google status |

All errors include a **Connect** URL redirecting to the project's Search Performance dashboard.

## Key Source Files

Reference these files when extending or debugging the GSC integration:

- [`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts) — OAuth scopes, provider ID, and documentation URL constants
- [`src/server/features/gsc/services/GscService.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/gsc/services/GscService.ts) — Core API client with token handling
- [`src/serverFunctions/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/gsc.ts) — TanStack Server Functions for connection management
- [`src/server/mcp/tools/search-console-tools.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/search-console-tools.ts) — Search Performance and URL Inspection command implementations
- [`src/db/gsc.schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/gsc.schema.ts) — Database schema for GSC connection persistence

## Summary

- **Hosted deployments** require zero configuration—users connect directly via OAuth
- **Self-hosted deployments** need `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, and `BETTER_AUTH_SECRET` environment variables
- The connection flow follows: OAuth grant → token storage → site selection → project binding
- [`GscService.ts`](https://github.com/every-app/open-seo/blob/main/GscService.ts) abstracts all GSC API interactions with automatic token refresh
- Two MCP tools expose GSC data: `get_search_console_performance` and `inspect_urls`
- Error states redirect users to `/p/:projectId/search-performance` for reconnection

## Frequently Asked Questions

### What's the difference between hosted and self-hosted GSC connection?

Hosted OpenSEO (managed by Every App) includes pre-configured OAuth credentials. Self-hosted instances require you to create a Google OAuth client and set `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, and `BETTER_AUTH_SECRET` environment variables. The `hasSelfHostedGoogleOAuthConfig` check in `src/server/features/google/oauth-config` determines which flow executes.

### What Google OAuth scopes does OpenSEO require?

OpenSEO requests `openid`, `email`, `profile`, and `https://www.googleapis.com/auth/webmasters.readonly`. The read-only Webmasters scope is defined in [`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts) as `GSC_OAUTH_PROVIDER_ID`. No write access to your Search Console properties is requested.

### Can users connect multiple GSC properties to one OpenSEO project?

No—each OpenSEO project binds to exactly one GSC property via the `setGscSite` function, which creates a single row in the `gsc` table. Users with access to multiple GSC accounts can view all available sites through `listGscSites`, but must select one per project.