# Shared Utilities for GSC Integration in OpenSEO: Architecture and Implementation

> Explore shared utilities for GSC integration in OpenSEO. Discover reusable TypeScript modules for consistent OAuth, typed API clients, and error management in src shared and src server lib.

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

---

**OpenSEO centralizes Google Search Console integration through reusable TypeScript modules located in `src/shared` and `src/server/lib`, providing consistent OAuth handling, typed API clients, and structured error management across the entire application.**

The OpenSEO codebase treats Google Search Console (GSC) connectivity as a first-class integration, abstracting all authentication and API concerns into shared utilities that both server-side services and client components can consume. These **shared utilities for GSC integration in OpenSEO** ensure that OAuth flows, API requests, and error handling remain consistent whether you're building MCP tools, UI components, or background jobs.

## Core GSC Utilities Overview

OpenSEO organizes GSC functionality into five primary utility modules that separate concerns between constants, client logic, error handling, request building, and high-level orchestration:

- **[`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts)** – OAuth constants and documentation URLs
- **[`src/server/lib/gscClient.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/gscClient.ts)** – Authenticated API client wrapper
- **[`src/server/lib/gscErrors.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/gscErrors.ts)** – Typed error classes
- **[`src/server/features/gsc/searchAnalytics.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/gsc/searchAnalytics.ts)** – Request builders and dimension constants
- **[`src/server/features/gsc/services/GscService.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/gsc/services/GscService.ts)** – Service layer coordinating all utilities

## OAuth Constants and Configuration ([`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts))

The foundation of GSC integration starts with constants that must be accessible to both the server (for Better Auth configuration) and the client (for "Connect to GSC" buttons). The [`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts) file exports three critical values:

- `GSC_OAUTH_PROVIDER_ID` – The identifier used when configuring the OAuth provider
- `GSC_OAUTH_SCOPES` – Required Google OAuth scopes for Search Console access
- `GSC_SELF_HOSTED_SETUP_DOCS_URL` – Documentation link for self-hosted instances

These constants ensure that both authentication handlers and UI components reference identical configuration values without duplication.

## API Client Wrapper ([`src/server/lib/gscClient.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/gscClient.ts))

The [`gscClient.ts`](https://github.com/every-app/open-seo/blob/main/gscClient.ts) module provides a thin wrapper around the Google Search Console REST API through the `createGscClient` factory function. This utility handles token retrieval, request signing, and error wrapping while exposing three core endpoints:

- `listSites()` – Retrieve verified GSC properties
- `querySearchAnalytics()` – Fetch search performance data
- `inspectUrl()` – Check indexing status for specific URLs

### Creating the GSC Client

When initializing the client, `createGscClient` accepts a user ID and optional Google account ID, retrieves an access token via `getAuth().api.getAccessToken`, and returns an object with methods that issue signed `fetch` calls to the GSC API.

```typescript
import { createGscClient } from '@/server/lib/gscClient';

async function listVerifiedSites(userId: string, gscAccountId?: string) {
  const client = createGscClient({ userId, gscAccountId });
  const sites = await client.listSites();
  console.log('Verified sites:', sites);
}

```

## Typed Error Handling ([`src/server/lib/gscErrors.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/gscErrors.ts))

GSC operations can fail in distinct ways—API rate limits, expired tokens, or missing connections—and OpenSEO provides specific error classes to distinguish these scenarios. The [`src/server/lib/gscErrors.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/gscErrors.ts) file exports:

- **`GscApiError`** – Wraps non-2xx HTTP responses from the GSC API
- **`GscTokenError`** – Indicates token minting or refresh failures
- **`GscNotConnectedError`** – Thrown when a project lacks a stored GSC connection

Higher-level services and MCP tools catch these errors to present user-friendly messages without leaking implementation details.

## Search Analytics Request Builders ([`src/server/features/gsc/searchAnalytics.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/gsc/searchAnalytics.ts))

Before querying search analytics data, MCP tool inputs must conform to the GSC API's expected shape. The [`src/server/features/gsc/searchAnalytics.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/gsc/searchAnalytics.ts) module provides pure-logic helpers that handle this translation.

This utility exports constants for valid dimensions, filter operators, search types, and date ranges, plus two key functions:

- `resolveDateRange()` – Converts relative ranges like `last_28_days` into ISO dates
- `buildSearchAnalyticsRequest()` – Constructs the final request object with proper `dimensionFilterGroups` structure

### Building Structured Requests

```typescript
import { buildSearchAnalyticsRequest } from '@/server/features/gsc/searchAnalytics';

const input = {
  projectId: 'proj_123',
  dimensions: ['query', 'page'],
  dateRange: 'last_28_days',
  filters: [{ dimension: 'country', operator: 'equals', expression: 'US' }],
  rowLimit: 500,
};

const request = buildSearchAnalyticsRequest(input);
// Returns GSC API-compatible object with startDate, endDate, and filter groups

```

## High-Level Service Orchestration ([`src/server/features/gsc/services/GscService.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/gsc/services/GscService.ts))

