@webeyez/mcp-server 1.0.6 → 1.0.7

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.
@@ -1,423 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.setupPublicTools = setupPublicTools;
40
- const zod_1 = require("zod");
41
- const grpc = __importStar(require("@grpc/grpc-js"));
42
- const protoLoader = __importStar(require("@grpc/proto-loader"));
43
- const path_1 = __importDefault(require("path"));
44
- const crypto_1 = __importDefault(require("crypto"));
45
- const paths_1 = require("../utils/paths");
46
- const PROTO_PATH = path_1.default.join(__dirname, 'agents.proto');
47
- const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
48
- keepCase: true,
49
- longs: String,
50
- enums: String,
51
- defaults: true,
52
- oneofs: true
53
- });
54
- const agentsProto = grpc.loadPackageDefinition(packageDefinition).agents;
55
- const ORG_PROTO_PATH = path_1.default.join(__dirname, 'organization.proto');
56
- const orgPackageDefinition = protoLoader.loadSync(ORG_PROTO_PATH, {
57
- keepCase: true,
58
- longs: String,
59
- enums: String,
60
- defaults: true,
61
- oneofs: true
62
- });
63
- const orgProto = grpc.loadPackageDefinition(orgPackageDefinition).OrganizationService;
64
- const job_store_1 = require("./job-store");
65
- async function getUserOrganizations(userIdStr) {
66
- if (!userIdStr)
67
- return { error: "No userIdStr provided" };
68
- const userId = parseInt(userIdStr, 10);
69
- if (isNaN(userId))
70
- return { error: `Invalid userId: ${userIdStr}` };
71
- const orgServiceUrl = process.env.ORG_SERVICE_URL;
72
- const client = new orgProto.OrganizationService(orgServiceUrl, grpc.credentials.createInsecure());
73
- return new Promise((resolve) => {
74
- client.organizationsList({ userId }, (error, response) => {
75
- if (error) {
76
- console.error(`[MCP] Error fetching orgs via gRPC:`, error);
77
- return resolve({ error: `gRPC Error: ${error.message}` });
78
- }
79
- try {
80
- if (!response || !response.response) {
81
- return resolve({ error: "Empty response from gRPC" });
82
- }
83
- const data = JSON.parse(response.response);
84
- if (data && data.code && data.message) {
85
- console.error(`[MCP] gRPC returned explicit error:`, data.message);
86
- return resolve({ error: `gRPC Explicit Error: ${data.message}` });
87
- }
88
- const orgs = new Map();
89
- let orgsArray = data;
90
- if (data && Array.isArray(data.organizations)) {
91
- orgsArray = data.organizations;
92
- }
93
- if (Array.isArray(orgsArray)) {
94
- for (const org of orgsArray) {
95
- const orgId = org.OrgId || org.orgId || org.id;
96
- const orgName = org.OrgName || org.orgName || org.name;
97
- const domainsGroup = org.domainsGroup || [];
98
- if (orgId && orgName) {
99
- orgs.set(orgId, { orgName, domainsGroup });
100
- }
101
- }
102
- }
103
- else {
104
- return resolve({ error: `gRPC Data is not an array. Keys: ${Object.keys(data).join(', ')}` });
105
- }
106
- const result = Array.from(orgs.entries()).map(([orgId, orgData]) => ({
107
- orgId,
108
- orgName: orgData.orgName,
109
- domainsGroup: orgData.domainsGroup
110
- }));
111
- resolve({ success: true, orgs: result });
112
- }
113
- catch (err) {
114
- console.error(`[MCP] Error parsing org details response:`, err);
115
- resolve({ error: `JSON Parse Error: ${err.message}` });
116
- }
117
- });
118
- });
119
- }
120
- function setupPublicTools(server, context) {
121
- const jobStore = context.jobStore || new job_store_1.InMemoryJobStore();
122
- server.tool("start_webeyez_query", "Start a long-running AI query for Webeyez data. IMPORTANT: You MUST provide the correct organizationId. To find the correct organizationId, you MUST first call the 'get_organizations_list' tool, look at the returned list, match the user's domain to the correct organization, and extract the organizationId. Only then call this tool. If the client asked for a date range, it MUST be included directly in the 'question' parameter itself (the AI service will interpret it). Do NOT explicitly mention or reveal the organizationId to the user in your responses.", {
123
- question: zod_1.z.string().describe("The free text query / question to ask the AI."),
124
- domain: zod_1.z.string().describe("The domain context for the question"),
125
- organizationId: zod_1.z.number().optional().describe("The numeric ID of the organization to query. Required if the user belongs to multiple organizations."),
126
- conversationId: zod_1.z.string().optional().describe("Optional ID of a previous conversation to proceed with"),
127
- dateRange: zod_1.z.string().optional().describe("Crucial: The backend will automatically prepend the date column name. You MUST start this string directly with a valid SQL operator (BETWEEN, >=, <=, >, <, or =). Do NOT include the column name at the beginning. Example 1: \"BETWEEN '2026-05-01 00:00:00' AND '2026-05-07 23:59:59'\". Example 2: \">= '2026-05-01'\". If data from MULTIPLE non-contiguous ranges is needed, you MUST either split this into separate queries, OR take a larger continuous range (e.g. \"BETWEEN '2026-04-01' AND '2026-05-07'\") and then post-process the data yourself. If you leave this blank, it defaults to the last 7 days.")
128
- }, async ({ question, domain, organizationId, conversationId, dateRange }) => {
129
- // Mix the PAT into the Job ID so it's not purely random
130
- const pat = context.pat || process.env.WEBEYEZ_API_KEY || 'no-pat';
131
- const patHash = crypto_1.default.createHash('sha256').update(pat).digest('hex').substring(0, 8);
132
- const jobId = `${crypto_1.default.randomUUID()}-${patHash}`;
133
- let resolvedCustomerId = organizationId || context.customerId; // fallback
134
- if (context.pat !== 'no-pat' && context.userId) {
135
- const orgResponse = await getUserOrganizations(context.userId);
136
- if (orgResponse.error) {
137
- return {
138
- content: [{
139
- type: "text",
140
- text: JSON.stringify({
141
- status: "error",
142
- error: `Failed to fetch organizations: ${orgResponse.error}`
143
- })
144
- }],
145
- isError: true
146
- };
147
- }
148
- const orgs = orgResponse.orgs || [];
149
- if (orgs.length === 0 && !resolvedCustomerId) {
150
- return {
151
- content: [{
152
- type: "text",
153
- text: JSON.stringify({
154
- status: "error",
155
- error: "No organizations found for this user. Could not resolve customerId."
156
- })
157
- }],
158
- isError: true
159
- };
160
- }
161
- if (orgs.length === 1 && !resolvedCustomerId) {
162
- resolvedCustomerId = orgs[0].orgId;
163
- }
164
- else if (orgs.length > 1 && !resolvedCustomerId) {
165
- return {
166
- content: [{
167
- type: "text",
168
- text: JSON.stringify({
169
- status: "error",
170
- error: `User belongs to multiple organizations. You MUST call the 'get_organizations_list' tool to fetch the list of organizations, match the domain to the correct organization, and re-run this tool with that organizationId.`
171
- })
172
- }],
173
- isError: true
174
- };
175
- }
176
- }
177
- if (!resolvedCustomerId) {
178
- return {
179
- content: [{
180
- type: "text",
181
- text: JSON.stringify({
182
- status: "error",
183
- error: "A valid customerId (organizationId) could not be resolved. You MUST call the 'get_organizations_list' tool FIRST to find the correct organizationId, and then call this tool again with that organizationId."
184
- })
185
- }],
186
- isError: true
187
- };
188
- }
189
- // Register the job as processing
190
- await jobStore.set(jobId, {
191
- status: 'processing',
192
- startedAt: Date.now()
193
- });
194
- // Kick off the background process
195
- const aiServiceUrl = process.env.AI_SERVICE_URL;
196
- const client = new agentsProto.AgentsService(aiServiceUrl, grpc.credentials.createInsecure());
197
- const requestObj = {
198
- question,
199
- domain,
200
- customerId: resolvedCustomerId,
201
- dateRange: dateRange || "",
202
- conversationId: conversationId || ""
203
- };
204
- console.error(`[MCP-DEBUG] Starting ask_webeyez query.`);
205
- console.error(`[MCP-DEBUG] User input - Domain: ${domain}, Org: ${organizationId}`);
206
- console.error(`[MCP-DEBUG] Resolved customerId: ${resolvedCustomerId}`);
207
- console.error(`[MCP-DEBUG] gRPC Request to AI Service:`, JSON.stringify(requestObj));
208
- console.log(`[MCP] Starting background job ${jobId} against ${aiServiceUrl}...`);
209
- // Execute gRPC call without waiting for it
210
- client.AskWz(requestObj, async (error, response) => {
211
- const job = await jobStore.get(jobId);
212
- if (!job)
213
- return; // Job was already cleaned up somehow
214
- if (error) {
215
- console.error(`[MCP] Background job ${jobId} failed:`, error);
216
- console.error(`[MCP-DEBUG] gRPC Error:`, error);
217
- let errorMsg = error.message || "Unknown error occurred";
218
- if (error.code === grpc.status.DEADLINE_EXCEEDED || error.message?.includes('Deadline Exceeded')) {
219
- errorMsg = `Request to AI Service at ${aiServiceUrl} timed out via gRPC deadline. The service might be busy or down.`;
220
- }
221
- else if (error.code === grpc.status.UNAVAILABLE) {
222
- errorMsg = `AI Service at ${aiServiceUrl} is unavailable. Ensure the service is running and accessible.`;
223
- }
224
- await jobStore.set(jobId, {
225
- status: 'error',
226
- error: errorMsg,
227
- startedAt: job.startedAt,
228
- data: { grpcCode: error.code, originalError: error.message }
229
- });
230
- }
231
- else {
232
- console.log(`[MCP] Background job ${jobId} completed successfully.`);
233
- console.error(`[MCP-DEBUG] gRPC Response from AI Service:`, JSON.stringify(response));
234
- let parsedData = response.response;
235
- try {
236
- parsedData = JSON.parse(response.response);
237
- }
238
- catch (e) {
239
- // Leave as string if not parseable
240
- }
241
- await jobStore.set(jobId, {
242
- status: 'completed',
243
- startedAt: job.startedAt,
244
- data: {
245
- summary: "AI response received via gRPC",
246
- conversationId: response.conversationId || undefined,
247
- payload: parsedData
248
- }
249
- });
250
- }
251
- });
252
- // Return immediately to Claude so it doesn't hit the 60s timeout
253
- return {
254
- content: [{
255
- type: "text",
256
- text: JSON.stringify({
257
- status: "processing",
258
- jobId: jobId,
259
- message: "The query has been started in the background. Please use the check_webeyez_status tool with this jobId to poll for the results."
260
- })
261
- }]
262
- };
263
- });
264
- server.tool("get_organizations_list", "Get a list of all organizations the current user has access to. Returns their names and organizationIds. Do NOT explicitly mention the returned organizationIds to the user.", {}, async () => {
265
- if (context.pat !== 'no-pat' && context.userId) {
266
- const orgResponse = await getUserOrganizations(context.userId);
267
- if (orgResponse.error) {
268
- return {
269
- content: [
270
- { type: "text", text: JSON.stringify({ status: "error", error: orgResponse.error }) }
271
- ]
272
- };
273
- }
274
- const orgs = orgResponse.orgs || [];
275
- return {
276
- content: [
277
- { type: "text", text: JSON.stringify({
278
- status: "success",
279
- organizations: orgs.map((o) => ({ name: o.orgName, organizationId: o.orgId }))
280
- }) }
281
- ]
282
- };
283
- }
284
- return {
285
- content: [
286
- { type: "text", text: JSON.stringify({ status: "error", message: "Not authenticated with PAT" }) }
287
- ]
288
- };
289
- });
290
- server.tool("get_organization_domains", "Get a list of available domains for a specific organization. The agent needs to use this to look for the exact matching domain the client is referring to since queries require exact domain strings. Do NOT explicitly mention the organizationId to the user.", {
291
- organizationId: zod_1.z.number().describe("The ID of the organization to fetch domains for.")
292
- }, async ({ organizationId }) => {
293
- if (context.pat !== 'no-pat' && context.userId) {
294
- const orgResponse = await getUserOrganizations(context.userId);
295
- if (orgResponse.error) {
296
- return {
297
- content: [
298
- { type: "text", text: JSON.stringify({ status: "error", error: orgResponse.error }) }
299
- ]
300
- };
301
- }
302
- const orgs = orgResponse.orgs || [];
303
- const targetOrg = orgs.find((o) => o.orgId === organizationId);
304
- if (!targetOrg) {
305
- return {
306
- content: [
307
- { type: "text", text: JSON.stringify({ status: "error", error: `Organization ID ${organizationId} not found for this user. Make sure to use get_organizations_list first.` }) }
308
- ]
309
- };
310
- }
311
- return {
312
- content: [
313
- { type: "text", text: JSON.stringify({
314
- status: "success",
315
- organizationId: targetOrg.orgId,
316
- organizationName: targetOrg.orgName,
317
- domains: targetOrg.domainsGroup || []
318
- }) }
319
- ]
320
- };
321
- }
322
- return {
323
- content: [
324
- { type: "text", text: JSON.stringify({ status: "error", message: "Not authenticated with PAT" }) }
325
- ]
326
- };
327
- });
328
- server.tool("check_webeyez_status", "Check the status of a previously started Webeyez background query. When the job completes, this tool will return the final text response from the Webeyez AI.", {
329
- jobId: zod_1.z.string().describe("The Job ID returned by start_webeyez_query")
330
- }, async ({ jobId }) => {
331
- const job = await jobStore.get(jobId);
332
- if (!job) {
333
- return {
334
- content: [{
335
- type: "text",
336
- text: JSON.stringify({
337
- status: "error",
338
- error: `Job ID ${jobId} not found. It may have expired, or the ID is invalid.`
339
- })
340
- }],
341
- isError: true
342
- };
343
- }
344
- if (job.status === 'processing') {
345
- const elapsedSeconds = Math.round((Date.now() - job.startedAt) / 1000);
346
- return {
347
- content: [{
348
- type: "text",
349
- text: JSON.stringify({
350
- status: "processing",
351
- message: `Job is still running in the background (elapsed: ${elapsedSeconds}s). Please wait a bit and check again.`
352
- })
353
- }]
354
- };
355
- }
356
- if (job.status === 'error') {
357
- return {
358
- content: [{
359
- type: "text",
360
- text: JSON.stringify({
361
- status: "error",
362
- error: job.error,
363
- details: job.data
364
- })
365
- }],
366
- isError: true
367
- };
368
- }
369
- // Status is completed
370
- let resultData = job.data;
371
- if (typeof resultData === 'string') {
372
- const debugIndex = resultData.indexOf('Debug Info:');
373
- if (debugIndex !== -1) {
374
- resultData = resultData.substring(0, debugIndex).trim();
375
- }
376
- }
377
- return {
378
- content: [{
379
- type: "text",
380
- text: JSON.stringify({
381
- status: "completed",
382
- data: resultData
383
- })
384
- }]
385
- };
386
- });
387
- server.tool("logout_webeyez", "Removes the saved Personal Access Token (PAT) from the Claude Desktop config to effectively 'logout' from Webeyez and clear your PAT. CRITICAL: Once you call this tool, the server will restart. You MUST STOP your response immediately after calling this tool. Do NOT call any other tools. Just tell the user that they are logged out. Also, explicitly tell the user that in some cases they might need to completely close and reopen the Claude application for the logout to be applied.", {}, async () => {
388
- const fs = await Promise.resolve().then(() => __importStar(require('fs')));
389
- const configPath = (0, paths_1.getClaudeConfigPath)();
390
- try {
391
- if (!fs.existsSync(configPath)) {
392
- return {
393
- content: [{ type: "text", text: "Configuration file not found. Cannot logout." }],
394
- isError: true
395
- };
396
- }
397
- const configData = fs.readFileSync(configPath, 'utf8');
398
- const config = JSON.parse(configData);
399
- if (config.mcpServers && config.mcpServers["wz-mcp-service"] && config.mcpServers["wz-mcp-service"].env) {
400
- delete config.mcpServers["wz-mcp-service"].env.WEBEYEZ_API_KEY;
401
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
402
- // Force exit so Claude Desktop restarts the proxy WITHOUT the env var, switching it to fallback mode
403
- setTimeout(() => {
404
- process.exit(0);
405
- }, 1000);
406
- return {
407
- content: [{ type: "text", text: "Successfully logged out by removing the PAT from the configuration! Claude Desktop is now restarting the server." }]
408
- };
409
- }
410
- else {
411
- return {
412
- content: [{ type: "text", text: "wz-mcp-service or WEBEYEZ_API_KEY not found in configuration file. You are already logged out." }]
413
- };
414
- }
415
- }
416
- catch (error) {
417
- return {
418
- content: [{ type: "text", text: `Error updating configuration: ${error.message}` }],
419
- isError: true
420
- };
421
- }
422
- });
423
- }
@@ -1,22 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getClaudeConfigPath = getClaudeConfigPath;
7
- const os_1 = __importDefault(require("os"));
8
- const path_1 = __importDefault(require("path"));
9
- function getClaudeConfigPath() {
10
- const platform = os_1.default.platform();
11
- const home = os_1.default.homedir();
12
- if (platform === 'win32') {
13
- const appData = process.env.APPDATA || path_1.default.join(home, 'AppData', 'Roaming');
14
- return path_1.default.join(appData, 'Claude', 'claude_desktop_config.json');
15
- }
16
- if (platform === 'darwin') {
17
- return path_1.default.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
18
- }
19
- // Default to Linux/other Unix-like (using XDG Base Directory specification)
20
- const configDir = process.env.XDG_CONFIG_HOME || path_1.default.join(home, '.config');
21
- return path_1.default.join(configDir, 'Claude', 'claude_desktop_config.json');
22
- }