feat(mcp-server): add 19 platform ops tools — flags.*, jobs.*, maintenance.*, settings.*, webhooks.*

flags (5): list, get, upsert (create-or-update), delete, killSwitch
jobs (4): list, get, trigger, listRuns
maintenance (3): getCurrent (admin full), set, scheduleWindow
settings (3): get, update, checkKillSwitch
webhooks (7): listSubscriptions, create, get, update, delete, listDeliveries, test, rotateSecret

All backed by existing platform-service endpoints — no new backend changes.
MCP server now has 84 tools across 16 namespaces.
This commit is contained in:
saravanakumardb1 2026-03-05 13:46:15 -08:00
parent 33dd530d5f
commit 0f57a48168
4 changed files with 780 additions and 1 deletions

View File

@ -528,3 +528,284 @@ export function trackerPublicStats(opts: PlatformClientOptions): Promise<{
totalVotes: number;
}>('/api/public/roadmap/stats', { method: 'GET' }, opts);
}
// ── Flags ─────────────────────────────────────────────────────────────────────
export interface FeatureFlagDoc {
id: string;
productId: string;
key: string;
enabled: boolean;
percentage: number;
description?: string;
platforms: string[];
regions?: string[];
createdAt: string;
updatedAt: string;
}
export function flagsList(opts: PlatformClientOptions): Promise<{ flags: FeatureFlagDoc[] }> {
return platformFetch<{ flags: FeatureFlagDoc[] }>('/api/flags', { method: 'GET' }, opts);
}
export function flagsGet(key: string, opts: PlatformClientOptions): Promise<FeatureFlagDoc> {
return platformFetch<FeatureFlagDoc>(
`/api/flags/${encodeURIComponent(key)}`,
{ method: 'GET' },
opts
);
}
export async function flagsUpsert(
key: string,
input: {
enabled: boolean;
percentage?: number;
description?: string;
platforms?: string[];
regions?: string[];
},
opts: PlatformClientOptions
): Promise<FeatureFlagDoc> {
// Try update first; fall back to create if not found
try {
return await platformFetch<FeatureFlagDoc>(
`/api/flags/${encodeURIComponent(key)}`,
{ method: 'PUT', body: JSON.stringify(input) },
opts
);
} catch (err) {
if (err instanceof Error && err.message.includes('404')) {
return platformFetch<FeatureFlagDoc>(
'/api/flags',
{ method: 'POST', body: JSON.stringify({ key, ...input }) },
opts
);
}
throw err;
}
}
export function flagsDelete(
key: string,
opts: PlatformClientOptions
): Promise<{ success: boolean }> {
return platformFetch<{ success: boolean }>(
`/api/flags/${encodeURIComponent(key)}`,
{ method: 'DELETE' },
opts
);
}
export function flagsKillSwitch(
input: { platform?: string; keys?: string[] },
opts: PlatformClientOptions
): Promise<{ disabled: string[]; count: number }> {
return platformFetch<{ disabled: string[]; count: number }>(
'/api/flags/kill',
{ method: 'POST', body: JSON.stringify(input) },
opts
);
}
// ── Jobs ──────────────────────────────────────────────────────────────────────
export function jobsList(opts: PlatformClientOptions): Promise<unknown[]> {
return platformFetch<unknown[]>('/api/jobs', { method: 'GET' }, opts);
}
export function jobsGet(id: string, opts: PlatformClientOptions): Promise<unknown> {
return platformFetch<unknown>(`/api/jobs/${encodeURIComponent(id)}`, { method: 'GET' }, opts);
}
export function jobsTrigger(jobName: string, opts: PlatformClientOptions): Promise<unknown> {
return platformFetch<unknown>(
'/api/jobs/trigger',
{ method: 'POST', body: JSON.stringify({ jobName }) },
opts
);
}
export function jobsListRuns(
name: string,
limit: number,
opts: PlatformClientOptions
): Promise<unknown> {
return platformFetch<unknown>(
`/api/jobs/${encodeURIComponent(name)}/runs?limit=${limit}`,
{ method: 'GET' },
opts
);
}
// ── Maintenance ───────────────────────────────────────────────────────────────
export function maintenanceGetCurrent(opts: PlatformClientOptions): Promise<unknown> {
return platformFetch<unknown>('/api/settings/maintenance/full', { method: 'GET' }, opts);
}
export function maintenanceSet(
input: {
mode: 'none' | 'scheduled' | 'active' | 'emergency';
message?: string;
affectedServices?: string[];
scheduledStart?: string;
scheduledEnd?: string;
},
opts: PlatformClientOptions
): Promise<unknown> {
return platformFetch<unknown>(
'/api/settings/maintenance',
{ method: 'PUT', body: JSON.stringify(input) },
opts
);
}
export function maintenanceScheduleWindow(
input: {
title: string;
message: string;
mode: string;
scheduledStart: string;
scheduledEnd: string;
affectedServices?: string[];
},
opts: PlatformClientOptions
): Promise<unknown> {
return platformFetch<unknown>(
'/api/settings/maintenance/schedule',
{ method: 'POST', body: JSON.stringify(input) },
opts
);
}
// ── Settings ──────────────────────────────────────────────────────────────────
export function settingsGet(opts: PlatformClientOptions): Promise<unknown> {
return platformFetch<unknown>('/api/settings', { method: 'GET' }, opts);
}
export function settingsUpdate(
settings: Record<string, unknown>,
opts: PlatformClientOptions
): Promise<unknown> {
return platformFetch<unknown>(
'/api/settings',
{ method: 'PUT', body: JSON.stringify({ settings }) },
opts
);
}
export function settingsCheckKillSwitch(
productId: string,
opts: Pick<PlatformClientOptions, 'requestId'>
): Promise<{ enabled: boolean; disabled: boolean; message: string }> {
return platformFetch<{ enabled: boolean; disabled: boolean; message: string }>(
`/api/settings/kill-switch?productId=${encodeURIComponent(productId)}`,
{ method: 'GET' },
opts
);
}
// ── Webhooks ──────────────────────────────────────────────────────────────────
export function webhooksListSubscriptions(
productId: string,
opts: PlatformClientOptions
): Promise<unknown[]> {
return platformFetch<unknown[]>(
`/api/webhooks/subscriptions?productId=${encodeURIComponent(productId)}`,
{ method: 'GET' },
opts
);
}
export function webhooksCreate(
input: {
productId: string;
url: string;
events: string[];
description?: string;
enabled?: boolean;
},
opts: PlatformClientOptions
): Promise<unknown> {
return platformFetch<unknown>(
'/api/webhooks/subscriptions',
{ method: 'POST', body: JSON.stringify(input) },
opts
);
}
export function webhooksGet(
id: string,
productId: string,
opts: PlatformClientOptions
): Promise<unknown> {
return platformFetch<unknown>(
`/api/webhooks/subscriptions/${encodeURIComponent(id)}?productId=${encodeURIComponent(productId)}`,
{ method: 'GET' },
opts
);
}
export function webhooksUpdate(
id: string,
productId: string,
input: { url?: string; events?: string[]; enabled?: boolean; description?: string },
opts: PlatformClientOptions
): Promise<unknown> {
return platformFetch<unknown>(
`/api/webhooks/subscriptions/${encodeURIComponent(id)}?productId=${encodeURIComponent(productId)}`,
{ method: 'PATCH', body: JSON.stringify(input) },
opts
);
}
export function webhooksDelete(
id: string,
productId: string,
opts: PlatformClientOptions
): Promise<{ success: boolean }> {
return platformFetch<{ success: boolean }>(
`/api/webhooks/subscriptions/${encodeURIComponent(id)}?productId=${encodeURIComponent(productId)}`,
{ method: 'DELETE' },
opts
);
}
export function webhooksListDeliveries(
id: string,
limit: number,
opts: PlatformClientOptions
): Promise<unknown> {
return platformFetch<unknown>(
`/api/webhooks/subscriptions/${encodeURIComponent(id)}/deliveries?limit=${limit}`,
{ method: 'GET' },
opts
);
}
export function webhooksTest(
id: string,
productId: string,
opts: PlatformClientOptions
): Promise<{ success: boolean; message: string }> {
return platformFetch<{ success: boolean; message: string }>(
`/api/webhooks/subscriptions/${encodeURIComponent(id)}/test?productId=${encodeURIComponent(productId)}`,
{ method: 'POST', body: '{}' },
opts
);
}
export function webhooksRotateSecret(
id: string,
productId: string,
opts: PlatformClientOptions
): Promise<{ secret: string; message: string }> {
return platformFetch<{ secret: string; message: string }>(
`/api/webhooks/subscriptions/${encodeURIComponent(id)}/rotate-secret?productId=${encodeURIComponent(productId)}`,
{ method: 'POST', body: '{}' },
opts
);
}

