# Multi-Tenant MCP Server Architecture
**Version**: 1.0
**Created**: 2026-01-02
**Status**: Planning
---
## Table of Contents
1. [Vision](#vision)
2. [Architecture Overview](#architecture-overview)
3. [Three-Layer Authentication Model](#three-layer-authentication-model)
4. [Access Tiers](#access-tiers)
5. [Route Structure](#route-structure)
6. [Configuration Schema](#configuration-schema)
7. [Data Model](#data-model)
8. [Tool Authorization System](#tool-authorization-system)
9. [Dashboard Features](#dashboard-features)
10. [Security Considerations](#security-considerations)
11. [Implementation Phases](#implementation-phases)
12. [Migration from Current Architecture](#migration-from-current-architecture)
---
## Vision
Transform the MCP server template from a single-user tool into an **enterprise-grade multi-tenant MCP SaaS framework** that supports:
- Multiple identity providers for different user types
- Shared backend services configured by admins
- Per-user service connections
- Self-service user dashboard alongside admin dashboard
- Flexible tool authorization based on auth context
**Target Use Cases**:
- Internal team tools with shared resources
- B2B SaaS MCP servers with per-customer isolation
- Public MCP servers with optional user accounts
- Enterprise deployments with Microsoft Entra SSO
---
## Architecture Overview
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ MULTI-TENANT MCP SERVER ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ MCP CLIENT │ │ USER DASHBOARD │ │ ADMIN DASHBOARD │ │
│ │ (Tool Access) │ │ (Self-Service) │ │ (Full Control) │ │
│ │ │ │ │ │ │ │
│ │ Claude.ai │ │ /dashboard │ │ /admin │ │
│ │ Claude Code │ │ Own data only │ │ All data │ │
│ │ API clients │ │ Tool testing │ │ Configuration │ │
│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │ │
│ │ SAME IDENTITY │ │ │
│ └──────────┬─────────┘ │ │
│ │ │ │
│ ┌──────────┴──────────┐ ┌─────────┴─────────┐ │
│ │ USER IDENTITY │ │ ADMIN IDENTITY │ │
│ │ (Layer 1 - Users) │ │ (Layer 1 - Admin)│ │
│ └──────────┬──────────┘ └─────────┬─────────┘ │
│ │ │ │
│ ════════════════════╪══════════════════════════════╪════════════════════════ │
│ │ │ │
│ ┌──────────┴──────────────────────────────┴──────────┐ │
│ │ LAYER 2: SHARED SERVICES │ │
│ │ (Admin-configured resources) │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Team │ │ Service │ │ Company │ │ Shared │ │ │
│ │ │ Calendar│ │ API Key │ │ CRM │ │ Database│ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ LAYER 3: USER SERVICES │ │
│ │ (Per-user connected accounts) │ │
│ │ │ │
│ │ User A: [Google Cal] [Xero] [GitHub] │ │
│ │ User B: [Google Cal] [QuickBooks] [Notion] │ │
│ │ User C: [Outlook] [Xero] │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
```
---
## Three-Layer Authentication Model
### Layer 1: Identity ("Who are you?")
Authenticates users and admins. Determines which person is making requests.
| Aspect | User Identity | Admin Identity |
|--------|---------------|----------------|
| **Purpose** | Identify MCP users and dashboard users | Identify server administrators |
| **Providers** | Google, Microsoft, GitHub, or None | Google, Microsoft, GitHub |
| **Storage** | User record in D1, session in KV | Session in KV |
| **Grants access to** | MCP tools, User Dashboard | Admin Dashboard |
| **Can be same provider** | Yes - admin is just a user with elevated privileges via ADMIN_EMAILS |
### Layer 2: Shared Services ("What can everyone use?")
Admin-configured credentials that power tools for ALL users.
| Aspect | Details |
|--------|---------|
| **Configured by** | Admin only |
| **Visible to users** | No (credentials hidden) |
| **Examples** | Team Google Calendar, Synergy Wholesale API key, Company Xero account |
| **Storage** | KV with encryption |
| **Use case** | "All users can check the team calendar" |
### Layer 3: User Services ("What can THIS user access?")
Per-user OAuth connections to external services.
| Aspect | Details |
|--------|---------|
| **Configured by** | Each user (self-service) |
| **Visible to** | Only that user |
| **Examples** | User's personal Google Calendar, User's Xero account |
| **Storage** | D1 with encryption, per user_id |
| **Use case** | "Check MY calendar" vs "Check the team calendar" |
---
## Access Tiers
### Tier 1: MCP Client Access
**Route**: `/mcp`, `/authorize`, `/callback`
**Auth**: User Identity (or none if public)
**Purpose**: Execute MCP tools via Claude.ai, Claude Code, or API
```
Claude.ai ──► /authorize ──► Identity Provider ──► /callback ──► /mcp (tools)
```
### Tier 2: User Dashboard Access
**Route**: `/dashboard/*`
**Auth**: User Identity (same as MCP)
**Purpose**: Self-service portal for users
Features:
- View own profile and sessions
- Connect personal services (Layer 3)
- Test tools interactively
- View own chat history
- Usage/quota information
### Tier 3: Admin Dashboard Access
**Route**: `/admin/*`
**Auth**: Admin Identity + ADMIN_EMAILS check
**Purpose**: Full server management
Features:
- All user dashboard features (for own account)
- View all users and sessions
- Configure shared services (Layer 2)
- Manage API tokens
- Server configuration
- Usage analytics across all users
---
## Route Structure
```
/ Public homepage
│
├── /mcp MCP protocol endpoint (SSE/WebSocket)
├── /authorize OAuth start for MCP clients
├── /callback OAuth callback for MCP clients
├── /register Dynamic client registration
│
├── /dashboard User dashboard (requires user auth)
│ ├── /dashboard/login User OAuth start
│ ├── /dashboard/callback User OAuth callback
│ ├── /dashboard/profile User profile
│ ├── /dashboard/services User service connections (Layer 3)
│ ├── /dashboard/tools Tool explorer and tester
│ ├── /dashboard/sessions User's MCP sessions
│ └── /dashboard/chat User's AI chat
│
├── /admin Admin dashboard (requires admin auth)
│ ├── /admin/login Admin OAuth start
│ ├── /admin/callback Admin OAuth callback
│ ├── /admin/users User management
│ ├── /admin/services Shared service config (Layer 2)
│ ├── /admin/tokens API token management
│ ├── /admin/analytics Usage analytics
│ └── /admin/chat AI chat (all sessions)
│
└── /api API endpoints
├── /api/auth/me Current user info
├── /api/user/* User API (user auth)
└── /api/admin/* Admin API (admin auth)
```
---
## Configuration Schema
### Environment Variables (wrangler.jsonc)
```jsonc
{
"vars": {
// ═══════════════════════════════════════════════════════════════════
// USER AUTHENTICATION (MCP Client + User Dashboard)
// ═══════════════════════════════════════════════════════════════════
// How users authenticate to use MCP tools
"USER_AUTH_MODE": "oauth", // "oauth" | "token" | "none"
// Which OAuth provider for users (if USER_AUTH_MODE=oauth)
"USER_IDENTITY_PROVIDER": "google", // "google" | "microsoft" | "github"
// ═══════════════════════════════════════════════════════════════════
// ADMIN AUTHENTICATION (Admin Dashboard)
// ═══════════════════════════════════════════════════════════════════
// Which OAuth provider for admins
"ADMIN_IDENTITY_PROVIDER": "google", // "google" | "microsoft" | "github"
// Email addresses with admin access (comma-separated)
"ADMIN_EMAILS": "admin@example.com,team@example.com",
// ═══════════════════════════════════════════════════════════════════
// FEATURE FLAGS
// ═══════════════════════════════════════════════════════════════════
"ENABLE_USER_DASHBOARD": "true", // Show /dashboard for users
"ENABLE_USER_SERVICES": "true", // Allow users to connect services (L3)
"ENABLE_ADMIN_CHAT": "true", // AI chat in admin dashboard
"ENABLE_USER_CHAT": "true", // AI chat in user dashboard
// ═══════════════════════════════════════════════════════════════════
// EXISTING SETTINGS
// ═══════════════════════════════════════════════════════════════════
"ENABLE_CONVERSATION_MEMORY": "true",
"CONVERSATION_TTL_HOURS": "168",
"MAX_CONTEXT_MESSAGES": "50",
"ENABLE_INTERNAL_AGENT": "true",
"INTERNAL_AGENT_MODEL": "@cf/qwen/qwen2.5-coder-32b-instruct"
}
}
```
### Secrets (per provider)
```bash
# Google (if used for users OR admin)
wrangler secret put GOOGLE_CLIENT_ID
wrangler secret put GOOGLE_CLIENT_SECRET
# Microsoft (if used for users OR admin)
wrangler secret put MICROSOFT_CLIENT_ID
wrangler secret put MICROSOFT_CLIENT_SECRET
wrangler secret put MICROSOFT_TENANT_ID # Optional, defaults to "common"
# GitHub - needs separate apps due to callback URL limitation
# For MCP/User flows (callback: /callback or /dashboard/callback)
wrangler secret put GITHUB_CLIENT_ID
wrangler secret put GITHUB_CLIENT_SECRET
# For Admin flows (callback: /admin/callback)
wrangler secret put GITHUB_ADMIN_CLIENT_ID
wrangler secret put GITHUB_ADMIN_CLIENT_SECRET
# Encryption key for stored tokens
wrangler secret put TOKEN_ENCRYPTION_KEY
# AI Gateway (for chat features)
wrangler secret put CF_AIG_TOKEN
```
### Example Configurations
**Scenario A: Internal team tool**
```jsonc
{
"USER_AUTH_MODE": "oauth",
"USER_IDENTITY_PROVIDER": "github", // Team uses GitHub
"ADMIN_IDENTITY_PROVIDER": "google", // Admin uses company Google
"ADMIN_EMAILS": "jeremy@jezweb.net"
}
```
**Scenario B: Public MCP with admin**
```jsonc
{
"USER_AUTH_MODE": "none", // Anyone can use tools
"ADMIN_IDENTITY_PROVIDER": "google", // Admin protected
"ADMIN_EMAILS": "admin@example.com",
"ENABLE_USER_DASHBOARD": "false" // No user dashboard if no auth
}
```
**Scenario C: Enterprise B2B**
```jsonc
{
"USER_AUTH_MODE": "oauth",
"USER_IDENTITY_PROVIDER": "microsoft", // Customers use Entra
"ADMIN_IDENTITY_PROVIDER": "google", // Internal team uses Google
"ADMIN_EMAILS": "team@company.com"
}
```
**Scenario D: Same provider for all**
```jsonc
{
"USER_AUTH_MODE": "oauth",
"USER_IDENTITY_PROVIDER": "google",
"ADMIN_IDENTITY_PROVIDER": "google", // Same - just email-restricted
"ADMIN_EMAILS": "admin@example.com"
}
```
---
## Data Model
### D1 Database Schema
```sql
-- ═══════════════════════════════════════════════════════════════════
-- USERS (Layer 1 - Identity)
-- ═══════════════════════════════════════════════════════════════════
CREATE TABLE users (
id TEXT PRIMARY KEY, -- Provider-specific ID
email TEXT NOT NULL UNIQUE,
name TEXT,
picture TEXT,
identity_provider TEXT NOT NULL, -- google, microsoft, github
created_at INTEGER NOT NULL,
last_seen_at INTEGER NOT NULL,
is_active INTEGER DEFAULT 1 -- Admin can disable users
);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_provider ON users(identity_provider);
-- ═══════════════════════════════════════════════════════════════════
-- USER SERVICE TOKENS (Layer 3 - Per-user external service auth)
-- ═══════════════════════════════════════════════════════════════════
CREATE TABLE user_service_tokens (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
service TEXT NOT NULL, -- google_calendar, xero, notion, etc.
access_token_encrypted TEXT NOT NULL,
refresh_token_encrypted TEXT,
expires_at INTEGER,
scopes TEXT, -- Space-separated scopes
service_user_id TEXT, -- ID in the external service
service_email TEXT, -- Email in the external service
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
UNIQUE(user_id, service)
);
CREATE INDEX idx_user_service_tokens_user ON user_service_tokens(user_id);
CREATE INDEX idx_user_service_tokens_service ON user_service_tokens(service);
-- ═══════════════════════════════════════════════════════════════════
-- USER SESSIONS (MCP and Dashboard sessions)
-- ═══════════════════════════════════════════════════════════════════
CREATE TABLE user_sessions (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
client_type TEXT NOT NULL, -- claude_ai, claude_code, dashboard, api
client_info TEXT, -- JSON: user agent, IP (hashed), etc.
created_at INTEGER NOT NULL,
last_activity_at INTEGER NOT NULL,
expires_at INTEGER NOT NULL
);
CREATE INDEX idx_user_sessions_user ON user_sessions(user_id);
CREATE INDEX idx_user_sessions_expires ON user_sessions(expires_at);
-- ═══════════════════════════════════════════════════════════════════
-- TOOL EXECUTIONS (Audit log)
-- ═══════════════════════════════════════════════════════════════════
CREATE TABLE tool_executions (
id TEXT PRIMARY KEY,
user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
session_id TEXT,
tool_name TEXT NOT NULL,
auth_type TEXT, -- none, shared, user
service_used TEXT, -- Which Layer 2/3 service
input_hash TEXT, -- Hash of input (not actual data)
success INTEGER NOT NULL,
error_message TEXT,
duration_ms INTEGER,
created_at INTEGER NOT NULL
);
CREATE INDEX idx_tool_executions_user ON tool_executions(user_id);
CREATE INDEX idx_tool_executions_tool ON tool_executions(tool_name);
CREATE INDEX idx_tool_executions_created ON tool_executions(created_at);
-- ═══════════════════════════════════════════════════════════════════
-- CHAT SESSIONS (Existing, extended)
-- ═══════════════════════════════════════════════════════════════════
-- Existing chat_sessions table, add user_id if not present
-- ALTER TABLE chat_sessions ADD COLUMN user_id TEXT REFERENCES users(id);
```
### KV Storage Schema
```
# Admin sessions (existing)
admin_session:{session_id} → { id, email, name, picture, provider, expiresAt }
# User sessions (new)
user_session:{session_id} → { id, userId, email, name, picture, provider, expiresAt }
# OAuth state (existing)
oauth_state:{state} → { type: "mcp"|"user"|"admin", redirectUri, codeVerifier, ... }
# Shared services - Layer 2 (new)
shared_service:{service_name} → {
type: "api_key" | "oauth" | "service_account",
credentials_encrypted: "...",
configured_by: "admin@example.com",
configured_at: 1234567890,
last_verified_at: 1234567890
}
# API tokens (existing)
auth_token:{token_id} → { id, label, token, createdAt, createdBy }
auth_tokens_list → [{ id, label, ... }]
# Rate limiting
rate_limit:{key} → { count, resetAt }
```
---
## Tool Authorization System
### Tool Definition with Auth
```typescript
interface ToolDefinition {
name: string;
description: string;
inputSchema: JSONSchema;
// NEW: Authorization requirements
auth?: {
// What type of auth this tool needs
type: 'none' | 'shared' | 'user' | 'user_or_shared';
// Which service (for shared/user types)
service?: string; // e.g., 'google_calendar', 'xero'
// Is auth required or optional?
required?: boolean; // default: true
// Scopes needed (for user OAuth services)
scopes?: string[];
};
}
```
### Auth Type Behaviors
| Type | Behavior |
|------|----------|
| `none` | Tool works without any service auth |
| `shared` | Uses admin-configured Layer 2 service |
| `user` | Uses user's Layer 3 connected service |
| `user_or_shared` | Tries user's service first, falls back to shared |
### Example Tool Definitions
```typescript
// No auth needed
{
name: 'echo',
description: 'Echo back the input',
auth: { type: 'none' }
}
// Uses shared service (admin-configured)
{
name: 'check_team_calendar',
description: 'Check the team calendar for availability',
auth: {
type: 'shared',
service: 'google_calendar',
required: true
}
}
// Uses user's own service
{
name: 'check_my_calendar',
description: 'Check your personal calendar',
auth: {
type: 'user',
service: 'google_calendar',
scopes: ['https://www.googleapis.com/auth/calendar.readonly'],
required: true
}
}
// Flexible - tries user first, falls back to shared
{
name: 'create_invoice',
description: 'Create an invoice in Xero',
auth: {
type: 'user_or_shared',
service: 'xero',
required: true
}
}
```
### Tool Execution Flow
```
User calls tool
│
▼
Check tool.auth.type
│
├── none ──────────────────────► Execute tool
│
├── shared ────► Get shared service credentials
│ │
│ ├── Found ──► Execute with shared creds
│ └── Missing ──► Error: "Admin must configure {service}"
│
├── user ──────► Get user's service token
│ │
│ ├── Found + valid ──► Execute with user creds
│ ├── Found + expired ──► Refresh token ──► Execute
│ └── Missing ──► Error: "Connect your {service} in dashboard"
│
└── user_or_shared ──► Try user first
│
├── User has token ──► Execute with user creds
└── No user token ──► Fall back to shared
│
├── Shared exists ──► Execute
└── No shared ──► Error
```
---
## Dashboard Features
### User Dashboard (`/dashboard`)
```
┌─────────────────────────────────────────────────────────────────────┐
│ MY MCP SERVER [Profile ▼] [Logout]│
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Profile │ │ Services │ │ Tools │ │ Chat │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ═══════════════════════════════════════════════════════════════ │
│ │
│ PROFILE TAB: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ [Avatar] Jeremy Dawes │ │
│ │ jeremy@jezweb.net │ │
│ │ Signed in with Google │ │
│ │ │ │
│ │ Member since: January 2026 │ │
│ │ Last active: Today │ │
│ │ Sessions: 3 active │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ SERVICES TAB: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Connected Services │ │
│ │ │ │
│ │ [✓] Google Calendar [Disconnect] │ │
│ │ jeremy@gmail.com │ │
│ │ Connected 3 days ago │ │
│ │ │ │
│ │ [ ] Xero [Connect] │ │
│ │ Not connected │ │
│ │ │ │
│ │ [ ] Notion [Connect] │ │
│ │ Not connected │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ TOOLS TAB: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Available Tools [Test Tool] │ │
│ │ │ │
│ │ check_my_calendar [Requires: Google Calendar ✓] │ │
│ │ create_invoice [Requires: Xero ✗] │ │
│ │ echo [No auth required] │ │
│ │ get_weather [Uses shared service] │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
### Admin Dashboard (`/admin`) - Extended
```
┌─────────────────────────────────────────────────────────────────────┐
│ MCP SERVER ADMIN [Profile ▼] [Logout]│
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ │
│ │ Over- │ │ Users │ │Shared │ │Tokens │ │ Tools │ │ Chat │ │
│ │ view │ │ │ │Service│ │ │ │ │ │ │ │
│ └───────┘ └───────┘ └───────┘ └───────┘ └───────┘ └───────┘ │
│ │
│ ═══════════════════════════════════════════════════════════════ │
│ │
│ SHARED SERVICES TAB (NEW): │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Layer 2: Shared Service Configuration │ │
│ │ │ │
│ │ These credentials are used by all users for shared tools. │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Google Calendar (Service Account) │ │ │
│ │ │ Status: ✓ Connected │ │ │
│ │ │ Calendar: team@company.com │ │ │
│ │ │ Last verified: 2 hours ago │ │ │
│ │ │ [Test Connection] [Reconfigure] [Remove] │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Weather API │ │ │
│ │ │ Status: ✓ Active │ │ │
│ │ │ Type: API Key │ │ │
│ │ │ [Test Connection] [Update Key] [Remove] │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ [+ Add Shared Service] │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ USERS TAB (NEW): │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ User Management [Export] [Invite] │ │
│ │ │ │
│ │ ┌────────┬────────────────┬──────────┬─────────┬────────┐ │ │
│ │ │ Status │ User │ Provider │ Sessions│ Actions│ │ │
│ │ ├────────┼────────────────┼──────────┼─────────┼────────┤ │ │
│ │ │ ✓ │ jeremy@jez... │ Google │ 3 │ [···] │ │ │
│ │ │ ✓ │ team@example.. │ GitHub │ 1 │ [···] │ │ │
│ │ │ ✗ │ old@user.com │ Google │ 0 │ [···] │ │ │
│ │ └────────┴────────────────┴──────────┴─────────┴────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
---
## Security Considerations
### Credential Storage
| Data | Storage | Encryption |
|------|---------|------------|
| Identity tokens (Layer 1) | KV (session) | Not persisted long-term |
| Shared service creds (Layer 2) | KV | AES-256-GCM |
| User service tokens (Layer 3) | D1 | AES-256-GCM |
| Encryption key | Cloudflare Secret | N/A |
### Encryption Implementation
```typescript
// Use Web Crypto API (available in Workers)
async function encrypt(plaintext: string, key: CryptoKey): Promise<string> {
const iv = crypto.getRandomValues(new Uint8Array(12));
const encoded = new TextEncoder().encode(plaintext);
const ciphertext = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
key,
encoded
);
// Return iv + ciphertext as base64
return btoa(String.fromCharCode(...iv, ...new Uint8Array(ciphertext)));
}
```
### Access Control
```typescript
// Middleware pattern
async function requireUser(c: Context, next: Next) {
const session = await getUserSession(c.req, c.env.OAUTH_KV);
if (!session) {
return c.redirect('/dashboard/login');
}
c.set('user', session);
return next();
}
async function requireAdmin(c: Context, next: Next) {
const session = await getAdminSession(c.req, c.env.OAUTH_KV);
if (!session || !isAdmin(session.email, c.env.ADMIN_EMAILS)) {
return c.redirect('/admin/login');
}
c.set('adminUser', session);
return next();
}
```
### Audit Logging
All tool executions logged with:
- User ID (if authenticated)
- Tool name
- Auth type used
- Success/failure
- Timestamp
- Duration
Sensitive data (inputs/outputs) NOT logged.
---
## Implementation Phases
### Phase 3a: Separate Identity Providers
**Complexity**: Medium
**Estimate**: 2-3 hours
- [ ] Rename `IDENTITY_PROVIDER` to `USER_IDENTITY_PROVIDER`
- [ ] Add `ADMIN_IDENTITY_PROVIDER` config
- [ ] Add `USER_AUTH_MODE` (oauth/token/none)
- [ ] Support separate GitHub OAuth apps (`GITHUB_ADMIN_*`)
- [ ] Update handler factory for dual routing
- [ ] Update SESSION.md
**Files to modify**:
- `src/types.ts`
- `src/index.ts`
- `src/oauth/handler-factory.ts`
- `src/oauth/github-handler.ts`
- `wrangler.jsonc`
### Phase 3b: User Dashboard (Basic)
**Complexity**: Medium
**Estimate**: 3-4 hours
- [ ] Create `/dashboard` route structure
- [ ] User session management (separate from admin)
- [ ] User profile page
- [ ] User sessions list
- [ ] Basic dashboard UI (HTML, similar to admin)
- [ ] `/api/user/*` endpoints
**Files to create**:
- `src/dashboard/routes.ts`
- `src/dashboard/session.ts`
- `src/dashboard/ui.ts`
- `src/dashboard/middleware.ts`
### Phase 3c: Layer 2 - Shared Services
**Complexity**: High
**Estimate**: 4-6 hours
- [ ] Shared service KV schema
- [ ] Encryption utilities
- [ ] Admin UI for service configuration
- [ ] Service connection testing
- [ ] API key storage
- [ ] OAuth service account storage
**Files to create**:
- `src/services/shared/index.ts`
- `src/services/shared/encryption.ts`
- `src/services/shared/types.ts`
- `src/admin/services-ui.ts`
### Phase 3d: Layer 3 - User Services
**Complexity**: High
**Estimate**: 4-6 hours
- [ ] D1 schema for user_service_tokens
- [ ] User service OAuth flows
- [ ] Token refresh automation
- [ ] User dashboard service connection UI
- [ ] Service disconnection
**Files to create**:
- `src/services/user/index.ts`
- `src/services/user/oauth.ts`
- `src/dashboard/services-ui.ts`
- `migrations/003_user_services.sql`
### Phase 3e: Tool Authorization System
**Complexity**: Medium
**Estimate**: 2-3 hours
- [ ] Extend tool definition schema
- [ ] Auth check middleware for tools
- [ ] Credential injection for tool execution
- [ ] Error messages for missing auth
- [ ] Tool explorer in dashboard (shows auth requirements)
**Files to modify**:
- `src/tools/index.ts`
- `src/tools/types.ts`
- `src/mcp/tool-executor.ts`
---
## Migration from Current Architecture
### Breaking Changes
| Current | New | Migration |
|---------|-----|-----------|
| `IDENTITY_PROVIDER` | `USER_IDENTITY_PROVIDER` + `ADMIN_IDENTITY_PROVIDER` | Rename in wrangler.jsonc |
| Single GitHub OAuth app | Potentially two (if GitHub used for both) | Create second app if needed |
| Admin-only dashboard | Admin + User dashboards | New routes, backward compatible |
### Backward Compatibility
- If only `IDENTITY_PROVIDER` is set (old config), default both USER and ADMIN to that value
- Existing admin sessions remain valid
- Existing API tokens remain valid
- No database migration needed for Phase 3a/3b
### Migration Steps
1. Deploy Phase 3a with backward-compatible defaults
2. Update wrangler.jsonc to use new variable names
3. Redeploy
4. Test both user and admin flows
5. Proceed to Phase 3b+
---
## Open Questions
1. **User quotas**: Should we implement usage limits per user?
2. **Billing integration**: Should Layer 3 services have cost tracking?
3. **Multi-org**: Should users belong to organizations?
4. **API rate limits**: Different limits for user vs admin vs shared?
5. **Webhook support**: Notify external systems on events?
---
## References
- [MCP Protocol Specification](https://modelcontextprotocol.io/)
- [Cloudflare Workers OAuth Provider](https://github.com/cloudflare/workers-oauth-provider)
- [Cloudflare D1 Documentation](https://developers.cloudflare.com/d1/)
- [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API)