@hailer/mcp 0.1.16 → 0.2.1

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.
Files changed (202) hide show
  1. package/dist/app.js +24 -20
  2. package/dist/core.d.ts +33 -9
  3. package/dist/core.js +279 -147
  4. package/dist/mcp/UserContextCache.js +18 -0
  5. package/dist/mcp/hailer-clients.d.ts +9 -1
  6. package/dist/mcp/hailer-clients.js +13 -3
  7. package/dist/mcp/signal-handler.js +1 -1
  8. package/dist/mcp/tool-registry.d.ts +3 -1
  9. package/dist/mcp/tool-registry.js +4 -1
  10. package/dist/mcp/tools/activity.js +43 -34
  11. package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
  12. package/dist/mcp/tools/bot-config/constants.js +94 -0
  13. package/dist/mcp/tools/{bot-config.d.ts → bot-config/core.d.ts} +6 -6
  14. package/dist/mcp/tools/{bot-config.js → bot-config/core.js} +15 -15
  15. package/dist/mcp/tools/bot-config/index.d.ts +10 -0
  16. package/dist/mcp/tools/bot-config/index.js +59 -0
  17. package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
  18. package/dist/mcp/tools/bot-config/tools.js +15 -0
  19. package/dist/mcp/tools/bot-config/types.d.ts +50 -0
  20. package/dist/mcp/tools/bot-config/types.js +6 -0
  21. package/dist/mcp/tools/bug-fixer-tools.d.ts +21 -0
  22. package/dist/mcp/tools/{giuseppe-tools.js → bug-fixer-tools.js} +61 -61
  23. package/dist/mcp/tools/user.js +10 -29
  24. package/dist/mcp/tools/workflow.js +36 -2
  25. package/dist/mcp/utils/data-transformers.d.ts +0 -8
  26. package/dist/mcp/utils/data-transformers.js +0 -28
  27. package/dist/mcp/utils/index.d.ts +4 -1
  28. package/dist/mcp/utils/index.js +17 -3
  29. package/dist/mcp/utils/pagination.d.ts +40 -0
  30. package/dist/mcp/utils/pagination.js +55 -0
  31. package/dist/mcp/utils/response-builder.d.ts +53 -0
  32. package/dist/mcp/utils/response-builder.js +110 -0
  33. package/dist/mcp/utils/tool-helpers.d.ts +0 -8
  34. package/dist/mcp/utils/tool-helpers.js +0 -24
  35. package/dist/mcp/utils/types.d.ts +1 -33
  36. package/dist/mcp/webhook-handler.d.ts +2 -2
  37. package/dist/mcp/webhook-handler.js +5 -3
  38. package/dist/mcp-server.d.ts +2 -2
  39. package/dist/mcp-server.js +167 -140
  40. package/package.json +1 -1
  41. package/REFACTOR_STATUS.md +0 -127
  42. package/dist/agents/bot-manager.d.ts +0 -48
  43. package/dist/agents/bot-manager.js +0 -254
  44. package/dist/agents/factory.d.ts +0 -150
  45. package/dist/agents/factory.js +0 -650
  46. package/dist/agents/giuseppe/ai.d.ts +0 -83
  47. package/dist/agents/giuseppe/ai.js +0 -466
  48. package/dist/agents/giuseppe/bot.d.ts +0 -110
  49. package/dist/agents/giuseppe/bot.js +0 -780
  50. package/dist/agents/giuseppe/config.d.ts +0 -25
  51. package/dist/agents/giuseppe/config.js +0 -227
  52. package/dist/agents/giuseppe/files.d.ts +0 -52
  53. package/dist/agents/giuseppe/files.js +0 -338
  54. package/dist/agents/giuseppe/git.d.ts +0 -48
  55. package/dist/agents/giuseppe/git.js +0 -298
  56. package/dist/agents/giuseppe/index.d.ts +0 -97
  57. package/dist/agents/giuseppe/index.js +0 -258
  58. package/dist/agents/giuseppe/lsp.d.ts +0 -113
  59. package/dist/agents/giuseppe/lsp.js +0 -485
  60. package/dist/agents/giuseppe/monitor.d.ts +0 -118
  61. package/dist/agents/giuseppe/monitor.js +0 -621
  62. package/dist/agents/giuseppe/prompt.d.ts +0 -5
  63. package/dist/agents/giuseppe/prompt.js +0 -94
  64. package/dist/agents/giuseppe/registries/pending-classification.d.ts +0 -28
  65. package/dist/agents/giuseppe/registries/pending-classification.js +0 -50
  66. package/dist/agents/giuseppe/registries/pending-fix.d.ts +0 -30
  67. package/dist/agents/giuseppe/registries/pending-fix.js +0 -42
  68. package/dist/agents/giuseppe/registries/pending.d.ts +0 -27
  69. package/dist/agents/giuseppe/registries/pending.js +0 -49
  70. package/dist/agents/giuseppe/specialist.d.ts +0 -47
  71. package/dist/agents/giuseppe/specialist.js +0 -237
  72. package/dist/agents/giuseppe/types.d.ts +0 -123
  73. package/dist/agents/giuseppe/types.js +0 -9
  74. package/dist/agents/hailer-expert/index.d.ts +0 -8
  75. package/dist/agents/hailer-expert/index.js +0 -14
  76. package/dist/agents/hal/daemon.d.ts +0 -142
  77. package/dist/agents/hal/daemon.js +0 -1103
  78. package/dist/agents/hal/definitions.d.ts +0 -55
  79. package/dist/agents/hal/definitions.js +0 -263
  80. package/dist/agents/hal/index.d.ts +0 -3
  81. package/dist/agents/hal/index.js +0 -8
  82. package/dist/agents/index.d.ts +0 -18
  83. package/dist/agents/index.js +0 -48
  84. package/dist/agents/shared/base.d.ts +0 -216
  85. package/dist/agents/shared/base.js +0 -846
  86. package/dist/agents/shared/services/agent-registry.d.ts +0 -107
  87. package/dist/agents/shared/services/agent-registry.js +0 -629
  88. package/dist/agents/shared/services/conversation-manager.d.ts +0 -50
  89. package/dist/agents/shared/services/conversation-manager.js +0 -136
  90. package/dist/agents/shared/services/mcp-client.d.ts +0 -56
  91. package/dist/agents/shared/services/mcp-client.js +0 -124
  92. package/dist/agents/shared/services/message-classifier.d.ts +0 -37
  93. package/dist/agents/shared/services/message-classifier.js +0 -187
  94. package/dist/agents/shared/services/message-formatter.d.ts +0 -89
  95. package/dist/agents/shared/services/message-formatter.js +0 -371
  96. package/dist/agents/shared/services/session-logger.d.ts +0 -106
  97. package/dist/agents/shared/services/session-logger.js +0 -446
  98. package/dist/agents/shared/services/tool-executor.d.ts +0 -41
  99. package/dist/agents/shared/services/tool-executor.js +0 -169
  100. package/dist/agents/shared/services/workspace-schema-cache.d.ts +0 -125
  101. package/dist/agents/shared/services/workspace-schema-cache.js +0 -578
  102. package/dist/agents/shared/specialist.d.ts +0 -91
  103. package/dist/agents/shared/specialist.js +0 -399
  104. package/dist/agents/shared/tool-schema-loader.d.ts +0 -62
  105. package/dist/agents/shared/tool-schema-loader.js +0 -232
  106. package/dist/agents/shared/types.d.ts +0 -327
  107. package/dist/agents/shared/types.js +0 -121
  108. package/dist/client/agents/base.d.ts +0 -207
  109. package/dist/client/agents/base.js +0 -744
  110. package/dist/client/agents/definitions.d.ts +0 -53
  111. package/dist/client/agents/definitions.js +0 -263
  112. package/dist/client/agents/orchestrator.d.ts +0 -141
  113. package/dist/client/agents/orchestrator.js +0 -1062
  114. package/dist/client/agents/specialist.d.ts +0 -86
  115. package/dist/client/agents/specialist.js +0 -340
  116. package/dist/client/bot-entrypoint.d.ts +0 -7
  117. package/dist/client/bot-entrypoint.js +0 -103
  118. package/dist/client/bot-manager.d.ts +0 -44
  119. package/dist/client/bot-manager.js +0 -173
  120. package/dist/client/bot-runner.d.ts +0 -35
  121. package/dist/client/bot-runner.js +0 -188
  122. package/dist/client/chat-agent-daemon.d.ts +0 -464
  123. package/dist/client/chat-agent-daemon.js +0 -1774
  124. package/dist/client/daemon-factory.d.ts +0 -106
  125. package/dist/client/daemon-factory.js +0 -301
  126. package/dist/client/factory.d.ts +0 -111
  127. package/dist/client/factory.js +0 -314
  128. package/dist/client/index.d.ts +0 -17
  129. package/dist/client/index.js +0 -38
  130. package/dist/client/multi-bot-manager.d.ts +0 -42
  131. package/dist/client/multi-bot-manager.js +0 -161
  132. package/dist/client/orchestrator-daemon.d.ts +0 -87
  133. package/dist/client/orchestrator-daemon.js +0 -444
  134. package/dist/client/server.d.ts +0 -8
  135. package/dist/client/server.js +0 -251
  136. package/dist/client/services/agent-registry.d.ts +0 -108
  137. package/dist/client/services/agent-registry.js +0 -630
  138. package/dist/client/services/conversation-manager.d.ts +0 -50
  139. package/dist/client/services/conversation-manager.js +0 -136
  140. package/dist/client/services/mcp-client.d.ts +0 -48
  141. package/dist/client/services/mcp-client.js +0 -105
  142. package/dist/client/services/message-classifier.d.ts +0 -37
  143. package/dist/client/services/message-classifier.js +0 -187
  144. package/dist/client/services/message-formatter.d.ts +0 -84
  145. package/dist/client/services/message-formatter.js +0 -353
  146. package/dist/client/services/session-logger.d.ts +0 -106
  147. package/dist/client/services/session-logger.js +0 -446
  148. package/dist/client/services/tool-executor.d.ts +0 -41
  149. package/dist/client/services/tool-executor.js +0 -169
  150. package/dist/client/services/workspace-schema-cache.d.ts +0 -149
  151. package/dist/client/services/workspace-schema-cache.js +0 -732
  152. package/dist/client/specialist-daemon.d.ts +0 -77
  153. package/dist/client/specialist-daemon.js +0 -197
  154. package/dist/client/specialists.d.ts +0 -53
  155. package/dist/client/specialists.js +0 -178
  156. package/dist/client/tool-schema-loader.d.ts +0 -62
  157. package/dist/client/tool-schema-loader.js +0 -232
  158. package/dist/client/types.d.ts +0 -327
  159. package/dist/client/types.js +0 -121
  160. package/dist/commands/seed-config.d.ts +0 -9
  161. package/dist/commands/seed-config.js +0 -372
  162. package/dist/lib/context-manager.d.ts +0 -111
  163. package/dist/lib/context-manager.js +0 -431
  164. package/dist/lib/prompt-length-manager.d.ts +0 -81
  165. package/dist/lib/prompt-length-manager.js +0 -457
  166. package/dist/mcp/tools/giuseppe-tools.d.ts +0 -21
  167. package/dist/modules/bug-reports/bug-config.d.ts +0 -25
  168. package/dist/modules/bug-reports/bug-config.js +0 -187
  169. package/dist/modules/bug-reports/bug-monitor.d.ts +0 -108
  170. package/dist/modules/bug-reports/bug-monitor.js +0 -510
  171. package/dist/modules/bug-reports/giuseppe-agent.d.ts +0 -58
  172. package/dist/modules/bug-reports/giuseppe-agent.js +0 -467
  173. package/dist/modules/bug-reports/giuseppe-ai.d.ts +0 -83
  174. package/dist/modules/bug-reports/giuseppe-ai.js +0 -466
  175. package/dist/modules/bug-reports/giuseppe-bot.d.ts +0 -110
  176. package/dist/modules/bug-reports/giuseppe-bot.js +0 -804
  177. package/dist/modules/bug-reports/giuseppe-daemon.d.ts +0 -80
  178. package/dist/modules/bug-reports/giuseppe-daemon.js +0 -617
  179. package/dist/modules/bug-reports/giuseppe-files.d.ts +0 -64
  180. package/dist/modules/bug-reports/giuseppe-files.js +0 -375
  181. package/dist/modules/bug-reports/giuseppe-git.d.ts +0 -48
  182. package/dist/modules/bug-reports/giuseppe-git.js +0 -298
  183. package/dist/modules/bug-reports/giuseppe-lsp.d.ts +0 -113
  184. package/dist/modules/bug-reports/giuseppe-lsp.js +0 -485
  185. package/dist/modules/bug-reports/giuseppe-prompt.d.ts +0 -5
  186. package/dist/modules/bug-reports/giuseppe-prompt.js +0 -94
  187. package/dist/modules/bug-reports/index.d.ts +0 -77
  188. package/dist/modules/bug-reports/index.js +0 -215
  189. package/dist/modules/bug-reports/pending-classification-registry.d.ts +0 -28
  190. package/dist/modules/bug-reports/pending-classification-registry.js +0 -50
  191. package/dist/modules/bug-reports/pending-fix-registry.d.ts +0 -30
  192. package/dist/modules/bug-reports/pending-fix-registry.js +0 -42
  193. package/dist/modules/bug-reports/pending-registry.d.ts +0 -27
  194. package/dist/modules/bug-reports/pending-registry.js +0 -49
  195. package/dist/modules/bug-reports/types.d.ts +0 -123
  196. package/dist/modules/bug-reports/types.js +0 -9
  197. package/dist/routes/agents.d.ts +0 -44
  198. package/dist/routes/agents.js +0 -311
  199. package/dist/services/agent-credential-store.d.ts +0 -73
  200. package/dist/services/agent-credential-store.js +0 -212
  201. package/dist/services/bug-monitor.d.ts +0 -23
  202. package/dist/services/bug-monitor.js +0 -275
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Response Builder Utility
3
+ *
4
+ * Centralized response formatting for MCP tools.
5
+ * Eliminates duplication of { type: 'text', text: '...' } patterns.
6
+ */
7
+ import type { McpResponse } from './types';
8
+ /**
9
+ * Build a text response (most common pattern)
10
+ */
11
+ export declare function textResponse(text: string): McpResponse;
12
+ /**
13
+ * Build an error response
14
+ */
15
+ export declare function errorResponse(message: string, details?: string): McpResponse;
16
+ /**
17
+ * Build a success response with optional emoji
18
+ */
19
+ export declare function successResponse(message: string, emoji?: string): McpResponse;
20
+ /**
21
+ * Build a JSON response (formatted with indentation)
22
+ */
23
+ export declare function jsonResponse(data: unknown, prefix?: string): McpResponse;
24
+ /**
25
+ * Build a paginated response
26
+ */
27
+ export declare function paginatedResponse(items: unknown[], options: {
28
+ currentPage: number;
29
+ limit: number;
30
+ totalCount?: number;
31
+ title?: string;
32
+ }): McpResponse;
33
+ /**
34
+ * Build a list response with header
35
+ */
36
+ export declare function listResponse(items: unknown[], header: string, emptyMessage?: string): McpResponse;
37
+ /**
38
+ * Extract error message from unknown error type
39
+ * Centralizes the common pattern: error instanceof Error ? error.message : String(error)
40
+ */
41
+ export declare function getErrorMessage(error: unknown): string;
42
+ /**
43
+ * Wrap an async operation with standard error handling
44
+ * Returns McpResponse on error, or result on success
45
+ */
46
+ export declare function withErrorHandling<T>(operation: () => Promise<T>, errorContext?: string): Promise<T | McpResponse>;
47
+ /**
48
+ * Type guard to check if result is an error response
49
+ */
50
+ export declare function isErrorResponse(result: unknown): result is McpResponse & {
51
+ isError: true;
52
+ };
53
+ //# sourceMappingURL=response-builder.d.ts.map
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ /**
3
+ * Response Builder Utility
4
+ *
5
+ * Centralized response formatting for MCP tools.
6
+ * Eliminates duplication of { type: 'text', text: '...' } patterns.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.textResponse = textResponse;
10
+ exports.errorResponse = errorResponse;
11
+ exports.successResponse = successResponse;
12
+ exports.jsonResponse = jsonResponse;
13
+ exports.paginatedResponse = paginatedResponse;
14
+ exports.listResponse = listResponse;
15
+ exports.getErrorMessage = getErrorMessage;
16
+ exports.withErrorHandling = withErrorHandling;
17
+ exports.isErrorResponse = isErrorResponse;
18
+ /**
19
+ * Build a text response (most common pattern)
20
+ */
21
+ function textResponse(text) {
22
+ return {
23
+ content: [{ type: 'text', text }]
24
+ };
25
+ }
26
+ /**
27
+ * Build an error response
28
+ */
29
+ function errorResponse(message, details) {
30
+ const text = details ? `${message}\n\n${details}` : message;
31
+ return {
32
+ content: [{ type: 'text', text }],
33
+ isError: true
34
+ };
35
+ }
36
+ /**
37
+ * Build a success response with optional emoji
38
+ */
39
+ function successResponse(message, emoji = '✅') {
40
+ return textResponse(`${emoji} ${message}`);
41
+ }
42
+ /**
43
+ * Build a JSON response (formatted with indentation)
44
+ */
45
+ function jsonResponse(data, prefix) {
46
+ const json = JSON.stringify(data, null, 2);
47
+ const text = prefix ? `${prefix}\n\n${json}` : json;
48
+ return textResponse(text);
49
+ }
50
+ /**
51
+ * Build a paginated response
52
+ */
53
+ function paginatedResponse(items, options) {
54
+ const { currentPage, limit, totalCount, title } = options;
55
+ const hasMore = items.length === limit;
56
+ let text = '';
57
+ if (title)
58
+ text += `${title}\n\n`;
59
+ text += `📊 **Page ${currentPage + 1}**\n`;
60
+ text += `- Items: ${items.length}\n`;
61
+ if (totalCount !== undefined)
62
+ text += `- Total: ${totalCount}\n`;
63
+ text += `- Has more: ${hasMore ? 'Yes' : 'No'}\n\n`;
64
+ text += JSON.stringify(items, null, 2);
65
+ return textResponse(text);
66
+ }
67
+ /**
68
+ * Build a list response with header
69
+ */
70
+ function listResponse(items, header, emptyMessage = 'No items found') {
71
+ if (items.length === 0) {
72
+ return textResponse(emptyMessage);
73
+ }
74
+ return textResponse(`${header}\n\n${JSON.stringify(items, null, 2)}`);
75
+ }
76
+ /**
77
+ * Extract error message from unknown error type
78
+ * Centralizes the common pattern: error instanceof Error ? error.message : String(error)
79
+ */
80
+ function getErrorMessage(error) {
81
+ if (error instanceof Error)
82
+ return error.message;
83
+ if (typeof error === 'string')
84
+ return error;
85
+ return String(error);
86
+ }
87
+ /**
88
+ * Wrap an async operation with standard error handling
89
+ * Returns McpResponse on error, or result on success
90
+ */
91
+ async function withErrorHandling(operation, errorContext) {
92
+ try {
93
+ return await operation();
94
+ }
95
+ catch (error) {
96
+ const message = getErrorMessage(error);
97
+ const fullMessage = errorContext ? `${errorContext}: ${message}` : message;
98
+ return errorResponse('Error', fullMessage);
99
+ }
100
+ }
101
+ /**
102
+ * Type guard to check if result is an error response
103
+ */
104
+ function isErrorResponse(result) {
105
+ return (typeof result === 'object' &&
106
+ result !== null &&
107
+ 'isError' in result &&
108
+ result.isError === true);
109
+ }
110
+ //# sourceMappingURL=response-builder.js.map
@@ -91,12 +91,4 @@ export declare function getRequiredWorkspaceId(args: {
91
91
  * ```
92
92
  */
93
93
  export declare function extractErrorMessage(error: unknown): string;
94
- /**
95
- * Creates a standard success response with formatted text.
96
- */
97
- export declare function successResponse(text: string): McpResponse;
98
- /**
99
- * Creates a standard error response with formatted text.
100
- */
101
- export declare function errorResponse(text: string): McpResponse;
102
94
  //# sourceMappingURL=tool-helpers.d.ts.map
@@ -11,8 +11,6 @@ exports.missingWorkspaceCacheResponse = missingWorkspaceCacheResponse;
11
11
  exports.getResolvedWorkspaceId = getResolvedWorkspaceId;
12
12
  exports.getRequiredWorkspaceId = getRequiredWorkspaceId;
13
13
  exports.extractErrorMessage = extractErrorMessage;
14
- exports.successResponse = successResponse;
15
- exports.errorResponse = errorResponse;
16
14
  const workspace_cache_1 = require("../workspace-cache");
17
15
  /**
18
16
  * Error thrown when workspace cache is not available
@@ -154,26 +152,4 @@ function extractErrorMessage(error) {
154
152
  }
155
153
  return String(error);
156
154
  }
157
- /**
158
- * Creates a standard success response with formatted text.
159
- */
160
- function successResponse(text) {
161
- return {
162
- content: [{
163
- type: "text",
164
- text,
165
- }],
166
- };
167
- }
168
- /**
169
- * Creates a standard error response with formatted text.
170
- */
171
- function errorResponse(text) {
172
- return {
173
- content: [{
174
- type: "text",
175
- text: `${text}`,
176
- }],
177
- };
178
- }
179
155
  //# sourceMappingURL=tool-helpers.js.map
@@ -133,6 +133,7 @@ export interface McpTextContent {
133
133
  }
134
134
  export interface McpResponse {
135
135
  content: McpTextContent[];
136
+ isError?: boolean;
136
137
  }
137
138
  export interface ActivityListFilters {
138
139
  processId?: string;
@@ -168,13 +169,6 @@ export interface SearchOptions {
168
169
  limit?: number;
169
170
  workspaceId?: string;
170
171
  }
171
- export interface InitData {
172
- processes: any[];
173
- users: Record<string, HailerUser>;
174
- currentUser?: HailerUser;
175
- workspace?: WorkspaceInfo;
176
- networks?: any[];
177
- }
178
172
  export interface WorkspaceInfo {
179
173
  _id: string;
180
174
  name: string;
@@ -182,32 +176,6 @@ export interface WorkspaceInfo {
182
176
  members?: string[];
183
177
  settings?: Record<string, any>;
184
178
  }
185
- export interface NetworkInfo {
186
- _id: string;
187
- name: string;
188
- workspaces: string[];
189
- }
190
- export interface ClientConfig {
191
- email: string;
192
- password: string;
193
- mcpServerApiKey: string;
194
- apiBaseUrl?: string;
195
- }
196
- export interface McpConfig {
197
- clients: ClientConfig[];
198
- logging?: {
199
- level: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';
200
- enableApiLogging: boolean;
201
- };
202
- }
203
- export interface ApiErrorInfo {
204
- status: number;
205
- statusText: string;
206
- message: string;
207
- operation: string;
208
- endpoint: string;
209
- responseBody?: string;
210
- }
211
179
  export interface SignalData {
212
180
  type: string;
213
181
  data: any;
@@ -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) {
@@ -16,10 +16,10 @@ export interface MCPServerConfig {
16
16
  port: number;
17
17
  corsOrigins: string[];
18
18
  toolRegistry: ToolRegistry;
19
- getDaemonStatus?: () => Array<{
19
+ getDaemonStatus?: () => Record<string, Array<{
20
20
  botId: string;
21
21
  state: any;
22
- }> | null;
22
+ }>>;
23
23
  }
24
24
  export declare class MCPServerService {
25
25
  private app;
@@ -166,23 +166,24 @@ class MCPServerService {
166
166
  req.logger.debug('No config found for apiKey, returning all tools');
167
167
  }
168
168
  }
169
- // Apply NUCLEAR tools blocking if NOT explicitly enabled
170
- if (!config_1.environment.ENABLE_NUCLEAR_TOOLS) {
171
- if (!filterConfig) {
172
- // No filter yet - create one excluding NUCLEAR
173
- filterConfig = {
174
- allowedGroups: [tool_registry_1.ToolGroup.READ, tool_registry_1.ToolGroup.WRITE, tool_registry_1.ToolGroup.PLAYGROUND]
175
- };
176
- }
177
- else if (filterConfig.allowedGroups) {
178
- // Filter has groups - remove NUCLEAR if present
179
- filterConfig.allowedGroups = filterConfig.allowedGroups.filter(g => g !== tool_registry_1.ToolGroup.NUCLEAR);
180
- }
181
- // Note: If filter has explicit allowedTools list, NUCLEAR tools won't be in it anyway
182
- req.logger.debug('NUCLEAR tools blocked (not explicitly enabled)');
169
+ // Apply default tool group filtering
170
+ // - NUCLEAR: Only if ENABLE_NUCLEAR_TOOLS=true
171
+ // - BOT_INTERNAL: Never exposed to MCP clients (autonomous bot tools only)
172
+ if (!filterConfig) {
173
+ // No filter yet - create default excluding NUCLEAR and BOT_INTERNAL
174
+ filterConfig = {
175
+ allowedGroups: [tool_registry_1.ToolGroup.READ, tool_registry_1.ToolGroup.WRITE, tool_registry_1.ToolGroup.PLAYGROUND]
176
+ };
177
+ req.logger.debug('Using default tool filter (excludes NUCLEAR and BOT_INTERNAL)');
183
178
  }
184
- else {
185
- req.logger.debug('NUCLEAR tools enabled by environment variable');
179
+ else if (filterConfig.allowedGroups) {
180
+ // Filter has groups - always remove BOT_INTERNAL, remove NUCLEAR unless enabled
181
+ filterConfig.allowedGroups = filterConfig.allowedGroups.filter(g => g !== tool_registry_1.ToolGroup.BOT_INTERNAL &&
182
+ (config_1.environment.ENABLE_NUCLEAR_TOOLS || g !== tool_registry_1.ToolGroup.NUCLEAR));
183
+ req.logger.debug('Filtered tool groups', {
184
+ allowedGroups: filterConfig.allowedGroups,
185
+ nuclearEnabled: config_1.environment.ENABLE_NUCLEAR_TOOLS
186
+ });
186
187
  }
187
188
  result = {
188
189
  tools: this.toolRegistry.getToolDefinitions(filterConfig)
@@ -248,141 +249,167 @@ class MCPServerService {
248
249
  res.end();
249
250
  }
250
251
  catch (error) {
251
- req.logger.error('MCP handler error', error, { apiKey: req.query.apiKey });
252
- this.sendMcpError(res, req.body?.id || null, -32000, `Server error: ${error instanceof Error ? error.message : String(error)}`, 500);
253
- }
254
- });
255
- // ===== Bot Configuration API =====
256
- // GET /api/bots - List all bots and their status
257
- this.app.get('/api/bots', async (req, res) => {
258
- req.logger.debug('List bots requested');
259
- try {
260
- const { AVAILABLE_BOTS, getBotState } = await Promise.resolve().then(() => __importStar(require('./mcp/tools/bot-config')));
261
- const state = getBotState();
262
- const bots = AVAILABLE_BOTS.map(bot => ({
263
- ...bot,
264
- enabled: state[bot.id] || false
265
- }));
266
- res.json({ bots });
267
- }
268
- catch (error) {
269
- req.logger.error('Failed to list bots', { error });
270
- res.status(500).json({ error: 'Failed to list bots' });
271
- }
272
- });
273
- // POST /api/bots/:id/enable - Enable a bot
274
- this.app.post('/api/bots/:id/enable', async (req, res) => {
275
- const { id } = req.params;
276
- req.logger.info('Enable bot requested', { botId: id });
277
- try {
278
- const { AVAILABLE_BOTS, setBotEnabled } = await Promise.resolve().then(() => __importStar(require('./mcp/tools/bot-config')));
279
- const bot = AVAILABLE_BOTS.find(b => b.id === id);
280
- if (!bot) {
281
- return res.status(404).json({ error: `Unknown bot: ${id}` });
252
+ const errorMessage = error instanceof Error ? error.message : String(error);
253
+ // Check for multi-workspace credentials error - return user-friendly error
254
+ // Use HTTP 200 so the JSON-RPC error reaches the client properly
255
+ if (errorMessage.includes('Multi-workspace credentials detected')) {
256
+ req.logger.warn('Multi-workspace credentials blocked', { apiKey: req.query.apiKey });
257
+ this.sendMcpError(res, req.body?.id || null, -32001, errorMessage, 200 // JSON-RPC errors should use HTTP 200 for proper client handling
258
+ );
282
259
  }
283
- setBotEnabled(id, true);
284
- res.json({ success: true, botId: id, enabled: true });
285
- }
286
- catch (error) {
287
- req.logger.error('Failed to enable bot', { botId: id, error });
288
- res.status(500).json({ error: 'Failed to enable bot' });
289
- }
290
- });
291
- // POST /api/bots/:id/disable - Disable a bot
292
- this.app.post('/api/bots/:id/disable', async (req, res) => {
293
- const { id } = req.params;
294
- req.logger.info('Disable bot requested', { botId: id });
295
- try {
296
- const { AVAILABLE_BOTS, setBotEnabled } = await Promise.resolve().then(() => __importStar(require('./mcp/tools/bot-config')));
297
- const bot = AVAILABLE_BOTS.find(b => b.id === id);
298
- if (!bot) {
299
- return res.status(404).json({ error: `Unknown bot: ${id}` });
260
+ else {
261
+ req.logger.error('MCP handler error', error, { apiKey: req.query.apiKey });
262
+ this.sendMcpError(res, req.body?.id || null, -32000, `Server error: ${errorMessage}`, 500);
300
263
  }
301
- setBotEnabled(id, false);
302
- res.json({ success: true, botId: id, enabled: false });
303
- }
304
- catch (error) {
305
- req.logger.error('Failed to disable bot', { botId: id, error });
306
- res.status(500).json({ error: 'Failed to disable bot' });
307
264
  }
308
265
  });
309
- // POST /api/bots/:id/toggle - Toggle a bot
310
- this.app.post('/api/bots/:id/toggle', async (req, res) => {
311
- const { id } = req.params;
312
- req.logger.info('Toggle bot requested', { botId: id });
313
- try {
314
- const { AVAILABLE_BOTS, getBotState, setBotEnabled } = await Promise.resolve().then(() => __importStar(require('./mcp/tools/bot-config')));
315
- const bot = AVAILABLE_BOTS.find(b => b.id === id);
316
- if (!bot) {
317
- return res.status(404).json({ error: `Unknown bot: ${id}` });
266
+ // ===== Bot Configuration API (only when MCP_CLIENT_ENABLED=true) =====
267
+ if (config_1.environment.MCP_CLIENT_ENABLED) {
268
+ // GET /api/bots - List all bots and their status
269
+ this.app.get('/api/bots', async (req, res) => {
270
+ req.logger.debug('List bots requested');
271
+ try {
272
+ const { AVAILABLE_BOTS, getBotState } = await Promise.resolve().then(() => __importStar(require('./bot-config')));
273
+ const state = getBotState();
274
+ const bots = AVAILABLE_BOTS.map(bot => ({
275
+ ...bot,
276
+ enabled: state[bot.id] || false
277
+ }));
278
+ res.json({ bots });
279
+ }
280
+ catch (error) {
281
+ req.logger.error('Failed to list bots', { error });
282
+ res.status(500).json({ error: 'Failed to list bots' });
318
283
  }
319
- const currentState = getBotState();
320
- const newState = !currentState[id];
321
- setBotEnabled(id, newState);
322
- res.json({ success: true, botId: id, enabled: newState });
323
- }
324
- catch (error) {
325
- req.logger.error('Failed to toggle bot', { botId: id, error });
326
- res.status(500).json({ error: 'Failed to toggle bot' });
327
- }
328
- });
329
- // ===== Bot Config Webhook API =====
330
- // Get secure webhook path (auto-generated token)
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
284
  });
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);
285
+ // POST /api/bots/:id/enable - Enable a bot
286
+ this.app.post('/api/bots/:id/enable', async (req, res) => {
287
+ const { id } = req.params;
288
+ req.logger.info('Enable bot requested', { botId: id });
289
+ try {
290
+ const { AVAILABLE_BOTS, setBotEnabled } = await Promise.resolve().then(() => __importStar(require('./bot-config')));
291
+ const bot = AVAILABLE_BOTS.find(b => b.id === id);
292
+ if (!bot) {
293
+ return res.status(404).json({ error: `Unknown bot: ${id}` });
294
+ }
295
+ setBotEnabled(id, true);
296
+ res.json({ success: true, botId: id, enabled: true });
348
297
  }
349
- else {
350
- req.logger.warn('Bot config webhook failed', { error: result.error });
351
- res.status(400).json(result);
298
+ catch (error) {
299
+ req.logger.error('Failed to enable bot', { botId: id, error });
300
+ res.status(500).json({ error: 'Failed to enable bot' });
352
301
  }
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',
302
+ });
303
+ // POST /api/bots/:id/disable - Disable a bot
304
+ this.app.post('/api/bots/:id/disable', async (req, res) => {
305
+ const { id } = req.params;
306
+ req.logger.info('Disable bot requested', { botId: id });
307
+ try {
308
+ const { AVAILABLE_BOTS, setBotEnabled } = await Promise.resolve().then(() => __importStar(require('./bot-config')));
309
+ const bot = AVAILABLE_BOTS.find(b => b.id === id);
310
+ if (!bot) {
311
+ return res.status(404).json({ error: `Unknown bot: ${id}` });
312
+ }
313
+ setBotEnabled(id, false);
314
+ res.json({ success: true, botId: id, enabled: false });
315
+ }
316
+ catch (error) {
317
+ req.logger.error('Failed to disable bot', { botId: id, error });
318
+ res.status(500).json({ error: 'Failed to disable bot' });
319
+ }
320
+ });
321
+ // POST /api/bots/:id/toggle - Toggle a bot
322
+ this.app.post('/api/bots/:id/toggle', async (req, res) => {
323
+ const { id } = req.params;
324
+ req.logger.info('Toggle bot requested', { botId: id });
325
+ try {
326
+ const { AVAILABLE_BOTS, getBotState, setBotEnabled } = await Promise.resolve().then(() => __importStar(require('./bot-config')));
327
+ const bot = AVAILABLE_BOTS.find(b => b.id === id);
328
+ if (!bot) {
329
+ return res.status(404).json({ error: `Unknown bot: ${id}` });
330
+ }
331
+ const currentState = getBotState();
332
+ const newState = !currentState[id];
333
+ setBotEnabled(id, newState);
334
+ res.json({ success: true, botId: id, enabled: newState });
335
+ }
336
+ catch (error) {
337
+ req.logger.error('Failed to toggle bot', { botId: id, error });
338
+ res.status(500).json({ error: 'Failed to toggle bot' });
339
+ }
340
+ });
341
+ // ===== Bot Config Webhook API =====
342
+ // Get secure webhook path (auto-generated token) - null if disabled
343
+ const webhookPath = (0, webhook_handler_1.getWebhookPath)();
344
+ if (webhookPath) {
345
+ // POST /webhook/{token} - Receives updates from Hailer workflow webhooks
346
+ this.app.post(webhookPath, (req, res) => {
347
+ req.logger.info('Bot config webhook received', {
348
+ activityId: req.body?._id,
349
+ activityName: req.body?.name,
350
+ workspaceId: req.body?.cid,
351
+ });
352
+ try {
353
+ const result = (0, webhook_handler_1.handleBotConfigWebhook)(req.body);
354
+ if (result.success) {
355
+ req.logger.info('Bot config updated via webhook', {
356
+ action: result.action,
357
+ workspaceId: result.workspaceId,
358
+ botType: result.botType,
359
+ });
360
+ res.status(200).json(result);
361
+ }
362
+ else {
363
+ req.logger.warn('Bot config webhook failed', { error: result.error });
364
+ res.status(400).json(result);
365
+ }
366
+ }
367
+ catch (error) {
368
+ req.logger.error('Bot config webhook error', { error });
369
+ res.status(500).json({
370
+ success: false,
371
+ error: error instanceof Error ? error.message : 'Internal error',
372
+ });
373
+ }
374
+ });
375
+ // GET /webhook/{token}/status - Status endpoint to see all workspace configs
376
+ this.app.get(`${webhookPath}/status`, (_req, res) => {
377
+ const configs = (0, webhook_handler_1.listWorkspaceConfigs)();
378
+ res.json({
379
+ timestamp: new Date().toISOString(),
380
+ workspaceCount: configs.length,
381
+ workspaces: configs.map((c) => ({
382
+ workspaceId: c.workspaceId,
383
+ workspaceName: c.workspaceName,
384
+ hasOrchestrator: !!c.orchestrator,
385
+ specialistCount: c.specialists.length,
386
+ enabledSpecialists: c.specialists.filter((s) => s.enabled).length,
387
+ lastSynced: c.lastSynced,
388
+ })),
389
+ });
359
390
  });
360
391
  }
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
- })),
392
+ else {
393
+ this.logger.info('Webhook endpoint disabled (no WEBHOOK_TOKEN)');
394
+ }
395
+ this.logger.debug('Routes configured', {
396
+ routes: [
397
+ '/health',
398
+ '/daemon/status',
399
+ '/api/mcp',
400
+ '/api/bots'
401
+ ]
376
402
  });
377
- });
378
- this.logger.debug('Routes configured', {
379
- routes: [
380
- '/health',
381
- '/daemon/status',
382
- '/api/mcp',
383
- '/api/bots'
384
- ]
385
- });
403
+ }
404
+ else {
405
+ this.logger.debug('Routes configured', {
406
+ routes: [
407
+ '/health',
408
+ '/daemon/status',
409
+ '/api/mcp'
410
+ ]
411
+ });
412
+ }
386
413
  }
387
414
  /**
388
415
  * Check if agent has access to a specific tool
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hailer/mcp",
3
- "version": "0.1.16",
3
+ "version": "0.2.1",
4
4
  "config": {
5
5
  "docker": {
6
6
  "registry": "registry.gitlab.com/hailer-repos/hailer-mcp"