View File

@ -0,0 +1,313 @@
/**
* Platform ops MCP tools flags.*, jobs.*, maintenance.*, settings.*
*
* Backed by: platform-service (port 4003).
* All tools require admin role.
*/
import { z } from 'zod';
import { registerTool } from '../tools/registry.js';
import {
flagsList,
flagsGet,
flagsUpsert,
flagsDelete,
flagsKillSwitch,
jobsList,
jobsGet,
jobsTrigger,
jobsListRuns,
maintenanceGetCurrent,
maintenanceSet,
maintenanceScheduleWindow,
settingsGet,
settingsUpdate,
settingsCheckKillSwitch,
} from '../../lib/platform-client.js';
import type { McpToolRequest } from '../tools/types.js';
const tokenOf = (req: McpToolRequest) => req.headers.authorization?.slice(7);
// ── flags.list ────────────────────────────────────────────────────────────────
registerTool({
name: 'flags.list',
description:
'List all feature flags for a product (key, enabled, percentage, platforms, regions). Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
productId: z.string().min(1).describe('Product ID to scope the flags'),
}),
async execute(args, req) {
return flagsList({ token: tokenOf(req), requestId: req.id, productId: args.productId });
},
});
// ── flags.get ─────────────────────────────────────────────────────────────────
registerTool({
name: 'flags.get',
description:
'Get a single feature flag by key including percentage rollout, platform targeting, and region targeting. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
key: z.string().min(1).describe('Flag key (e.g. "new_dashboard", "kill_switch")'),
productId: z.string().min(1).describe('Product ID'),
}),
async execute(args, req) {
return flagsGet(args.key, {
token: tokenOf(req),
requestId: req.id,
productId: args.productId,
});
},
});
// ── flags.upsert ──────────────────────────────────────────────────────────────
registerTool({
name: 'flags.upsert',
description:
'Create or update a feature flag. If the key already exists it is updated; otherwise created. Percentage is 0-100 rollout. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
key: z.string().min(1).describe('Flag key (snake_case, e.g. "new_onboarding_flow")'),
productId: z.string().min(1).describe('Product ID'),
enabled: z.boolean().describe('Whether the flag is active'),
percentage: z.coerce
.number()
.min(0)
.max(100)
.optional()
.default(100)
.describe('Rollout % (0-100)'),
description: z.string().optional().describe('Human-readable description'),
platforms: z
.array(z.string())
.optional()
.default([])
.describe('Platform allowlist (ios, android, web, desktop) — empty = all platforms'),
regions: z
.array(z.string())
.optional()
.describe('Region allowlist (us, eu, apac) — empty = all regions'),
}),
async execute(args, req) {
const { key, productId, ...input } = args;
return flagsUpsert(key, input, { token: tokenOf(req), requestId: req.id, productId });
},
});
// ── flags.delete ──────────────────────────────────────────────────────────────
registerTool({
name: 'flags.delete',
description: 'Permanently delete a feature flag by key. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
key: z.string().min(1).describe('Flag key to delete'),
productId: z.string().min(1).describe('Product ID'),
}),
async execute(args, req) {
return flagsDelete(args.key, {
token: tokenOf(req),
requestId: req.id,
productId: args.productId,
});
},
});
// ── flags.killSwitch ──────────────────────────────────────────────────────────
registerTool({
name: 'flags.killSwitch',
description:
'Emergency: disable all flags (or specific keys/platform) at once. Returns list of disabled flag keys and count. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
productId: z.string().min(1).describe('Product ID'),
platform: z
.string()
.optional()
.describe('Only disable flags for this platform (ios/android/web/desktop)'),
keys: z
.array(z.string())
.optional()
.describe('Specific flag keys to disable (empty = disable all matching flags)'),
}),
async execute(args, req) {
const { productId, ...input } = args;
return flagsKillSwitch(input, { token: tokenOf(req), requestId: req.id, productId });
},
});
// ── jobs.list ─────────────────────────────────────────────────────────────────
registerTool({
name: 'jobs.list',
description:
'List all registered background job definitions (name, schedule, enabled, lastRunAt). Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({}),
async execute(_args, req) {
return jobsList({ token: tokenOf(req), requestId: req.id });
},
});
// ── jobs.get ──────────────────────────────────────────────────────────────────
registerTool({
name: 'jobs.get',
description:
'Get a specific job definition by its ID (e.g. "job_cleanup_expired_sessions"). Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
jobId: z.string().min(1).describe('Job definition ID (e.g. "job_cleanup_expired_sessions")'),
}),
async execute(args, req) {
return jobsGet(args.jobId, { token: tokenOf(req), requestId: req.id });
},
});
// ── jobs.trigger ──────────────────────────────────────────────────────────────
registerTool({
name: 'jobs.trigger',
description:
'Manually trigger a named background job outside its normal schedule. Returns the run record with status and timing. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
jobName: z
.string()
.min(1)
.describe('Registered job name (e.g. "cleanup_expired_sessions", "send_daily_brief")'),
}),
async execute(args, req) {
return jobsTrigger(args.jobName, { token: tokenOf(req), requestId: req.id });
},
});
// ── jobs.listRuns ─────────────────────────────────────────────────────────────
registerTool({
name: 'jobs.listRuns',
description:
'List recent run records for a job (status, startedAt, finishedAt, error if any). Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
jobName: z.string().min(1).describe('Job name to get runs for'),
limit: z.coerce.number().min(1).max(100).default(20),
}),
async execute(args, req) {
return jobsListRuns(args.jobName, args.limit, { token: tokenOf(req), requestId: req.id });
},
});
// ── maintenance.getCurrent ────────────────────────────────────────────────────
registerTool({
name: 'maintenance.getCurrent',
description:
'Get the current maintenance configuration including mode, bypass rules, message, and upcoming scheduled windows. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({}),
async execute(_args, req) {
return maintenanceGetCurrent({ token: tokenOf(req), requestId: req.id });
},
});
// ── maintenance.set ───────────────────────────────────────────────────────────
registerTool({
name: 'maintenance.set',
description:
'Set the maintenance mode (none | scheduled | active | emergency). Use "active" for immediate maintenance, "emergency" for critical incidents, "none" to restore service. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
mode: z
.enum(['none', 'scheduled', 'active', 'emergency'])
.describe('"none" = service restored, "active" = maintenance now, "emergency" = critical'),
message: z.string().optional().describe('User-facing maintenance message'),
affectedServices: z.array(z.string()).optional().describe('List of affected service names'),
scheduledStart: z
.string()
.optional()
.describe('ISO 8601 scheduled start (for "scheduled" mode)'),
scheduledEnd: z.string().optional().describe('ISO 8601 scheduled end'),
}),
async execute(args, req) {
return maintenanceSet(args, { token: tokenOf(req), requestId: req.id });
},
});
// ── maintenance.scheduleWindow ────────────────────────────────────────────────
registerTool({
name: 'maintenance.scheduleWindow',
description:
'Schedule a future maintenance window with a title, message, start/end times, and list of affected services. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
title: z.string().min(1).describe('Short title (e.g. "Database migration v2.4")'),
message: z.string().min(1).describe('User-facing message during the window'),
mode: z.enum(['scheduled', 'active']).default('scheduled'),
scheduledStart: z.string().min(1).describe('ISO 8601 start time'),
scheduledEnd: z.string().min(1).describe('ISO 8601 end time (must be after start)'),
affectedServices: z.array(z.string()).optional().describe('Affected service names'),
}),
async execute(args, req) {
return maintenanceScheduleWindow(args, { token: tokenOf(req), requestId: req.id });
},
});
// ── settings.get ──────────────────────────────────────────────────────────────
registerTool({
name: 'settings.get',
description:
'Get user settings for the authenticated user (global settings + device overrides map). Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
productId: z.string().min(1).describe('Product ID to scope the settings'),
}),
async execute(args, req) {
return settingsGet({ token: tokenOf(req), requestId: req.id, productId: args.productId });
},
});
// ── settings.update ───────────────────────────────────────────────────────────
registerTool({
name: 'settings.update',
description:
'Merge-update user settings for the authenticated user. Existing keys are preserved; only provided keys are overwritten. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
productId: z.string().min(1).describe('Product ID'),
settings: z
.record(z.unknown())
.describe('Key-value pairs to merge into the user settings object'),
}),
async execute(args, req) {
return settingsUpdate(args.settings, {
token: tokenOf(req),
requestId: req.id,
productId: args.productId,
});
},
});
// ── settings.checkKillSwitch ──────────────────────────────────────────────────
registerTool({
name: 'settings.checkKillSwitch',
description:
'Check if the kill_switch feature flag is active for a product (same endpoint that mobile clients poll at launch). Returns { enabled, disabled, message }. No auth required on backend — useful for ops monitoring.',
requiredRole: 'admin',
inputSchema: z.object({
productId: z.string().min(1).describe('Product ID to check'),
}),
async execute(args, req) {
return settingsCheckKillSwitch(args.productId, { requestId: req.id });
},
});

View File

@ -0,0 +1,178 @@
/**
* Webhooks MCP tools webhooks.*
*
* Backed by: platform-service (port 4003) webhooks module.
* All tools require admin role.
*/
import { z } from 'zod';
import { registerTool } from '../tools/registry.js';
import { config } from '../../lib/config.js';
import {
webhooksListSubscriptions,
webhooksCreate,
webhooksGet,
webhooksUpdate,
webhooksDelete,
webhooksListDeliveries,
webhooksTest,
webhooksRotateSecret,
} from '../../lib/platform-client.js';
import type { McpToolRequest } from '../tools/types.js';
const tokenOf = (req: McpToolRequest) => req.headers.authorization?.slice(7);
// ── webhooks.listSubscriptions ────────────────────────────────────────────────
registerTool({
name: 'webhooks.listSubscriptions',
description:
'List all webhook subscriptions for a product (url, events, enabled, createdAt). Secrets are redacted. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
productId: z.string().min(1).describe('Product ID'),
}),
async execute(args, req) {
return webhooksListSubscriptions(args.productId, { token: tokenOf(req), requestId: req.id });
},
});
// ── webhooks.create ───────────────────────────────────────────────────────────
registerTool({
name: 'webhooks.create',
description:
'Create a new webhook subscription. The signing secret is returned ONCE at creation — store it securely. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
productId: z.string().min(1).describe('Product ID'),
url: z.string().url().describe('HTTPS endpoint URL that will receive events'),
events: z
.array(z.string())
.min(1)
.describe('Event types to subscribe to (e.g. ["user.created", "session.completed"])'),
description: z.string().optional().describe('Human-readable description of this subscription'),
enabled: z.boolean().optional().default(true),
}),
async execute(args, req) {
return webhooksCreate(args, { token: tokenOf(req), requestId: req.id });
},
});
// ── webhooks.get ──────────────────────────────────────────────────────────────
registerTool({
name: 'webhooks.get',
description:
'Get a single webhook subscription by ID. Secret is partially redacted (first 8 chars shown). Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
subscriptionId: z.string().min(1).describe('Webhook subscription ID'),
productId: z.string().min(1).describe('Product ID'),
}),
async execute(args, req) {
return webhooksGet(args.subscriptionId, args.productId, {
token: tokenOf(req),
requestId: req.id,
});
},
});
// ── webhooks.update ───────────────────────────────────────────────────────────
registerTool({
name: 'webhooks.update',
description:
'Update a webhook subscription (url, events list, enabled state, or description). Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
subscriptionId: z.string().min(1).describe('Webhook subscription ID'),
productId: z.string().min(1).describe('Product ID'),
url: z.string().url().optional().describe('New endpoint URL'),
events: z.array(z.string()).optional().describe('Replacement event types list'),
enabled: z.boolean().optional().describe('Enable or disable this subscription'),
description: z.string().optional().describe('Updated description'),
}),
async execute(args, req) {
const { subscriptionId, productId, ...updates } = args;
return webhooksUpdate(subscriptionId, productId, updates, {
token: tokenOf(req),
requestId: req.id,
});
},
});
// ── webhooks.delete ───────────────────────────────────────────────────────────
registerTool({
name: 'webhooks.delete',
description: 'Permanently delete a webhook subscription. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
subscriptionId: z.string().min(1).describe('Webhook subscription ID to delete'),
productId: z.string().min(1).describe('Product ID'),
}),
async execute(args, req) {
return webhooksDelete(args.subscriptionId, args.productId, {
token: tokenOf(req),
requestId: req.id,
});
},
});
// ── webhooks.listDeliveries ───────────────────────────────────────────────────
registerTool({
name: 'webhooks.listDeliveries',
description:
'List recent delivery attempts for a webhook subscription (status, event, attempts, responseCode). Useful for debugging failed deliveries. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
subscriptionId: z.string().min(1).describe('Webhook subscription ID'),
limit: z.coerce.number().min(1).max(config.QUERY_MAX_LIMIT).default(config.QUERY_DEFAULT_LIMIT),
}),
async execute(args, req) {
return webhooksListDeliveries(args.subscriptionId, args.limit, {
token: tokenOf(req),
requestId: req.id,
});
},
});
// ── webhooks.test ─────────────────────────────────────────────────────────────
registerTool({
name: 'webhooks.test',
description:
'Send a test event payload to a webhook subscription to verify the endpoint is reachable and responding. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
subscriptionId: z.string().min(1).describe('Webhook subscription ID to test'),
productId: z.string().min(1).describe('Product ID'),
}),
async execute(args, req) {
return webhooksTest(args.subscriptionId, args.productId, {
token: tokenOf(req),
requestId: req.id,
});
},
});
// ── webhooks.rotateSecret ─────────────────────────────────────────────────────
registerTool({
name: 'webhooks.rotateSecret',
description:
'Rotate the HMAC signing secret for a webhook subscription. The new secret is returned ONCE — update your consumer immediately. Requires admin role.',
requiredRole: 'admin',
inputSchema: z.object({
subscriptionId: z.string().min(1).describe('Webhook subscription ID'),
productId: z.string().min(1).describe('Product ID'),
}),
async execute(args, req) {
return webhooksRotateSecret(args.subscriptionId, args.productId, {
token: tokenOf(req),
requestId: req.id,
});
},
});

