@dynamic-mockups/mcp 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.
- package/package.json +1 -1
- package/src/index.js +101 -17
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -29,6 +29,9 @@ const API_KEY = process.env.DYNAMIC_MOCKUPS_API_KEY;
|
|
|
29
29
|
const SERVER_NAME = "dynamic-mockups-mcp";
|
|
30
30
|
const SERVER_VERSION = "1.0.0";
|
|
31
31
|
|
|
32
|
+
// Transport mode tracking (set during server startup)
|
|
33
|
+
let currentTransportMode = "stdio";
|
|
34
|
+
|
|
32
35
|
// =============================================================================
|
|
33
36
|
// API Knowledge Base
|
|
34
37
|
// =============================================================================
|
|
@@ -407,20 +410,72 @@ const server = new Server(
|
|
|
407
410
|
// HTTP Client
|
|
408
411
|
// =============================================================================
|
|
409
412
|
|
|
413
|
+
/**
|
|
414
|
+
* Returns MCP tracking headers for API requests.
|
|
415
|
+
* @param {string} toolName - The name of the MCP tool being called
|
|
416
|
+
* @returns {Object} Headers object with tracking information
|
|
417
|
+
*/
|
|
418
|
+
function getMcpTrackingHeaders(toolName) {
|
|
419
|
+
return {
|
|
420
|
+
"x-mcp-server": "true",
|
|
421
|
+
"x-mcp-tool": toolName,
|
|
422
|
+
"x-mcp-transport-mode": currentTransportMode,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Tracks MCP tool usage by sending event to /mcp/track endpoint (fire and forget).
|
|
428
|
+
* Called after every tool execution with success/error status.
|
|
429
|
+
*
|
|
430
|
+
* @param {string} toolName - The name of the MCP tool
|
|
431
|
+
* @param {string} apiKey - The API key for the request
|
|
432
|
+
* @param {Object} result - The tool execution result
|
|
433
|
+
* @param {boolean} result.success - Whether the tool executed successfully
|
|
434
|
+
* @param {string|null} result.error - Error message if failed, null otherwise
|
|
435
|
+
*/
|
|
436
|
+
function trackToolUsage(toolName, apiKey, { success, error }) {
|
|
437
|
+
if (!apiKey) return; // Skip tracking if no API key
|
|
438
|
+
|
|
439
|
+
try {
|
|
440
|
+
// Fire and forget - don't await or handle errors
|
|
441
|
+
axios.post(
|
|
442
|
+
`${API_BASE_URL}/mcp/track`,
|
|
443
|
+
{
|
|
444
|
+
tool: toolName,
|
|
445
|
+
success,
|
|
446
|
+
error: error || null,
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
headers: {
|
|
450
|
+
"Accept": "application/json",
|
|
451
|
+
"Content-Type": "application/json",
|
|
452
|
+
"x-api-key": apiKey,
|
|
453
|
+
...getMcpTrackingHeaders(toolName),
|
|
454
|
+
},
|
|
455
|
+
timeout: 5000, // Short timeout for tracking
|
|
456
|
+
}
|
|
457
|
+
).catch(() => {}); // Silently ignore errors
|
|
458
|
+
} catch {
|
|
459
|
+
// Silently ignore any errors - tracking should never break functionality
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
410
463
|
/**
|
|
411
464
|
* Creates an API client with the provided API key.
|
|
412
465
|
* For stdio transport: uses environment variable
|
|
413
466
|
* For HTTP transport: uses client-provided API key from Authorization header
|
|
414
467
|
*
|
|
415
468
|
* @param {string} apiKey - The API key to use for requests
|
|
469
|
+
* @param {string} toolName - The name of the MCP tool (for tracking)
|
|
416
470
|
*/
|
|
417
|
-
function createApiClient(apiKey) {
|
|
471
|
+
function createApiClient(apiKey, toolName) {
|
|
418
472
|
return axios.create({
|
|
419
473
|
baseURL: API_BASE_URL,
|
|
420
474
|
headers: {
|
|
421
475
|
"Accept": "application/json",
|
|
422
476
|
"Content-Type": "application/json",
|
|
423
477
|
"x-api-key": apiKey || "",
|
|
478
|
+
...getMcpTrackingHeaders(toolName),
|
|
424
479
|
},
|
|
425
480
|
timeout: 60000, // 60 second timeout for render operations
|
|
426
481
|
validateStatus: (status) => status < 500, // Only throw on 5xx errors
|
|
@@ -1285,7 +1340,7 @@ async function handleGetCatalogs(args, extra) {
|
|
|
1285
1340
|
if (error) return error;
|
|
1286
1341
|
|
|
1287
1342
|
try {
|
|
1288
|
-
const response = await createApiClient(apiKey).get("/catalogs");
|
|
1343
|
+
const response = await createApiClient(apiKey, "get_catalogs").get("/catalogs");
|
|
1289
1344
|
return ResponseFormatter.fromApiResponse(response);
|
|
1290
1345
|
} catch (err) {
|
|
1291
1346
|
return ResponseFormatter.fromError(err, "Failed to get catalogs");
|
|
@@ -1304,7 +1359,7 @@ async function handleGetCollections(args = {}, extra) {
|
|
|
1304
1359
|
params.append("include_all_catalogs", args.include_all_catalogs);
|
|
1305
1360
|
}
|
|
1306
1361
|
|
|
1307
|
-
const response = await createApiClient(apiKey).get(`/collections?${params}`);
|
|
1362
|
+
const response = await createApiClient(apiKey, "get_collections").get(`/collections?${params}`);
|
|
1308
1363
|
return ResponseFormatter.fromApiResponse(response);
|
|
1309
1364
|
} catch (err) {
|
|
1310
1365
|
return ResponseFormatter.fromError(err, "Failed to get collections");
|
|
@@ -1320,7 +1375,7 @@ async function handleCreateCollection(args, extra) {
|
|
|
1320
1375
|
const payload = { name: args.name };
|
|
1321
1376
|
if (args.catalog_uuid) payload.catalog_uuid = args.catalog_uuid;
|
|
1322
1377
|
|
|
1323
|
-
const response = await createApiClient(apiKey).post("/collections", payload);
|
|
1378
|
+
const response = await createApiClient(apiKey, "create_collection").post("/collections", payload);
|
|
1324
1379
|
return ResponseFormatter.fromApiResponse(response, `Collection "${args.name}" created`);
|
|
1325
1380
|
} catch (err) {
|
|
1326
1381
|
return ResponseFormatter.fromError(err, "Failed to create collection");
|
|
@@ -1341,7 +1396,7 @@ async function handleGetMockups(args = {}, extra) {
|
|
|
1341
1396
|
}
|
|
1342
1397
|
if (args.name) params.append("name", args.name);
|
|
1343
1398
|
|
|
1344
|
-
const response = await createApiClient(apiKey).get(`/mockups?${params}`);
|
|
1399
|
+
const response = await createApiClient(apiKey, "get_mockups").get(`/mockups?${params}`);
|
|
1345
1400
|
return ResponseFormatter.fromApiResponse(response);
|
|
1346
1401
|
} catch (err) {
|
|
1347
1402
|
return ResponseFormatter.fromError(err, "Failed to get mockups");
|
|
@@ -1354,7 +1409,7 @@ async function handleGetMockupByUuid(args, extra) {
|
|
|
1354
1409
|
if (error) return error;
|
|
1355
1410
|
|
|
1356
1411
|
try {
|
|
1357
|
-
const response = await createApiClient(apiKey).get(`/mockup/${args.uuid}`);
|
|
1412
|
+
const response = await createApiClient(apiKey, "get_mockup_by_uuid").get(`/mockup/${args.uuid}`);
|
|
1358
1413
|
return ResponseFormatter.fromApiResponse(response);
|
|
1359
1414
|
} catch (err) {
|
|
1360
1415
|
return ResponseFormatter.fromError(err, "Failed to get mockup");
|
|
@@ -1375,7 +1430,7 @@ async function handleCreateRender(args, extra) {
|
|
|
1375
1430
|
if (args.export_options) payload.export_options = args.export_options;
|
|
1376
1431
|
if (args.text_layers) payload.text_layers = args.text_layers;
|
|
1377
1432
|
|
|
1378
|
-
const response = await createApiClient(apiKey).post("/renders", payload);
|
|
1433
|
+
const response = await createApiClient(apiKey, "create_render").post("/renders", payload);
|
|
1379
1434
|
return ResponseFormatter.fromApiResponse(response, "Render created (1 credit used)");
|
|
1380
1435
|
} catch (err) {
|
|
1381
1436
|
return ResponseFormatter.fromError(err, "Failed to create render");
|
|
@@ -1391,7 +1446,7 @@ async function handleCreateBatchRender(args, extra) {
|
|
|
1391
1446
|
const payload = { renders: args.renders };
|
|
1392
1447
|
if (args.export_options) payload.export_options = args.export_options;
|
|
1393
1448
|
|
|
1394
|
-
const response = await createApiClient(apiKey).post("/renders/batch", payload);
|
|
1449
|
+
const response = await createApiClient(apiKey, "create_batch_render").post("/renders/batch", payload);
|
|
1395
1450
|
const count = args.renders?.length || 0;
|
|
1396
1451
|
return ResponseFormatter.fromApiResponse(response, `Batch render complete (${count} credits used)`);
|
|
1397
1452
|
} catch (err) {
|
|
@@ -1413,7 +1468,7 @@ async function handleExportPrintFiles(args, extra) {
|
|
|
1413
1468
|
if (args.export_options) payload.export_options = args.export_options;
|
|
1414
1469
|
if (args.text_layers) payload.text_layers = args.text_layers;
|
|
1415
1470
|
|
|
1416
|
-
const response = await createApiClient(apiKey).post("/renders/print-files", payload);
|
|
1471
|
+
const response = await createApiClient(apiKey, "export_print_files").post("/renders/print-files", payload);
|
|
1417
1472
|
return ResponseFormatter.fromApiResponse(response, "Print files exported");
|
|
1418
1473
|
} catch (err) {
|
|
1419
1474
|
return ResponseFormatter.fromError(err, "Failed to export print files");
|
|
@@ -1431,7 +1486,7 @@ async function handleUploadPsd(args, extra) {
|
|
|
1431
1486
|
if (args.psd_category_id) payload.psd_category_id = args.psd_category_id;
|
|
1432
1487
|
if (args.mockup_template) payload.mockup_template = args.mockup_template;
|
|
1433
1488
|
|
|
1434
|
-
const response = await createApiClient(apiKey).post("/psd/upload", payload);
|
|
1489
|
+
const response = await createApiClient(apiKey, "upload_psd").post("/psd/upload", payload);
|
|
1435
1490
|
return ResponseFormatter.fromApiResponse(response, "PSD uploaded successfully");
|
|
1436
1491
|
} catch (err) {
|
|
1437
1492
|
return ResponseFormatter.fromError(err, "Failed to upload PSD");
|
|
@@ -1449,7 +1504,7 @@ async function handleDeletePsd(args, extra) {
|
|
|
1449
1504
|
payload.delete_related_mockups = args.delete_related_mockups;
|
|
1450
1505
|
}
|
|
1451
1506
|
|
|
1452
|
-
const response = await createApiClient(apiKey).post("/psd/delete", payload);
|
|
1507
|
+
const response = await createApiClient(apiKey, "delete_psd").post("/psd/delete", payload);
|
|
1453
1508
|
return ResponseFormatter.fromApiResponse(response, "PSD deleted successfully");
|
|
1454
1509
|
} catch (err) {
|
|
1455
1510
|
return ResponseFormatter.fromError(err, "Failed to delete PSD");
|
|
@@ -1483,17 +1538,29 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
|
|
|
1483
1538
|
|
|
1484
1539
|
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
1485
1540
|
const { name, arguments: args } = request.params;
|
|
1541
|
+
const apiKey = getApiKey(extra);
|
|
1486
1542
|
|
|
1487
1543
|
const handler = toolHandlers[name];
|
|
1488
1544
|
if (!handler) {
|
|
1489
|
-
|
|
1545
|
+
const result = ResponseFormatter.error(`Unknown tool: ${name}`);
|
|
1546
|
+
trackToolUsage(name, apiKey, { success: false, error: `Unknown tool: ${name}` });
|
|
1547
|
+
return result;
|
|
1490
1548
|
}
|
|
1491
1549
|
|
|
1492
1550
|
try {
|
|
1493
1551
|
// Pass extra context (contains requestInfo with headers for HTTP transport)
|
|
1494
|
-
|
|
1552
|
+
const result = await handler(args || {}, extra);
|
|
1553
|
+
|
|
1554
|
+
// Track tool usage (fire and forget)
|
|
1555
|
+
const isError = result.isError || false;
|
|
1556
|
+
const errorMessage = isError && result.content?.[0]?.text ? result.content[0].text : null;
|
|
1557
|
+
trackToolUsage(name, apiKey, { success: !isError, error: errorMessage });
|
|
1558
|
+
|
|
1559
|
+
return result;
|
|
1495
1560
|
} catch (err) {
|
|
1496
|
-
|
|
1561
|
+
const result = ResponseFormatter.fromError(err, `Error executing ${name}`);
|
|
1562
|
+
trackToolUsage(name, apiKey, { success: false, error: err.message || `Error executing ${name}` });
|
|
1563
|
+
return result;
|
|
1497
1564
|
}
|
|
1498
1565
|
});
|
|
1499
1566
|
|
|
@@ -1506,6 +1573,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
1506
1573
|
* Used by: Claude Desktop, Claude Code, Cursor, Windsurf
|
|
1507
1574
|
*/
|
|
1508
1575
|
async function startStdioServer() {
|
|
1576
|
+
currentTransportMode = "stdio";
|
|
1509
1577
|
const transport = new StdioServerTransport();
|
|
1510
1578
|
await server.connect(transport);
|
|
1511
1579
|
console.error(`Dynamic Mockups MCP Server v${SERVER_VERSION} running (stdio)`);
|
|
@@ -1525,6 +1593,8 @@ async function startStdioServer() {
|
|
|
1525
1593
|
* @returns {Promise<{app: Express, httpServer: Server}>}
|
|
1526
1594
|
*/
|
|
1527
1595
|
async function startHttpServer(options = {}) {
|
|
1596
|
+
currentTransportMode = "http";
|
|
1597
|
+
|
|
1528
1598
|
const {
|
|
1529
1599
|
port = process.env.PORT || 3000,
|
|
1530
1600
|
host = process.env.HOST || "0.0.0.0",
|
|
@@ -1606,15 +1676,29 @@ async function startHttpServer(options = {}) {
|
|
|
1606
1676
|
connectionServer.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
|
|
1607
1677
|
connectionServer.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
1608
1678
|
const { name, arguments: args } = request.params;
|
|
1679
|
+
const apiKey = getApiKey(extra);
|
|
1680
|
+
|
|
1609
1681
|
const handler = toolHandlers[name];
|
|
1610
1682
|
if (!handler) {
|
|
1611
|
-
|
|
1683
|
+
const result = ResponseFormatter.error(`Unknown tool: ${name}`);
|
|
1684
|
+
trackToolUsage(name, apiKey, { success: false, error: `Unknown tool: ${name}` });
|
|
1685
|
+
return result;
|
|
1612
1686
|
}
|
|
1687
|
+
|
|
1613
1688
|
try {
|
|
1614
1689
|
// Pass extra context (contains requestInfo with headers for API key extraction)
|
|
1615
|
-
|
|
1690
|
+
const result = await handler(args || {}, extra);
|
|
1691
|
+
|
|
1692
|
+
// Track tool usage (fire and forget)
|
|
1693
|
+
const isError = result.isError || false;
|
|
1694
|
+
const errorMessage = isError && result.content?.[0]?.text ? result.content[0].text : null;
|
|
1695
|
+
trackToolUsage(name, apiKey, { success: !isError, error: errorMessage });
|
|
1696
|
+
|
|
1697
|
+
return result;
|
|
1616
1698
|
} catch (err) {
|
|
1617
|
-
|
|
1699
|
+
const result = ResponseFormatter.fromError(err, `Error executing ${name}`);
|
|
1700
|
+
trackToolUsage(name, apiKey, { success: false, error: err.message || `Error executing ${name}` });
|
|
1701
|
+
return result;
|
|
1618
1702
|
}
|
|
1619
1703
|
});
|
|
1620
1704
|
|