schemeog-mcp 2.2.1 → 2.3.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/index.js +248 -18
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -209,15 +209,208 @@ function generateId() {
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
/**
|
|
212
|
-
*
|
|
212
|
+
* Допустимые именованные цвета для элементов
|
|
213
|
+
*/
|
|
214
|
+
const VALID_COLORS = ["white", "light_blue", "light_green", "light_yellow", "light_orange", "light_red", "light_purple", "light_gray", "light_pink", "light_teal"];
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Маппинг HEX цветов к именованным (ближайшее соответствие)
|
|
218
|
+
*/
|
|
219
|
+
const HEX_TO_NAMED_COLOR = {
|
|
220
|
+
// Белые/серые
|
|
221
|
+
"#FFFFFF": "white", "#FFF": "white", "#FAFAFA": "white", "#F5F5F5": "light_gray",
|
|
222
|
+
"#E0E0E0": "light_gray", "#BDBDBD": "light_gray", "#9E9E9E": "light_gray",
|
|
223
|
+
"#CFD8DC": "light_gray", "#ECEFF1": "light_gray",
|
|
224
|
+
|
|
225
|
+
// Синие
|
|
226
|
+
"#E3F2FD": "light_blue", "#BBDEFB": "light_blue", "#90CAF9": "light_blue",
|
|
227
|
+
"#64B5F6": "light_blue", "#42A5F5": "light_blue", "#2196F3": "light_blue",
|
|
228
|
+
"#1E88E5": "light_blue", "#1976D2": "light_blue",
|
|
229
|
+
|
|
230
|
+
// Зелёные
|
|
231
|
+
"#E8F5E9": "light_green", "#C8E6C9": "light_green", "#A5D6A7": "light_green",
|
|
232
|
+
"#81C784": "light_green", "#66BB6A": "light_green", "#4CAF50": "light_green",
|
|
233
|
+
"#43A047": "light_green", "#388E3C": "light_green", "#DCEDC8": "light_green",
|
|
234
|
+
|
|
235
|
+
// Жёлтые
|
|
236
|
+
"#FFFDE7": "light_yellow", "#FFF9C4": "light_yellow", "#FFF59D": "light_yellow",
|
|
237
|
+
"#FFF176": "light_yellow", "#FFEE58": "light_yellow", "#FFEB3B": "light_yellow",
|
|
238
|
+
"#FDD835": "light_yellow", "#FBC02D": "light_yellow",
|
|
239
|
+
|
|
240
|
+
// Оранжевые
|
|
241
|
+
"#FFF3E0": "light_orange", "#FFE0B2": "light_orange", "#FFCC80": "light_orange",
|
|
242
|
+
"#FFB74D": "light_orange", "#FFA726": "light_orange", "#FF9800": "light_orange",
|
|
243
|
+
"#FB8C00": "light_orange", "#F57C00": "light_orange",
|
|
244
|
+
"#FFE5B4": "light_orange", "#FFCCBC": "light_orange", // персиковые
|
|
245
|
+
|
|
246
|
+
// Красные
|
|
247
|
+
"#FFEBEE": "light_red", "#FFCDD2": "light_red", "#EF9A9A": "light_red",
|
|
248
|
+
"#E57373": "light_red", "#EF5350": "light_red", "#F44336": "light_red",
|
|
249
|
+
"#E53935": "light_red", "#D32F2F": "light_red",
|
|
250
|
+
"#FCE4EC": "light_pink", "#F8BBD0": "light_pink", // розовые → light_pink
|
|
251
|
+
|
|
252
|
+
// Фиолетовые
|
|
253
|
+
"#F3E5F5": "light_purple", "#E1BEE7": "light_purple", "#CE93D8": "light_purple",
|
|
254
|
+
"#BA68C8": "light_purple", "#AB47BC": "light_purple", "#9C27B0": "light_purple",
|
|
255
|
+
"#8E24AA": "light_purple", "#7B1FA2": "light_purple",
|
|
256
|
+
"#D1C4E9": "light_purple", "#EDE7F6": "light_purple",
|
|
257
|
+
|
|
258
|
+
// Розовые
|
|
259
|
+
"#FCE4EC": "light_pink", "#F8BBD0": "light_pink", "#F48FB1": "light_pink",
|
|
260
|
+
"#F06292": "light_pink", "#EC407A": "light_pink", "#E91E63": "light_pink",
|
|
261
|
+
|
|
262
|
+
// Бирюзовые/Teal
|
|
263
|
+
"#E0F7FA": "light_teal", "#B2DFDB": "light_teal", "#80CBC4": "light_teal",
|
|
264
|
+
"#4DB6AC": "light_teal", "#26A69A": "light_teal", "#009688": "light_teal",
|
|
265
|
+
"#E0F2F1": "light_teal",
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Конвертировать HEX цвет в ближайший именованный
|
|
270
|
+
*/
|
|
271
|
+
function hexToNamedColor(hex) {
|
|
272
|
+
if (!hex) return "white";
|
|
273
|
+
|
|
274
|
+
// Если уже именованный — проверяем и возвращаем
|
|
275
|
+
if (VALID_COLORS.includes(hex)) return hex;
|
|
276
|
+
|
|
277
|
+
// Нормализуем HEX
|
|
278
|
+
const normalizedHex = hex.toUpperCase().trim();
|
|
279
|
+
|
|
280
|
+
// Ищем точное соответствие
|
|
281
|
+
if (HEX_TO_NAMED_COLOR[normalizedHex]) {
|
|
282
|
+
return HEX_TO_NAMED_COLOR[normalizedHex];
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Если не нашли — пытаемся определить по оттенку
|
|
286
|
+
// Простой алгоритм: анализируем RGB компоненты
|
|
287
|
+
const match = normalizedHex.match(/^#?([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})$/i);
|
|
288
|
+
if (match) {
|
|
289
|
+
const r = parseInt(match[1], 16);
|
|
290
|
+
const g = parseInt(match[2], 16);
|
|
291
|
+
const b = parseInt(match[3], 16);
|
|
292
|
+
|
|
293
|
+
// Определяем доминирующий цвет
|
|
294
|
+
const max = Math.max(r, g, b);
|
|
295
|
+
const min = Math.min(r, g, b);
|
|
296
|
+
const brightness = (r + g + b) / 3;
|
|
297
|
+
|
|
298
|
+
// Белый/серый
|
|
299
|
+
if (max - min < 30) {
|
|
300
|
+
return brightness > 200 ? "white" : "light_gray";
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Красный доминирует
|
|
304
|
+
if (r === max && r > g + 20 && r > b + 20) {
|
|
305
|
+
return g > 150 ? "light_orange" : (b > 100 ? "light_pink" : "light_red");
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Зелёный доминирует
|
|
309
|
+
if (g === max && g > r + 20 && g > b + 20) {
|
|
310
|
+
return b > 150 ? "light_teal" : "light_green";
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Синий доминирует
|
|
314
|
+
if (b === max && b > r + 20 && b > g + 20) {
|
|
315
|
+
return r > 150 ? "light_purple" : "light_blue";
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Жёлтый (R и G высокие, B низкий)
|
|
319
|
+
if (r > 200 && g > 200 && b < 150) {
|
|
320
|
+
return "light_yellow";
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Оранжевый (R высокий, G средний, B низкий)
|
|
324
|
+
if (r > 200 && g > 100 && g < 200 && b < 150) {
|
|
325
|
+
return "light_orange";
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Fallback
|
|
330
|
+
return "light_blue";
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Нормализовать элементы — исправить типы, цвета, удалить лишние поля
|
|
335
|
+
* Возвращает { elements, warnings }
|
|
336
|
+
*/
|
|
337
|
+
function normalizeElements(elements) {
|
|
338
|
+
if (!elements || !Array.isArray(elements)) return { elements: [], warnings: [] };
|
|
339
|
+
|
|
340
|
+
const warnings = [];
|
|
341
|
+
|
|
342
|
+
const normalized = elements.map((el, index) => {
|
|
343
|
+
const fixed = {
|
|
344
|
+
id: el.id || generateId(),
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
// Тип: block/note → card
|
|
348
|
+
if (el.type === "block" || el.type === "note" || el.type === "group") {
|
|
349
|
+
fixed.type = "card";
|
|
350
|
+
warnings.push(`Элемент ${index + 1}: type "${el.type}" → "card"`);
|
|
351
|
+
} else if (el.type === "condition" || el.type === "diamond") {
|
|
352
|
+
fixed.type = "condition";
|
|
353
|
+
} else {
|
|
354
|
+
fixed.type = el.type || "card";
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Title: text → title
|
|
358
|
+
if (el.text && !el.title) {
|
|
359
|
+
fixed.title = el.text;
|
|
360
|
+
warnings.push(`Элемент ${index + 1}: "text" → "title"`);
|
|
361
|
+
} else {
|
|
362
|
+
fixed.title = el.title || "";
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Цвет: HEX → именованный
|
|
366
|
+
if (el.color) {
|
|
367
|
+
if (el.color.startsWith("#")) {
|
|
368
|
+
const namedColor = hexToNamedColor(el.color);
|
|
369
|
+
fixed.color = namedColor;
|
|
370
|
+
warnings.push(`Элемент ${index + 1}: color "${el.color}" → "${namedColor}"`);
|
|
371
|
+
} else if (VALID_COLORS.includes(el.color)) {
|
|
372
|
+
fixed.color = el.color;
|
|
373
|
+
} else {
|
|
374
|
+
fixed.color = "white";
|
|
375
|
+
warnings.push(`Элемент ${index + 1}: неизвестный color "${el.color}" → "white"`);
|
|
376
|
+
}
|
|
377
|
+
} else {
|
|
378
|
+
fixed.color = "white";
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// borderColor — оставляем если валидный
|
|
382
|
+
const validBorderColors = ["default", "blue", "green", "orange", "purple", "red", "teal", "yellow", "gray", "black"];
|
|
383
|
+
if (el.borderColor && validBorderColors.includes(el.borderColor)) {
|
|
384
|
+
fixed.borderColor = el.borderColor;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Description
|
|
388
|
+
if (el.description) {
|
|
389
|
+
fixed.description = el.description;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Tags
|
|
393
|
+
if (el.tags && Array.isArray(el.tags)) {
|
|
394
|
+
fixed.tags = el.tags;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// НЕ копируем: x, y, width, height — они автоматические
|
|
398
|
+
if (el.x !== undefined || el.y !== undefined || el.width !== undefined || el.height !== undefined) {
|
|
399
|
+
warnings.push(`Элемент ${index + 1}: x/y/width/height удалены (рассчитываются автоматически)`);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return fixed;
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
return { elements: normalized, warnings };
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Подготовить элементы — нормализовать и добавить ID
|
|
410
|
+
* Возвращает { elements, warnings }
|
|
213
411
|
*/
|
|
214
412
|
function prepareElements(elements) {
|
|
215
|
-
|
|
216
|
-
return elements.map(el => ({
|
|
217
|
-
id: el.id || generateId(),
|
|
218
|
-
type: el.type || "card",
|
|
219
|
-
...el
|
|
220
|
-
}));
|
|
413
|
+
return normalizeElements(elements);
|
|
221
414
|
}
|
|
222
415
|
|
|
223
416
|
/**
|
|
@@ -256,11 +449,22 @@ const TOOLS = [
|
|
|
256
449
|
Возвращает:
|
|
257
450
|
- name: название схемы
|
|
258
451
|
- description: описание
|
|
259
|
-
- elements: массив элементов (
|
|
452
|
+
- elements: массив элементов (card или condition)
|
|
260
453
|
- connections: массив связей между элементами
|
|
454
|
+
- tagsDictionary: справочник тегов
|
|
455
|
+
|
|
456
|
+
Формат элемента:
|
|
457
|
+
{
|
|
458
|
+
"id": "unique_id",
|
|
459
|
+
"type": "card", // card или condition
|
|
460
|
+
"title": "Название", // заголовок
|
|
461
|
+
"color": "light_blue", // ТОЛЬКО: white, light_blue, light_green, light_yellow, light_orange, light_red, light_purple, light_gray, light_pink, light_teal
|
|
462
|
+
"description": "Текст", // описание
|
|
463
|
+
"tags": ["тег1"] // массив тегов
|
|
464
|
+
}
|
|
261
465
|
|
|
262
|
-
|
|
263
|
-
|
|
466
|
+
ВАЖНО: НЕ используй x, y, width, height — они рассчитываются автоматически!
|
|
467
|
+
ВАЖНО: Цвета ТОЛЬКО именованные (light_blue), НЕ HEX (#FFFFFF)!`,
|
|
264
468
|
inputSchema: {
|
|
265
469
|
type: "object",
|
|
266
470
|
properties: {
|
|
@@ -834,12 +1038,15 @@ ${JSON.stringify(schema.data?.connections || [], null, 2)}`,
|
|
|
834
1038
|
}
|
|
835
1039
|
|
|
836
1040
|
case "create_schema": {
|
|
837
|
-
const { name, description, project_id, elements, connections } = args;
|
|
1041
|
+
const { name, description, project_id, elements, connections, tagsDictionary } = args;
|
|
1042
|
+
|
|
1043
|
+
// Нормализуем элементы и получаем предупреждения
|
|
1044
|
+
const { elements: normalizedElements, warnings } = prepareElements(elements);
|
|
838
1045
|
|
|
839
1046
|
const data = {
|
|
840
|
-
elements:
|
|
1047
|
+
elements: normalizedElements,
|
|
841
1048
|
connections: prepareConnections(connections),
|
|
842
|
-
tagsDictionary: [],
|
|
1049
|
+
tagsDictionary: tagsDictionary || [],
|
|
843
1050
|
};
|
|
844
1051
|
|
|
845
1052
|
const result = await apiRequest("/schemas", {
|
|
@@ -849,6 +1056,16 @@ ${JSON.stringify(schema.data?.connections || [], null, 2)}`,
|
|
|
849
1056
|
|
|
850
1057
|
const projectInfo = project_id ? `Проект: ${project_id}` : 'Проект: Общие';
|
|
851
1058
|
|
|
1059
|
+
// Формируем сообщение о предупреждениях
|
|
1060
|
+
let warningsText = "";
|
|
1061
|
+
if (warnings.length > 0) {
|
|
1062
|
+
warningsText = `\n\n⚠️ Автоисправления (${warnings.length}):\n• ${warnings.slice(0, 10).join("\n• ")}`;
|
|
1063
|
+
if (warnings.length > 10) {
|
|
1064
|
+
warningsText += `\n• ...и ещё ${warnings.length - 10}`;
|
|
1065
|
+
}
|
|
1066
|
+
warningsText += `\n\n💡 Подсказка: используй type "card"/"condition", цвета light_blue/light_green/etc, НЕ указывай x/y/width/height`;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
852
1069
|
return {
|
|
853
1070
|
content: [{
|
|
854
1071
|
type: "text",
|
|
@@ -861,7 +1078,7 @@ ${projectInfo}
|
|
|
861
1078
|
|
|
862
1079
|
URL: ${API_BASE_URL}/canvas?id=${result.schema.id}
|
|
863
1080
|
|
|
864
|
-
Открой ссылку в браузере чтобы увидеть
|
|
1081
|
+
Открой ссылку в браузере чтобы увидеть схему.${warningsText}`,
|
|
865
1082
|
}],
|
|
866
1083
|
};
|
|
867
1084
|
}
|
|
@@ -876,6 +1093,7 @@ URL: ${API_BASE_URL}/canvas?id=${result.schema.id}
|
|
|
876
1093
|
|
|
877
1094
|
// Формируем обновление
|
|
878
1095
|
const updateBody = {};
|
|
1096
|
+
let warnings = [];
|
|
879
1097
|
|
|
880
1098
|
if (name) updateBody.name = name;
|
|
881
1099
|
if (description !== undefined) updateBody.description = description;
|
|
@@ -883,7 +1101,9 @@ URL: ${API_BASE_URL}/canvas?id=${result.schema.id}
|
|
|
883
1101
|
// Обновляем данные только если переданы
|
|
884
1102
|
const newData = { ...currentData };
|
|
885
1103
|
if (elements !== undefined) {
|
|
886
|
-
|
|
1104
|
+
const normalized = prepareElements(elements);
|
|
1105
|
+
newData.elements = normalized.elements;
|
|
1106
|
+
warnings = normalized.warnings;
|
|
887
1107
|
}
|
|
888
1108
|
if (connections !== undefined) {
|
|
889
1109
|
newData.connections = prepareConnections(connections);
|
|
@@ -895,6 +1115,16 @@ URL: ${API_BASE_URL}/canvas?id=${result.schema.id}
|
|
|
895
1115
|
body: JSON.stringify(updateBody),
|
|
896
1116
|
});
|
|
897
1117
|
|
|
1118
|
+
// Формируем сообщение о предупреждениях
|
|
1119
|
+
let warningsText = "";
|
|
1120
|
+
if (warnings.length > 0) {
|
|
1121
|
+
warningsText = `\n\n⚠️ Автоисправления (${warnings.length}):\n• ${warnings.slice(0, 10).join("\n• ")}`;
|
|
1122
|
+
if (warnings.length > 10) {
|
|
1123
|
+
warningsText += `\n• ...и ещё ${warnings.length - 10}`;
|
|
1124
|
+
}
|
|
1125
|
+
warningsText += `\n\n💡 Подсказка: используй type "card"/"condition", цвета light_blue/light_green/etc, НЕ указывай x/y/width/height`;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
898
1128
|
return {
|
|
899
1129
|
content: [{
|
|
900
1130
|
type: "text",
|
|
@@ -903,7 +1133,7 @@ URL: ${API_BASE_URL}/canvas?id=${result.schema.id}
|
|
|
903
1133
|
Элементов: ${result.schema.data?.elements?.length || 0}
|
|
904
1134
|
Связей: ${result.schema.data?.connections?.length || 0}
|
|
905
1135
|
|
|
906
|
-
URL: ${API_BASE_URL}/canvas?id=${schema_id}`,
|
|
1136
|
+
URL: ${API_BASE_URL}/canvas?id=${schema_id}${warningsText}`,
|
|
907
1137
|
}],
|
|
908
1138
|
};
|
|
909
1139
|
}
|
|
@@ -1208,7 +1438,7 @@ async function main() {
|
|
|
1208
1438
|
const server = new Server(
|
|
1209
1439
|
{
|
|
1210
1440
|
name: "schemeog-mcp",
|
|
1211
|
-
version: "2.
|
|
1441
|
+
version: "2.3.0",
|
|
1212
1442
|
},
|
|
1213
1443
|
{
|
|
1214
1444
|
capabilities: {
|
|
@@ -1238,7 +1468,7 @@ async function main() {
|
|
|
1238
1468
|
const transport = new StdioServerTransport();
|
|
1239
1469
|
await server.connect(transport);
|
|
1240
1470
|
|
|
1241
|
-
console.error("SchemeOG MCP Server v2.
|
|
1471
|
+
console.error("SchemeOG MCP Server v2.3 запущен");
|
|
1242
1472
|
}
|
|
1243
1473
|
|
|
1244
1474
|
main().catch(console.error);
|