@mentagen/mcp 0.8.4 → 0.8.6
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/dist/resources/index.js +27 -20
- package/dist/tools/batch-update.js +11 -2
- package/dist/tools/create-graph.js +5 -2
- package/dist/tools/create.js +6 -5
- package/dist/tools/index.js +13 -2
- package/dist/tools/patch-content.js +20 -7
- package/dist/tools/todo.js +2 -1
- package/dist/tools/update.js +5 -4
- package/dist/utils/colors.js +27 -0
- package/dist/utils/normalize.js +11 -0
- package/package.json +1 -1
package/dist/resources/index.js
CHANGED
|
@@ -1,23 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
import { VALID_NODE_COLORS } from '../utils/colors.js';
|
|
2
|
+
/** RGB values for node colors, keyed by color name. */
|
|
3
|
+
const COLOR_RGB = {
|
|
4
|
+
amber: 'rgb(253, 230, 138)',
|
|
5
|
+
blue: 'rgb(0, 120, 255)',
|
|
6
|
+
cyan: 'rgb(0, 231, 255)',
|
|
7
|
+
emerald: 'rgb(0, 230, 118)',
|
|
8
|
+
fuchsia: 'rgb(233, 30, 99)',
|
|
9
|
+
green: 'rgb(52, 211, 153)',
|
|
10
|
+
indigo: 'rgb(72, 77, 201)',
|
|
11
|
+
lime: 'rgb(209, 250, 229)',
|
|
12
|
+
orange: 'rgb(252, 165, 0)',
|
|
13
|
+
pink: 'rgb(244, 143, 177)',
|
|
14
|
+
purple: 'rgb(156, 39, 176)',
|
|
15
|
+
red: 'rgb(239, 68, 68)',
|
|
16
|
+
rose: 'rgb(255, 204, 203)',
|
|
17
|
+
sky: 'rgb(0, 184, 230)',
|
|
18
|
+
slate: 'rgb(107, 114, 128)',
|
|
19
|
+
teal: 'rgb(0, 210, 183)',
|
|
20
|
+
violet: 'rgb(123, 31, 162)',
|
|
21
|
+
yellow: 'rgb(255, 251, 235)',
|
|
22
|
+
};
|
|
23
|
+
/** Derive from VALID_NODE_COLORS so the resource stays in sync. */
|
|
24
|
+
const NODE_COLORS = VALID_NODE_COLORS.map(name => ({
|
|
25
|
+
name,
|
|
26
|
+
rgb: COLOR_RGB[name] ?? '',
|
|
27
|
+
}));
|
|
21
28
|
/**
|
|
22
29
|
* Supported node types with their default colors.
|
|
23
30
|
*
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { nodeColorSchema } from '../utils/colors.js';
|
|
2
3
|
import { formatError } from '../utils/errors.js';
|
|
4
|
+
import { normalizeTextEscapes } from '../utils/normalize.js';
|
|
3
5
|
import { unitsToPixels } from '../utils/units.js';
|
|
4
6
|
import { snapToGrid } from './grid-calc.js';
|
|
5
7
|
import { validateTypeChange } from './update.js';
|
|
@@ -19,7 +21,7 @@ const nodeUpdateSchema = z.object({
|
|
|
19
21
|
content: z.string().optional().describe('New content for the node'),
|
|
20
22
|
code: z.string().optional().describe('Code content for code-type nodes'),
|
|
21
23
|
url: z.string().optional().describe('URL for url-type nodes'),
|
|
22
|
-
color:
|
|
24
|
+
color: nodeColorSchema.optional().describe('Node color'),
|
|
23
25
|
x: z.coerce.number().optional().describe('X position in grid units'),
|
|
24
26
|
y: z.coerce.number().optional().describe('Y position in grid units'),
|
|
25
27
|
width: z.coerce.number().optional().describe('Node width in grid units'),
|
|
@@ -82,7 +84,14 @@ function buildNodeUpdateData(data, migrationFields) {
|
|
|
82
84
|
cleanData[key] = snapToGrid(unitsToPixels(value));
|
|
83
85
|
}
|
|
84
86
|
else if (key === 'todos') {
|
|
85
|
-
|
|
87
|
+
const todos = value.map(t => ({
|
|
88
|
+
...t,
|
|
89
|
+
text: normalizeTextEscapes(t.text),
|
|
90
|
+
}));
|
|
91
|
+
cleanData[key] = todos;
|
|
92
|
+
}
|
|
93
|
+
else if (key === 'content') {
|
|
94
|
+
cleanData[key] = normalizeTextEscapes(value);
|
|
86
95
|
}
|
|
87
96
|
else {
|
|
88
97
|
cleanData[key] = value;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { nodeColorSchema } from '../utils/colors.js';
|
|
2
3
|
import { formatError } from '../utils/errors.js';
|
|
4
|
+
import { normalizeTextEscapes } from '../utils/normalize.js';
|
|
3
5
|
import { pixelsToUnits, unitsToPixels } from '../utils/units.js';
|
|
4
6
|
import { applyExtension, generateId, normalizeTodos } from './create.js';
|
|
5
7
|
import { snapToGrid } from './grid-calc.js';
|
|
@@ -25,7 +27,7 @@ const nodeInputSchema = z.object({
|
|
|
25
27
|
.optional()
|
|
26
28
|
.default('markdown')
|
|
27
29
|
.describe('Node type'),
|
|
28
|
-
color:
|
|
30
|
+
color: nodeColorSchema.optional().describe('Node color'),
|
|
29
31
|
x: z.coerce.number().optional().describe('X position in grid units'),
|
|
30
32
|
y: z.coerce.number().optional().describe('Y position in grid units'),
|
|
31
33
|
width: z.coerce.number().optional().describe('Width in grid units'),
|
|
@@ -80,7 +82,8 @@ async function createNode(client, boardId, nodeInput, idMap) {
|
|
|
80
82
|
const autoSize = calculateNodeSize(finalName, type);
|
|
81
83
|
const dims = calcNodeDimensions(nodeInput, autoSize);
|
|
82
84
|
const isCodeNode = type === 'code';
|
|
83
|
-
const
|
|
85
|
+
const rawContent = nodeInput.content ?? '';
|
|
86
|
+
const nodeContent = isCodeNode ? rawContent : normalizeTextEscapes(rawContent);
|
|
84
87
|
// Only pass todos for non-code nodes
|
|
85
88
|
const todos = !isCodeNode && nodeInput.todos ? normalizeTodos(nodeInput.todos) : undefined;
|
|
86
89
|
try {
|
package/dist/tools/create.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { randomUUID } from 'crypto';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { nodeColorSchema } from '../utils/colors.js';
|
|
3
4
|
import { formatError } from '../utils/errors.js';
|
|
5
|
+
import { normalizeTextEscapes } from '../utils/normalize.js';
|
|
4
6
|
import { pixelsToUnits, unitsToPixels } from '../utils/units.js';
|
|
5
7
|
import { getNodeUrl } from '../utils/urls.js';
|
|
6
8
|
import { snapToGrid } from './grid-calc.js';
|
|
@@ -62,8 +64,7 @@ export const createSchema = z.object({
|
|
|
62
64
|
.optional()
|
|
63
65
|
.default('markdown')
|
|
64
66
|
.describe('Node type: markdown (default), code, or url'),
|
|
65
|
-
color:
|
|
66
|
-
.string()
|
|
67
|
+
color: nodeColorSchema
|
|
67
68
|
.optional()
|
|
68
69
|
.describe('Optional color override. If not specified, uses default for type (markdown=cyan, code=indigo, url=blue)'),
|
|
69
70
|
x: z.coerce
|
|
@@ -113,7 +114,7 @@ export function generateId() {
|
|
|
113
114
|
export function normalizeTodos(todos) {
|
|
114
115
|
return todos.map((todo, index) => ({
|
|
115
116
|
id: randomUUID(),
|
|
116
|
-
text: todo.text,
|
|
117
|
+
text: normalizeTextEscapes(todo.text),
|
|
117
118
|
completed: todo.completed ?? false,
|
|
118
119
|
order: index,
|
|
119
120
|
}));
|
|
@@ -139,14 +140,14 @@ function buildNodeFields(params, color, position) {
|
|
|
139
140
|
}
|
|
140
141
|
nodeFields.url = params.url;
|
|
141
142
|
if (params.content) {
|
|
142
|
-
nodeFields.content = params.content;
|
|
143
|
+
nodeFields.content = normalizeTextEscapes(params.content);
|
|
143
144
|
}
|
|
144
145
|
if (params.todos) {
|
|
145
146
|
nodeFields.todos = normalizeTodos(params.todos);
|
|
146
147
|
}
|
|
147
148
|
}
|
|
148
149
|
else {
|
|
149
|
-
nodeFields.content = params.content || '';
|
|
150
|
+
nodeFields.content = normalizeTextEscapes(params.content || '');
|
|
150
151
|
if (params.todos) {
|
|
151
152
|
nodeFields.todos = normalizeTodos(params.todos);
|
|
152
153
|
}
|
package/dist/tools/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Using raw objects like { method: 'tools/list' } causes "Schema is missing
|
|
3
3
|
// a method literal" errors because the SDK validates against ZodLiteral types.
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { VALID_NODE_COLORS } from '../utils/colors.js';
|
|
5
6
|
import { batchUpdateSchema, handleBatchUpdate } from './batch-update.js';
|
|
6
7
|
import { boardsSchema, handleBoards } from './boards.js';
|
|
7
8
|
import { checkCollisionSchema, handleCheckCollision, } from './check-collision.js';
|
|
@@ -279,6 +280,7 @@ export function registerTools(server, client, config) {
|
|
|
279
280
|
},
|
|
280
281
|
color: {
|
|
281
282
|
type: 'string',
|
|
283
|
+
enum: [...VALID_NODE_COLORS],
|
|
282
284
|
description: 'Optional color override. If not specified, uses default for type (markdown=cyan, code=indigo)',
|
|
283
285
|
},
|
|
284
286
|
x: {
|
|
@@ -344,6 +346,7 @@ export function registerTools(server, client, config) {
|
|
|
344
346
|
},
|
|
345
347
|
color: {
|
|
346
348
|
type: 'string',
|
|
349
|
+
enum: [...VALID_NODE_COLORS],
|
|
347
350
|
description: 'Node color name (see mentagen://colors resource for available colors)',
|
|
348
351
|
},
|
|
349
352
|
x: {
|
|
@@ -407,7 +410,11 @@ export function registerTools(server, client, config) {
|
|
|
407
410
|
nodeId: { type: 'string', description: 'The node ID' },
|
|
408
411
|
name: { type: 'string', description: 'New name' },
|
|
409
412
|
content: { type: 'string', description: 'New content' },
|
|
410
|
-
color: {
|
|
413
|
+
color: {
|
|
414
|
+
type: 'string',
|
|
415
|
+
enum: [...VALID_NODE_COLORS],
|
|
416
|
+
description: 'Node color',
|
|
417
|
+
},
|
|
411
418
|
x: {
|
|
412
419
|
type: 'number',
|
|
413
420
|
description: 'X position in grid units (1 unit = 16px)',
|
|
@@ -559,7 +566,11 @@ export function registerTools(server, client, config) {
|
|
|
559
566
|
enum: ['text', 'markdown', 'code', 'url'],
|
|
560
567
|
description: 'Node type (default: text).',
|
|
561
568
|
},
|
|
562
|
-
color: {
|
|
569
|
+
color: {
|
|
570
|
+
type: 'string',
|
|
571
|
+
enum: [...VALID_NODE_COLORS],
|
|
572
|
+
description: 'Node color',
|
|
573
|
+
},
|
|
563
574
|
x: {
|
|
564
575
|
type: 'number',
|
|
565
576
|
description: 'X position in grid units (1 unit = 16px)',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { NodeType } from '../client/types.js';
|
|
3
3
|
import { formatError } from '../utils/errors.js';
|
|
4
|
+
import { normalizeTextEscapes } from '../utils/normalize.js';
|
|
4
5
|
const replaceOp = z.object({
|
|
5
6
|
type: z.literal('replace'),
|
|
6
7
|
oldString: z.string().min(1),
|
|
@@ -68,12 +69,20 @@ export async function handlePatchContent(client, params) {
|
|
|
68
69
|
nodeId: params.nodeId,
|
|
69
70
|
});
|
|
70
71
|
const { field, text: initialText } = getNodeTextField(node);
|
|
72
|
+
const isCode = field === 'code';
|
|
71
73
|
let text = initialText;
|
|
72
74
|
const appliedOps = [];
|
|
75
|
+
/** Normalize escape sequences for non-code content fields. */
|
|
76
|
+
const normalize = (s) => isCode ? s : normalizeTextEscapes(s);
|
|
73
77
|
for (const op of params.operations) {
|
|
74
78
|
switch (op.type) {
|
|
75
79
|
case 'replace': {
|
|
76
|
-
const
|
|
80
|
+
const normalizedOp = {
|
|
81
|
+
...op,
|
|
82
|
+
oldString: normalize(op.oldString),
|
|
83
|
+
newString: normalize(op.newString),
|
|
84
|
+
};
|
|
85
|
+
const { result, error } = applyReplace(text, normalizedOp);
|
|
77
86
|
if (error) {
|
|
78
87
|
return {
|
|
79
88
|
content: [
|
|
@@ -95,14 +104,18 @@ export async function handlePatchContent(client, params) {
|
|
|
95
104
|
appliedOps.push(`replace: "${truncate(op.oldString, 30)}" → "${truncate(op.newString, 30)}"`);
|
|
96
105
|
break;
|
|
97
106
|
}
|
|
98
|
-
case 'prepend':
|
|
99
|
-
|
|
100
|
-
|
|
107
|
+
case 'prepend': {
|
|
108
|
+
const content = normalize(op.content);
|
|
109
|
+
text = content + text;
|
|
110
|
+
appliedOps.push(`prepend: ${content.length} chars`);
|
|
101
111
|
break;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
112
|
+
}
|
|
113
|
+
case 'append': {
|
|
114
|
+
const content = normalize(op.content);
|
|
115
|
+
text = text + content;
|
|
116
|
+
appliedOps.push(`append: ${content.length} chars`);
|
|
105
117
|
break;
|
|
118
|
+
}
|
|
106
119
|
}
|
|
107
120
|
}
|
|
108
121
|
await client.updateNode({
|
package/dist/tools/todo.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from 'crypto';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { formatError } from '../utils/errors.js';
|
|
4
|
+
import { normalizeTextEscapes } from '../utils/normalize.js';
|
|
4
5
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
6
|
// Schemas
|
|
6
7
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -36,7 +37,7 @@ export async function handleAddTodo(client, params) {
|
|
|
36
37
|
const existingTodos = node.todos || [];
|
|
37
38
|
const newTodo = {
|
|
38
39
|
id: randomUUID(),
|
|
39
|
-
text: params.text,
|
|
40
|
+
text: normalizeTextEscapes(params.text),
|
|
40
41
|
completed: false,
|
|
41
42
|
order: existingTodos.length,
|
|
42
43
|
};
|
package/dist/tools/update.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { NodeType } from '../client/types.js';
|
|
3
|
+
import { nodeColorSchema } from '../utils/colors.js';
|
|
3
4
|
import { formatError } from '../utils/errors.js';
|
|
5
|
+
import { normalizeTextEscapes } from '../utils/normalize.js';
|
|
4
6
|
import { pixelsToUnits, unitsToPixels } from '../utils/units.js';
|
|
5
7
|
import { snapToGrid } from './grid-calc.js';
|
|
6
8
|
import { calculateNodeSize } from './size-calc.js';
|
|
@@ -40,8 +42,7 @@ export const updateSchema = z.object({
|
|
|
40
42
|
.string()
|
|
41
43
|
.optional()
|
|
42
44
|
.describe('The URL for url-type nodes (e.g., https://example.com). Only for type="url".'),
|
|
43
|
-
color:
|
|
44
|
-
.string()
|
|
45
|
+
color: nodeColorSchema
|
|
45
46
|
.optional()
|
|
46
47
|
.describe('Node color name (use mentagen_colors to see available colors)'),
|
|
47
48
|
x: z.coerce.number().optional().describe('X position in grid units'),
|
|
@@ -132,11 +133,11 @@ function addContentFields(data, params, nodeType) {
|
|
|
132
133
|
data.url = params.url;
|
|
133
134
|
}
|
|
134
135
|
if (params.content !== undefined) {
|
|
135
|
-
data.content = params.content;
|
|
136
|
+
data.content = normalizeTextEscapes(params.content);
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
139
|
else if (params.content !== undefined) {
|
|
139
|
-
data.content = params.content;
|
|
140
|
+
data.content = normalizeTextEscapes(params.content);
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
143
|
/**
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Valid node color names matching the Mongoose schema enum (BaseColor).
|
|
4
|
+
* Keep in sync with: src/common/constants/color.ts
|
|
5
|
+
*/
|
|
6
|
+
export const VALID_NODE_COLORS = [
|
|
7
|
+
'amber',
|
|
8
|
+
'blue',
|
|
9
|
+
'cyan',
|
|
10
|
+
'emerald',
|
|
11
|
+
'fuchsia',
|
|
12
|
+
'green',
|
|
13
|
+
'indigo',
|
|
14
|
+
'lime',
|
|
15
|
+
'orange',
|
|
16
|
+
'pink',
|
|
17
|
+
'purple',
|
|
18
|
+
'red',
|
|
19
|
+
'rose',
|
|
20
|
+
'sky',
|
|
21
|
+
'slate',
|
|
22
|
+
'teal',
|
|
23
|
+
'violet',
|
|
24
|
+
'yellow',
|
|
25
|
+
];
|
|
26
|
+
/** Zod schema for validating node colors against the Mongoose enum. */
|
|
27
|
+
export const nodeColorSchema = z.enum(VALID_NODE_COLORS);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize literal escape sequences commonly produced by LLMs in MCP tool parameters.
|
|
3
|
+
*
|
|
4
|
+
* LLMs often write `\n` as two literal characters (backslash + n) instead of
|
|
5
|
+
* an actual newline character. This converts those sequences for text content fields.
|
|
6
|
+
*
|
|
7
|
+
* Should NOT be applied to code fields where escape sequences are intentional.
|
|
8
|
+
*/
|
|
9
|
+
export function normalizeTextEscapes(text) {
|
|
10
|
+
return text.replaceAll('\\n', '\n').replaceAll('\\t', '\t');
|
|
11
|
+
}
|