@mentagen/mcp 0.6.0 → 0.8.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/dist/index.js +6 -0
- package/dist/prompts/index.js +259 -0
- package/dist/resources/index.js +93 -0
- package/dist/tools/batch-update.js +91 -30
- package/dist/tools/create-graph.js +43 -22
- package/dist/tools/create.js +36 -5
- package/dist/tools/index.js +112 -63
- package/dist/tools/size-calc.js +1 -1
- package/dist/tools/todo.js +171 -0
- package/dist/tools/update.js +133 -18
- package/package.json +2 -2
package/dist/tools/update.js
CHANGED
|
@@ -4,6 +4,23 @@ import { formatError } from '../utils/errors.js';
|
|
|
4
4
|
import { pixelsToUnits, unitsToPixels } from '../utils/units.js';
|
|
5
5
|
import { snapToGrid } from './grid-calc.js';
|
|
6
6
|
import { calculateNodeSize } from './size-calc.js';
|
|
7
|
+
/**
|
|
8
|
+
* Node types that can be converted between safely.
|
|
9
|
+
* These are text-based types where content can be migrated without data loss.
|
|
10
|
+
* Note: text nodes are accepted for conversion (legacy) but MCP only creates markdown.
|
|
11
|
+
*/
|
|
12
|
+
const CONVERTIBLE_TYPES = [
|
|
13
|
+
NodeType.Text,
|
|
14
|
+
NodeType.Markdown,
|
|
15
|
+
NodeType.Code,
|
|
16
|
+
NodeType.URL,
|
|
17
|
+
];
|
|
18
|
+
/**
|
|
19
|
+
* Check if a type is convertible (text-based).
|
|
20
|
+
*/
|
|
21
|
+
function isConvertibleType(type) {
|
|
22
|
+
return CONVERTIBLE_TYPES.includes(type);
|
|
23
|
+
}
|
|
7
24
|
export const updateSchema = z.object({
|
|
8
25
|
boardId: z.string().describe('The board ID containing the node'),
|
|
9
26
|
nodeId: z.string().describe('The node ID to update'),
|
|
@@ -11,8 +28,12 @@ export const updateSchema = z.object({
|
|
|
11
28
|
.string()
|
|
12
29
|
.optional()
|
|
13
30
|
.describe('Move node to a different board. Provide the target board ID to move the node while preserving its type and properties.'),
|
|
31
|
+
type: z
|
|
32
|
+
.enum(['markdown', 'code', 'url'])
|
|
33
|
+
.optional()
|
|
34
|
+
.describe('Change node type. Safe conversions: markdown ↔ code. Content is auto-migrated between fields.'),
|
|
14
35
|
name: z.string().optional().describe('New name for the node'),
|
|
15
|
-
content: z.string().optional().describe('Content for
|
|
36
|
+
content: z.string().optional().describe('Content for markdown nodes.'),
|
|
16
37
|
code: z
|
|
17
38
|
.string()
|
|
18
39
|
.optional()
|
|
@@ -34,8 +55,70 @@ export const updateSchema = z.object({
|
|
|
34
55
|
.optional()
|
|
35
56
|
.default(true)
|
|
36
57
|
.describe('Auto-resize node when name changes (default: true). Set to false to keep current size.'),
|
|
58
|
+
todos: z
|
|
59
|
+
.array(z.object({
|
|
60
|
+
id: z.string().describe('Unique identifier (UUID v4)'),
|
|
61
|
+
text: z.string().describe('Todo text content'),
|
|
62
|
+
completed: z.boolean().describe('Completion status'),
|
|
63
|
+
order: z.number().describe('Sort order (0-based)'),
|
|
64
|
+
}))
|
|
65
|
+
.optional()
|
|
66
|
+
.describe('Replace all todos on this markdown node. Only markdown/text nodes support todos; code nodes do not. Each todo needs id, text, completed, and order fields.'),
|
|
37
67
|
});
|
|
38
68
|
const POSITION_FIELDS = ['x', 'y', 'width', 'height'];
|
|
69
|
+
/**
|
|
70
|
+
* Compute migration fields when converting TO code type.
|
|
71
|
+
*/
|
|
72
|
+
function migrateToCode(node, params, fields) {
|
|
73
|
+
// Migrate content → code (unless code explicitly provided)
|
|
74
|
+
if (params.code === undefined && params.content === undefined) {
|
|
75
|
+
fields.code = node.content || '';
|
|
76
|
+
}
|
|
77
|
+
// Always clear content field when moving to code type
|
|
78
|
+
fields.content = null;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Compute migration fields when converting FROM code type.
|
|
82
|
+
*/
|
|
83
|
+
function migrateFromCode(node, params, fields) {
|
|
84
|
+
// Always clear code field
|
|
85
|
+
fields.code = null;
|
|
86
|
+
// Migrate code → content (unless content explicitly provided)
|
|
87
|
+
if (params.content === undefined && params.code === undefined) {
|
|
88
|
+
fields.content = node.code || '';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Validate and compute migration fields for a type change.
|
|
93
|
+
*
|
|
94
|
+
* Returns the fields that need to be set/unset to migrate content
|
|
95
|
+
* between node types, or an error if the conversion is unsafe.
|
|
96
|
+
*/
|
|
97
|
+
export function validateTypeChange(currentType, targetType, node, params) {
|
|
98
|
+
if (currentType === targetType) {
|
|
99
|
+
return { valid: true, fields: {} };
|
|
100
|
+
}
|
|
101
|
+
if (!isConvertibleType(currentType)) {
|
|
102
|
+
return {
|
|
103
|
+
valid: false,
|
|
104
|
+
error: `Cannot convert from '${currentType}' to '${targetType}'. Only markdown, code, and url types support conversion.`,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const fields = { type: targetType };
|
|
108
|
+
if (targetType === NodeType.URL && !params.url && !node.url) {
|
|
109
|
+
return {
|
|
110
|
+
valid: false,
|
|
111
|
+
error: `Converting to 'url' type requires a url field. Provide url parameter.`,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (targetType === NodeType.Code) {
|
|
115
|
+
migrateToCode(node, params, fields);
|
|
116
|
+
}
|
|
117
|
+
else if (currentType === NodeType.Code) {
|
|
118
|
+
migrateFromCode(node, params, fields);
|
|
119
|
+
}
|
|
120
|
+
return { valid: true, fields };
|
|
121
|
+
}
|
|
39
122
|
/**
|
|
40
123
|
* Add content fields to update data based on node type.
|
|
41
124
|
*/
|
|
@@ -61,15 +144,21 @@ function addContentFields(data, params, nodeType) {
|
|
|
61
144
|
/**
|
|
62
145
|
* Builds the update data object, mapping content/url to the correct field based on node type.
|
|
63
146
|
* Code nodes store their content in `code`, url nodes use `url`, others use `content`.
|
|
147
|
+
*
|
|
148
|
+
* @param params - Update parameters from the tool call
|
|
149
|
+
* @param nodeType - The target node type (after any type change)
|
|
150
|
+
* @param migrationFields - Fields from type migration (type change, content migration)
|
|
64
151
|
*/
|
|
65
|
-
function buildUpdateData(params, nodeType) {
|
|
66
|
-
const data = {};
|
|
152
|
+
function buildUpdateData(params, nodeType, migrationFields = {}) {
|
|
153
|
+
const data = { ...migrationFields };
|
|
67
154
|
if (params.name !== undefined)
|
|
68
155
|
data.name = params.name;
|
|
69
156
|
if (params.color !== undefined)
|
|
70
157
|
data.color = params.color;
|
|
71
158
|
if (params.targetBoardId !== undefined)
|
|
72
159
|
data.board = params.targetBoardId;
|
|
160
|
+
if (params.todos !== undefined)
|
|
161
|
+
data.todos = params.todos;
|
|
73
162
|
addContentFields(data, params, nodeType);
|
|
74
163
|
for (const field of POSITION_FIELDS) {
|
|
75
164
|
const value = params[field];
|
|
@@ -116,29 +205,55 @@ function formatSuccessResponse(node) {
|
|
|
116
205
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
117
206
|
};
|
|
118
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Process type change request and return migration context.
|
|
210
|
+
*/
|
|
211
|
+
function processTypeChange(currentNode, params) {
|
|
212
|
+
if (!params.type || params.type === currentNode.type) {
|
|
213
|
+
return { valid: true, fields: {}, targetType: currentNode.type };
|
|
214
|
+
}
|
|
215
|
+
const result = validateTypeChange(currentNode.type, params.type, currentNode, params);
|
|
216
|
+
if (!result.valid) {
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
return { valid: true, fields: result.fields, targetType: params.type };
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Build params with auto-size dimensions applied.
|
|
223
|
+
*/
|
|
224
|
+
function applyAutoSize(params, autoSize) {
|
|
225
|
+
return {
|
|
226
|
+
...params,
|
|
227
|
+
width: params.width !== undefined
|
|
228
|
+
? params.width
|
|
229
|
+
: autoSize?.width !== undefined
|
|
230
|
+
? pixelsToUnits(autoSize.width)
|
|
231
|
+
: undefined,
|
|
232
|
+
height: params.height !== undefined
|
|
233
|
+
? params.height
|
|
234
|
+
: autoSize?.height !== undefined
|
|
235
|
+
? pixelsToUnits(autoSize.height)
|
|
236
|
+
: undefined,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
119
239
|
export async function handleUpdate(client, params) {
|
|
120
240
|
try {
|
|
121
241
|
const { node: currentNode, autoSize } = await getNodeContext(client, params);
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
? params.height
|
|
132
|
-
: autoSize?.height !== undefined
|
|
133
|
-
? pixelsToUnits(autoSize.height)
|
|
134
|
-
: undefined,
|
|
135
|
-
}, currentNode.type);
|
|
242
|
+
const typeChange = processTypeChange(currentNode, params);
|
|
243
|
+
if (!typeChange.valid) {
|
|
244
|
+
return {
|
|
245
|
+
content: [{ type: 'text', text: typeChange.error }],
|
|
246
|
+
isError: true,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
const paramsWithSize = applyAutoSize(params, autoSize);
|
|
250
|
+
const data = buildUpdateData(paramsWithSize, typeChange.targetType, typeChange.fields);
|
|
136
251
|
if (!data) {
|
|
137
252
|
return {
|
|
138
253
|
content: [
|
|
139
254
|
{
|
|
140
255
|
type: 'text',
|
|
141
|
-
text: `At least one field to update must be provided (name, content, url, color, x, y, width, height, targetBoardId).`,
|
|
256
|
+
text: `At least one field to update must be provided (name, content, url, color, x, y, width, height, targetBoardId, type, todos).`,
|
|
142
257
|
},
|
|
143
258
|
],
|
|
144
259
|
isError: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mentagen/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "MCP server for Mentagen knowledge base integration with Cursor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": "dist/index.js",
|
|
@@ -45,6 +45,6 @@
|
|
|
45
45
|
"node": ">=18"
|
|
46
46
|
},
|
|
47
47
|
"resolutions": {
|
|
48
|
-
"tar": "7.5.
|
|
48
|
+
"tar": "^7.5.4"
|
|
49
49
|
}
|
|
50
50
|
}
|