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
- Register the identifier in
server/src/addons.tsusing theADDON_IDSconstant object - Seed the database in
server/src/db/seeds.tsto make the addon toggleable in the admin panel - Gate backend logic using
isAddonEnabled(ADDON_IDS.YOUR_ADDON)to protect features from running when disabled - Register MCP tools conditionally in
server/src/mcp/index.tsfor AI integration - Implement frontend toggles in
shared/src/pages/Admin/Addons.tsxand feature components usingaddonEnabled()checks
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →