Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/development/backend/database.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,29 @@ import { drizzle } from 'drizzle-orm/better-sqlite3';
import { drizzle } from 'drizzle-orm/libsql';
```

### Database Connection Patterns

When accessing the database in route handlers, always use `getDb()` to obtain the database connection dynamically:

```typescript
import { getDb } from '../../../db';
import { YourService } from '../../../services/yourService';

export default async function yourRoute(server: FastifyInstance) {
const db = getDb();
const yourService = new YourService(db);
// ... rest of route handler
}
```

**Why this pattern is required:**
- `server.db` may be `null` during certain initialization states
- `getDb()` always returns the active database connection
- This ensures consistent behavior across all endpoints
- Other working endpoints already follow this pattern

**Avoid:** Direct usage of `server.db` as it can cause "Cannot read properties of null" errors.

## Database Structure

The database schema is defined in `src/db/schema.sqlite.ts`. This is the **single source of truth** for all database schema definitions and works across all supported database types.
Expand Down
90 changes: 90 additions & 0 deletions docs/development/backend/events.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
---
title: Global Event Bus
description: Type-safe event system for decoupled communication between core systems and plugins in DeployStack.
---

# Global Event Bus

The Global Event Bus enables decoupled communication between core systems and plugins through a type-safe event system. Core systems emit events when important actions occur, and plugins can react without direct coupling to business logic.

## Overview

Key features:
- **Type Safety**: Full TypeScript integration with strongly-typed event data
- **Plugin Isolation**: Secure event listener registration with error isolation
- **Performance**: Fire-and-forget event processing
- **Security**: Events cannot be intercepted or modified by plugins

## Event Naming Convention

Events follow the `domain.action` pattern, aligned with the permission structure:

- `user.registered`, `user.login`, `user.logout`, `user.updated`, `user.deleted`
- `team.created`, `team.updated`, `team.member_added`, `team.member_removed`
- `settings.updated`, `settings.smtp_configured`, `settings.github_configured`
- `mcp.server_installed`, `mcp.server_uninstalled`, `mcp.server_configured`

## Event Data Structure

```typescript
interface EventData<T = any> {
userId?: string;
teamId?: string;
data: T;
}

interface EventContext {
db: AnyDatabase | null;
logger: FastifyBaseLogger;
user?: { id: string; email: string; roleId: string; };
request?: { ip: string; userAgent?: string; requestId: string; };
timestamp: Date;
}
```

## Event Constants

```typescript
import { EVENT_NAMES } from '../events';

