@hailer/mcp 1.0.28 → 1.1.2

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 (233) hide show
  1. package/.claude/.session-checked +1 -0
  2. package/.claude/agents/agent-ada-skill-builder.md +10 -2
  3. package/.claude/agents/agent-alejandro-function-fields.md +104 -37
  4. package/.claude/agents/agent-bjorn-config-audit.md +41 -21
  5. package/.claude/agents/agent-builder-agent-creator.md +13 -3
  6. package/.claude/agents/agent-code-simplifier.md +53 -0
  7. package/.claude/agents/agent-dmitri-activity-crud.md +126 -11
  8. package/.claude/agents/agent-giuseppe-app-builder.md +212 -22
  9. package/.claude/agents/agent-gunther-mcp-tools.md +7 -36
  10. package/.claude/agents/agent-helga-workflow-config.md +75 -10
  11. package/.claude/agents/agent-igor-activity-mover-automation.md +125 -0
  12. package/.claude/agents/agent-ingrid-doc-templates.md +164 -36
  13. package/.claude/agents/agent-ivan-monolith.md +154 -0
  14. package/.claude/agents/agent-kenji-data-reader.md +15 -8
  15. package/.claude/agents/agent-lars-code-inspector.md +56 -8
  16. package/.claude/agents/agent-marco-mockup-builder.md +110 -0
  17. package/.claude/agents/agent-marcus-api-documenter.md +323 -0
  18. package/.claude/agents/agent-marketplace-publisher.md +232 -72
  19. package/.claude/agents/agent-marketplace-reviewer.md +255 -79
  20. package/.claude/agents/agent-permissions-handler.md +208 -0
  21. package/.claude/agents/agent-simple-writer.md +48 -0
  22. package/.claude/agents/agent-svetlana-code-review.md +127 -14
  23. package/.claude/agents/agent-tanya-test-runner.md +333 -0
  24. package/.claude/agents/agent-ui-designer.md +100 -0
  25. package/.claude/agents/agent-viktor-sql-insights.md +19 -6
  26. package/.claude/agents/agent-web-search.md +55 -0
  27. package/.claude/agents/agent-yevgeni-discussions.md +7 -1
  28. package/.claude/agents/agent-zara-zapier.md +159 -0
  29. package/.claude/commands/app-squad.md +135 -0
  30. package/.claude/commands/audit-squad.md +158 -0
  31. package/.claude/commands/autoplan.md +563 -0
  32. package/.claude/commands/cleanup-squad.md +98 -0
  33. package/.claude/commands/config-squad.md +106 -0
  34. package/.claude/commands/crud-squad.md +87 -0
  35. package/.claude/commands/data-squad.md +97 -0
  36. package/.claude/commands/debug-squad.md +303 -0
  37. package/.claude/commands/doc-squad.md +65 -0
  38. package/.claude/commands/handoff.md +137 -0
  39. package/.claude/commands/health.md +49 -0
  40. package/.claude/commands/help.md +2 -1
  41. package/.claude/commands/help:agents.md +96 -16
  42. package/.claude/commands/help:commands.md +55 -11
  43. package/.claude/commands/help:faq.md +16 -1
  44. package/.claude/commands/help:skills.md +93 -0
  45. package/.claude/commands/hotfix-squad.md +112 -0
  46. package/.claude/commands/integration-squad.md +82 -0
  47. package/.claude/commands/janitor-squad.md +167 -0
  48. package/.claude/commands/learn-auto.md +120 -0
  49. package/.claude/commands/learn.md +120 -0
  50. package/.claude/commands/mcp-list.md +27 -0
  51. package/.claude/commands/onboard-squad.md +140 -0
  52. package/.claude/commands/plan-workspace.md +732 -0
  53. package/.claude/commands/prd.md +131 -0
  54. package/.claude/commands/project-status.md +82 -0
  55. package/.claude/commands/publish.md +138 -0
  56. package/.claude/commands/recap.md +69 -0
  57. package/.claude/commands/restore.md +64 -0
  58. package/.claude/commands/review-squad.md +152 -0
  59. package/.claude/commands/save.md +24 -0
  60. package/.claude/commands/stats.md +19 -0
  61. package/.claude/commands/swarm.md +210 -0
  62. package/.claude/commands/tool-builder.md +3 -1
  63. package/.claude/commands/ws-pull.md +1 -1
  64. package/.claude/commands/yolo-off.md +17 -0
  65. package/.claude/commands/yolo.md +82 -0
  66. package/.claude/hooks/_shared-memory.cjs +305 -0
  67. package/.claude/hooks/_utils.cjs +134 -0
  68. package/.claude/hooks/agent-failure-detector.cjs +164 -79
  69. package/.claude/hooks/agent-usage-logger.cjs +204 -0
  70. package/.claude/hooks/app-edit-guard.cjs +20 -4
  71. package/.claude/hooks/auto-learn.cjs +316 -0
  72. package/.claude/hooks/bash-guard.cjs +282 -0
  73. package/.claude/hooks/builder-mode-manager.cjs +183 -54
  74. package/.claude/hooks/bulk-activity-guard.cjs +283 -0
  75. package/.claude/hooks/context-watchdog.cjs +292 -0
  76. package/.claude/hooks/delegation-reminder.cjs +478 -0
  77. package/.claude/hooks/design-system-lint.cjs +283 -0
  78. package/.claude/hooks/post-scaffold-hook.cjs +16 -3
  79. package/.claude/hooks/prompt-guard.cjs +366 -0
  80. package/.claude/hooks/publish-template-guard.cjs +16 -0
  81. package/.claude/hooks/session-start.cjs +35 -0
  82. package/.claude/hooks/shared-memory-writer.cjs +147 -0
  83. package/.claude/hooks/skill-injector.cjs +140 -0
  84. package/.claude/hooks/skill-usage-logger.cjs +258 -0
  85. package/.claude/hooks/src-edit-guard.cjs +16 -1
  86. package/.claude/hooks/sync-marketplace-agents.cjs +53 -8
  87. package/.claude/scripts/yolo-toggle.cjs +142 -0
  88. package/.claude/settings.json +141 -14
  89. package/.claude/skills/SDK-activity-patterns/SKILL.md +428 -0
  90. package/.claude/skills/SDK-document-templates/SKILL.md +1033 -0
  91. package/.claude/skills/SDK-function-fields/SKILL.md +542 -0
  92. package/.claude/skills/SDK-generate-skill/SKILL.md +92 -0
  93. package/.claude/skills/SDK-init-skill/SKILL.md +127 -0
  94. package/.claude/skills/SDK-insight-queries/SKILL.md +787 -0
  95. package/.claude/skills/SDK-ws-config-skill/SKILL.md +1139 -0
  96. package/.claude/skills/agent-structure/SKILL.md +98 -0
  97. package/.claude/skills/api-documentation-patterns/SKILL.md +474 -0
  98. package/.claude/skills/chrome-mcp-reference/SKILL.md +370 -0
  99. package/.claude/skills/delegation-routing/SKILL.md +202 -0
  100. package/.claude/skills/frontend-design/SKILL.md +254 -0
  101. package/.claude/skills/hailer-activity-mover/SKILL.md +213 -0
  102. package/.claude/skills/hailer-api-client/SKILL.md +518 -0
  103. package/.claude/skills/hailer-app-builder/SKILL.md +939 -11
  104. package/.claude/skills/hailer-apps-pictures/SKILL.md +269 -0
  105. package/.claude/skills/hailer-design-system/SKILL.md +235 -0
  106. package/.claude/skills/hailer-monolith-automations/SKILL.md +686 -0
  107. package/.claude/skills/hailer-permissions-system/SKILL.md +121 -0
  108. package/.claude/skills/hailer-project-protocol/SKILL.md +488 -0
  109. package/.claude/skills/hailer-rest-api/SKILL.md +61 -0
  110. package/.claude/skills/hailer-rest-api/hailer-activities.md +184 -0
  111. package/.claude/skills/hailer-rest-api/hailer-admin.md +473 -0
  112. package/.claude/skills/hailer-rest-api/hailer-calendar.md +256 -0
  113. package/.claude/skills/hailer-rest-api/hailer-feed.md +249 -0
  114. package/.claude/skills/hailer-rest-api/hailer-insights.md +195 -0
  115. package/.claude/skills/hailer-rest-api/hailer-messaging.md +276 -0
  116. package/.claude/skills/hailer-rest-api/hailer-workflows.md +283 -0
  117. package/.claude/skills/insight-join-patterns/SKILL.md +3 -0
  118. package/.claude/skills/integration-patterns/SKILL.md +421 -0
  119. package/.claude/skills/json-only-output/SKILL.md +52 -12
  120. package/.claude/skills/lsp-setup/SKILL.md +160 -0
  121. package/.claude/skills/mcp-direct-tools/SKILL.md +153 -0
  122. package/.claude/skills/optional-parameters/SKILL.md +32 -23
  123. package/.claude/skills/publish-hailer-app/SKILL.md +76 -12
  124. package/.claude/skills/testing-patterns/SKILL.md +630 -0
  125. package/.claude/skills/tool-builder/SKILL.md +250 -0
  126. package/.claude/skills/tool-parameter-usage/SKILL.md +59 -45
  127. package/.claude/skills/tool-response-verification/SKILL.md +82 -48
  128. package/.claude/skills/zapier-hailer-patterns/SKILL.md +581 -0
  129. package/.env.example +26 -7
  130. package/CLAUDE.md +290 -224
  131. package/dist/CLAUDE.md +370 -0
  132. package/dist/app.d.ts +1 -1
  133. package/dist/app.js +101 -101
  134. package/dist/bot/bot-config.d.ts +26 -0
  135. package/dist/bot/bot-config.js +135 -0
  136. package/dist/bot/bot-manager.d.ts +40 -0
  137. package/dist/bot/bot-manager.js +137 -0
  138. package/dist/bot/bot.d.ts +127 -0
  139. package/dist/bot/bot.js +1328 -0
  140. package/dist/bot/operation-logger.d.ts +28 -0
  141. package/dist/bot/operation-logger.js +132 -0
  142. package/dist/bot/services/conversation-manager.d.ts +60 -0
  143. package/dist/bot/services/conversation-manager.js +246 -0
  144. package/dist/bot/services/index.d.ts +9 -0
  145. package/dist/bot/services/index.js +18 -0
  146. package/dist/bot/services/message-classifier.d.ts +42 -0
  147. package/dist/bot/services/message-classifier.js +228 -0
  148. package/dist/bot/services/message-formatter.d.ts +88 -0
  149. package/dist/bot/services/message-formatter.js +411 -0
  150. package/dist/bot/services/session-logger.d.ts +162 -0
  151. package/dist/bot/services/session-logger.js +724 -0
  152. package/dist/bot/services/token-billing.d.ts +78 -0
  153. package/dist/bot/services/token-billing.js +233 -0
  154. package/dist/bot/services/types.d.ts +169 -0
  155. package/dist/bot/services/types.js +12 -0
  156. package/dist/bot/services/typing-indicator.d.ts +23 -0
  157. package/dist/bot/services/typing-indicator.js +60 -0
  158. package/dist/bot/services/workspace-schema-cache.d.ts +122 -0
  159. package/dist/bot/services/workspace-schema-cache.js +506 -0
  160. package/dist/bot/tool-executor.d.ts +28 -0
  161. package/dist/bot/tool-executor.js +48 -0
  162. package/dist/bot/workspace-overview.d.ts +12 -0
  163. package/dist/bot/workspace-overview.js +94 -0
  164. package/dist/cli.d.ts +1 -8
  165. package/dist/cli.js +1 -249
  166. package/dist/config.d.ts +96 -3
  167. package/dist/config.js +148 -37
  168. package/dist/core.d.ts +5 -0
  169. package/dist/core.js +61 -8
  170. package/dist/lib/discussion-lock.d.ts +42 -0
  171. package/dist/lib/discussion-lock.js +110 -0
  172. package/dist/lib/logger.d.ts +0 -1
  173. package/dist/lib/logger.js +39 -23
  174. package/dist/lib/request-logger.d.ts +77 -0
  175. package/dist/lib/request-logger.js +147 -0
  176. package/dist/mcp/UserContextCache.js +16 -13
  177. package/dist/mcp/hailer-clients.js +18 -17
  178. package/dist/mcp/signal-handler.js +29 -13
  179. package/dist/mcp/tool-registry.d.ts +4 -15
  180. package/dist/mcp/tool-registry.js +94 -32
  181. package/dist/mcp/tools/activity.js +28 -69
  182. package/dist/mcp/tools/app-core.js +9 -4
  183. package/dist/mcp/tools/app-marketplace.js +22 -12
  184. package/dist/mcp/tools/app-member.js +5 -2
  185. package/dist/mcp/tools/app-scaffold.js +32 -18
  186. package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
  187. package/dist/mcp/tools/bot-config/constants.js +94 -0
  188. package/dist/mcp/tools/bot-config/core.d.ts +253 -0
  189. package/dist/mcp/tools/bot-config/core.js +2456 -0
  190. package/dist/mcp/tools/bot-config/index.d.ts +10 -0
  191. package/dist/mcp/tools/bot-config/index.js +59 -0
  192. package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
  193. package/dist/mcp/tools/bot-config/tools.js +15 -0
  194. package/dist/mcp/tools/bot-config/types.d.ts +50 -0
  195. package/dist/mcp/tools/bot-config/types.js +6 -0
  196. package/dist/mcp/tools/discussion.js +107 -77
  197. package/dist/mcp/tools/document.d.ts +11 -0
  198. package/dist/mcp/tools/document.js +741 -0
  199. package/dist/mcp/tools/file.js +5 -2
  200. package/dist/mcp/tools/insight.js +36 -12
  201. package/dist/mcp/tools/investigate.d.ts +9 -0
  202. package/dist/mcp/tools/investigate.js +254 -0
  203. package/dist/mcp/tools/user.d.ts +2 -4
  204. package/dist/mcp/tools/user.js +9 -50
  205. package/dist/mcp/tools/workflow.d.ts +1 -0
  206. package/dist/mcp/tools/workflow.js +164 -52
  207. package/dist/mcp/utils/hailer-api-client.js +26 -17
  208. package/dist/mcp/webhook-handler.d.ts +64 -3
  209. package/dist/mcp/webhook-handler.js +219 -9
  210. package/dist/mcp-server.d.ts +4 -0
  211. package/dist/mcp-server.js +237 -25
  212. package/dist/plugins/bug-fixer/index.d.ts +2 -0
  213. package/dist/plugins/bug-fixer/index.js +18 -0
  214. package/dist/plugins/bug-fixer/tools.d.ts +45 -0
  215. package/dist/plugins/bug-fixer/tools.js +1096 -0
  216. package/package.json +10 -10
  217. package/scripts/test-hal-tools.ts +154 -0
  218. package/.claude/agents/agent-nora-name-functions.md +0 -123
  219. package/.claude/assistant-knowledge.md +0 -23
  220. package/.claude/commands/install-plugin.md +0 -261
  221. package/.claude/commands/list-plugins.md +0 -42
  222. package/.claude/commands/marketplace-setup.md +0 -33
  223. package/.claude/commands/publish-plugin.md +0 -55
  224. package/.claude/commands/uninstall-plugin.md +0 -87
  225. package/.claude/hooks/interactive-mode.cjs +0 -87
  226. package/.claude/hooks/mcp-server-guard.cjs +0 -108
  227. package/.claude/skills/marketplace-publishing.md +0 -155
  228. package/dist/bot/chat-bot.d.ts +0 -31
  229. package/dist/bot/chat-bot.js +0 -357
  230. package/dist/mcp/tools/metrics.d.ts +0 -13
  231. package/dist/mcp/tools/metrics.js +0 -546
  232. package/dist/stdio-server.d.ts +0 -14
  233. package/dist/stdio-server.js +0 -114
