TREK Addons System Architecture: How Feature Modules Are Implemented

The TREK addons system uses a centralized identifier registry, database-backed state management, and runtime feature gating to enable or disable optional modules like MCP, AirTrail, and Journey thumbnails from a single admin panel.

The mauriceboe/TREK repository implements a modular addons architecture that allows administrators to toggle optional features without code changes. This system separates core functionality from extended capabilities through a consistent pattern of identifier constants, database persistence, and conditional execution checks. Understanding the TREK addons system architecture reveals how the application maintains a lightweight core while supporting extensible features such as Model Context Protocol (MCP) integration, AI parsing, and collaboration tools.

Core Components of the Addons Architecture

Add-on Identifiers

All supported addons are enumerated in a central constant object. The server/src/addons.ts file exports ADDON_IDS, which serves as the single source of truth for addon references throughout the codebase. Every addon—whether MCP, AIRTRAIL, JOURNEY, LLM_PARSING, COLLAB, or PACKING—is defined here with a stable string identifier.

Using constants prevents typos and ensures type safety when checking addon status across different services. Any code that needs to verify if a feature is active imports ADDON_IDS from this file rather than using raw strings.

Database Schema

The system persists addon state in two primary tables: addons for core modules and photo_providers for photo-provider specific addons. Each row contains three critical columns: id (matching the ADDON_IDS constant), enabled (boolean flag), and config (JSON column for feature-specific settings).

During application startup, server/src/db/seeds.ts populates the addons table with rows for every key defined in ADDON_IDS. This seeding ensures that all potential features exist in the database with default disabled states, ready for activation through the admin interface.

Admin Service API

The server/src/services/adminService.ts file provides the central API for addon management through three key functions:

  • isAddonEnabled(id): Returns a boolean by querying the database (lines 66-68)
  • listAddons(): Retrieves the complete addon list including configuration objects (lines 71-84)
  • updateAddon(id, data): Toggles the enabled flag or updates the JSON config column (lines 33-51)

All runtime feature checks route through isAddonEnabled, creating a single bottleneck for permission validation. When updating the LLM_PARSING addon, updateAddon additionally handles encryption for stored API keys before persistence.

Runtime Feature Gating Implementation

Feature gating occurs at multiple architectural layers. Services, controllers, and MCP tools check addon status before executing protected logic.

HTTP Route Protection: In server/src/nest/platform/platform.routes.ts, MCP API endpoints check isAddonEnabled(ADDON_IDS.MCP) before registration (lines 123, 169, 191). If disabled, the routes return 404 or skip mounting entirely.

Service-Level Guards: The AirTrail synchronization service in server/src/services/airtrail/airtrailSync.ts validates ADDON_IDS.AIRTRAIL at line 21 before proceeding with sync operations. Similarly, the thumbnail generation service in server/src/services/memories/thumbnailService.ts checks ADDON_IDS.JOURNEY at line 15.

