πŸ’° Calculate your AI costsπŸ“… Book a 30-min DemoπŸ“ž Call/text (571) 293-0242

MCP & OAuth Architecture

Backend architecture reference for the MCP server and OAuth connector subsystems, covering the data model, control flow, validation, and extensibility.


Overview

This document targets backend engineers extending or operating the MCP server and OAuth connector subsystems. It summarizes the data model, control flow, and key modules involved in provisioning external integrations.

For client-facing API usage, see:


Component Map

LayerResponsibilityKey Modules
PersistenceStore providers, services, connections, and user tokensOauthProvider, OauthService, ConnectedService, MCPServer, MCPServerConnection
API (Accounts)Orchestrate OAuth flows, expose discovery endpoints, manage connected servicesAccount views and URLs
API (Mentor)CRUD for MCP servers and connectionsMCPServerViewSet, MCPServerConnectionViewSet
Chat ConsumerDetect missing credentials, drive the in-chat OAuth handshakeChat WebSocket/SSE consumer, MCPOAuthCoordinator
LangChain RuntimeResolve connections at inference time, build headersMCPServer.resolve_connection, LangChain tools
UtilitiesCredential store access, token refresh logicget_cred, connected services utilities

Data Model Relationships

  • OauthProvider exposes one or more OauthService entries.
  • OauthService grants ConnectedService records (per user, per platform).
  • MCPServer is registered on a Platform and has one or more MCPServerConnection entries.
  • MCPServerConnection optionally uses a ConnectedService (for OAuth-backed auth) and can be scoped to a Mentor.
  • ConnectedService has a unique constraint on (user, provider, platform, service) to enforce one connection per surface.

auth_type vs auth_scope on MCPServer

Two orthogonal fields drive credential selection:

FieldValuesSemantics
auth_typenone, token, oauth2How credentials are presented on the wire.
auth_scopeplatform, mentor, userWhose credentials are used. Controls whether the chat prompts for in-chat OAuth.

auth_scope="user" combined with auth_type="oauth2" is the trigger for the real-time in-chat OAuth handshake.

Connection scope rules (on MCPServerConnection)

  • scope="platform" β€” platform is auto-filled from the server's platform (unless the server is featured).
  • scope="user" β€” requires user or connected_service.
  • scope="mentor" β€” requires a mentor; optionally reuses platform-level credentials when no mentor-specific ones exist.
  • auth_type="oauth2" β€” always requires connected_service.

OAuth Flow Internals

Discovery

The service-list view surfaces enabled OauthService records. A scope endpoint narrows down to a specific service's scopes.

Start

The start view resolves scopes, builds the provider adapter, and stores a UUID-based hash in the cache to guard the callback. Cache entries expire after one hour to limit replay windows.

Callback

The callback view verifies state, exchanges the code for a token, normalizes the scope/service identifier, and syncs metadata. Tokens are stored raw (access_token, refresh_token, token_type) and lowercased for schema consistency.

Refresh

Downstream modules call the refresh function, which uses the provider adapter to refresh tokens and re-sync metadata.

Error handling is intentionally strict: multiple services per connection raise ValueError early; missing tenant credentials return 404 / 400 responses.


MCP Server Runtime Resolution

LangChain Tool -> MCPServer.resolve_connection(platform, user, mentor)
                   |
                   |- User-scoped connection exists?
                   |    YES -> Load connection -> Optional ConnectedService (if oauth2)
                   |
                   |- Mentor-scoped connection exists?
                   |    YES -> Use mentor credentials
                   |
                   |- Platform-scoped connection exists?
                   |    YES -> Use stored platform credentials
                   |
                   |- Server is featured?
                   |    YES -> Use global connection
                   |    NO  -> Trigger in-chat OAuth if auth_scope="user"
                   |           otherwise fail with 401 / no connection
                   |
                   -> render_headers(access_token?)
                      If oauth2: ensure fresh access token (refresh if needed)
                      Return: authorization headers

Async code paths use aresolve_connection() with .afirst() queries to avoid blocking the event loop. Headers are merged with extra_headers, and explicit credentials override anything pre-existing.


In-Chat OAuth Handshake

When a resolution would fail and the server is configured with auth_scope="user" + auth_type="oauth2", the chat consumer pauses the turn instead of erroring. The coordinator:

  1. Builds the OAuth authorization URL for the current user.
  2. Emits an oauth_required event to the frontend over the existing WebSocket/SSE channel.
  3. Polls the database every 10 seconds for a newly created ConnectedService and MCPServerConnection.
  4. On success, emits oauth_connection_resolved and resumes chat processing.
  5. On timeout (MCP_OAUTH_MAX_WAIT_SECONDS, default 300s), raises a ChatValidationError which surfaces to the client as an error event.

See In-Chat MCP Events for the message schemas and recommended client handling.


Validation and Security

Credential sourcing

get_cred("auth_{provider}", tenant) is the single source of truth for client IDs and secrets. Tenant-specific overrides live alongside global (tenant="main") credentials. The serializer guard ensures the admin has installed the necessary client credentials before connections are created.

State integrity

Cache entries expire after one hour. The callback splits state into (org, provider, service, user_id, hash) and verifies the cached value before exchanging the token.

Access control

  • OAuth endpoints restrict methods (GET, DELETE) to read-only client operations, leaving creation solely to the managed OAuth flow.
  • MCP endpoints require tenant admin privileges for all create / update / delete operations.

Extensibility

Adding a new OAuth provider or service

  1. Seed OauthProvider and OauthService records via the management command.
  2. Provision client credentials (auth_{provider}) in the credential store.
  3. Update the front-end to surface the new service β€” the API is discovery-driven, so minimal UI changes are needed.

Extending MCP authentication types

  1. Add a new enum member to MCPServer.AuthType.
  2. Extend connection validation to handle the new type.
  3. Update render_headers() to format the outbound headers.
  4. Update front-end forms and documentation.

Supporting multi-tenant shared servers

Mark the server is_featured=true. Platform admins in other tenants can then create their own connections, while the original tenant retains control of the server metadata.

Copyright Β© ibl.ai | support@iblai.zendesk.com