@sisense/mcp-server 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.
- package/LICENSE.md +35 -0
- package/README.md +121 -0
- package/dist/ai-hspmgr2c.js +3148 -0
- package/dist/fileFromPath-s6ap5vrh.js +128 -0
- package/dist/index-29n08mw7.js +95 -0
- package/dist/index-atgbxy7h.js +98603 -0
- package/dist/index-d6843g0v.js +372 -0
- package/dist/index-dcrjg3fk.js +207 -0
- package/dist/index-dxfb3krz.js +1489 -0
- package/dist/index-er0yspcy.js +918 -0
- package/dist/index-g8bgq79c.js +53 -0
- package/dist/index-p1pxtmwn.js +162 -0
- package/dist/index-qdth51hx.js +250 -0
- package/dist/index-tqba2rwh.js +603 -0
- package/dist/index-vrapm0b4.js +765 -0
- package/dist/index-vx54d05h.js +475 -0
- package/dist/sse-server-3343e7xh.js +117 -0
- package/dist/sse-server-36t17nga.js +12127 -0
- package/dist/sse-server-3e0efmg2.js +6276 -0
- package/dist/sse-server-4b60tg0c.js +136 -0
- package/dist/sse-server-4g9za0qq.js +89 -0
- package/dist/sse-server-4jjec4fz.js +9753 -0
- package/dist/sse-server-5tmgacdx.js +62 -0
- package/dist/sse-server-7wcvyxyj.js +31 -0
- package/dist/sse-server-brx9qtyd.js +2131 -0
- package/dist/sse-server-epd916s3.js +167 -0
- package/dist/sse-server-gt7tx6n2.js +2240 -0
- package/dist/sse-server-mkesh468.js +53 -0
- package/dist/sse-server-nwjjjz6x.js +113 -0
- package/dist/sse-server-qj4zxq0f.js +267 -0
- package/dist/sse-server-rr3dp62e.js +116721 -0
- package/dist/sse-server-ss0mydv4.js +3980 -0
- package/dist/sse-server-txz5g5t0.js +5328 -0
- package/dist/sse-server.js +30432 -0
- package/dist/view.html +3016 -0
- package/dist/widget-renderer-66ws3xtk.js +312 -0
- package/package.json +82 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import {
|
|
2
|
+
require_dist_cjs as require_dist_cjs2,
|
|
3
|
+
sanitizeError,
|
|
4
|
+
toKebabCase
|
|
5
|
+
} from "./sse-server-36t17nga.js";
|
|
6
|
+
import {
|
|
7
|
+
require_dist_cjs
|
|
8
|
+
} from "./index-p1pxtmwn.js";
|
|
9
|
+
import"./index-er0yspcy.js";
|
|
10
|
+
import {
|
|
11
|
+
Fs
|
|
12
|
+
} from "./sse-server-brx9qtyd.js";
|
|
13
|
+
import"./sse-server-3e0efmg2.js";
|
|
14
|
+
import"./sse-server-4b60tg0c.js";
|
|
15
|
+
import"./sse-server-4jjec4fz.js";
|
|
16
|
+
import"./sse-server-nwjjjz6x.js";
|
|
17
|
+
import"./sse-server-qj4zxq0f.js";
|
|
18
|
+
import"./sse-server-txz5g5t0.js";
|
|
19
|
+
import"./sse-server-epd916s3.js";
|
|
20
|
+
import"./sse-server-5tmgacdx.js";
|
|
21
|
+
import"./sse-server-4g9za0qq.js";
|
|
22
|
+
import"./index-g8bgq79c.js";
|
|
23
|
+
import"./sse-server-mkesh468.js";
|
|
24
|
+
import"./sse-server-3343e7xh.js";
|
|
25
|
+
import {
|
|
26
|
+
__toESM
|
|
27
|
+
} from "./sse-server-7wcvyxyj.js";
|
|
28
|
+
|
|
29
|
+
// src/utils/widget-renderer/widget-renderer.ts
|
|
30
|
+
var import_client_s3 = __toESM(require_dist_cjs2(), 1);
|
|
31
|
+
var import_credential_provider_web_identity = __toESM(require_dist_cjs(), 1);
|
|
32
|
+
import { readFileSync } from "node:fs";
|
|
33
|
+
import { basename } from "node:path";
|
|
34
|
+
|
|
35
|
+
// src/utils/widget-renderer/widget-ct-runner.ts
|
|
36
|
+
import { spawn } from "node:child_process";
|
|
37
|
+
import { writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
38
|
+
import { join, dirname } from "node:path";
|
|
39
|
+
import { tmpdir } from "node:os";
|
|
40
|
+
import { randomBytes } from "node:crypto";
|
|
41
|
+
import { fileURLToPath } from "node:url";
|
|
42
|
+
var __filename2 = fileURLToPath(import.meta.url);
|
|
43
|
+
var __dirname2 = dirname(__filename2);
|
|
44
|
+
function getProjectRoot() {
|
|
45
|
+
const cwd = process.cwd();
|
|
46
|
+
if (existsSync(join(cwd, "playwright-ct.config.ts"))) {
|
|
47
|
+
return cwd;
|
|
48
|
+
}
|
|
49
|
+
const relativePath = join(__dirname2, "..", "..", "..");
|
|
50
|
+
if (existsSync(join(relativePath, "playwright-ct.config.ts"))) {
|
|
51
|
+
return relativePath;
|
|
52
|
+
}
|
|
53
|
+
return cwd;
|
|
54
|
+
}
|
|
55
|
+
var PROJECT_ROOT = getProjectRoot();
|
|
56
|
+
async function renderChartWidgetWithPlaywrightCT(config) {
|
|
57
|
+
const startTime = Date.now();
|
|
58
|
+
const width = config.width || 1000;
|
|
59
|
+
const height = config.height || 600;
|
|
60
|
+
let screenshotPath = "";
|
|
61
|
+
try {
|
|
62
|
+
const testId = randomBytes(8).toString("hex");
|
|
63
|
+
const testDir = join(tmpdir(), `widget-ct-${testId}`);
|
|
64
|
+
mkdirSync(testDir, { recursive: true });
|
|
65
|
+
const configPath = join(testDir, "widget-config.json");
|
|
66
|
+
const configData = {
|
|
67
|
+
widgetProps: config.widgetProps,
|
|
68
|
+
sisenseContextProviderProps: config.sisenseContextProviderProps,
|
|
69
|
+
width,
|
|
70
|
+
height,
|
|
71
|
+
outputName: config.outputName
|
|
72
|
+
};
|
|
73
|
+
const serializedConfig = Fs.stringify(configData);
|
|
74
|
+
writeFileSync(configPath, serializedConfig, "utf8");
|
|
75
|
+
const screenshotsDir = join(PROJECT_ROOT, "__screenshots__");
|
|
76
|
+
mkdirSync(screenshotsDir, { recursive: true });
|
|
77
|
+
const chartTitleKebab = toKebabCase(config.outputName || "untitled-widget");
|
|
78
|
+
const timestamp = Date.now().toString();
|
|
79
|
+
const customFilename = `widget-${chartTitleKebab}-${timestamp}.png`;
|
|
80
|
+
screenshotPath = join(screenshotsDir, customFilename);
|
|
81
|
+
const testFilePath = join(PROJECT_ROOT, "src/utils/widget-renderer/widget-renderer.spec.tsx");
|
|
82
|
+
return await new Promise((resolve) => {
|
|
83
|
+
const renderStart = Date.now();
|
|
84
|
+
console.info("Starting Playwright CT", {
|
|
85
|
+
cwd: PROJECT_ROOT,
|
|
86
|
+
testFile: testFilePath,
|
|
87
|
+
configPath,
|
|
88
|
+
screenshotPath,
|
|
89
|
+
chromiumPath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
|
|
90
|
+
});
|
|
91
|
+
const playwrightProcess = spawn("node", [
|
|
92
|
+
"./node_modules/.bin/playwright",
|
|
93
|
+
"test",
|
|
94
|
+
"--config=playwright-ct.config.ts",
|
|
95
|
+
testFilePath,
|
|
96
|
+
"--reporter=list"
|
|
97
|
+
], {
|
|
98
|
+
cwd: PROJECT_ROOT,
|
|
99
|
+
env: {
|
|
100
|
+
...process.env,
|
|
101
|
+
WIDGET_CONFIG_PATH: configPath,
|
|
102
|
+
SCREENSHOT_OUTPUT_PATH: screenshotPath
|
|
103
|
+
},
|
|
104
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
105
|
+
});
|
|
106
|
+
console.info("Playwright process spawned", { pid: playwrightProcess.pid });
|
|
107
|
+
let stdout = "";
|
|
108
|
+
let stderr = "";
|
|
109
|
+
playwrightProcess.stdout?.on("data", (data) => {
|
|
110
|
+
const chunk = data.toString();
|
|
111
|
+
stdout += chunk;
|
|
112
|
+
console.info("Playwright stdout:", chunk.trim());
|
|
113
|
+
});
|
|
114
|
+
playwrightProcess.stderr?.on("data", (data) => {
|
|
115
|
+
const chunk = data.toString();
|
|
116
|
+
stderr += chunk;
|
|
117
|
+
console.info("Playwright stderr:", chunk.trim());
|
|
118
|
+
});
|
|
119
|
+
const killTimeout = setTimeout(() => {
|
|
120
|
+
console.error("Playwright process timed out after 120s, killing...");
|
|
121
|
+
playwrightProcess.kill("SIGKILL");
|
|
122
|
+
}, 120000);
|
|
123
|
+
playwrightProcess.on("error", (err) => {
|
|
124
|
+
clearTimeout(killTimeout);
|
|
125
|
+
const totalMs = Date.now() - startTime;
|
|
126
|
+
resolve({
|
|
127
|
+
pngPath: screenshotPath,
|
|
128
|
+
success: false,
|
|
129
|
+
error: `Failed to spawn Playwright process: ${err.message}`,
|
|
130
|
+
timings: {
|
|
131
|
+
renderMs: 0,
|
|
132
|
+
totalMs
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
playwrightProcess.on("close", (code) => {
|
|
137
|
+
clearTimeout(killTimeout);
|
|
138
|
+
console.info("Playwright process exited", {
|
|
139
|
+
code,
|
|
140
|
+
stdoutLength: stdout.length,
|
|
141
|
+
stderrLength: stderr.length
|
|
142
|
+
});
|
|
143
|
+
const renderMs = Date.now() - renderStart;
|
|
144
|
+
const totalMs = Date.now() - startTime;
|
|
145
|
+
if (code === 0) {
|
|
146
|
+
resolve({
|
|
147
|
+
pngPath: screenshotPath,
|
|
148
|
+
success: true,
|
|
149
|
+
timings: {
|
|
150
|
+
renderMs,
|
|
151
|
+
totalMs
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
} else {
|
|
155
|
+
resolve({
|
|
156
|
+
pngPath: screenshotPath,
|
|
157
|
+
success: false,
|
|
158
|
+
error: stderr || stdout,
|
|
159
|
+
timings: {
|
|
160
|
+
renderMs,
|
|
161
|
+
totalMs
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
} catch (err) {
|
|
168
|
+
const totalMs = Date.now() - startTime;
|
|
169
|
+
return {
|
|
170
|
+
pngPath: screenshotPath,
|
|
171
|
+
success: false,
|
|
172
|
+
error: `Failed to create chart: ${err instanceof Error ? err.message : String(err)}`,
|
|
173
|
+
timings: {
|
|
174
|
+
renderMs: 0,
|
|
175
|
+
totalMs
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// src/utils/widget-renderer/widget-renderer.ts
|
|
182
|
+
var s3Client;
|
|
183
|
+
function getS3Client() {
|
|
184
|
+
if (s3Client !== undefined) {
|
|
185
|
+
return s3Client;
|
|
186
|
+
}
|
|
187
|
+
if (!process.env.SCREENSHOTS_BUCKET) {
|
|
188
|
+
s3Client = null;
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
const region = process.env.AWS_REGION || "us-east-1";
|
|
192
|
+
if (process.env.AWS_WEB_IDENTITY_TOKEN_FILE) {
|
|
193
|
+
s3Client = new import_client_s3.S3Client({
|
|
194
|
+
region,
|
|
195
|
+
credentials: import_credential_provider_web_identity.fromTokenFile()
|
|
196
|
+
});
|
|
197
|
+
} else {
|
|
198
|
+
s3Client = new import_client_s3.S3Client({ region });
|
|
199
|
+
}
|
|
200
|
+
return s3Client;
|
|
201
|
+
}
|
|
202
|
+
async function renderChartWidget(args) {
|
|
203
|
+
const {
|
|
204
|
+
widgetProps,
|
|
205
|
+
width = 800,
|
|
206
|
+
height = 500,
|
|
207
|
+
outputName,
|
|
208
|
+
maxRetries = 2,
|
|
209
|
+
sisenseUrl,
|
|
210
|
+
sisenseToken,
|
|
211
|
+
baseUrl
|
|
212
|
+
} = args;
|
|
213
|
+
const sisenseContextProviderProps = {
|
|
214
|
+
url: sisenseUrl,
|
|
215
|
+
token: sisenseToken,
|
|
216
|
+
showRuntimeErrors: true
|
|
217
|
+
};
|
|
218
|
+
console.info("Rendering chart from WidgetProps", {
|
|
219
|
+
chartType: widgetProps.chartType,
|
|
220
|
+
title: widgetProps.title,
|
|
221
|
+
width,
|
|
222
|
+
height
|
|
223
|
+
});
|
|
224
|
+
let lastError = null;
|
|
225
|
+
for (let attempt = 0;attempt <= maxRetries; attempt++) {
|
|
226
|
+
try {
|
|
227
|
+
if (attempt > 0) {
|
|
228
|
+
console.info(`Retry attempt ${attempt}/${maxRetries}`);
|
|
229
|
+
}
|
|
230
|
+
const result = await renderChartWidgetWithPlaywrightCT({
|
|
231
|
+
widgetProps,
|
|
232
|
+
sisenseContextProviderProps,
|
|
233
|
+
width,
|
|
234
|
+
height,
|
|
235
|
+
outputName: outputName || widgetProps.title || "widget"
|
|
236
|
+
});
|
|
237
|
+
if (!result.success) {
|
|
238
|
+
throw new Error(result.error || "Widget rendering failed");
|
|
239
|
+
}
|
|
240
|
+
console.info("Reading generated widget image", {
|
|
241
|
+
path: result.pngPath
|
|
242
|
+
});
|
|
243
|
+
if (!result.pngPath) {
|
|
244
|
+
throw new Error("Widget rendering succeeded but no output path was returned");
|
|
245
|
+
}
|
|
246
|
+
const imageBuffer = readFileSync(result.pngPath);
|
|
247
|
+
const filename = basename(result.pngPath);
|
|
248
|
+
if (!filename) {
|
|
249
|
+
throw new Error(`Invalid screenshot path: ${result.pngPath}`);
|
|
250
|
+
}
|
|
251
|
+
const client = getS3Client();
|
|
252
|
+
if (client && process.env.SCREENSHOTS_BUCKET) {
|
|
253
|
+
const bucket = process.env.SCREENSHOTS_BUCKET;
|
|
254
|
+
try {
|
|
255
|
+
await client.send(new import_client_s3.PutObjectCommand({
|
|
256
|
+
Bucket: bucket,
|
|
257
|
+
Key: filename,
|
|
258
|
+
Body: imageBuffer,
|
|
259
|
+
ContentType: "image/png"
|
|
260
|
+
}));
|
|
261
|
+
console.info("Screenshot uploaded to S3", { bucket, filename });
|
|
262
|
+
} catch (s3Error) {
|
|
263
|
+
const sanitized2 = sanitizeError(s3Error);
|
|
264
|
+
console.error("Failed to upload screenshot to S3", {
|
|
265
|
+
bucket,
|
|
266
|
+
filename,
|
|
267
|
+
error: sanitized2.message
|
|
268
|
+
});
|
|
269
|
+
throw new Error(`S3 upload failed: ${sanitized2.message}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const publicUrl = `${baseUrl.replace(/\/$/, "")}/screenshots/${filename}`;
|
|
273
|
+
console.info("Widget rendered successfully", {
|
|
274
|
+
outputPath: result.pngPath,
|
|
275
|
+
imageSize: imageBuffer.length,
|
|
276
|
+
publicUrl,
|
|
277
|
+
timings: result.timings
|
|
278
|
+
});
|
|
279
|
+
const toolResult = {
|
|
280
|
+
content: [
|
|
281
|
+
{
|
|
282
|
+
type: "text",
|
|
283
|
+
text: publicUrl
|
|
284
|
+
}
|
|
285
|
+
]
|
|
286
|
+
};
|
|
287
|
+
console.info(">>> CHART TOOL RESULT", {
|
|
288
|
+
contentCount: toolResult.content.length,
|
|
289
|
+
publicUrl
|
|
290
|
+
});
|
|
291
|
+
return toolResult;
|
|
292
|
+
} catch (error) {
|
|
293
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
294
|
+
if (attempt < maxRetries) {
|
|
295
|
+
const sanitized2 = sanitizeError(lastError);
|
|
296
|
+
console.warn(`Render attempt ${attempt + 1} failed, retrying...`, {
|
|
297
|
+
error: sanitized2.message
|
|
298
|
+
});
|
|
299
|
+
await new Promise((resolve) => setTimeout(resolve, 1000 * Math.pow(2, attempt)));
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const sanitized = sanitizeError(lastError, false);
|
|
304
|
+
console.error("Failed to render widget after retries", {
|
|
305
|
+
attempts: maxRetries + 1,
|
|
306
|
+
error: sanitized.message
|
|
307
|
+
});
|
|
308
|
+
throw new Error(sanitized.message || "Widget rendering failed");
|
|
309
|
+
}
|
|
310
|
+
export {
|
|
311
|
+
renderChartWidget
|
|
312
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sisense/mcp-server",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"mcpName": "io.github.sisense/mcp-server",
|
|
5
|
+
"homepage": "https://github.com/sisense/sisense-mcp-server#readme",
|
|
6
|
+
"description": "MCP server leveraging Sisense Intelligence for actionable insights and analytics.",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/sisense/sisense-mcp-server.git"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"Sisense",
|
|
13
|
+
"Sisense Intelligence",
|
|
14
|
+
"MCP Server",
|
|
15
|
+
"Analytics"
|
|
16
|
+
],
|
|
17
|
+
"author": "Sisense",
|
|
18
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"main": "dist/sse-server.js",
|
|
21
|
+
"bin": {
|
|
22
|
+
"sisense-mcp-server": "dist/sse-server.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md",
|
|
27
|
+
"LICENSE.md"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "cross-env INPUT=view.html vite build && bun build src/sse-server.ts --outdir dist --target node --format esm --splitting",
|
|
34
|
+
"build:view": "cross-env INPUT=view.html vite build",
|
|
35
|
+
"dev": "bun run --watch src/sse-server.ts",
|
|
36
|
+
"start": "bun run dist/sse-server.js",
|
|
37
|
+
"test": "bun test",
|
|
38
|
+
"test:e2e": "bun test e2e",
|
|
39
|
+
"lint": "eslint src/**/*.ts",
|
|
40
|
+
"type-check": "tsc --noEmit",
|
|
41
|
+
"format": "prettier --write \"src/**/*.{ts,tsx}\" \"e2e/**/*.{ts,tsx}\"",
|
|
42
|
+
"format:check": "prettier --check \"src/**/*.{ts,tsx}\" \"e2e/**/*.{ts,tsx}\"",
|
|
43
|
+
"postinstall": "playwright install chromium"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@aws-sdk/client-s3": "^3.756.0",
|
|
47
|
+
"@aws-sdk/s3-request-presigner": "^3.756.0",
|
|
48
|
+
"@modelcontextprotocol/ext-apps": "1.0.1",
|
|
49
|
+
"@modelcontextprotocol/sdk": "1.26.0",
|
|
50
|
+
"@playwright/experimental-ct-react": "^1.56.1",
|
|
51
|
+
"@playwright/test": "^1.56.1",
|
|
52
|
+
"@sisense/sdk-ai-core": "0.5.1",
|
|
53
|
+
"@sisense/sdk-data": "2.20.0",
|
|
54
|
+
"@sisense/sdk-ui": "2.20.0",
|
|
55
|
+
"playwright": "^1.56.1",
|
|
56
|
+
"react": "^18.3.1",
|
|
57
|
+
"react-dom": "^18.3.1",
|
|
58
|
+
"zod": "^3.22.4"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@types/bun": "^1.0.0",
|
|
62
|
+
"@types/node": "^20.10.0",
|
|
63
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
64
|
+
"cross-env": "^7.0.3",
|
|
65
|
+
"@types/react": "^18.3.1",
|
|
66
|
+
"@types/react-dom": "^18.3.1",
|
|
67
|
+
"@typescript-eslint/eslint-plugin": "^6.13.0",
|
|
68
|
+
"@typescript-eslint/parser": "^6.13.0",
|
|
69
|
+
"eslint": "^8.54.0",
|
|
70
|
+
"prettier": "^3.0.0",
|
|
71
|
+
"typescript": "^5.3.3",
|
|
72
|
+
"vite": "^6.0.0",
|
|
73
|
+
"vite-plugin-singlefile": "^2.3.0"
|
|
74
|
+
},
|
|
75
|
+
"engines": {
|
|
76
|
+
"node": ">=18.0.0",
|
|
77
|
+
"bun": ">=1.0.0"
|
|
78
|
+
},
|
|
79
|
+
"bugs": {
|
|
80
|
+
"url": "https://github.com/sisense/sisense-mcp-server/issues"
|
|
81
|
+
}
|
|
82
|
+
}
|