How to Add Custom Addons to TREK: A Complete Implementation Guide

You can add custom addons to TREK by registering a new identifier in server/src/addons.ts, inserting a seed record in server/src/db/seeds.ts, and wrapping your backend logic with the isAddonEnabled() utility.

TREK is a modular travel planning application where features are packaged as optional addons that administrators can toggle on or off. If you need to extend TREK with proprietary features or third-party integrations, you can add custom addons to the codebase following the same architecture used for built-in features like Packing, Budget, and Collab.

Step 1: Register the Addon Identifier

Every addon in TREK is identified by a string constant defined in the central registry. Open server/src/addons.ts and add your custom addon to the ADDON_IDS object.

// server/src/addons.ts
export const ADDON_IDS = {
  MCP: 'mcp',
  PACKING: 'packing',
  BUDGET: 'budget',
  DOCUMENTS: 'documents',
  VACAY: 'vacay',
  ATLAS: 'atlas',
  COLLAB: 'collab',
  JOURNEY: 'journey',
  AIRTRAIL: 'airtrail',
  // Your custom addon
  WEATHER: 'weather',
} as const;

This constant serves as the single source of truth for your addon's identifier across the entire application.

Step 2: Seed the Database Entry

To make your addon appear in the admin panel, you must insert a record into the database. Modify server/src/db/seeds.ts to add a seed entry for the Addon entity.

// server/src/db/seeds.ts
await queryRunner.manager.insert(Addon, {
  id: ADDON_IDS.WEATHER,
  enabled: false,
  name: 'Weather',
  description: 'Show forecast for trip destinations',
});

This seed ensures the addon appears in the Admin → Addons panel with a toggle switch defaulted to off.

Step 3: Gate Backend Logic

Protect your addon's functionality by checking the activation state before executing logic. Import isAddonEnabled from server/src/addons.ts and use it to guard your services.

// server/src/services/weather/weather.service.ts
import { isAddonEnabled } from '../addons';
import { ADDON_IDS } from '../../addons';

export async function getWeatherForPlace(placeId: string) {
  if (!isAddonEnabled(ADDON_IDS.WEATHER)) {
    return null; // Add-on disabled → no data
  }
  // Fetch forecast from external API
}

This pattern mirrors the implementation in server/src/mcp/tools/collab.ts, where the Collab tool returns early if the addon is disabled.

Step 4: Register MCP Tools (Optional)

If your addon requires AI-driven access via MCP (Model Context Protocol), register tools conditionally in server/src/mcp/index.ts.

// server/src/mcp/tools/weather.ts
import { ADDON_IDS } from '../../addons';
import { isAddonEnabled } from '../../utils/addon';

export function registerWeatherTool(server: McpServer) {
  if (!isAddonEnabled(ADDON_IDS.WEATHER)) return;

  server.registerTool('weather-forecast', async ({ placeId }) => {
    const weather = await getWeatherForPlace(placeId);
    return weather ?? { error: 'Weather addon disabled' };
  });
}

Register the tool in the main MCP index file only when the addon is enabled.

Step 5: Build Frontend Components

Admin Toggle

Add a toggle row to shared/src/pages/Admin/Addons.tsx so admins can enable your addon.

// shared/src/pages/Admin/Addons.tsx
<Row key="weather">
  <Cell>{t('Weather')}</Cell>
  <Cell>{t('Show forecast for trip destinations')}</Cell>
  <Cell>
    <Switch
      checked={addons.weather.enabled}
      onChange={() => toggleAddon('weather')}
    />
  </Cell>
</Row>

Feature UI

Conditionally render your component based on the addon state. Follow the pattern used in shared/src/components/Trip/Collab.tsx.

// shared/src/components/Trip/WeatherPanel.tsx
if (!addonEnabled('weather')) return null;

return <div className="weather-panel">{/* Weather UI */}</div>;

Step 6: Run Migrations and Test

After modifying the seed file, start the development server to apply the database changes.

npm run start:dev

Navigate to Admin → Addons and enable your custom addon. Verify that:

  • The UI components appear only when enabled
  • API routes return data when active and null when inactive
  • MCP tools respond correctly via the AI interface

Summary

Frequently Asked Questions

Where are addon identifiers defined in TREK?

Addon identifiers are defined as string constants in server/src/addons.ts within the ADDON_IDS object. This central registry ensures consistent naming across the backend and frontend.

How does TREK check if an addon is enabled?

The backend uses the isAddonEnabled() utility exported from server/src/addons.ts to verify activation state. The frontend uses a similar addonEnabled() helper to conditionally render UI components.

Can I add environment variables for my custom addon?

Yes. Add any required API keys or configuration variables to your environment file and document them in wiki/Environment-Variables.md. Access them in your service code via process.env.YOUR_KEY after checking that the addon is enabled.

Do I need to restart the server after adding a new addon?

You must run the database seeding process (typically via npm run start:dev) to insert the new addon record into the database. Once seeded, toggling the addon on or off in the admin panel does not require a server restart.

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 →