Skip to content

Commit 553f8c3

Browse files
authored
Merge pull request #203 from deploystackio/main
Enhance device management
2 parents 98ef749 + b666687 commit 553f8c3

15 files changed

+1693
-53
lines changed

docs/development/backend/database.mdx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,29 @@ import { drizzle } from 'drizzle-orm/better-sqlite3';
147147
import { drizzle } from 'drizzle-orm/libsql';
148148
```
149149

150+
### Database Connection Patterns
151+
152+
When accessing the database in route handlers, always use `getDb()` to obtain the database connection dynamically:
153+
154+
```typescript
155+
import { getDb } from '../../../db';
156+
import { YourService } from '../../../services/yourService';
157+
158+
export default async function yourRoute(server: FastifyInstance) {
159+
const db = getDb();
160+
const yourService = new YourService(db);
161+
// ... rest of route handler
162+
}
163+
```
164+
165+
**Why this pattern is required:**
166+
- `server.db` may be `null` during certain initialization states
167+
- `getDb()` always returns the active database connection
168+
- This ensures consistent behavior across all endpoints
169+
- Other working endpoints already follow this pattern
170+
171+
**Avoid:** Direct usage of `server.db` as it can cause "Cannot read properties of null" errors.
172+
150173
## Database Structure
151174

152175
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.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
title: Global Event Bus
3+
description: Type-safe event system for decoupled communication between core systems and plugins in DeployStack.
4+
---
5+
6+
# Global Event Bus
7+
8+
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.
9+
10+
## Overview
11+
12+
Key features:
13+
- **Type Safety**: Full TypeScript integration with strongly-typed event data
14+
- **Plugin Isolation**: Secure event listener registration with error isolation
15+
- **Performance**: Fire-and-forget event processing
16+
- **Security**: Events cannot be intercepted or modified by plugins
17+
18+
## Event Naming Convention
19+
20+
Events follow the `domain.action` pattern, aligned with the permission structure:
21+
22+
- `user.registered`, `user.login`, `user.logout`, `user.updated`, `user.deleted`
23+
- `team.created`, `team.updated`, `team.member_added`, `team.member_removed`
24+
- `settings.updated`, `settings.smtp_configured`, `settings.github_configured`
25+
- `mcp.server_installed`, `mcp.server_uninstalled`, `mcp.server_configured`
26+
27+
## Event Data Structure
28+
29+
```typescript
30+
interface EventData<T = any> {
31+
userId?: string;
32+
teamId?: string;
33+
data: T;
34+
}
35+
36+
interface EventContext {
37+
db: AnyDatabase | null;
38+
logger: FastifyBaseLogger;
39+
user?: { id: string; email: string; roleId: string; };
40+
request?: { ip: string; userAgent?: string; requestId: string; };
41+
timestamp: Date;
42+
}
43+
```
44+
45+
## Event Constants
46+
47+
```typescript
48+
import { EVENT_NAMES } from '../events';
49+
50+
EVENT_NAMES.USER_REGISTERED // 'user.registered'
51+
EVENT_NAMES.TEAM_CREATED // 'team.created'
52+
EVENT_NAMES.SETTINGS_UPDATED // 'settings.updated'
53+
```
54+
55+
## Usage in Core Routes
56+
57+
Emit events after successful operations:
58+
59+
```typescript
60+
// After successful user registration
61+
server.eventBus?.emitWithContext(
62+
EVENT_NAMES.USER_REGISTERED,
63+
{ userId: newUser.id, data: { user: newUser, source: 'email_registration' } },
64+
eventContext
65+
);
66+
```
67+
68+
## Plugin Event Listeners
69+
70+
Plugins register event listeners in their configuration:
71+
72+
```typescript
73+
class EmailNotificationPlugin implements Plugin {
74+
eventListeners: EventListeners = {
75+
[EVENT_NAMES.USER_REGISTERED]: this.handleUserRegistered.bind(this)
76+
};
77+
78+
private async handleUserRegistered(eventData: EventData, context: EventContext) {
79+
await this.sendWelcomeEmail(eventData.data.user);
80+
}
81+
}
82+
```
83+
84+
For detailed plugin event listener examples, see the [Plugin System Documentation](/development/backend/plugins).
85+
86+
## Available Events
87+
88+
Events are emitted for user lifecycle (`user.*`), team management (`team.*`), settings changes (`settings.*`), and MCP operations (`mcp.*`). Each event includes relevant data and context.
89+
90+
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).
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
---
2+
title: MCP Configuration Architecture
3+
description: Developer guide to DeployStack's three-tier MCP server configuration system for arguments and environment variables.
4+
sidebar: MCP Configuration Architecture
5+
---
6+
7+
# MCP Configuration Architecture
8+
9+
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.
10+
11+
## Architecture Overview
12+
13+
The three-tier system separates MCP server configuration into distinct layers:
14+
15+
1. **Template Level** - Fixed arguments and schemas defined in the MCP catalog
16+
2. **Team Level** - Shared team configurations and credentials
17+
3. **User Level** - Personal configurations for individual team members
18+
19+
This architecture solves the fundamental challenge of supporting multiple users within the same team installation while allowing individual customization.
20+
21+
## Lock/Unlock Control System
22+
23+
The system's core feature is sophisticated lock/unlock controls that determine configuration boundaries:
24+
25+
**Global Administrator Controls:**
26+
- **Categorization**: Classify every config element as Template/Team/User configurable
27+
- **Lock States**: Set `default_team_locked` and `visible_to_users` controls
28+
- **Security Boundaries**: Define what can never be changed vs. team/user configurable
29+
30+
**Team Administrator Controls:**
31+
- **Lock/Unlock Elements**: Control what users can modify within schema boundaries
32+
- **Credential Management**: Manage team secrets with visibility controls
33+
34+
**Runtime Access:**
35+
- Users see only unlocked elements they can configure
36+
- Locked elements are inherited but not modifiable
37+
38+
## Design Problem
39+
40+
### The Multi-User Team Challenge
41+
42+
Traditional MCP configurations assume a single user per installation. DeployStack's team-based approach requires supporting scenarios like:
43+
44+
**Team Setup:**
45+
- Team: "DevOps Team"
46+
- Members: User A, User B
47+
- Devices: Each user has laptop + desktop
48+
- Total Configurations: 4 different configurations for the same MCP server
49+
50+
**User Requirements:**
51+
- User A needs access to `/Users/userA/Desktop`
52+
- User B needs access to `/Users/userB/Desktop` and `/Users/userB/Projects`
53+
- Both users share the same team API credentials
54+
- Each user may have different debug settings
55+
56+
### Solution Architecture
57+
58+
The three-tier system addresses this by:
59+
60+
1. **Template Level**: Defines what arguments are fixed vs configurable
61+
2. **Team Level**: Manages shared credentials and team-wide settings
62+
3. **User Level**: Allows individual customization per user and device
63+
64+
## Database Schema
65+
66+
### Tier 1: MCP Catalog (`mcpServers`)
67+
68+
The catalog defines the configuration structure for each MCP server type:
69+
70+
```sql
71+
-- Template Level (with lock controls)
72+
template_args: text('template_args') -- [{value, locked, description}]
73+
template_env: text('template_env') -- Fixed environment variables
74+
75+
-- Team Schema (with lock/visibility controls)
76+
team_args_schema: text('team_args_schema') -- Schema with lock controls
77+
team_env_schema: text('team_env_schema') -- [{name, type, required, default_team_locked, visible_to_users}]
78+
79+
-- User Schema
80+
user_args_schema: text('user_args_schema') -- User-configurable argument schema
81+
user_env_schema: text('user_env_schema') -- User-configurable environment schema
82+
```
83+
84+
### Tier 2: Team Installation (`mcpServerInstallations`)
85+
86+
Team installations manage shared configurations:
87+
88+
```sql
89+
installation_name: text('installation_name') -- Team-friendly name
90+
team_args: text('team_args') -- Team-level arguments (JSON array)
91+
team_env: text('team_env') -- Team environment variables (JSON object)
92+
```
93+
94+
### Tier 3: User Configuration (`mcpUserConfigurations`)
95+
96+
Individual user configurations support multiple devices:
97+
98+
```sql
99+
installation_id: text('installation_id') -- References team installation
100+
user_id: text('user_id') -- User who owns this config
101+
device_name: text('device_name') -- "MacBook Pro", "Work PC", etc.
102+
103+
user_args: text('user_args') -- User arguments (JSON array)
104+
user_env: text('user_env') -- User environment variables (JSON object)
105+
```
106+
107+
## Configuration Flow
108+
109+
### Runtime Assembly
110+
111+
### Configuration Schema Step
112+
113+
Global administrators categorize configuration elements through the Configuration Schema Step:
114+
115+
1. **Extract Elements**: Parse Claude Desktop config for all args and env vars
116+
2. **Categorize Each Element**: Assign to Template/Team/User tiers
117+
3. **Set Lock Controls**: Define `default_team_locked` and `visible_to_users`
118+
4. **Generate Schema**: Create the three-tier schema structure
119+
120+
### Runtime Assembly
121+
122+
At runtime, configurations are assembled by merging all three tiers with lock/unlock controls applied:
123+
124+
```javascript
125+
const assembleConfiguration = (server, teamInstallation, userConfig) => {
126+
const finalArgs = [
127+
...server.template_args.map(arg => arg.value), // Fixed template args
128+
...(teamInstallation.team_args || []), // Team shared args
129+
...(userConfig.user_args || []) // User personal args
130+
];
131+
132+
const finalEnv = {
133+
...(server.template_env || {}), // Fixed template env
134+
...(teamInstallation.team_env || {}), // Team shared env
135+
...(userConfig.user_env || {}) // User personal env
136+
};
137+
138+
return { args: finalArgs, env: finalEnv };
139+
};
140+
```
141+
142+
## Service Layer
143+
144+
### McpUserConfigurationService
145+
146+
The service layer provides complete CRUD operations for user configurations:
147+
148+
**Key Methods:**
149+
- `createUserConfiguration()` - Create new user config with validation
150+
- `getUserConfiguration()` - Retrieve user config with team access control
151+
- `updateUserConfiguration()` - Update with schema validation
152+
- `deleteUserConfiguration()` - Remove user config
153+
- `updateUserArgs()` - Partial update for arguments only
154+
- `updateUserEnv()` - Partial update for environment variables only
155+
156+
**Security Features:**
157+
- Team-based access control
158+
- User isolation (users can only access their own configs)
159+
- Schema validation against server-defined schemas
160+
- Input sanitization and type checking
161+
162+
## API Endpoints
163+
164+
## API Endpoints
165+
166+
Configuration management through REST API:
167+
168+
- Team installations: `/api/teams/{teamId}/mcp/installations/`
169+
- User configurations: `/api/teams/{teamId}/mcp/installations/{installationId}/user-configs/`
170+
- Schema validation: Built into all endpoints
171+
172+
## Schema Example
173+
174+
Configuration schema with lock/unlock controls:
175+
176+
```json
177+
{
178+
"template_args": [
179+
{"value": "-y", "locked": true, "description": ""},
180+
{"value": "@modelcontextprotocol/server-memory", "locked": true, "description": ""}
181+
],
182+
"team_env_schema": [
183+
{
184+
"name": "MEMORY_FILE_PATH",
185+
"type": "string",
186+
"required": true,
187+
"default_team_locked": true,
188+
"visible_to_users": false
189+
}
190+
],
191+
"user_env_schema": [
192+
{
193+
"name": "DEBUG_MODE",
194+
"type": "string",
195+
"required": false,
196+
"locked": false
197+
}
198+
]
199+
}
200+
```
201+
202+
203+
204+
## Related Documentation
205+
206+
For specific implementation details:
207+
208+
- [Backend API](/development/backend/api) - Complete API endpoint documentation
209+
- [Database Schema](/development/backend/database) - Database structure and relationships
210+
- [Teams](/teams) - Team management and structure
211+
- [MCP Configuration System](/mcp-configuration) - User-facing configuration guide
212+
- [MCP Installation](/mcp-installation) - Installation and team setup
213+
214+
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.

docs/development/backend/plugins.mdx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,33 @@ async initialize(app: FastifyInstance, db: AnyDatabase | null) {
419419
}
420420
```
421421

422+
### Event Listeners
423+
424+
Plugins can listen to system events and react to core application changes:
425+
426+
```typescript
427+
import { EVENT_NAMES, type EventListeners } from '../events';
428+
429+
class MyPlugin implements Plugin {
430+
eventListeners: EventListeners = {
431+
[EVENT_NAMES.USER_REGISTERED]: this.handleUserRegistered.bind(this),
432+
[EVENT_NAMES.TEAM_CREATED]: this.handleTeamCreated.bind(this)
433+
};
434+
435+
private async handleUserRegistered(eventData, context) {
436+
// React to user registration
437+
context.logger.info(`New user: ${eventData.data.user.email}`);
438+
}
439+
440+
private async handleTeamCreated(eventData, context) {
441+
// React to team creation
442+
await this.setupTeamResources(eventData.data.team);
443+
}
444+
}
445+
```
446+
447+
For complete event documentation, see the [Global Event Bus](./events) guide.
448+
422449
### Access to Core Services
423450

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

432460
## Plugin Lifecycle
433461

0 commit comments

Comments
 (0)