MCP Tool Registration: Individual MCP tools in server/src/mcp/tools/*.ts verify addon status before registering their handlers. The collab tools check ADDON_IDS.COLLAB (line 11), while packing tools validate ADDON_IDS.PACKING (lines 22-24).

MCP Integration and Conditional Registration

The Model Context Protocol layer implements dynamic resource registration based on addon state. During bootstrap, server/src/mcp/index.ts iterates through available addons, registering resources and prompts only for those marked enabled.

For example, when ADDON_IDS.PACKING is active, the system registers packing list prompts and budget resources. If disabled, these MCP capabilities remain unavailable to connected clients. This conditional registration prevents exposing tools for features that lack backend support.

Admin Control and Configuration Flow

The architecture follows a predictable lifecycle from startup to runtime toggling:

  1. Database Seeding: Application initialization runs seeds.ts, inserting rows for every ADDON_IDS value with enabled: false by default
  2. Runtime Validation: Incoming requests trigger isAddonEnabled checks, which query the current database state
  3. Conditional Execution: Enabled addons proceed with normal logic; disabled addons return early or throw 404 errors
  4. Admin Toggling: Administrators use the PATCH /admin/addons/:id endpoint, which routes to updateAddon to modify the enabled flag or update JSON configuration
  5. UI Synchronization: The frontend fetches the current state via listAddons() to conditionally render navigation tabs and feature panels

Practical Implementation Examples

Checking Addon Status in a Service

When implementing a feature that depends on an optional addon, import the identifiers and admin service:

import { ADDON_IDS } from '../../addons';
import { isAddonEnabled } from '../services/adminService';

export async function syncAirTrail() {
  if (!isAddonEnabled(ADDON_IDS.AIRTRAIL)) {
    // Feature disabled - exit early
    return false;
  }
  // Proceed with AirTrail synchronization logic
  const data = await fetchAirTrailData();
  return processData(data);
}

Reference: server/src/services/airtrail/airtrailSync.ts (line 21)

Enabling Addons Programmatically

To toggle an addon from administrative code or tests:

import { ADDON_IDS } from '../addons';
import { updateAddon } from '../services/adminService';

// Enable the packing addon
await updateAddon(ADDON_IDS.PACKING, { enabled: true });

// Configure with specific settings
await updateAddon(ADDON_IDS.LLM_PARSING, { 
  enabled: true, 
  config: { apiKey: 'encrypted_value' } 
});

Reference: server/src/services/adminService.ts (lines 33-51)

Conditional MCP Resource Registration

MCP servers register capabilities only when their corresponding addon is active:

import { ADDON_IDS } from '../../addons';
import { isAddonEnabled } from '../services/adminService';
import { mcpServer } from './server';

if (isAddonEnabled(ADDON_IDS.PACKING)) {
  mcpServer.registerResource('packing-lists', {
    // Resource definition
  });
}

if (isAddonEnabled(ADDON_IDS.BUDGET)) {
  mcpServer.registerResource('budget-tracking', {
    // Resource definition
  });
}

Reference: server/src/mcp/resources.ts (line 110 and related sections)

Frontend Conditional Rendering

The client-side application queries the addon state to determine UI visibility:

// React component example
const [addons, setAddons] = useState([]);

useEffect(() => {
  fetch('/api/admin/addons')
    .then(r => r.json())
    .then(setAddons);
}, []);

const hasBudget = addons.find(a => 
  a.id === ADDON_IDS.BUDGET && a.enabled
);

return (
  <nav>
    {hasBudget && <Tab label="Budget" to="/budget" />}
  </nav>
);

The endpoint returns the array constructed by listAddons() from adminService.ts.

Summary

  • Centralized Registry: server/src/addons.ts maintains ADDON_IDS as the single source of truth for all optional features
  • Database Persistence: The addons table stores enabled flags and JSON configuration, seeded automatically at startup
  • Uniform API: adminService.ts provides isAddonEnabled, listAddons, and updateAddon for all state management needs
  • Layered Gating: Features are guarded at the HTTP route, service method, and MCP registration levels using consistent identifier checks
  • Dynamic MCP Exposure: Model Context Protocol resources register conditionally based on realtime addon state
  • Admin Control: The PATCH /admin/addons/:id endpoint enables runtime toggling without application restarts

Frequently Asked Questions

How does TREK handle addon configuration storage?

The addons table includes a JSON config column that stores feature-specific settings. When updateAddon is called with configuration data, it merges the new values into this JSON field. For sensitive data like API keys in the LLM_PARSING addon, the service applies encryption before saving to the database.

What happens when an addon is disabled at runtime?

When isAddonEnabled returns false, the calling code typically returns early from functions or throws 404 errors for HTTP routes. For MCP integrations, disabled addons simply skip resource registration during server initialization. The frontend removes corresponding navigation elements by filtering the list returned from listAddons().

Can third-party developers create custom addons for TREK?

The current architecture in mauriceboe/TREK supports the predefined set of addons listed in ADDON_IDS. While the database schema and adminService.ts could theoretically support dynamic addon IDs, the codebase currently references specific ADDON_IDS constants throughout the feature gating logic. Extending the system would require adding new identifiers to server/src/addons.ts and implementing corresponding guard checks in the relevant services.

Where is the addon state checked most frequently?

The most critical checks occur in server/src/nest/platform/platform.routes.ts for MCP API exposure, server/src/services/airtrail/airtrailSync.ts for external integrations, and server/src/mcp/tools/*.ts for AI tool availability. Every check imports ADDON_IDS from the central registry and queries the database through isAddonEnabled to ensure consistent behavior across the application.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →