@hailer/mcp 0.1.16 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,11 +19,11 @@ export declare function verifyWebhookSignature(payload: string, signature: strin
19
19
  * Production: WEBHOOK_TOKEN env var (injected by AWS Secrets Manager)
20
20
  * Development: Falls back to file-based token for local testing
21
21
  */
22
- export declare function getWebhookToken(): string;
22
+ export declare function getWebhookToken(): string | null;
23
23
  /**
24
24
  * Get the full webhook path with token (no prefix for security through obscurity)
25
25
  */
26
- export declare function getWebhookPath(): string;
26
+ export declare function getWebhookPath(): string | null;
27
27
  interface WebhookField {
28
28
  id: string;
29
29
  type: string;
@@ -107,9 +107,10 @@ function getWebhookToken() {
107
107
  logger.debug('Using webhook token from environment');
108
108
  return process.env.WEBHOOK_TOKEN;
109
109
  }
110
- // Development only: File-based fallback
110
+ // Production without WEBHOOK_TOKEN: webhooks disabled (optional feature)
111
111
  if (process.env.NODE_ENV === 'production') {
112
- throw new Error('WEBHOOK_TOKEN environment variable is required in production');
112
+ logger.info('WEBHOOK_TOKEN not set - webhook endpoint disabled');
113
+ return null;
113
114
  }
114
115
  const configDir = path.join(process.cwd(), BOT_CONFIG_DIR);
115
116
  const secretPath = path.join(configDir, WEBHOOK_SECRET_FILE);
@@ -140,7 +141,8 @@ function getWebhookToken() {
140
141
  * Get the full webhook path with token (no prefix for security through obscurity)
141
142
  */
142
143
  function getWebhookPath() {
143
- return `/${getWebhookToken()}`;
144
+ const token = getWebhookToken();
145
+ return token ? `/${token}` : null;
144
146
  }
145
147
  let botUpdateCallback = null;
146
148
  function onBotUpdate(callback) {
@@ -327,54 +327,59 @@ class MCPServerService {
327
327
  }
328
328
  });
329
329
  // ===== Bot Config Webhook API =====
330
- // Get secure webhook path (auto-generated token)
330
+ // Get secure webhook path (auto-generated token) - null if disabled
331
331
  const webhookPath = (0, webhook_handler_1.getWebhookPath)();
332
- // POST /webhook/{token} - Receives updates from Hailer workflow webhooks
333
- this.app.post(webhookPath, (req, res) => {
334
- req.logger.info('Bot config webhook received', {
335
- activityId: req.body?._id,
336
- activityName: req.body?.name,
337
- workspaceId: req.body?.cid,
338
- });
339
- try {
340
- const result = (0, webhook_handler_1.handleBotConfigWebhook)(req.body);
341
- if (result.success) {
342
- req.logger.info('Bot config updated via webhook', {
343
- action: result.action,
344
- workspaceId: result.workspaceId,
345
- botType: result.botType,
346
- });
347
- res.status(200).json(result);
332
+ if (webhookPath) {
333
+ // POST /webhook/{token} - Receives updates from Hailer workflow webhooks
334
+ this.app.post(webhookPath, (req, res) => {
335
+ req.logger.info('Bot config webhook received', {
336
+ activityId: req.body?._id,
337
+ activityName: req.body?.name,
338
+ workspaceId: req.body?.cid,
339
+ });
340
+ try {
341
+ const result = (0, webhook_handler_1.handleBotConfigWebhook)(req.body);
342
+ if (result.success) {
343
+ req.logger.info('Bot config updated via webhook', {
344
+ action: result.action,
345
+ workspaceId: result.workspaceId,
346
+ botType: result.botType,
347
+ });
348
+ res.status(200).json(result);
349
+ }
350
+ else {
351
+ req.logger.warn('Bot config webhook failed', { error: result.error });
352
+ res.status(400).json(result);
353
+ }
348
354
  }
349
- else {
350
- req.logger.warn('Bot config webhook failed', { error: result.error });
351
- res.status(400).json(result);
355
+ catch (error) {
356
+ req.logger.error('Bot config webhook error', { error });
357
+ res.status(500).json({
358
+ success: false,
359
+ error: error instanceof Error ? error.message : 'Internal error',
360
+ });
352
361
  }
353
- }
354
- catch (error) {
355
- req.logger.error('Bot config webhook error', { error });
356
- res.status(500).json({
357
- success: false,
358
- error: error instanceof Error ? error.message : 'Internal error',
362
+ });
363
+ // GET /webhook/{token}/status - Status endpoint to see all workspace configs
364
+ this.app.get(`${webhookPath}/status`, (_req, res) => {
365
+ const configs = (0, webhook_handler_1.listWorkspaceConfigs)();
366
+ res.json({
367
+ timestamp: new Date().toISOString(),
368
+ workspaceCount: configs.length,
369
+ workspaces: configs.map((c) => ({
370
+ workspaceId: c.workspaceId,
371
+ workspaceName: c.workspaceName,
372
+ hasOrchestrator: !!c.orchestrator,
373
+ specialistCount: c.specialists.length,
374
+ enabledSpecialists: c.specialists.filter((s) => s.enabled).length,
375
+ lastSynced: c.lastSynced,
376
+ })),
359
377
  });
360
- }
361
- });
362
- // GET /webhook/{token}/status - Status endpoint to see all workspace configs
363
- this.app.get(`${webhookPath}/status`, (_req, res) => {
364
- const configs = (0, webhook_handler_1.listWorkspaceConfigs)();
365
- res.json({
366
- timestamp: new Date().toISOString(),
367
- workspaceCount: configs.length,
368
- workspaces: configs.map((c) => ({
369
- workspaceId: c.workspaceId,
370
- workspaceName: c.workspaceName,
371
- hasOrchestrator: !!c.orchestrator,
372
- specialistCount: c.specialists.length,
373
- enabledSpecialists: c.specialists.filter((s) => s.enabled).length,
374
- lastSynced: c.lastSynced,
375
- })),
376
378
  });
377
- });
379
+ }
380
+ else {
381
+ this.logger.info('Webhook endpoint disabled (no WEBHOOK_TOKEN)');
382
+ }
378
383
  this.logger.debug('Routes configured', {
379
384
  routes: [
380
385
  '/health',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hailer/mcp",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "config": {
5
5
  "docker": {
6
6
  "registry": "registry.gitlab.com/hailer-repos/hailer-mcp"