EVENT_NAMES.USER_REGISTERED // 'user.registered'
EVENT_NAMES.TEAM_CREATED // 'team.created'
EVENT_NAMES.SETTINGS_UPDATED // 'settings.updated'
```

## Usage in Core Routes

Emit events after successful operations:

```typescript
// After successful user registration
server.eventBus?.emitWithContext(
EVENT_NAMES.USER_REGISTERED,
{ userId: newUser.id, data: { user: newUser, source: 'email_registration' } },
eventContext
);
```

## Plugin Event Listeners

Plugins register event listeners in their configuration:

```typescript
class EmailNotificationPlugin implements Plugin {
eventListeners: EventListeners = {
[EVENT_NAMES.USER_REGISTERED]: this.handleUserRegistered.bind(this)
};

private async handleUserRegistered(eventData: EventData, context: EventContext) {
await this.sendWelcomeEmail(eventData.data.user);
}
}
```

For detailed plugin event listener examples, see the [Plugin System Documentation](/development/backend/plugins).

## Available Events

Events are emitted for user lifecycle (`user.*`), team management (`team.*`), settings changes (`settings.*`), and MCP operations (`mcp.*`). Each event includes relevant data and context.

For complete event schemas and data structures, see the [event type definitions](https://github.com/deploystackio/deploystack/blob/main/services/backend/src/events/types.ts).
214 changes: 214 additions & 0 deletions docs/development/backend/mcp-configuration-architecture.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
---
title: MCP Configuration Architecture
description: Developer guide to DeployStack's three-tier MCP server configuration system for arguments and environment variables.
sidebar: MCP Configuration Architecture
---

# MCP Configuration Architecture

DeployStack implements a sophisticated three-tier configuration architecture for managing MCP server command line arguments and environment variables. This system supports multi-user teams while maintaining clean separation between fixed template parameters, shared team settings, and individual user configurations.

## Architecture Overview

The three-tier system separates MCP server configuration into distinct layers:

1. **Template Level** - Fixed arguments and schemas defined in the MCP catalog
2. **Team Level** - Shared team configurations and credentials
3. **User Level** - Personal configurations for individual team members

This architecture solves the fundamental challenge of supporting multiple users within the same team installation while allowing individual customization.

## Lock/Unlock Control System

The system's core feature is sophisticated lock/unlock controls that determine configuration boundaries:

**Global Administrator Controls:**
- **Categorization**: Classify every config element as Template/Team/User configurable
- **Lock States**: Set `default_team_locked` and `visible_to_users` controls
- **Security Boundaries**: Define what can never be changed vs. team/user configurable

**Team Administrator Controls:**
- **Lock/Unlock Elements**: Control what users can modify within schema boundaries
- **Credential Management**: Manage team secrets with visibility controls

**Runtime Access:**
- Users see only unlocked elements they can configure
- Locked elements are inherited but not modifiable

## Design Problem

### The Multi-User Team Challenge

Traditional MCP configurations assume a single user per installation. DeployStack's team-based approach requires supporting scenarios like:

**Team Setup:**
- Team: "DevOps Team"
- Members: User A, User B
- Devices: Each user has laptop + desktop
- Total Configurations: 4 different configurations for the same MCP server

**User Requirements:**
- User A needs access to `/Users/userA/Desktop`
- User B needs access to `/Users/userB/Desktop` and `/Users/userB/Projects`
- Both users share the same team API credentials
- Each user may have different debug settings

### Solution Architecture

The three-tier system addresses this by:

1. **Template Level**: Defines what arguments are fixed vs configurable
2. **Team Level**: Manages shared credentials and team-wide settings
3. **User Level**: Allows individual customization per user and device

## Database Schema

### Tier 1: MCP Catalog (`mcpServers`)

The catalog defines the configuration structure for each MCP server type:

```sql
-- Template Level (with lock controls)
template_args: text('template_args') -- [{value, locked, description}]
template_env: text('template_env') -- Fixed environment variables

-- Team Schema (with lock/visibility controls)
team_args_schema: text('team_args_schema') -- Schema with lock controls
team_env_schema: text('team_env_schema') -- [{name, type, required, default_team_locked, visible_to_users}]

-- User Schema
user_args_schema: text('user_args_schema') -- User-configurable argument schema
user_env_schema: text('user_env_schema') -- User-configurable environment schema
```

### Tier 2: Team Installation (`mcpServerInstallations`)

Team installations manage shared configurations:

```sql
installation_name: text('installation_name') -- Team-friendly name
team_args: text('team_args') -- Team-level arguments (JSON array)
team_env: text('team_env') -- Team environment variables (JSON object)
```

### Tier 3: User Configuration (`mcpUserConfigurations`)

Individual user configurations support multiple devices:

```sql
installation_id: text('installation_id') -- References team installation
user_id: text('user_id') -- User who owns this config
device_name: text('device_name') -- "MacBook Pro", "Work PC", etc.

user_args: text('user_args') -- User arguments (JSON array)
user_env: text('user_env') -- User environment variables (JSON object)
```

## Configuration Flow

### Runtime Assembly

### Configuration Schema Step

Global administrators categorize configuration elements through the Configuration Schema Step:

1. **Extract Elements**: Parse Claude Desktop config for all args and env vars
2. **Categorize Each Element**: Assign to Template/Team/User tiers
3. **Set Lock Controls**: Define `default_team_locked` and `visible_to_users`
4. **Generate Schema**: Create the three-tier schema structure

### Runtime Assembly

At runtime, configurations are assembled by merging all three tiers with lock/unlock controls applied:

```javascript
const assembleConfiguration = (server, teamInstallation, userConfig) => {
const finalArgs = [
...server.template_args.map(arg => arg.value), // Fixed template args
...(teamInstallation.team_args || []), // Team shared args
...(userConfig.user_args || []) // User personal args
];

const finalEnv = {
...(server.template_env || {}), // Fixed template env
...(teamInstallation.team_env || {}), // Team shared env
...(userConfig.user_env || {}) // User personal env
};

return { args: finalArgs, env: finalEnv };
};
```

## Service Layer

### McpUserConfigurationService

The service layer provides complete CRUD operations for user configurations:

**Key Methods:**
- `createUserConfiguration()` - Create new user config with validation
- `getUserConfiguration()` - Retrieve user config with team access control
- `updateUserConfiguration()` - Update with schema validation
- `deleteUserConfiguration()` - Remove user config
- `updateUserArgs()` - Partial update for arguments only
- `updateUserEnv()` - Partial update for environment variables only

**Security Features:**
- Team-based access control
- User isolation (users can only access their own configs)
- Schema validation against server-defined schemas
- Input sanitization and type checking

## API Endpoints

## API Endpoints

Configuration management through REST API:

- Team installations: `/api/teams/{teamId}/mcp/installations/`
- User configurations: `/api/teams/{teamId}/mcp/installations/{installationId}/user-configs/`
- Schema validation: Built into all endpoints

## Schema Example

Configuration schema with lock/unlock controls:

```json
{
"template_args": [
{"value": "-y", "locked": true, "description": ""},
{"value": "@modelcontextprotocol/server-memory", "locked": true, "description": ""}
],
"team_env_schema": [
{
"name": "MEMORY_FILE_PATH",
"type": "string",
"required": true,
"default_team_locked": true,
"visible_to_users": false
}
],
"user_env_schema": [
{
"name": "DEBUG_MODE",
"type": "string",
"required": false,
"locked": false
}
]
}
```



## Related Documentation

For specific implementation details:

- [Backend API](/development/backend/api) - Complete API endpoint documentation
- [Database Schema](/development/backend/database) - Database structure and relationships
- [Teams](/teams) - Team management and structure
- [MCP Configuration System](/mcp-configuration) - User-facing configuration guide
- [MCP Installation](/mcp-installation) - Installation and team setup

The three-tier configuration architecture provides a robust foundation for managing complex MCP server configurations in multi-user team environments while maintaining security, flexibility, and ease of use.
28 changes: 28 additions & 0 deletions docs/development/backend/plugins.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,33 @@ async initialize(app: FastifyInstance, db: AnyDatabase | null) {
}
```

### Event Listeners

Plugins can listen to system events and react to core application changes:

```typescript
import { EVENT_NAMES, type EventListeners } from '../events';

class MyPlugin implements Plugin {
eventListeners: EventListeners = {
[EVENT_NAMES.USER_REGISTERED]: this.handleUserRegistered.bind(this),
[EVENT_NAMES.TEAM_CREATED]: this.handleTeamCreated.bind(this)
};

private async handleUserRegistered(eventData, context) {
// React to user registration
context.logger.info(`New user: ${eventData.data.user.email}`);
}

private async handleTeamCreated(eventData, context) {
// React to team creation
await this.setupTeamResources(eventData.data.team);
}
}
```

For complete event documentation, see the [Global Event Bus](./events) guide.

### Access to Core Services

Plugins receive access to:
Expand All @@ -428,6 +455,7 @@ Plugins receive access to:
- **Logger** (`logger`) - For structured logging with plugin context
- **Schema Access** - Access to the generated database schema including your tables
- **Global Settings** - Plugins can define and access their own global settings
- **Event System** - Listen to and react to core application events

## Plugin Lifecycle

Expand Down
Loading