---
title: "Database"
description: "Store OAuth tokens and triggers in your database with ORM adapters"
---
The Integrate SDK can persist OAuth provider tokens and scheduled triggers in your database instead of relying on hand-written callbacks for every CRUD operation. This enables:

- **Multi-device sync**: Access tokens across devices and sessions
- **Server-side usage**: Use tokens in API routes and background jobs
- **Long-term persistence**: Tokens survive browser clears and device changes
- **Trigger storage**: Schedule tool executions with database-backed triggers

## Adapters vs callbacks

You can configure persistence in two ways:

| Approach | When to use |
|----------|-------------|
| **Database adapter** (recommended) | Drizzle, Prisma, or MongoDB. Same callback shape as hand-written token storage |
| **Raw callbacks** | Custom storage, legacy setups, or non-standard schemas |

With adapters, pass `database` to `createMCPServer`. Token and trigger CRUD are handled for you. You still provide **`getSessionContext`** to map requests to a `userId` (session cookie, Clerk, service headers, etc.).

```typescript
import { createMCPServer, githubIntegration } from "integrate-sdk/server";
import { drizzleAdapter } from "integrate-sdk/adapters/drizzle";
import { db } from "./db";
import { providerToken, trigger } from "./db/schema";

export const { client } = createMCPServer({
  apiKey: process.env.INTEGRATE_API_KEY,
  integrations: [githubIntegration({ scopes: ["repo"] })],
  database: drizzleAdapter(db, {
    provider: "pg",
    schema: { providerToken, trigger },
  }),
  getSessionContext: async (req) => {
    const session = await auth.api.getSession({ headers: req.headers });
    return session?.user?.id ? { userId: session.user.id } : undefined;
  },
});
```

Explicit callbacks on `createMCPServer` still work and **override** adapter defaults when both are set.

## Connected-provider tool loading

When using a database adapter, pair token storage with [connected-only tool discovery](/docs/guides/mcp-tool-scoping):

```typescript
import { getVercelAITools } from "integrate-sdk/server";

const tools = await getVercelAITools(serverClient, {
  context: { userId: session.user.id },
  connectedOnly: true,
  mode: "code",
});
```

Use `listConnectedProviders` from `integrate-sdk/server` when you need the connected provider list outside AI helpers.

## Guides

- [Drizzle](/docs/database/drizzle): PostgreSQL, MySQL, SQLite
- [Prisma](/docs/database/prisma): `@prisma/client`
- [MongoDB](/docs/database/mongodb): native driver collections
- [Schema](/docs/database/schema): canonical tables and indexes
- [Triggers](/docs/database/triggers): composing `onComplete` and `getCallbackUrl`
- [Hooks](/docs/database/hooks): cache invalidation, identity resolution, trigger authorization

## Examples

Runnable Next.js examples for each database adapter live in the [examples repository](https://github.com/integratedotdev/examples):

| Adapter | Example |
|---------|---------|
| Drizzle | [`database/drizzle-example`](https://github.com/integratedotdev/examples/tree/main/database/drizzle-example) |
| Prisma | [`database/prisma-example`](https://github.com/integratedotdev/examples/tree/main/database/prisma-example) |
| MongoDB | [`database/mongodb-example`](https://github.com/integratedotdev/examples/tree/main/database/mongodb-example) |

Each example connects GitHub OAuth, persists tokens with the adapter, and lists repositories from the browser.

## What stays app-specific

`getSessionContext` cannot be fully abstracted. Each app wires its auth library or internal service headers. Optional [hooks](/docs/database/hooks) cover app-specific behavior (cache revalidation, GitHub email resolution, trigger ownership repair) without forking the adapter.

## Next steps

- [OAuth authorization](/docs/getting-started/basic-usage)
- [Triggers (getting started)](/docs/getting-started/triggers)
- [Built-in Integrations](/docs/integrations)