The `GscService` class ties together the client, repository, and request builders to provide project-scoped GSC operations. Rather than manually coordinating tokens and API calls throughout the codebase, developers interact with methods like:

- `getPerformance()` – Retrieve analytics data for a project
- `inspectUrls()` – Batch check indexing status
- `listSitesForUserWithGrantStatus()` – Show available sites with connection status

`GscService` pulls the stored connection from `GscConnectionRepository`, builds requests using the search analytics utilities, creates a client via `createGscClient`, and executes the API call. All errors bubble up as the typed error classes defined in [`gscErrors.ts`](https://github.com/every-app/open-seo/blob/main/gscErrors.ts).

```typescript
import { GscService } from '@/server/features/gsc/services/GscService';

async function getPerformanceExample(projectId: string) {
  const perf = await GscService.getPerformance({
    projectId,
    dimensions: ['query'],
    dateRange: 'last_7_days',
  });
  console.log('Site URL:', perf.siteUrl);
  console.log('Rows returned:', perf.rows.length);
}

```

## Integration Flow: How the Utilities Work Together

Understanding the relationship between these modules clarifies the data flow when a user requests GSC data:

1. **UI components** import `GSC_OAUTH_PROVIDER_ID` and `GSC_OAUTH_SCOPES` from [`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts) to render the "Connect GSC" button using Better Auth.

2. **When executing a tool**, `GscService` checks for an existing connection in the repository. If none exists, it throws `GscNotConnectedError`.

3. **Token retrieval** happens inside `createGscClient`, which fetches credentials and prepares authenticated headers.

4. **Request shaping** occurs in `buildSearchAnalyticsRequest`, translating Zod-validated MCP inputs into GSC API parameters including dimension filter groups.

5. **Error classification** ensures that API failures become `GscApiError`, token issues become `GscTokenError`, and missing connections remain `GscNotConnectedError`, allowing the MCP layer to render appropriate user guidance.

```typescript
import {
  GscApiError,
  GscTokenError,
  GscNotConnectedError,
} from '@/server/lib/gscErrors';

try {
  await GscService.getPerformance({ projectId: 'proj_123' });
} catch (e) {
  if (e instanceof GscNotConnectedError) {
    console.warn('Project has no GSC connection – prompt the user to connect.');
  } else if (e instanceof GscTokenError) {
    console.warn('Token expired or revoked – ask the user to reconnect.');
  } else if (e instanceof GscApiError) {
    console.error(`GSC API returned ${e.status}: ${e.message}`);
  }
}

```

## Summary

- **[`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts)** exports OAuth constants like `GSC_OAUTH_PROVIDER_ID` and `GSC_OAUTH_SCOPES` for use in both server authentication and client UI components.
- **`createGscClient`** in [`src/server/lib/gscClient.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/gscClient.ts) manages token retrieval via `getAuth().api.getAccessToken` and provides authenticated methods for `listSites`, `querySearchAnalytics`, and `inspectUrl`.
- **Typed error classes** (`GscApiError`, `GscTokenError`, `GscNotConnectedError`) enable granular error handling strategies throughout the application.
- **Pure helper functions** like `buildSearchAnalyticsRequest` and `resolveDateRange` translate high-level inputs into GSC API-compatible request shapes.
- **[`GscService.ts`](https://github.com/every-app/open-seo/blob/main/GscService.ts)** orchestrates the entire integration flow, combining repository access, client instantiation, and error bubbling for consistent project-scoped operations.

## Frequently Asked Questions

### How does OpenSEO handle GSC authentication tokens?

OpenSEO retrieves GSC access tokens through the `createGscClient` function in [`src/server/lib/gscClient.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/gscClient.ts), which internally calls `getAuth().api.getAccessToken` using the user ID and optional Google account ID. This ensures tokens are fetched fresh for each API interaction and properly signed for Google Search Console requests.

### What error types should I catch when calling GSC APIs in OpenSEO?

You should catch three specific error classes from [`src/server/lib/gscErrors.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/gscErrors.ts): `GscNotConnectedError` when a project lacks a GSC connection, `GscTokenError` when authentication tokens expire or are revoked, and `GscApiError` when the GSC API returns a non-2xx status code. These typed errors allow you to render specific user guidance for each failure mode.

### Can I use the GSC utilities outside of the MCP tools?

Yes. The shared utilities are designed for reuse across the entire codebase. You can import `createGscClient` directly for custom API operations, use `buildSearchAnalyticsRequest` for manual request construction, or leverage `GscService` methods in background jobs, scheduled tasks, or custom API routes without duplicating authentication or error-handling logic.

### Where are the GSC API constants defined for OAuth configuration?

OAuth-related constants including `GSC_OAUTH_PROVIDER_ID`, `GSC_OAUTH_SCOPES`, and `GSC_SELF_HOSTED_SETUP_DOCS_URL` are defined in [`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts). This location allows both server-side authentication handlers and client-side UI components to import the same values, ensuring consistency in OAuth flows across the application.