@warlok-net/mcp-server 0.1.0
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/README.md +40 -0
- package/dist/chunk-HTA6LITF.mjs +678 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +719 -0
- package/dist/cli.mjs +32 -0
- package/dist/index.d.mts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +706 -0
- package/dist/index.mjs +8 -0
- package/package.json +55 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/index.ts
|
|
27
|
+
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
28
|
+
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
29
|
+
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
30
|
+
var import_sdk = require("@warlok-net/sdk");
|
|
31
|
+
|
|
32
|
+
// src/screenshot.ts
|
|
33
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
34
|
+
var import_node_path = __toESM(require("path"));
|
|
35
|
+
async function loadChromium() {
|
|
36
|
+
try {
|
|
37
|
+
const playwright = await import("playwright");
|
|
38
|
+
return playwright.chromium;
|
|
39
|
+
} catch {
|
|
40
|
+
throw new Error(
|
|
41
|
+
"capture_ui_screenshot requires Playwright. Install it with: npm i playwright && npx playwright install chromium"
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
var DEFAULT_VIEWPORT_WIDTH = 1440;
|
|
46
|
+
var DEFAULT_VIEWPORT_HEIGHT = 900;
|
|
47
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
48
|
+
var DEFAULT_WAIT_MS = 250;
|
|
49
|
+
var DEFAULT_FORMAT = "png";
|
|
50
|
+
async function captureUiScreenshot(input) {
|
|
51
|
+
const startedAt = Date.now();
|
|
52
|
+
const url = parseHttpUrl(input.url);
|
|
53
|
+
const width = clampInteger(input.viewportWidth, 320, 7680, DEFAULT_VIEWPORT_WIDTH);
|
|
54
|
+
const height = clampInteger(
|
|
55
|
+
input.viewportHeight,
|
|
56
|
+
240,
|
|
57
|
+
4320,
|
|
58
|
+
DEFAULT_VIEWPORT_HEIGHT
|
|
59
|
+
);
|
|
60
|
+
const timeoutMs = clampInteger(input.timeoutMs, 5e3, 12e4, DEFAULT_TIMEOUT_MS);
|
|
61
|
+
const waitForMs = clampInteger(input.waitForMs, 0, 3e4, DEFAULT_WAIT_MS);
|
|
62
|
+
const fullPage = input.fullPage ?? true;
|
|
63
|
+
const colorScheme = input.colorScheme ?? "dark";
|
|
64
|
+
const format = input.format ?? inferFormatFromOutputPath(input.outputPath);
|
|
65
|
+
const outputPath = resolveOutputPath(url, input.outputPath, format);
|
|
66
|
+
await import_promises.default.mkdir(import_node_path.default.dirname(outputPath), { recursive: true });
|
|
67
|
+
const chromium = await loadChromium();
|
|
68
|
+
const browser = await chromium.launch({ headless: true });
|
|
69
|
+
try {
|
|
70
|
+
const context = await browser.newContext({
|
|
71
|
+
viewport: { width, height },
|
|
72
|
+
colorScheme,
|
|
73
|
+
deviceScaleFactor: 1
|
|
74
|
+
});
|
|
75
|
+
const page = await context.newPage();
|
|
76
|
+
page.setDefaultTimeout(timeoutMs);
|
|
77
|
+
await page.goto(url.toString(), { waitUntil: "networkidle", timeout: timeoutMs });
|
|
78
|
+
if (input.waitForSelector) {
|
|
79
|
+
await page.waitForSelector(input.waitForSelector, {
|
|
80
|
+
state: "visible",
|
|
81
|
+
timeout: timeoutMs
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (waitForMs > 0) {
|
|
85
|
+
await page.waitForTimeout(waitForMs);
|
|
86
|
+
}
|
|
87
|
+
if (input.selector) {
|
|
88
|
+
const locator = page.locator(input.selector).first();
|
|
89
|
+
await locator.waitFor({ state: "visible", timeout: timeoutMs });
|
|
90
|
+
await locator.screenshot({
|
|
91
|
+
path: outputPath,
|
|
92
|
+
type: format
|
|
93
|
+
});
|
|
94
|
+
} else {
|
|
95
|
+
await page.screenshot({
|
|
96
|
+
path: outputPath,
|
|
97
|
+
fullPage,
|
|
98
|
+
type: format
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
await context.close();
|
|
102
|
+
} finally {
|
|
103
|
+
await browser.close();
|
|
104
|
+
}
|
|
105
|
+
const stat = await import_promises.default.stat(outputPath);
|
|
106
|
+
return {
|
|
107
|
+
outputPath,
|
|
108
|
+
bytes: stat.size,
|
|
109
|
+
width,
|
|
110
|
+
height,
|
|
111
|
+
url: url.toString(),
|
|
112
|
+
selector: input.selector ?? null,
|
|
113
|
+
fullPage,
|
|
114
|
+
colorScheme,
|
|
115
|
+
format,
|
|
116
|
+
durationMs: Date.now() - startedAt
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function parseHttpUrl(value) {
|
|
120
|
+
let parsed;
|
|
121
|
+
try {
|
|
122
|
+
parsed = new URL(value);
|
|
123
|
+
} catch {
|
|
124
|
+
throw new Error("Invalid URL. Expected a valid absolute URL.");
|
|
125
|
+
}
|
|
126
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
127
|
+
throw new Error("Invalid URL protocol. Only http/https URLs are supported.");
|
|
128
|
+
}
|
|
129
|
+
return parsed;
|
|
130
|
+
}
|
|
131
|
+
function resolveOutputPath(url, requestedPath, format) {
|
|
132
|
+
if (requestedPath) {
|
|
133
|
+
const absolute = import_node_path.default.isAbsolute(requestedPath) ? requestedPath : import_node_path.default.resolve(outputRoot(), requestedPath);
|
|
134
|
+
return ensureExt(absolute, format);
|
|
135
|
+
}
|
|
136
|
+
const now = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
137
|
+
const host = sanitizeSegment(url.host);
|
|
138
|
+
const pathname = sanitizeSegment(url.pathname.replace(/\//g, "_") || "root");
|
|
139
|
+
const filename = `${now}-${host}-${pathname}.${format}`;
|
|
140
|
+
return import_node_path.default.join(outputRoot(), filename);
|
|
141
|
+
}
|
|
142
|
+
function outputRoot() {
|
|
143
|
+
const configured = process.env.WARLOK_SCREENSHOT_DIR;
|
|
144
|
+
if (configured && configured.trim()) {
|
|
145
|
+
return import_node_path.default.resolve(configured.trim());
|
|
146
|
+
}
|
|
147
|
+
return import_node_path.default.resolve(process.cwd(), "tmp", "mcp-screenshots");
|
|
148
|
+
}
|
|
149
|
+
function ensureExt(value, format) {
|
|
150
|
+
const ext = import_node_path.default.extname(value).toLowerCase();
|
|
151
|
+
if (ext === ".png" || ext === ".jpg" || ext === ".jpeg" || ext === ".webp") {
|
|
152
|
+
return value;
|
|
153
|
+
}
|
|
154
|
+
return `${value}.${format}`;
|
|
155
|
+
}
|
|
156
|
+
function sanitizeSegment(value) {
|
|
157
|
+
return value.toLowerCase().replace(/[^a-z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 120) || "capture";
|
|
158
|
+
}
|
|
159
|
+
function inferFormatFromOutputPath(pathname) {
|
|
160
|
+
if (!pathname) return DEFAULT_FORMAT;
|
|
161
|
+
const ext = import_node_path.default.extname(pathname).toLowerCase();
|
|
162
|
+
if (ext === ".jpg" || ext === ".jpeg") return "jpeg";
|
|
163
|
+
return "png";
|
|
164
|
+
}
|
|
165
|
+
function clampInteger(value, min, max, fallback) {
|
|
166
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
167
|
+
const rounded = Math.round(value);
|
|
168
|
+
if (rounded < min) return min;
|
|
169
|
+
if (rounded > max) return max;
|
|
170
|
+
return rounded;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/index.ts
|
|
174
|
+
var TOOLS = [
|
|
175
|
+
{
|
|
176
|
+
name: "generate_3d_model",
|
|
177
|
+
description: "Generate a 3D model from text description or image. Returns an asset_id to track status.",
|
|
178
|
+
inputSchema: {
|
|
179
|
+
type: "object",
|
|
180
|
+
properties: {
|
|
181
|
+
name: {
|
|
182
|
+
type: "string",
|
|
183
|
+
description: "Name for the generation (max 100 characters)"
|
|
184
|
+
},
|
|
185
|
+
type: {
|
|
186
|
+
type: "string",
|
|
187
|
+
enum: [
|
|
188
|
+
"character",
|
|
189
|
+
"creature",
|
|
190
|
+
"environment",
|
|
191
|
+
"prop",
|
|
192
|
+
"weapon",
|
|
193
|
+
"vehicle",
|
|
194
|
+
"material",
|
|
195
|
+
"noun",
|
|
196
|
+
"person",
|
|
197
|
+
"place",
|
|
198
|
+
"thing"
|
|
199
|
+
],
|
|
200
|
+
description: "Entity or legacy asset type"
|
|
201
|
+
},
|
|
202
|
+
prompt: {
|
|
203
|
+
type: "string",
|
|
204
|
+
description: "Text prompt describing what to generate"
|
|
205
|
+
},
|
|
206
|
+
image_url: {
|
|
207
|
+
type: "string",
|
|
208
|
+
description: "Optional URL to a reference image"
|
|
209
|
+
},
|
|
210
|
+
auto_rig: {
|
|
211
|
+
type: "boolean",
|
|
212
|
+
description: "Whether to rig for Mixamo when generating a person",
|
|
213
|
+
default: true
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
required: ["type"]
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
name: "get_model_status",
|
|
221
|
+
description: "Check generation status by asset_id.",
|
|
222
|
+
inputSchema: {
|
|
223
|
+
type: "object",
|
|
224
|
+
properties: {
|
|
225
|
+
asset_id: {
|
|
226
|
+
type: "string",
|
|
227
|
+
description: "The generation ID"
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
required: ["asset_id"]
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: "list_models",
|
|
235
|
+
description: "List generations for the current account.",
|
|
236
|
+
inputSchema: {
|
|
237
|
+
type: "object",
|
|
238
|
+
properties: {
|
|
239
|
+
limit: {
|
|
240
|
+
type: "number",
|
|
241
|
+
description: "Maximum rows to return (default: 20)",
|
|
242
|
+
default: 20
|
|
243
|
+
},
|
|
244
|
+
type: {
|
|
245
|
+
type: "string",
|
|
246
|
+
enum: [
|
|
247
|
+
"character",
|
|
248
|
+
"creature",
|
|
249
|
+
"environment",
|
|
250
|
+
"prop",
|
|
251
|
+
"weapon",
|
|
252
|
+
"vehicle",
|
|
253
|
+
"material",
|
|
254
|
+
"noun",
|
|
255
|
+
"person",
|
|
256
|
+
"place",
|
|
257
|
+
"thing"
|
|
258
|
+
],
|
|
259
|
+
description: "Filter by entity/legacy type"
|
|
260
|
+
},
|
|
261
|
+
status: {
|
|
262
|
+
type: "string",
|
|
263
|
+
enum: [
|
|
264
|
+
"pending",
|
|
265
|
+
"preprocessing",
|
|
266
|
+
"generating",
|
|
267
|
+
"rigging",
|
|
268
|
+
"complete",
|
|
269
|
+
"failed"
|
|
270
|
+
],
|
|
271
|
+
description: "Filter by generation status"
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
name: "get_model_download_url",
|
|
278
|
+
description: "Get the best available download URL for a completed generation.",
|
|
279
|
+
inputSchema: {
|
|
280
|
+
type: "object",
|
|
281
|
+
properties: {
|
|
282
|
+
asset_id: {
|
|
283
|
+
type: "string",
|
|
284
|
+
description: "The generation ID"
|
|
285
|
+
},
|
|
286
|
+
format: {
|
|
287
|
+
type: "string",
|
|
288
|
+
enum: ["model", "rigged"],
|
|
289
|
+
description: "Preferred format (default: best available)"
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
required: ["asset_id"]
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: "delete_model",
|
|
297
|
+
description: "Delete a generation.",
|
|
298
|
+
inputSchema: {
|
|
299
|
+
type: "object",
|
|
300
|
+
properties: {
|
|
301
|
+
asset_id: {
|
|
302
|
+
type: "string",
|
|
303
|
+
description: "The generation ID to delete"
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
required: ["asset_id"]
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
name: "capture_ui_screenshot",
|
|
311
|
+
description: "Capture a deterministic Playwright screenshot for UI/design iteration.",
|
|
312
|
+
inputSchema: {
|
|
313
|
+
type: "object",
|
|
314
|
+
properties: {
|
|
315
|
+
url: {
|
|
316
|
+
type: "string",
|
|
317
|
+
description: "HTTP/HTTPS URL to capture"
|
|
318
|
+
},
|
|
319
|
+
output_path: {
|
|
320
|
+
type: "string",
|
|
321
|
+
description: "Optional output file path. Relative paths are resolved in tmp/mcp-screenshots."
|
|
322
|
+
},
|
|
323
|
+
selector: {
|
|
324
|
+
type: "string",
|
|
325
|
+
description: "Optional CSS selector to capture only a specific element."
|
|
326
|
+
},
|
|
327
|
+
wait_for_selector: {
|
|
328
|
+
type: "string",
|
|
329
|
+
description: "Optional selector to wait for before capture (useful for async UI)."
|
|
330
|
+
},
|
|
331
|
+
wait_for_ms: {
|
|
332
|
+
type: "number",
|
|
333
|
+
description: "Extra delay before capture in milliseconds (default: 250).",
|
|
334
|
+
default: 250
|
|
335
|
+
},
|
|
336
|
+
timeout_ms: {
|
|
337
|
+
type: "number",
|
|
338
|
+
description: "Navigation and selector timeout in milliseconds (default: 30000).",
|
|
339
|
+
default: 3e4
|
|
340
|
+
},
|
|
341
|
+
full_page: {
|
|
342
|
+
type: "boolean",
|
|
343
|
+
description: "Capture full page when selector is not set (default: true).",
|
|
344
|
+
default: true
|
|
345
|
+
},
|
|
346
|
+
viewport_width: {
|
|
347
|
+
type: "number",
|
|
348
|
+
description: "Viewport width in px (default: 1440).",
|
|
349
|
+
default: 1440
|
|
350
|
+
},
|
|
351
|
+
viewport_height: {
|
|
352
|
+
type: "number",
|
|
353
|
+
description: "Viewport height in px (default: 900).",
|
|
354
|
+
default: 900
|
|
355
|
+
},
|
|
356
|
+
color_scheme: {
|
|
357
|
+
type: "string",
|
|
358
|
+
enum: ["light", "dark", "no-preference"],
|
|
359
|
+
description: "Preferred color scheme (default: dark).",
|
|
360
|
+
default: "dark"
|
|
361
|
+
},
|
|
362
|
+
format: {
|
|
363
|
+
type: "string",
|
|
364
|
+
enum: ["png", "jpeg"],
|
|
365
|
+
description: "Screenshot format (default inferred from output_path, else png)."
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
required: ["url"]
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
];
|
|
372
|
+
function createServer(apiKey2, baseUrl2) {
|
|
373
|
+
const client = new import_sdk.WarlokClient({ apiKey: apiKey2, baseUrl: baseUrl2 });
|
|
374
|
+
const server = new import_server.Server(
|
|
375
|
+
{
|
|
376
|
+
name: "warlok-mcp",
|
|
377
|
+
version: "0.1.0"
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
capabilities: {
|
|
381
|
+
tools: {}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
);
|
|
385
|
+
server.setRequestHandler(import_types.ListToolsRequestSchema, async () => ({
|
|
386
|
+
tools: TOOLS
|
|
387
|
+
}));
|
|
388
|
+
server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
|
|
389
|
+
const { name, arguments: args } = request.params;
|
|
390
|
+
try {
|
|
391
|
+
switch (name) {
|
|
392
|
+
case "generate_3d_model": {
|
|
393
|
+
const generation = await client.generate({
|
|
394
|
+
name: stringOrUndefined(args, "name"),
|
|
395
|
+
type: requiredType(args),
|
|
396
|
+
prompt: stringOrUndefined(args, "prompt"),
|
|
397
|
+
imageUrl: stringOrUndefined(args, "image_url"),
|
|
398
|
+
options: {
|
|
399
|
+
autoRig: booleanOrUndefined(args, "auto_rig")
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
return {
|
|
403
|
+
content: [
|
|
404
|
+
{
|
|
405
|
+
type: "text",
|
|
406
|
+
text: JSON.stringify(
|
|
407
|
+
{
|
|
408
|
+
success: true,
|
|
409
|
+
asset_id: generation.id,
|
|
410
|
+
name: generation.name,
|
|
411
|
+
entity_type: generation.entityType,
|
|
412
|
+
status: generation.status,
|
|
413
|
+
message: `Generation started. Use get_model_status with asset_id "${generation.id}".`
|
|
414
|
+
},
|
|
415
|
+
null,
|
|
416
|
+
2
|
|
417
|
+
)
|
|
418
|
+
}
|
|
419
|
+
]
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
case "get_model_status": {
|
|
423
|
+
const generation = await client.getGeneration(requiredAssetId(args));
|
|
424
|
+
return {
|
|
425
|
+
content: [
|
|
426
|
+
{
|
|
427
|
+
type: "text",
|
|
428
|
+
text: JSON.stringify(formatGenerationStatus(generation), null, 2)
|
|
429
|
+
}
|
|
430
|
+
]
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
case "list_models": {
|
|
434
|
+
const generations = await client.listGenerations({
|
|
435
|
+
limit: numberOrUndefined(args, "limit") || 20,
|
|
436
|
+
type: typeOrUndefined(args),
|
|
437
|
+
status: statusOrUndefined(args)
|
|
438
|
+
});
|
|
439
|
+
return {
|
|
440
|
+
content: [
|
|
441
|
+
{
|
|
442
|
+
type: "text",
|
|
443
|
+
text: JSON.stringify(
|
|
444
|
+
{
|
|
445
|
+
total: generations.length,
|
|
446
|
+
models: generations.map((generation) => ({
|
|
447
|
+
id: generation.id,
|
|
448
|
+
name: generation.name,
|
|
449
|
+
entity_type: generation.entityType,
|
|
450
|
+
status: generation.status,
|
|
451
|
+
created_at: generation.createdAt
|
|
452
|
+
}))
|
|
453
|
+
},
|
|
454
|
+
null,
|
|
455
|
+
2
|
|
456
|
+
)
|
|
457
|
+
}
|
|
458
|
+
]
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
case "get_model_download_url": {
|
|
462
|
+
const generation = await client.getGeneration(requiredAssetId(args));
|
|
463
|
+
if (generation.status !== "complete") {
|
|
464
|
+
return {
|
|
465
|
+
content: [
|
|
466
|
+
{
|
|
467
|
+
type: "text",
|
|
468
|
+
text: JSON.stringify({
|
|
469
|
+
success: false,
|
|
470
|
+
error: `Generation is not complete. Current status: ${generation.status}`
|
|
471
|
+
})
|
|
472
|
+
}
|
|
473
|
+
]
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
const format = downloadFormatOrUndefined(args);
|
|
477
|
+
const url = format === "model" ? generation.outputs.modelUrl : format === "rigged" ? generation.outputs.riggedModelUrl : generation.outputs.riggedModelUrl || generation.outputs.modelUrl;
|
|
478
|
+
if (!url) {
|
|
479
|
+
return {
|
|
480
|
+
content: [
|
|
481
|
+
{
|
|
482
|
+
type: "text",
|
|
483
|
+
text: JSON.stringify({
|
|
484
|
+
success: false,
|
|
485
|
+
error: "No download URL available for this generation"
|
|
486
|
+
})
|
|
487
|
+
}
|
|
488
|
+
]
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
return {
|
|
492
|
+
content: [
|
|
493
|
+
{
|
|
494
|
+
type: "text",
|
|
495
|
+
text: JSON.stringify(
|
|
496
|
+
{
|
|
497
|
+
success: true,
|
|
498
|
+
asset_id: generation.id,
|
|
499
|
+
name: generation.name,
|
|
500
|
+
download_url: url,
|
|
501
|
+
format: format || "best_available",
|
|
502
|
+
prompt: generation.input.prompt || null,
|
|
503
|
+
preview_png_url: generation.outputs.previewPngUrl || null,
|
|
504
|
+
nanobanana_png_url: generation.outputs.promptImageUrl || generation.provenance?.preprocessPromptImageUrl || null
|
|
505
|
+
},
|
|
506
|
+
null,
|
|
507
|
+
2
|
|
508
|
+
)
|
|
509
|
+
}
|
|
510
|
+
]
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
case "delete_model": {
|
|
514
|
+
const assetId = requiredAssetId(args);
|
|
515
|
+
await client.deleteGeneration(assetId);
|
|
516
|
+
return {
|
|
517
|
+
content: [
|
|
518
|
+
{
|
|
519
|
+
type: "text",
|
|
520
|
+
text: JSON.stringify({
|
|
521
|
+
success: true,
|
|
522
|
+
message: `Generation ${assetId} deleted successfully`
|
|
523
|
+
})
|
|
524
|
+
}
|
|
525
|
+
]
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
case "capture_ui_screenshot": {
|
|
529
|
+
const screenshot = await captureUiScreenshot({
|
|
530
|
+
url: requiredUrl(args),
|
|
531
|
+
outputPath: stringOrUndefined(args, "output_path"),
|
|
532
|
+
selector: stringOrUndefined(args, "selector"),
|
|
533
|
+
waitForSelector: stringOrUndefined(args, "wait_for_selector"),
|
|
534
|
+
waitForMs: numberOrUndefined(args, "wait_for_ms"),
|
|
535
|
+
timeoutMs: numberOrUndefined(args, "timeout_ms"),
|
|
536
|
+
fullPage: booleanOrUndefined(args, "full_page"),
|
|
537
|
+
viewportWidth: numberOrUndefined(args, "viewport_width"),
|
|
538
|
+
viewportHeight: numberOrUndefined(args, "viewport_height"),
|
|
539
|
+
colorScheme: colorSchemeOrUndefined(args),
|
|
540
|
+
format: screenshotFormatOrUndefined(args)
|
|
541
|
+
});
|
|
542
|
+
return {
|
|
543
|
+
content: [
|
|
544
|
+
{
|
|
545
|
+
type: "text",
|
|
546
|
+
text: JSON.stringify(
|
|
547
|
+
{
|
|
548
|
+
success: true,
|
|
549
|
+
...screenshot
|
|
550
|
+
},
|
|
551
|
+
null,
|
|
552
|
+
2
|
|
553
|
+
)
|
|
554
|
+
}
|
|
555
|
+
]
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
default:
|
|
559
|
+
throw new import_types.McpError(import_types.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
560
|
+
}
|
|
561
|
+
} catch (error) {
|
|
562
|
+
if (error instanceof import_types.McpError) {
|
|
563
|
+
throw error;
|
|
564
|
+
}
|
|
565
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
566
|
+
return {
|
|
567
|
+
content: [
|
|
568
|
+
{
|
|
569
|
+
type: "text",
|
|
570
|
+
text: JSON.stringify({ success: false, error: message })
|
|
571
|
+
}
|
|
572
|
+
],
|
|
573
|
+
isError: true
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
return server;
|
|
578
|
+
}
|
|
579
|
+
function requiredAssetId(args) {
|
|
580
|
+
const value = stringOrUndefined(args, "asset_id");
|
|
581
|
+
if (!value) {
|
|
582
|
+
throw new import_types.McpError(import_types.ErrorCode.InvalidParams, "asset_id is required");
|
|
583
|
+
}
|
|
584
|
+
return value;
|
|
585
|
+
}
|
|
586
|
+
function requiredType(args) {
|
|
587
|
+
const value = stringOrUndefined(args, "type");
|
|
588
|
+
if (!value) {
|
|
589
|
+
throw new import_types.McpError(import_types.ErrorCode.InvalidParams, "type is required");
|
|
590
|
+
}
|
|
591
|
+
return value;
|
|
592
|
+
}
|
|
593
|
+
function requiredUrl(args) {
|
|
594
|
+
const value = stringOrUndefined(args, "url");
|
|
595
|
+
if (!value) {
|
|
596
|
+
throw new import_types.McpError(import_types.ErrorCode.InvalidParams, "url is required");
|
|
597
|
+
}
|
|
598
|
+
return value;
|
|
599
|
+
}
|
|
600
|
+
function typeOrUndefined(args) {
|
|
601
|
+
const value = stringOrUndefined(args, "type");
|
|
602
|
+
return value;
|
|
603
|
+
}
|
|
604
|
+
function statusOrUndefined(args) {
|
|
605
|
+
const value = stringOrUndefined(args, "status");
|
|
606
|
+
return value;
|
|
607
|
+
}
|
|
608
|
+
function downloadFormatOrUndefined(args) {
|
|
609
|
+
const value = stringOrUndefined(args, "format");
|
|
610
|
+
if (value === "model" || value === "rigged") {
|
|
611
|
+
return value;
|
|
612
|
+
}
|
|
613
|
+
return void 0;
|
|
614
|
+
}
|
|
615
|
+
function stringOrUndefined(args, key) {
|
|
616
|
+
const value = args?.[key];
|
|
617
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
618
|
+
}
|
|
619
|
+
function booleanOrUndefined(args, key) {
|
|
620
|
+
const value = args?.[key];
|
|
621
|
+
return typeof value === "boolean" ? value : void 0;
|
|
622
|
+
}
|
|
623
|
+
function numberOrUndefined(args, key) {
|
|
624
|
+
const value = args?.[key];
|
|
625
|
+
return typeof value === "number" ? value : void 0;
|
|
626
|
+
}
|
|
627
|
+
function colorSchemeOrUndefined(args) {
|
|
628
|
+
const value = stringOrUndefined(args, "color_scheme");
|
|
629
|
+
if (value === "light" || value === "dark" || value === "no-preference") {
|
|
630
|
+
return value;
|
|
631
|
+
}
|
|
632
|
+
return void 0;
|
|
633
|
+
}
|
|
634
|
+
function screenshotFormatOrUndefined(args) {
|
|
635
|
+
const value = stringOrUndefined(args, "format");
|
|
636
|
+
if (value === "png" || value === "jpeg") {
|
|
637
|
+
return value;
|
|
638
|
+
}
|
|
639
|
+
return void 0;
|
|
640
|
+
}
|
|
641
|
+
function formatGenerationStatus(generation) {
|
|
642
|
+
const base = {
|
|
643
|
+
success: true,
|
|
644
|
+
asset_id: generation.id,
|
|
645
|
+
name: generation.name,
|
|
646
|
+
entity_type: generation.entityType,
|
|
647
|
+
status: generation.status,
|
|
648
|
+
progress: generation.progress,
|
|
649
|
+
prompt: generation.input.prompt || null,
|
|
650
|
+
preview_png_url: generation.outputs.previewPngUrl || null,
|
|
651
|
+
nanobanana_png_url: generation.outputs.promptImageUrl || generation.provenance?.preprocessPromptImageUrl || null,
|
|
652
|
+
created_at: generation.createdAt,
|
|
653
|
+
updated_at: generation.updatedAt
|
|
654
|
+
};
|
|
655
|
+
if (generation.status === "complete") {
|
|
656
|
+
return {
|
|
657
|
+
...base,
|
|
658
|
+
model_url: generation.outputs.modelUrl,
|
|
659
|
+
rigged_model_url: generation.outputs.riggedModelUrl,
|
|
660
|
+
mixamo_ready: generation.outputs.mixamoReady
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
if (generation.status === "failed") {
|
|
664
|
+
return {
|
|
665
|
+
...base,
|
|
666
|
+
error: generation.error
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
return {
|
|
670
|
+
...base,
|
|
671
|
+
message: getStatusMessage(generation.status, generation.progress)
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
function getStatusMessage(status, progress) {
|
|
675
|
+
const messages = {
|
|
676
|
+
pending: "Queued for processing",
|
|
677
|
+
preprocessing: "Preparing specialized PNG",
|
|
678
|
+
generating: "Generating 3D mesh",
|
|
679
|
+
rigging: "Rigging for Mixamo"
|
|
680
|
+
};
|
|
681
|
+
return `${messages[status] || "Processing"} (${progress}% complete)`;
|
|
682
|
+
}
|
|
683
|
+
async function runServer(apiKey2, baseUrl2) {
|
|
684
|
+
const server = createServer(apiKey2, baseUrl2);
|
|
685
|
+
const transport = new import_stdio.StdioServerTransport();
|
|
686
|
+
await server.connect(transport);
|
|
687
|
+
process.on("SIGINT", async () => {
|
|
688
|
+
await server.close();
|
|
689
|
+
process.exit(0);
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// src/cli.ts
|
|
694
|
+
var apiKey = process.env.WARLOK_API_KEY;
|
|
695
|
+
var baseUrl = process.env.WARLOK_API_URL;
|
|
696
|
+
if (!apiKey) {
|
|
697
|
+
console.error("Error: WARLOK_API_KEY environment variable is required");
|
|
698
|
+
console.error("");
|
|
699
|
+
console.error("Usage:");
|
|
700
|
+
console.error(" WARLOK_API_KEY=wk_... warlok-mcp");
|
|
701
|
+
console.error("");
|
|
702
|
+
console.error("Or set it in your Claude Desktop config:");
|
|
703
|
+
console.error(" {");
|
|
704
|
+
console.error(' "mcpServers": {');
|
|
705
|
+
console.error(' "warlok": {');
|
|
706
|
+
console.error(' "command": "npx",');
|
|
707
|
+
console.error(' "args": ["@warlok-net/mcp-server"],');
|
|
708
|
+
console.error(' "env": {');
|
|
709
|
+
console.error(' "WARLOK_API_KEY": "wk_..."');
|
|
710
|
+
console.error(" }");
|
|
711
|
+
console.error(" }");
|
|
712
|
+
console.error(" }");
|
|
713
|
+
console.error(" }");
|
|
714
|
+
process.exit(1);
|
|
715
|
+
}
|
|
716
|
+
runServer(apiKey, baseUrl).catch((error) => {
|
|
717
|
+
console.error("Server error:", error);
|
|
718
|
+
process.exit(1);
|
|
719
|
+
});
|