@@ -1,546 +0,0 @@
1
- "use strict";
2
- /**
3
- * Victoria Metrics Tools - Query and List Metrics
4
- *
5
- * Tools for querying Hailer metrics from Victoria Metrics:
6
- * - Query metrics with PromQL (READ)
7
- * - List available metrics (READ)
8
- */
9
- var __importDefault = (this && this.__importDefault) || function (mod) {
10
- return (mod && mod.__esModule) ? mod : { "default": mod };
11
- };
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.searchUserForMetricsTool = exports.searchWorkspaceForMetricsTool = exports.listMetricsTool = exports.queryMetricTool = void 0;
14
- const zod_1 = require("zod");
15
- const tool_registry_1 = require("../tool-registry");
16
- const index_1 = require("../utils/index");
17
- const axios_1 = __importDefault(require("axios"));
18
- const hailer_clients_1 = require("../hailer-clients");
19
- const logger = (0, index_1.createLogger)({ component: 'metrics-tools' });
20
- // ============================================================================
21
- // CONFIGURATION
22
- // ============================================================================
23
- const METRICS_CONFIG = {
24
- host: process.env.METRICS_QUERY_HOST || 'observer.hailer.com',
25
- port: Number(process.env.METRICS_QUERY_PORT) || 443,
26
- secure: process.env.METRICS_QUERY_SECURE !== 'false',
27
- user: process.env.METRICS_QUERY_USER || 'metrics',
28
- password: process.env.METRICS_QUERY_PASSWORD || 'verysecuremetricspassword42',
29
- };
30
- // Metrics Admin API credentials (for workspace/user search - requires whitelisted user)
31
- const METRICS_ADMIN_CONFIG = {
32
- email: process.env.METRICS_ADMIN_EMAIL || '',
33
- password: process.env.METRICS_ADMIN_PASSWORD || '',
34
- apiBaseUrl: process.env.METRICS_ADMIN_API_URL || 'https://api.hailer.com',
35
- };
36
- logger.info('Metrics admin config loaded', {
37
- email: METRICS_ADMIN_CONFIG.email || '(not set)',
38
- apiBaseUrl: METRICS_ADMIN_CONFIG.apiBaseUrl,
39
- hasPassword: !!METRICS_ADMIN_CONFIG.password,
40
- });
41
- // ============================================================================
42
- // HELPER FUNCTIONS
43
- // ============================================================================
44
- // Cached admin client for metrics operations
45
- let metricsAdminClient = null;
46
- let metricsAdminManager = null;
47
- /**
48
- * Get or create metrics admin client for privileged operations
49
- */
50
- async function getMetricsAdminClient() {
51
- if (!METRICS_ADMIN_CONFIG.email || !METRICS_ADMIN_CONFIG.password) {
52
- throw new Error('METRICS_ADMIN_EMAIL and METRICS_ADMIN_PASSWORD must be set for workspace/user search');
53
- }
54
- // Return cached client if connected
55
- if (metricsAdminClient && metricsAdminManager?.isConnected()) {
56
- return metricsAdminClient;
57
- }
58
- // Clean up stale connection
59
- if (metricsAdminManager) {
60
- metricsAdminManager.disconnect();
61
- }
62
- logger.info('Creating metrics admin connection', {
63
- email: METRICS_ADMIN_CONFIG.email,
64
- apiBaseUrl: METRICS_ADMIN_CONFIG.apiBaseUrl,
65
- });
66
- // Create new connection using WebSocket (same as regular clients)
67
- metricsAdminManager = new hailer_clients_1.HailerClientManager(METRICS_ADMIN_CONFIG.apiBaseUrl, METRICS_ADMIN_CONFIG.email, METRICS_ADMIN_CONFIG.password);
68
- metricsAdminClient = await metricsAdminManager.connect();
69
- logger.info('Metrics admin connected successfully');
70
- return metricsAdminClient;
71
- }
72
- /**
73
- * Make admin API request with special credentials via WebSocket
74
- */
75
- async function metricsAdminRequest(method, params) {
76
- const client = await getMetricsAdminClient();
77
- return client.socket.request(method, params);
78
- }
79
- /**
80
- * Get Victoria Metrics base URL
81
- */
82
- function getMetricsBaseUrl() {
83
- const protocol = METRICS_CONFIG.secure ? 'https' : 'http';
84
- const port = METRICS_CONFIG.port !== (METRICS_CONFIG.secure ? 443 : 80)
85
- ? `:${METRICS_CONFIG.port}`
86
- : '';
87
- return `${protocol}://${METRICS_CONFIG.host}${port}`;
88
- }
89
- /**
90
- * Get Basic Auth credentials
91
- */
92
- function getAuthHeader() {
93
- return {
94
- username: METRICS_CONFIG.user,
95
- password: METRICS_CONFIG.password,
96
- };
97
- }
98
- /**
99
- * Parse time range string to PromQL duration
100
- */
101
- function parseTimeRange(timeRange) {
102
- const validRanges = ['1h', '24h', '3d', '7d', '30d'];
103
- if (!validRanges.includes(timeRange)) {
104
- throw new Error(`Invalid time range "${timeRange}". Valid options: ${validRanges.join(', ')}`);
105
- }
106
- return timeRange;
107
- }
108
- /**
109
- * Build PromQL query from parameters
110
- */
111
- function buildPromQLQuery(metric, timeRange, aggregation, groupBy, filters) {
112
- // Build label filters
113
- let labelFilters = '';
114
- if (filters && Object.keys(filters).length > 0) {
115
- const filterPairs = Object.entries(filters).map(([key, value]) => `${key}="${value}"`);
116
- labelFilters = filterPairs.join(',');
117
- }
118
- // Build metric selector
119
- const metricSelector = labelFilters
120
- ? `${metric}{${labelFilters}}`
121
- : metric;
122
- // Build aggregation with groupBy
123
- const groupByClause = groupBy && groupBy.length > 0
124
- ? ` by (${groupBy.join(', ')})`
125
- : '';
126
- // Build final query
127
- const query = `${aggregation}${groupByClause} (increase(${metricSelector}[${timeRange}]))`;
128
- return query;
129
- }
130
- /**
131
- * Format query results for display
132
- */
133
- function formatQueryResults(data) {
134
- if (!data || !data.result || data.result.length === 0) {
135
- return {
136
- formatted: 'No data returned for this query.',
137
- raw: data,
138
- };
139
- }
140
- // Single value result
141
- if (data.result.length === 1 && !data.result[0].metric) {
142
- const value = data.result[0].value[1];
143
- return {
144
- formatted: `Result: ${value}`,
145
- raw: { value: Number(value) },
146
- };
147
- }
148
- // Multiple values or grouped results
149
- const results = data.result.map((item) => {
150
- const labels = item.metric || {};
151
- const value = item.value[1];
152
- return {
153
- labels,
154
- value: Number(value),
155
- };
156
- });
157
- // Format as table
158
- let formatted = `Found ${results.length} result(s):\n\n`;
159
- for (const result of results) {
160
- const labelStr = Object.entries(result.labels)
161
- .map(([k, v]) => `${k}="${v}"`)
162
- .join(', ');
163
- formatted += `- ${labelStr ? `{${labelStr}}` : 'total'}: ${result.value}\n`;
164
- }
165
- return {
166
- formatted,
167
- raw: results,
168
- };
169
- }
170
- // ============================================================================
171
- // TOOL 1: QUERY METRIC
172
- // ============================================================================
173
- /**
174
- * Generate ASCII bar chart from data
175
- */
176
- function generateAsciiChart(data, options = {}) {
177
- const { maxWidth = 40, showPercent = true } = options;
178
- if (data.length === 0)
179
- return 'No data to display';
180
- const maxValue = Math.max(...data.map(d => d.value));
181
- const total = data.reduce((sum, d) => sum + d.value, 0);
182
- // Find max label length for alignment
183
- const maxLabelLen = Math.max(...data.map(d => d.label.length), 10);
184
- const lines = data.map(item => {
185
- const barLength = maxValue > 0 ? Math.round((item.value / maxValue) * maxWidth) : 0;
186
- const bar = '█'.repeat(barLength);
187
- const label = item.label.padEnd(maxLabelLen);
188
- const valueStr = item.value.toLocaleString();
189
- const percent = showPercent && total > 0
190
- ? ` (${Math.round(item.value / total * 100)}%)`
191
- : '';
192
- return `${label} ${bar} ${valueStr}${percent}`;
193
- });
194
- return lines.join('\n');
195
- }
196
- /**
197
- * Format grouped results as ASCII chart
198
- */
199
- function formatAsChart(data, groupByLabel) {
200
- if (!data || !data.result || data.result.length === 0) {
201
- return 'No data to display';
202
- }
203
- const chartData = data.result
204
- .map((item) => ({
205
- label: item.metric?.[groupByLabel] || 'unknown',
206
- value: Math.round(parseFloat(item.value?.[1] || 0)),
207
- }))
208
- .filter((item) => item.value > 0)
209
- .sort((a, b) => b.value - a.value)
210
- .slice(0, 10); // Top 10
211
- const total = chartData.reduce((sum, d) => sum + d.value, 0);
212
- let output = generateAsciiChart(chartData);
213
- output += `\n\nTotal: ${total.toLocaleString()}`;
214
- return output;
215
- }
216
- // ============================================================================
217
- // TOOL 1: QUERY METRIC
218
- // ============================================================================
219
- exports.queryMetricTool = {
220
- name: 'query_metric',
221
- group: tool_registry_1.ToolGroup.READ,
222
- description: `Query metrics from Victoria Metrics using PromQL. Available metrics:
223
- - hailer.activity.create - Activities created (labels: workspace, process, team, userRole)
224
- - hailer.message.create - Messages sent (labels: workspace, discussion, userRole)
225
- - hailer.file.upload - Files uploaded (labels: workspace)
226
- - hailer.telemetry - UI events (labels: workspace, user, element)
227
-
228
- Supports ASCII chart visualization for grouped data (format: "chart")`,
229
- schema: zod_1.z.object({
230
- metric: zod_1.z.string().describe('Metric name (e.g., "hailer.activity.create")'),
231
- timeRange: zod_1.z.enum(['1h', '24h', '3d', '7d', '30d']).optional().default('24h').describe('Time range: 1h, 24h, 3d, 7d, 30d'),
232
- aggregation: zod_1.z.enum(['sum', 'avg', 'max', 'min', 'count']).optional().default('sum').describe('Aggregation function'),
233
- groupBy: zod_1.z.preprocess((val) => {
234
- if (typeof val === 'string') {
235
- try {
236
- return JSON.parse(val);
237
- }
238
- catch {
239
- return val.split(',').map(s => s.trim()).filter(Boolean);
240
- }
241
- }
242
- return val;
243
- }, zod_1.z.array(zod_1.z.string()).optional()).describe('Group by labels like ["workspace", "process"] (optional)'),
244
- filters: zod_1.z.preprocess((val) => typeof val === 'string' ? JSON.parse(val) : val, zod_1.z.record(zod_1.z.string()).optional()).describe('Label filters like { "workspace": "abc123", "userRole": "admin" } (optional)'),
245
- format: zod_1.z.enum(["json", "chart"]).optional().default("json").describe("Output format: 'json' for raw data, 'chart' for ASCII bar chart"),
246
- }),
247
- async execute(args, _context) {
248
- try {
249
- logger.info('Querying metric', {
250
- metric: args.metric,
251
- timeRange: args.timeRange,
252
- aggregation: args.aggregation,
253
- groupBy: args.groupBy,
254
- filters: args.filters,
255
- });
256
- // Parse and validate time range
257
- const timeRange = parseTimeRange(args.timeRange || '24h');
258
- // Build PromQL query
259
- const promqlQuery = buildPromQLQuery(args.metric, timeRange, args.aggregation || 'sum', args.groupBy, args.filters);
260
- logger.debug('Built PromQL query', { query: promqlQuery });
261
- // Execute query
262
- const baseUrl = getMetricsBaseUrl();
263
- const url = `${baseUrl}/api/v1/query`;
264
- const response = await axios_1.default.get(url, {
265
- params: { query: promqlQuery },
266
- auth: getAuthHeader(),
267
- timeout: 10000,
268
- });
269
- if (response.data.status !== 'success') {
270
- throw new Error(`Query failed: ${response.data.error || 'Unknown error'}`);
271
- }
272
- // Format results
273
- const { formatted, raw } = formatQueryResults(response.data.data);
274
- // Format output based on requested format
275
- const format = args.format || 'json';
276
- let responseText;
277
- if (format === 'chart' && args.groupBy && args.groupBy.length > 0) {
278
- // ASCII chart for grouped data
279
- const chartTitle = `📊 ${args.metric} by ${args.groupBy[0]} (${args.timeRange})\n${'─'.repeat(50)}\n`;
280
- responseText = chartTitle + formatAsChart(response.data.data, args.groupBy[0]);
281
- }
282
- else {
283
- // JSON format (default)
284
- responseText = `📊 **Metric Query Results**
285
-
286
- **Query:** \`${promqlQuery}\`
287
- **Time Range:** ${args.timeRange}
288
- **Aggregation:** ${args.aggregation}
289
-
290
- ${formatted}
291
-
292
- **Raw Data:**
293
- \`\`\`json
294
- ${JSON.stringify(raw, null, 2)}
295
- \`\`\``;
296
- }
297
- return {
298
- content: [
299
- {
300
- type: 'text',
301
- text: responseText,
302
- },
303
- ],
304
- };
305
- }
306
- catch (error) {
307
- logger.error('Failed to query metric', error, {
308
- metric: args.metric,
309
- timeRange: args.timeRange,
310
- });
311
- const errorMessage = error instanceof Error
312
- ? error.message
313
- : String(error);
314
- return {
315
- content: [
316
- {
317
- type: 'text',
318
- text: `❌ Failed to query metric: ${errorMessage}\n\n💡 **Troubleshooting:**\n- Verify the metric name is correct (use list_metrics to see available metrics)\n- Check time range is valid (1h, 24h, 3d, 7d, 30d)\n- Ensure filters match existing labels\n- Verify Victoria Metrics connection settings`,
319
- },
320
- ],
321
- };
322
- }
323
- },
324
- };
325
- // ============================================================================
326
- // TOOL 2: LIST METRICS
327
- // ============================================================================
328
- exports.listMetricsTool = {
329
- name: 'list_metrics',
330
- group: tool_registry_1.ToolGroup.READ,
331
- description: 'List all available Hailer metrics from Victoria Metrics',
332
- schema: zod_1.z.object({}),
333
- async execute(_args, _context) {
334
- try {
335
- logger.info('Listing available metrics');
336
- // Query Victoria Metrics for all metric names
337
- const baseUrl = getMetricsBaseUrl();
338
- const url = `${baseUrl}/api/v1/label/__name__/values`;
339
- const response = await axios_1.default.get(url, {
340
- auth: getAuthHeader(),
341
- timeout: 10000,
342
- });
343
- if (response.data.status !== 'success') {
344
- throw new Error(`Query failed: ${response.data.error || 'Unknown error'}`);
345
- }
346
- const metrics = response.data.data || [];
347
- // Filter for Hailer metrics only
348
- const hailerMetrics = metrics.filter((m) => m.startsWith('hailer.'));
349
- // Group metrics by category
350
- const categorized = {
351
- activity: [],
352
- message: [],
353
- file: [],
354
- telemetry: [],
355
- other: [],
356
- };
357
- for (const metric of hailerMetrics) {
358
- if (metric.includes('activity')) {
359
- categorized.activity.push(metric);
360
- }
361
- else if (metric.includes('message')) {
362
- categorized.message.push(metric);
363
- }
364
- else if (metric.includes('file')) {
365
- categorized.file.push(metric);
366
- }
367
- else if (metric.includes('telemetry')) {
368
- categorized.telemetry.push(metric);
369
- }
370
- else {
371
- categorized.other.push(metric);
372
- }
373
- }
374
- // Format output
375
- let formatted = `📊 **Available Hailer Metrics** (${hailerMetrics.length} total)\n\n`;
376
- if (categorized.activity.length > 0) {
377
- formatted += `**Activity Metrics:**\n`;
378
- for (const metric of categorized.activity) {
379
- formatted += `- \`${metric}\` - Activities created/updated\n`;
380
- }
381
- formatted += '\n';
382
- }
383
- if (categorized.message.length > 0) {
384
- formatted += `**Message Metrics:**\n`;
385
- for (const metric of categorized.message) {
386
- formatted += `- \`${metric}\` - Messages and discussions\n`;
387
- }
388
- formatted += '\n';
389
- }
390
- if (categorized.file.length > 0) {
391
- formatted += `**File Metrics:**\n`;
392
- for (const metric of categorized.file) {
393
- formatted += `- \`${metric}\` - File uploads and operations\n`;
394
- }
395
- formatted += '\n';
396
- }
397
- if (categorized.telemetry.length > 0) {
398
- formatted += `**Telemetry Metrics:**\n`;
399
- for (const metric of categorized.telemetry) {
400
- formatted += `- \`${metric}\` - UI events and user interactions\n`;
401
- }
402
- formatted += '\n';
403
- }
404
- if (categorized.other.length > 0) {
405
- formatted += `**Other Metrics:**\n`;
406
- for (const metric of categorized.other) {
407
- formatted += `- \`${metric}\`\n`;
408
- }
409
- formatted += '\n';
410
- }
411
- formatted += `\n💡 **Usage:**\nUse \`query_metric\` with any of these metric names to query data.\n\n`;
412
- formatted += `**Common Labels:**\n- workspace: Workspace ID\n- process: Workflow ID\n- team: Team ID\n- userRole: User role (admin, member, etc.)\n- discussion: Discussion ID\n- user: User ID\n- element: UI element name`;
413
- return {
414
- content: [
415
- {
416
- type: 'text',
417
- text: formatted,
418
- },
419
- ],
420
- };
421
- }
422
- catch (error) {
423
- logger.error('Failed to list metrics', error);
424
- const errorMessage = error instanceof Error
425
- ? error.message
426
- : String(error);
427
- return {
428
- content: [
429
- {
430
- type: 'text',
431
- text: `❌ Failed to list metrics: ${errorMessage}\n\n💡 **Troubleshooting:**\n- Verify Victoria Metrics connection settings\n- Check network connectivity to ${METRICS_CONFIG.host}\n- Ensure credentials are correct`,
432
- },
433
- ],
434
- };
435
- }
436
- },
437
- };
438
- // ============================================================================
439
- // TOOL 3: SEARCH WORKSPACE FOR METRICS
440
- // ============================================================================
441
- exports.searchWorkspaceForMetricsTool = {
442
- name: 'search_workspace_for_metrics',
443
- group: tool_registry_1.ToolGroup.READ,
444
- description: `Search for workspaces by name to get their IDs for metric queries.
445
- Use this tool when user mentions a workspace by name (e.g. "Sales Team") to find its ID for filtering metrics.`,
446
- schema: zod_1.z.object({
447
- name: zod_1.z.string().min(3).describe("Workspace name to search (min 3 characters)"),
448
- limit: zod_1.z.number().min(1).max(100).optional().default(20).describe("Maximum results to return (default 20, max 100)"),
449
- }),
450
- async execute(args, _context) {
451
- try {
452
- logger.info('Searching workspace for metrics', { name: args.name });
453
- // Use admin credentials for this privileged operation
454
- const result = await metricsAdminRequest('v3.metrics.workspaceSearch', [{ name: args.name }]);
455
- if (!result || result.length === 0) {
456
- return {
457
- content: [{
458
- type: 'text',
459
- text: `No workspaces found matching "${args.name}"`,
460
- }],
461
- };
462
- }
463
- const totalCount = result.length;
464
- const limitedResults = result.slice(0, args.limit || 20);
465
- const workspaceList = limitedResults
466
- .map((ws) => `- ${ws.name} (ID: ${ws._id})`)
467
- .join('\n');
468
- const truncationNote = totalCount > limitedResults.length
469
- ? `\n\n⚠️ Showing ${limitedResults.length} of ${totalCount} results. Use a more specific search term or increase limit.`
470
- : '';
471
- return {
472
- content: [{
473
- type: 'text',
474
- text: `Found ${totalCount} workspace(s)${totalCount > limitedResults.length ? ` (showing first ${limitedResults.length})` : ''}:\n${workspaceList}${truncationNote}\n\n💡 Use the ID in metric queries with filters: { "workspace": "ID" }`,
475
- }],
476
- };
477
- }
478
- catch (error) {
479
- logger.error('Failed to search workspaces for metrics', error);
480
- const errorMessage = error instanceof Error ? error.message : String(error);
481
- return {
482
- content: [{
483
- type: 'text',
484
- text: `❌ Error searching workspaces: ${errorMessage}`,
485
- }],
486
- };
487
- }
488
- },
489
- };
490
- // ============================================================================
491
- // TOOL 4: SEARCH USER FOR METRICS
492
- // ============================================================================
493
- exports.searchUserForMetricsTool = {
494
- name: 'search_user_for_metrics',
495
- group: tool_registry_1.ToolGroup.READ,
496
- description: `Look up user details by ID for metric analysis.
497
- Use this after getting user IDs from grouped metric results to see who they are.`,
498
- schema: zod_1.z.object({
499
- userIds: zod_1.z.preprocess((val) => {
500
- if (typeof val === 'string') {
501
- try {
502
- return JSON.parse(val);
503
- }
504
- catch {
505
- return val.split(',').map(s => s.trim()).filter(Boolean);
506
- }
507
- }
508
- return val;
509
- }, zod_1.z.array(zod_1.z.string())).describe("Array of user IDs to look up"),
510
- }),
511
- async execute(args, _context) {
512
- try {
513
- logger.info('Looking up users for metrics', { userIds: args.userIds });
514
- // Use admin credentials for this privileged operation
515
- const result = await metricsAdminRequest('v3.metrics.user.list', [{ userIds: args.userIds }]);
516
- if (!result || result.length === 0) {
517
- return {
518
- content: [{
519
- type: 'text',
520
- text: `No users found for provided IDs`,
521
- }],
522
- };
523
- }
524
- const userList = result
525
- .map((user) => `- ${user.name || 'No name'} (${user.email || 'no email'}) - ID: ${user._id}`)
526
- .join('\n');
527
- return {
528
- content: [{
529
- type: 'text',
530
- text: `Found ${result.length} user(s):\n${userList}`,
531
- }],
532
- };
533
- }
534
- catch (error) {
535
- logger.error('Failed to look up users for metrics', error);
536
- const errorMessage = error instanceof Error ? error.message : String(error);
537
- return {
538
- content: [{
539
- type: 'text',
540
- text: `❌ Error looking up users: ${errorMessage}`,
541
- }],
542
- };
543
- }
544
- },
545
- };
546
- //# sourceMappingURL=metrics.js.map
@@ -1,14 +0,0 @@
1
- /**
2
- * Stdio MCP Server for Claude Desktop
3
- *
4
- * Uses @modelcontextprotocol/sdk with StdioServerTransport for
5
- * JSON-RPC communication via stdin/stdout.
6
- *
7
- * This is the entry point when running as Claude Desktop MCP connector.
8
- */
9
- import { ToolRegistry } from './mcp/tool-registry';
10
- /**
11
- * Start the stdio MCP server with all registered tools
12
- */
13
- export declare function startStdioServer(toolRegistry: ToolRegistry): Promise<void>;
14
- //# sourceMappingURL=stdio-server.d.ts.map
@@ -1,114 +0,0 @@
1
- "use strict";
2
- /**
3
- * Stdio MCP Server for Claude Desktop
4
- *
5
- * Uses @modelcontextprotocol/sdk with StdioServerTransport for
6
- * JSON-RPC communication via stdin/stdout.
7
- *
8
- * This is the entry point when running as Claude Desktop MCP connector.
9
- */
10
- Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.startStdioServer = startStdioServer;
12
- const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
13
- const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
14
- const logger_1 = require("./lib/logger");
15
- const tool_registry_1 = require("./mcp/tool-registry");
16
- const UserContextCache_1 = require("./mcp/UserContextCache");
17
- const config_1 = require("./config");
18
- const logger = (0, logger_1.createLogger)({ component: 'stdio-server' });
19
- /**
20
- * Start the stdio MCP server with all registered tools
21
- */
22
- async function startStdioServer(toolRegistry) {
23
- logger.info('Starting stdio MCP server for Claude Desktop');
24
- // Get API key from environment (set in Claude Desktop config)
25
- const apiKey = process.env.MCP_CLIENT_API_KEY;
26
- if (!apiKey) {
27
- logger.error('MCP_CLIENT_API_KEY environment variable is required for stdio mode');
28
- process.exit(1);
29
- }
30
- // Create MCP server
31
- const server = new mcp_js_1.McpServer({
32
- name: 'hailer-mcp-server',
33
- version: '1.0.0',
34
- });
35
- // Get tool definitions (filtered by allowed groups, excluding NUCLEAR unless enabled)
36
- const allowedGroups = config_1.environment.ENABLE_NUCLEAR_TOOLS
37
- ? [tool_registry_1.ToolGroup.READ, tool_registry_1.ToolGroup.WRITE, tool_registry_1.ToolGroup.PLAYGROUND, tool_registry_1.ToolGroup.NUCLEAR]
38
- : [tool_registry_1.ToolGroup.READ, tool_registry_1.ToolGroup.WRITE, tool_registry_1.ToolGroup.PLAYGROUND];
39
- // Get tools with their original Zod schemas - MCP SDK requires Zod, not JSON Schema
40
- const tools = toolRegistry.getToolsWithZodSchemas({ allowedGroups });
41
- logger.info('Registering tools for stdio server', {
42
- toolCount: tools.length,
43
- allowedGroups,
44
- });
45
- // Register each tool with the MCP server using Zod schemas
46
- for (const tool of tools) {
47
- // Extract shape from ZodObject for MCP SDK compatibility
48
- // MCP SDK expects a "raw shape" (object with Zod types as values), not a ZodObject
49
- const zodSchema = tool.schema;
50
- const shape = zodSchema._def?.typeName === 'ZodObject'
51
- ? zodSchema._def.shape()
52
- : zodSchema;
53
- logger.debug('Registering tool with Zod schema', {
54
- name: tool.name,
55
- schemaTypeName: zodSchema._def?.typeName || 'unknown',
56
- shapeKeys: Object.keys(shape || {})
57
- });
58
- // Use tool() method with shape object - MCP SDK will recognize Zod types
59
- server.tool(tool.name, tool.description, shape, async (args) => {
60
- const startTime = Date.now();
61
- logger.debug('Tool call received', { toolName: tool.name, args });
62
- try {
63
- // Get user context (cached)
64
- const userContext = await UserContextCache_1.UserContextCache.getContext(apiKey);
65
- // Execute the tool
66
- const result = await toolRegistry.executeTool(tool.name, args, userContext);
67
- const duration = Date.now() - startTime;
68
- logger.info('Tool call completed', { toolName: tool.name, duration });
69
- // Handle different result formats
70
- if (result && typeof result === 'object' && 'content' in result) {
71
- // Result is already in MCP format { content: [...] }
72
- return result;
73
- }
74
- // Wrap result in MCP format
75
- return {
76
- content: [{
77
- type: 'text',
78
- text: typeof result === 'string' ? result : JSON.stringify(result, null, 2)
79
- }]
80
- };
81
- }
82
- catch (error) {
83
- const duration = Date.now() - startTime;
84
- const errorMessage = error instanceof Error ? error.message : String(error);
85
- logger.error('Tool call failed', error, { toolName: tool.name, duration });
86
- return {
87
- content: [{
88
- type: 'text',
89
- text: `Error: ${errorMessage}`
90
- }],
91
- isError: true
92
- };
93
- }
94
- });
95
- }
96
- // Create stdio transport
97
- const transport = new stdio_js_1.StdioServerTransport();
98
- // Handle graceful shutdown
99
- process.on('SIGINT', async () => {
100
- logger.info('Received SIGINT, shutting down stdio server');
101
- await server.close();
102
- process.exit(0);
103
- });
104
- process.on('SIGTERM', async () => {
105
- logger.info('Received SIGTERM, shutting down stdio server');
106
- await server.close();
107
- process.exit(0);
108
- });
109
- // Connect and start serving
110
- logger.info('Connecting stdio transport');
111
- await server.connect(transport);
112
- logger.info('Stdio MCP server is running');
113
- }
114
- //# sourceMappingURL=stdio-server.js.map