Oblive Docs
Extend Integrations

Direct Backend API Providers

Build fixed provider adapters with backend-owned credentials, reviewed tools, and resource-scoped settings.

Lemon Squeezy, Upstash, MongoDB, and Google Analytics are catalog-managed MCP-shaped integrations whose tools are implemented directly in the backend. They reuse the integration gateway and run capability model, but they do not proxy an arbitrary remote MCP server and never install a vendor client in an agent runtime.

Ownership

ConcernOwner
Display, grants, auth contract, tool policyGit agent-pack catalog
Fixed runtime key, limits, reviewed tools@oblive/types runtime registry
Credential and organization connection statePostgreSQL integration row
Non-secret resource scopeTyped integration settings JSONB
Credential decryption and provider callsBackend integration gateway/provider
Run authorizationShort-lived execution capability and epoch

Settings are part of the material authorization fingerprint. Changing a key prefix, database allowlist, or GA4 property selection advances the organization authorization epoch and clears reusable sessions. Provider credentials remain encrypted and are never projected with settings.

The shipped catalog grants Upstash, MongoDB, and Google Analytics to Operator and Chat. Engineering also receives Upstash and MongoDB, Growth also receives Google Analytics, and Growth and Support receive Lemon Squeezy. A grant alone is not readiness: the backend still requires the connector to be catalog-active, enabled, credentialed, scoped, and explicitly tooled before it emits an execution-bound capability.

Permission mode and tool selection remain gateway concerns, not provider concerns. First connection defaults to read-only and materializes the current eligible inventory. A mode-only update materializes all tools currently eligible for the target mode, while an explicit tool list is authoritative. Same-mode reconnect preserves a non-empty narrowing. A valid connection with no eligible tools remains credentialed in needs_tools, and future provider tools never expand an existing selection automatically.

Provider Boundaries

Upstash

The backend accepts an Upstash root REST URL and either all-key or literal-prefix scope. The exact URL is the database identity; no separate database ID is accepted. The credential is a write-only read-only REST token. The provider accepts only a fixed command mapping, checks every key before network I/O, rejects redirects, and bounds command count, request bytes, response bytes, concurrency, and total time. Prefix tenant:1 also matches tenant:10; use delimiter-terminated prefixes such as tenant:1: when the boundary matters.

Lemon Squeezy

Lemon Squeezy uses the official HTTPS JSON API directly. Setup validates the write-only API key, discovers at most 200 accessible stores, and materializes one to 50 selected store IDs in typed settings. “Select all” stores the current discovery snapshot; stores created later remain blocked until settings are updated. Every MCP tool schema enumerates the currently selected store IDs so an agent can resolve its authorized scope without receiving integration settings in its safe context. The backend still rechecks that scope before provider I/O.

The provider exposes 22 fixed reads for stores, customers, products, variants, prices, orders, order items, subscriptions, subscription invoices/items, and discounts. Ask before writes and Autonomous add only customer and checkout creation. Every resource operation carries a configured store ID, proves resource or parent ownership, uses explicit page-number pagination, and returns curated bounded fields. The two writes use the existing durable action lifecycle and are never retried after an ambiguous response.

Refunds, subscription changes, usage records, licenses, files and download URLs, webhooks, affiliates, checkout retrieval, and account-level user details remain unavailable. There is no community stdio package, Node runtime, arbitrary API path, raw relationship projection, or hidden fallback in the backend execution path.

MongoDB Atlas

Setup accepts a bare Atlas SRV host plus the raw database username and password. The backend validates the structured input, percent-encodes userinfo, constructs a pathless mongodb+srv credential, and stores only that canonical credential in the existing encrypted column. Settings carry the exact application database plus collection and dotted-field allowlists. The provider pins Atlas SRV, TLS, authSource: admin, appName: oblive-integration, bounded pools and timeouts, the configured database, and a read-only driver surface. It validates operators and projections, runs an explain preflight, and rejects a winning COLLSCAN before returning bounded results.

Setup resource discovery is stateless and organization scoped:

POST /organizations/{organizationId}/integrations/{integrationRef}/mongodb/resources
Content-Type: application/json

{
  "connection": {
    "clusterHost": "cluster0.example.mongodb.net",
    "username": "oblive-reader",
    "password": "write-only"
  },
  "databaseName": "application"
}

