Connect AI clients with MCP
Give an AI client read-only access to your account, selected teams, or approved admin data.
VibeKit serves a remote MCP endpoint at /api/mcp. It uses your existing Better Auth account and a separate OAuth grant for each approved connection. A client receives read-only tools; it cannot change accounts, billing, feedback, settings, or credentials.
Install before starting the app
The JWT and OAuth plugins need their database tables during auth initialization, including ordinary session lookup. Apply migrations before running the updated application:
bun install
bun run db:generate
bun run --cwd packages/database db:migrate
bun run buildReview and authorize production migrations before running them. The MCP migration is 20260905000000_mcp_oauth; it adds signing keys, OAuth storage, connection grants, and access events. It does not replace the existing user or session tables.
If you see public.jwks does not exist, the application is running against a database without the migration. Check the actual database target. If older tables were created with schema push and migration history is incomplete, reconcile that history before running pending migrations. Do not reset a populated database or blindly rerun existing table-creation migrations.
Set NEXT_PUBLIC_SITE_URL to the final origin and keep BETTER_AUTH_SECRET stable. HTTPS is required outside loopback development. The auth issuer, callback handling, resource metadata, and access-token audience all derive from this origin.
Optional setup with your agent
During site setup, your agent can help connect an AI client if you want to use MCP. You can decline or defer it and finish the rest of setup. The agent records your choice, checks the site URL and database prerequisites, registers the chosen client, and tests a permitted read after you approve access.
You complete sign-in and consent with your own verified account. Admin permissions are optional and start unchecked. Select only the teams and site-wide data the client needs. The agent can use authorized setup tools to configure the connection; the site's MCP tools themselves cannot change settings, credentials, roles, or deployments.
Skipping this step creates no client connection. It does not disable /api/mcp or remove the OAuth tables, which the current auth configuration also needs during ordinary sign-in.
Disabling remote access
Products that should not expose the remote interface set MCP_ENABLED=false. This gates tool discovery and execution at /api/mcp, the consent endpoint and consent/login pages, new MCP OAuth authorization/token operations, and the OAuth discovery metadata — not just the settings link. Ordinary password, social, and OTP sign-in keep working, and the OAuth tables stay migrated.
Disable policy: existing grant rows are preserved but their use is suspended while disabled. Explicit revocations stay revoked, so re-enabling never reactivates a previously revoked connection. Connection management remains available to inspect and revoke old grants.
Set up Codex
Use the installed Codex client's MCP settings or CLI. Check codex mcp add --help for the flags supported by your version. Choose a name for this site and substitute its actual URL below. Inspect an existing entry with codex mcp get my-site before adding another one.
codex mcp add my-site \
--url https://your-domain.example/api/mcp \
--oauth-resource https://your-domain.example/api/mcp \
--oauth-client-registration cimdFor local development, use http://localhost:3000/api/mcp for both URLs, matching NEXT_PUBLIC_SITE_URL. Codex starts OAuth and opens an authorization page. Complete sign-in, review the permissions and selected teams, then allow access. A saved server entry alone does not mean login succeeded.
If the callback times out, restart login instead of reusing the old URL. This example requests profile and selected-team reads with refresh access:
codex mcp login my-site --oauth-client-registration cimd \
--scopes profile:read,teams:read,offline_accessFor site-wide account and status reads, request profile:read,admin:users:read,admin:site:read,offline_access instead, then explicitly check those admin permissions on the consent page. Add admin:feedback:read only if you want site-wide feedback access. Your account must already have the admin role. Each approval creates a separate grant; remove obsolete grants from Connected apps.
After login succeeds, refresh Codex's MCP inventory if needed. Ask it to call vibekit_get_me and vibekit_list_teams, or vibekit_admin_get_stats if you approved admin:site:read. Confirm that each call succeeds and reflects the granted access. A tool result marked isError is a failure even if the HTTP request succeeds. Sign-in and consent use the browser; ordinary tool calls do not need browser automation.
For a temporary test connection, revoke access at /auth/mcp/connections and confirm the client can no longer read. Keep your intended working connection. Removing the Codex entry with codex mcp remove my-site removes local configuration; use the site's revocation control to revoke the server-side grant.
Connect an app
- Add
https://your-domain.example/api/mcpto your MCP client. - Sign in through the site’s normal login flow.
- Review the app name and requested permissions. Admin permissions start unchecked.
- Select the teams the app may read, then choose Allow access.
- Open Settings → Connected apps to inspect or revoke the connection.
Accounts without a team can manage connections at /auth/mcp/connections. Administrators can switch to All site connections on the same page and revoke another user's connection.
Access tokens last five minutes. Connections and refresh tokens last at most 30 days. Revoking a connection blocks its access tokens and refresh attempts. Account bans, administrator demotion, removed team memberships, ended login sessions, deleted consent, and disabled OAuth clients are checked against current database state.
A new connection creates a separate grant. Approving another connection does not expand an existing grant. Revoke older connections you no longer use.
Permissions
| Scope | Data shared |
|---|---|
profile:read | Your profile and the connection's permissions. |
teams:read | Selected teams you still belong to, their members, and dashboard summaries. |
billing:read | Selected-team subscription summaries and available plans. |
feedback:read | Your own feedback, including private submissions. |
notifications:read | Your personal notifications and notifications for selected teams. |
content:read | Enabled public feedback, roadmap items, and release notes. |
admin:users:read | Safe account fields across the site. |
admin:feedback:read | Feedback across the site, including private submissions. |
admin:site:read | Site counts, enabled modules, and integration configuration status. |
offline_access | Refresh access while the connection and associated login session remain valid. |
Admin scopes require a current administrator account. They apply site-wide to their listed data categories. Ordinary team scopes remain limited to selected memberships even when the person is an administrator. Impersonated sessions cannot approve connections.
Available tools
All tool names start with vibekit_. The client discovers only tools allowed by its scopes and the site's enabled modules.
- Identity:
get_me. - Teams:
list_teams,get_team,list_team_members,get_team_dashboard. - Billing:
get_team_subscription,list_plans. - Feedback:
list_my_feedback,get_my_feedback,list_public_feedback,get_public_feedback,get_roadmap. - Notifications:
list_notifications,get_unread_count. Notification results contain ID, type, link, creation time, and read status; raw notification data is excluded. - Releases:
list_releases. - Administration:
admin_list_users,admin_get_user,admin_list_feedback,admin_get_feedback,admin_get_stats,admin_get_settings,admin_get_integration_status.
Lists accept limit and offset where paging applies. The default limit is 25 and the maximum is 100. nextOffset: null means the result has no next page. Feedback and user lists support bounded search; feedback also supports a status filter.
Tool results include structured JSON and matching text. Account results exclude credentials and session data. Feedback results include the submission and official response; they do not download private attachments or expose raw request metadata. Integration status does not return keys or trigger provider handshakes.
Example requests:
- “List the teams this connection can read and show the current plan for the first one.”
- “Find my private feedback about exports and read its official response.”
- “As an administrator, find unverified accounts using the site's user tools.”
Client compatibility
The server uses the official TypeScript SDK v2 with stateless handling for both current and legacy requests. OAuth client discovery uses Better Auth's Client ID Metadata Documents plugin with the MCP 2026-07-28 profile. Dynamic client registration is disabled. Clients must support metadata documents or use a pre-registered OAuth client.
The provider's server-only adminCreateOAuthClient API can register a known client. Use exact redirect URLs, token_endpoint_auth_method: "none" for a public client, PKCE, and only the read scopes needed. Loopback callbacks require application_type: "native". Registration is an administrative action; the subsequent user consent still limits data access.
Metadata is available at /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server/api/auth. Requests use POST /api/mcp; session-oriented GET and DELETE operations are not supported. Browser requests must use the configured site origin. Server-side and native clients may omit Origin.
Limits and audit records
Requests are limited to 64 KiB and results to 100,000 characters. Reads are rate-limited per user, per client/user pair, and by the existing request-identity policy. A rate-limit response includes Retry-After. Without a trusted proxy configuration, unknown network clients share a rate-limit bucket.
McpAccessEvent records the grant, user, client, tool, outcome, timestamp, and request ID. It does not store tokens or result bodies. Retain them with bun run maintenance:prune-mcp (access events older than 90 days, tokens expired over 7 days ago, expired client assertions; grants and consents are kept for the account/team deletion lifecycle). It supports --dry-run and --events-days; no new background scheduler is installed.
Tested clients
Verified against a local build: Codex connected over OAuth, discovered 15 tools, and completed five authenticated reads (get_me, list_teams, list_plans, list_notifications, get_unread_count), then lost access after revocation. That proves the documented flow for that client build only — test every client version you advertise before claiming universal support.
Verify an installation
Run the normal checks:
bun run lint
bun x turbo type-check
bun run test
bun run test:i18n-parity
bun run buildThe OAuth integration test requires a disposable local PostgreSQL database whose name starts with vibekit_mcp_. Apply all migrations to that database, then run:
MCP_TEST_DATABASE=true bun test \
packages/api/mcp/mcp.integration.test.mjs \
packages/auth/lib/mcp-initialization.integration.test.mjsSet DATABASE_URL to that disposable database and NEXT_PUBLIC_SITE_URL to a free loopback port before the command. The test starts an HTTP server at that origin. It checks actual sign-in, OAuth consent, PKCE token exchange, read tools, role and membership changes, and revocation.
The same endpoint can be inspected with the official MCP Inspector. Test the actual client applications you plan to support before advertising compatibility. Local protocol tests do not prove a production deployment or a connection in every client.
The pinned @better-auth/cimd package has a Bun patch that disables Node’s automatic address selection after the transport has resolved and validated DNS. This preserves connection pinning and fixes metadata fetches in Node. Verify it with node packages/auth/lib/mcp-metadata.check.mjs; the check fetches Codex’s public metadata and rejects a private address. Keep the patch until an upstream version passes this check without it.
Extend the integration
Tool registration and safe data projections live in packages/api/mcp/server.ts. Reuse the existing tRPC caller where its contract fits; keep additional queries bounded and explicitly scoped. The shared tRPC middleware rejects mutations for MCP identities.
Create OAuth plugins with createMcpAuthPlugins() for each auth instance. Better Auth mutates extension state during initialization; reusing plugin instances can register cimd twice during development reloads. The initialization regression test covers this case.
OAuth configuration and grant checks live in packages/auth/lib/mcp*.ts. The consent handler binds the selected grant to the provider through request-local storage, so parallel consent screens do not share team selections. Authorization checks run on the server; tool annotations are descriptive hints.
The repository’s add-auth-flow-or-better-auth-plugin skill includes the admin MCP setup workflow, and vibekit-deploy routes optional setup to it. Add a separate task-specific skill only when recurring work needs guidance beyond the tool descriptions. It must use the same authorized endpoint and contain no credentials.