View File

@ -13,6 +13,11 @@
* nomgap.* fasting sessions, push triggers
* peakpulse.* adventure sessions, GPS routes, stats
* tracker.* items, votes, comments, public roadmap
* flags.* feature flag CRUD + kill switch
* jobs.* background job list, trigger, run history
* maintenance.* maintenance mode + scheduled windows
* settings.* user settings + kill switch check
* webhooks.* subscription CRUD, deliveries, test, rotate secret
*
* Auth: JWT Bearer tokens issued by platform-service (same JWT_SECRET).
* Role gating: viewer / admin / super_admin per tool.
@ -37,12 +42,14 @@ import './modules/chronomind/chronomind-tools.js';
import './modules/nomgap/nomgap-tools.js';
import './modules/peakpulse/peakpulse-tools.js';
import './modules/tracker/tracker-tools.js';
import './modules/platform/ops-tools.js';
import './modules/platform/webhooks-tools.js';
const app = await createServiceApp({
name: 'mcp-server',
version: '0.1.0',
description:
'ByteLyst MCP Server — platform.*, extraction.*, support.*, mindlyst.*, lysnrai.*, jarvis.*, chronomind.*, nomgap.*, peakpulse.*, tracker.*',
'ByteLyst MCP Server — platform.*, extraction.*, support.*, mindlyst.*, lysnrai.*, jarvis.*, chronomind.*, nomgap.*, peakpulse.*, tracker.*, flags.*, jobs.*, maintenance.*, settings.*, webhooks.*',
corsOrigin: config.CORS_ORIGIN,
logLevel: config.LOG_LEVEL,
});