The operation authenticates, lists at most 50 supported collections, and inspects a deterministic, indexed sample of at most 20 documents per collection. Its response contains only collection names, dotted field names, sample counts, and truncation flags—never values. Existing connections may omit connection and reuse the encrypted credential. Discovery does not persist settings or advance the authorization epoch.

There is no authoritative field catalog for schemaless MongoDB documents, so discovered fields are suggestions. The UI preserves explicitly configured but unobserved paths as manual entries. “Allow all discovered collections and sampled fields” expands the current bounded result into the existing explicit settings array; it is not a wildcard and never authorizes collections created later.

Runtime operations always select the typed settings database explicitly; the pathless credential is not a second resource-scope authority. Legacy root or explicitly matching-database SRV credentials remain valid at the provider boundary.

Filters accept ordinary JSON scalars and two strict one-key Extended JSON literals:

{
  "_id": { "$gte": { "$oid": "507f1f77bcf86cd799439011" } },
  "createdAt": { "$lt": { "$date": "2026-08-11T00:00:00Z" } }
}

$oid requires exactly 24 hexadecimal characters. $date requires an ISO-8601 timestamp with a timezone. Both literals work as direct equality values, in bounded equality arrays, with $eq/$ne/$gt/$gte/$lt/$lte, and inside $in/$nin. The backend applies the field allowlist first, converts the two supported types, and then runs the existing indexed-plan preflight. Plain strings remain strings; unsupported or malformed Extended JSON and arbitrary objects are rejected rather than inferred.

Google Analytics

Google Analytics shares the deployment Google OAuth client but stores a credential on its own integration row. One-use state is bound to the google-analytics runtime. Authorization always asks for openid, email, and analytics.readonly; the client cannot select tools or broader scopes.

The Cloud project that owns the OAuth client must enable the Google Analytics Admin API (analyticsadmin.googleapis.com) for property discovery and configuration, and the Google Analytics Data API (analyticsdata.googleapis.com) for reports. The connected identity must have Viewer or higher access to every GA4 property selected for the integration.

OAuth completion stores the credential but leaves the connector disabled. The frontend then reads bounded property options from:

GET /organizations/{organizationId}/integrations/{integrationRef}/google-analytics/properties

Saving settings performs live property verification before it enables the connector:

PUT /organizations/{organizationId}/integrations/{integrationRef}/settings
Content-Type: application/json

{
  "settings": {
    "kind": "google-analytics",
    "propertyIds": ["123456789"],
    "defaultPropertyId": "123456789"
  }
}

Reauthorization also leaves Analytics disabled until this verification runs again. Disconnect clears the credential, tools, and property settings so a different identity cannot inherit stale scope.

Adding a Direct Provider

Use a direct provider when the vendor API is stable enough to expose a substantially smaller and safer surface than a general client or hosted MCP. Direct providers may include reviewed writes only when they use the existing durable action lifecycle.

  1. Add strict shared runtime, tool, settings, auth, and access-mode contracts.
  2. Store only non-secret resource scope in settings; keep credentials in the existing encrypted column and organization/integration AAD boundary.
  3. Implement TIntegrationToolProvider behind createIntegrationToolProvider. Use fixed tools, strict Zod input and response projections, bounded output, timeouts, cancellation, and close().
  4. Validate resource scope before provider I/O where possible and again against provider authority when configuration depends on identity.
  5. Keep all tools explicitly read-only or route consequential behavior through the existing action lifecycle. Do not add a second gateway or credential path.
  6. Add catalog parity, runtime dispatch, invalid-input, provider-failure, cancellation, concurrency, isolation, truncation, and credential-redaction tests.
  7. Document HTTP changes in backend OpenAPI and update both operator setup and this developer path.

Dashboard URLs supplied during onboarding are operator navigation aids only. Never persist them as runtime endpoints or infer secrets from their path identifiers.

These providers reuse the existing typed JSONB settings column, integration gateway URLs, and control-plane commands. Adding Lemon Squeezy therefore requires OpenAPI and documentation updates, but no database migration or oblivectl contract change.

Release Verification

Run the focused provider and OAuth tests, catalog tests, OpenAPI synchronization, repository typecheck/lint/format checks, and the docs build. Exercise connect, configuration, a bounded read, scope change, credential rotation, reauthorization, disable, and disconnect with separate organization rows.