@mentagen/mcp 0.8.3 → 0.8.5

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.
@@ -1,23 +1,30 @@
1
- const NODE_COLORS = [
2
- { name: 'amber', rgb: 'rgb(253, 230, 138)' },
3
- { name: 'blue', rgb: 'rgb(0, 120, 255)' },
4
- { name: 'cyan', rgb: 'rgb(0, 231, 255)' },
5
- { name: 'emerald', rgb: 'rgb(0, 230, 118)' },
6
- { name: 'fuchsia', rgb: 'rgb(233, 30, 99)' },
7
- { name: 'green', rgb: 'rgb(52, 211, 153)' },
8
- { name: 'indigo', rgb: 'rgb(72, 77, 201)' },
9
- { name: 'lime', rgb: 'rgb(209, 250, 229)' },
10
- { name: 'orange', rgb: 'rgb(252, 165, 0)' },
11
- { name: 'pink', rgb: 'rgb(244, 143, 177)' },
12
- { name: 'purple', rgb: 'rgb(156, 39, 176)' },
13
- { name: 'red', rgb: 'rgb(239, 68, 68)' },
14
- { name: 'rose', rgb: 'rgb(255, 204, 203)' },
15
- { name: 'sky', rgb: 'rgb(0, 184, 230)' },
16
- { name: 'slate', rgb: 'rgb(107, 114, 128)' },
17
- { name: 'teal', rgb: 'rgb(0, 210, 183)' },
18
- { name: 'violet', rgb: 'rgb(123, 31, 162)' },
19
- { name: 'yellow', rgb: 'rgb(255, 251, 235)' },
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,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { nodeColorSchema } from '../utils/colors.js';
2
3
  import { formatError } from '../utils/errors.js';
3
4
  import { unitsToPixels } from '../utils/units.js';
4
5
  import { snapToGrid } from './grid-calc.js';
@@ -6,8 +7,8 @@ import { validateTypeChange } from './update.js';
6
7
  const todoItemSchema = z.object({
7
8
  id: z.string().describe('Unique identifier (UUID v4)'),
8
9
  text: z.string().describe('Todo text content'),
9
- completed: z.boolean().describe('Completion status'),
10
- order: z.number().describe('Sort order (0-based)'),
10
+ completed: z.coerce.boolean().describe('Completion status'),
11
+ order: z.coerce.number().describe('Sort order (0-based)'),
11
12
  });
12
13
  const nodeUpdateSchema = z.object({
13
14
  nodeId: z.string().describe('The node ID to update'),
@@ -19,11 +20,11 @@ const nodeUpdateSchema = z.object({
19
20
  content: z.string().optional().describe('New content for the node'),
20
21
  code: z.string().optional().describe('Code content for code-type nodes'),
21
22
  url: z.string().optional().describe('URL for url-type nodes'),
22
- color: z.string().optional().describe('Node color'),
23
- x: z.number().optional().describe('X position in grid units'),
24
- y: z.number().optional().describe('Y position in grid units'),
25
- width: z.number().optional().describe('Node width in grid units'),
26
- height: z.number().optional().describe('Node height in grid units'),
23
+ color: nodeColorSchema.optional().describe('Node color'),
24
+ x: z.coerce.number().optional().describe('X position in grid units'),
25
+ y: z.coerce.number().optional().describe('Y position in grid units'),
26
+ width: z.coerce.number().optional().describe('Node width in grid units'),
27
+ height: z.coerce.number().optional().describe('Node height in grid units'),
27
28
  todos: z
28
29
  .array(todoItemSchema)
29
30
  .optional()
@@ -4,10 +4,10 @@ import { formatError } from '../utils/errors.js';
4
4
  import { pixelsToUnits, unitsToPixels } from '../utils/units.js';
5
5
  export const checkCollisionSchema = z.object({
6
6
  boardId: z.string().describe('The board ID to check against'),
7
- x: z.number().describe('Left position of the rectangle in grid units'),
8
- y: z.number().describe('Top position of the rectangle in grid units'),
9
- width: z.number().describe('Width of the rectangle in grid units'),
10
- height: z.number().describe('Height of the rectangle in grid units'),
7
+ x: z.coerce.number().describe('Left position of the rectangle in grid units'),
8
+ y: z.coerce.number().describe('Top position of the rectangle in grid units'),
9
+ width: z.coerce.number().describe('Width of the rectangle in grid units'),
10
+ height: z.coerce.number().describe('Height of the rectangle in grid units'),
11
11
  excludeNodeId: z
12
12
  .string()
13
13
  .optional()
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { nodeColorSchema } from '../utils/colors.js';
2
3
  import { formatError } from '../utils/errors.js';
3
4
  import { pixelsToUnits, unitsToPixels } from '../utils/units.js';
4
5
  import { applyExtension, generateId, normalizeTodos } from './create.js';
@@ -6,7 +7,7 @@ import { snapToGrid } from './grid-calc.js';
6
7
  import { calculateNodeSize } from './size-calc.js';
7
8
  const todoInputSchema = z.object({
8
9
  text: z.string().describe('Todo text content'),
9
- completed: z
10
+ completed: z.coerce
10
11
  .boolean()
11
12
  .optional()
12
13
  .default(false)
@@ -25,11 +26,11 @@ const nodeInputSchema = z.object({
25
26
  .optional()
26
27
  .default('markdown')
27
28
  .describe('Node type'),
28
- color: z.string().optional().describe('Node color'),
29
- x: z.number().optional().describe('X position in grid units'),
30
- y: z.number().optional().describe('Y position in grid units'),
31
- width: z.number().optional().describe('Width in grid units'),
32
- height: z.number().optional().describe('Height in grid units'),
29
+ color: nodeColorSchema.optional().describe('Node color'),
30
+ x: z.coerce.number().optional().describe('X position in grid units'),
31
+ y: z.coerce.number().optional().describe('Y position in grid units'),
32
+ width: z.coerce.number().optional().describe('Width in grid units'),
33
+ height: z.coerce.number().optional().describe('Height in grid units'),
33
34
  todos: z
34
35
  .array(todoInputSchema)
35
36
  .optional()
@@ -1,5 +1,6 @@
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';
4
5
  import { pixelsToUnits, unitsToPixels } from '../utils/units.js';
5
6
  import { getNodeUrl } from '../utils/urls.js';
@@ -62,24 +63,29 @@ export const createSchema = z.object({
62
63
  .optional()
63
64
  .default('markdown')
64
65
  .describe('Node type: markdown (default), code, or url'),
65
- color: z
66
- .string()
66
+ color: nodeColorSchema
67
67
  .optional()
68
68
  .describe('Optional color override. If not specified, uses default for type (markdown=cyan, code=indigo, url=blue)'),
69
- x: z.number().optional().describe('X position in grid units (default: 6)'),
70
- y: z.number().optional().describe('Y position in grid units (default: 6)'),
71
- width: z
69
+ x: z.coerce
70
+ .number()
71
+ .optional()
72
+ .describe('X position in grid units (default: 6)'),
73
+ y: z.coerce
74
+ .number()
75
+ .optional()
76
+ .describe('Y position in grid units (default: 6)'),
77
+ width: z.coerce
72
78
  .number()
73
79
  .optional()
74
80
  .describe('Width in grid units (default: auto-calculated)'),
75
- height: z
81
+ height: z.coerce
76
82
  .number()
77
83
  .optional()
78
84
  .describe('Height in grid units (default: auto-calculated)'),
79
85
  todos: z
80
86
  .array(z.object({
81
87
  text: z.string().describe('Todo text content'),
82
- completed: z
88
+ completed: z.coerce
83
89
  .boolean()
84
90
  .optional()
85
91
  .default(false)
@@ -4,7 +4,7 @@ import { formatError } from '../utils/errors.js';
4
4
  import { getNodeUrl } from '../utils/urls.js';
5
5
  export const extractBoardContentSchema = z.object({
6
6
  boardId: z.string().describe('The board ID to extract content from'),
7
- includeEmpty: z
7
+ includeEmpty: z.coerce
8
8
  .boolean()
9
9
  .default(false)
10
10
  .describe('Include nodes with no content (default: false)'),
@@ -4,8 +4,10 @@ import { formatError } from '../utils/errors.js';
4
4
  import { pixelsToUnits, unitsToPixels } from '../utils/units.js';
5
5
  export const findPositionSchema = z.object({
6
6
  boardId: z.string().describe('The board ID to search'),
7
- width: z.number().describe('Width of the node to place in grid units'),
8
- height: z.number().describe('Height of the node to place in grid units'),
7
+ width: z.coerce.number().describe('Width of the node to place in grid units'),
8
+ height: z.coerce
9
+ .number()
10
+ .describe('Height of the node to place in grid units'),
9
11
  anchorNodeId: z
10
12
  .string()
11
13
  .optional()
@@ -14,15 +16,15 @@ export const findPositionSchema = z.object({
14
16
  .enum(['right', 'below', 'left', 'above'])
15
17
  .optional()
16
18
  .describe('Search direction from anchor or start (default: right)'),
17
- gap: z
19
+ gap: z.coerce
18
20
  .number()
19
21
  .optional()
20
22
  .describe('Gap between nodes in grid units. Use 6+ for connected nodes, 1 for unrelated (default: 1)'),
21
- startX: z
23
+ startX: z.coerce
22
24
  .number()
23
25
  .optional()
24
26
  .describe('Explicit start X in grid units (ignored if anchorNodeId provided)'),
25
- startY: z
27
+ startY: z.coerce
26
28
  .number()
27
29
  .optional()
28
30
  .describe('Explicit start Y in grid units (ignored if anchorNodeId provided)'),
@@ -7,23 +7,23 @@ export const gridCalcSchema = z
7
7
  operation: z
8
8
  .enum(['snap', 'next_x', 'next_y', 'grid_units'])
9
9
  .describe('The calculation operation to perform'),
10
- value: z
10
+ value: z.coerce
11
11
  .number()
12
12
  .optional()
13
13
  .describe('Value to snap to the grid in units (for snap operation)'),
14
- position: z
14
+ position: z.coerce
15
15
  .number()
16
16
  .optional()
17
17
  .describe('Current x or y position in units (for next_x/next_y operations)'),
18
- size: z
18
+ size: z.coerce
19
19
  .number()
20
20
  .optional()
21
21
  .describe('Width or height of the current node in units (for next_x/next_y)'),
22
- gap: z
22
+ gap: z.coerce
23
23
  .number()
24
24
  .optional()
25
25
  .describe('Gap between nodes in grid units. Use 6+ for connected nodes, 1 for unrelated (default: 1)'),
26
- units: z
26
+ units: z.coerce
27
27
  .number()
28
28
  .optional()
29
29
  .describe('Number of grid units (for grid_units operation - deprecated, returns same value)'),
@@ -32,19 +32,19 @@ export const gridCalcSchema = z
32
32
  .string()
33
33
  .optional()
34
34
  .describe('Board ID - if provided, checks for collisions'),
35
- nodeWidth: z
35
+ nodeWidth: z.coerce
36
36
  .number()
37
37
  .optional()
38
38
  .describe('Width of node being placed in grid units (required with boardId)'),
39
- nodeHeight: z
39
+ nodeHeight: z.coerce
40
40
  .number()
41
41
  .optional()
42
42
  .describe('Height of node being placed in grid units (required with boardId)'),
43
- nodeY: z
43
+ nodeY: z.coerce
44
44
  .number()
45
45
  .optional()
46
46
  .describe('Y position of node in grid units (required for next_x collision check)'),
47
- nodeX: z
47
+ nodeX: z.coerce
48
48
  .number()
49
49
  .optional()
50
50
  .describe('X position of node in grid units (required for next_y collision check)'),
@@ -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: { type: 'string', description: 'Node 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: { type: 'string', description: 'Node 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)',
@@ -3,7 +3,7 @@ import { EdgeDirection } from '../client/types.js';
3
3
  import { formatError } from '../utils/errors.js';
4
4
  export const listEdgesSchema = z.object({
5
5
  boardId: z.string().describe('The board ID to list edges from'),
6
- limit: z
6
+ limit: z.coerce
7
7
  .number()
8
8
  .max(1000)
9
9
  .optional()
@@ -6,7 +6,7 @@ import { pixelsToUnits } from '../utils/units.js';
6
6
  import { getNodeUrl } from '../utils/urls.js';
7
7
  export const listNodesSchema = z.object({
8
8
  boardId: z.string().describe('The board ID to list nodes from'),
9
- limit: z
9
+ limit: z.coerce
10
10
  .number()
11
11
  .max(1000)
12
12
  .default(1000)
@@ -4,7 +4,7 @@ import { formatError } from '../utils/errors.js';
4
4
  import { pixelsToUnits } from '../utils/units.js';
5
5
  export const listPositionsSchema = z.object({
6
6
  boardId: z.string().describe('The board ID to list node positions from'),
7
- limit: z
7
+ limit: z.coerce
8
8
  .number()
9
9
  .max(1000)
10
10
  .default(1000)
@@ -5,7 +5,7 @@ const replaceOp = z.object({
5
5
  type: z.literal('replace'),
6
6
  oldString: z.string().min(1),
7
7
  newString: z.string(),
8
- replaceAll: z.boolean().optional(),
8
+ replaceAll: z.coerce.boolean().optional(),
9
9
  });
10
10
  const prependOp = z.object({
11
11
  type: z.literal('prepend'),
@@ -15,11 +15,11 @@ export const searchSchema = z.object({
15
15
  .string()
16
16
  .optional()
17
17
  .describe('Optional organization ID to limit search to boards in that organization'),
18
- limit: z
18
+ limit: z.coerce
19
19
  .number()
20
20
  .default(10)
21
21
  .describe('Maximum number of results (default: 10)'),
22
- semanticRatio: z
22
+ semanticRatio: z.coerce
23
23
  .number()
24
24
  .default(0.5)
25
25
  .describe('Balance between semantic (1.0) and keyword (0.0) search'),
@@ -20,7 +20,6 @@ const AVG_CHAR_WIDTH = 8.5;
20
20
  const NODE_TYPES_WITH_ICONS = new Set([
21
21
  'ai-chat',
22
22
  'board',
23
- 'contact',
24
23
  'pdf',
25
24
  'file',
26
25
  'mermaid',
@@ -13,7 +13,7 @@ export const toggleTodoSchema = z.object({
13
13
  boardId: z.string().describe('The board ID containing the node'),
14
14
  nodeId: z.string().describe('The node ID containing the todo'),
15
15
  todoId: z.string().describe('The todo ID to toggle'),
16
- completed: z
16
+ completed: z.coerce
17
17
  .boolean()
18
18
  .optional()
19
19
  .describe('Set explicit completion state. Omit to toggle current state.'),
@@ -1,5 +1,6 @@
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';
4
5
  import { pixelsToUnits, unitsToPixels } from '../utils/units.js';
5
6
  import { snapToGrid } from './grid-calc.js';
@@ -40,15 +41,14 @@ export const updateSchema = z.object({
40
41
  .string()
41
42
  .optional()
42
43
  .describe('The URL for url-type nodes (e.g., https://example.com). Only for type="url".'),
43
- color: z
44
- .string()
44
+ color: nodeColorSchema
45
45
  .optional()
46
46
  .describe('Node color name (use mentagen_colors to see available colors)'),
47
- x: z.number().optional().describe('X position in grid units'),
48
- y: z.number().optional().describe('Y position in grid units'),
49
- width: z.number().optional().describe('Node width in grid units'),
50
- height: z.number().optional().describe('Node height in grid units'),
51
- autoSize: z
47
+ x: z.coerce.number().optional().describe('X position in grid units'),
48
+ y: z.coerce.number().optional().describe('Y position in grid units'),
49
+ width: z.coerce.number().optional().describe('Node width in grid units'),
50
+ height: z.coerce.number().optional().describe('Node height in grid units'),
51
+ autoSize: z.coerce
52
52
  .boolean()
53
53
  .optional()
54
54
  .default(true)
@@ -57,8 +57,8 @@ export const updateSchema = z.object({
57
57
  .array(z.object({
58
58
  id: z.string().describe('Unique identifier (UUID v4)'),
59
59
  text: z.string().describe('Todo text content'),
60
- completed: z.boolean().describe('Completion status'),
61
- order: z.number().describe('Sort order (0-based)'),
60
+ completed: z.coerce.boolean().describe('Completion status'),
61
+ order: z.coerce.number().describe('Sort order (0-based)'),
62
62
  }))
63
63
  .optional()
64
64
  .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.'),
@@ -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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentagen/mcp",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "MCP server for Mentagen knowledge base integration with Cursor",
5
5
  "type": "module",
6
6
  "bin": "dist/index.js",
@@ -28,7 +28,7 @@
28
28
  ],
29
29
  "license": "UNLICENSED",
30
30
  "dependencies": {
31
- "@modelcontextprotocol/sdk": "^1.0.0",
31
+ "@modelcontextprotocol/sdk": "1.26.0",
32
32
  "@resvg/resvg-js": "^2.6.2",
33
33
  "ejson2": "^1.1.0",
34
34
  "papaparse": "^5.5.3",
@@ -45,6 +45,8 @@
45
45
  "node": ">=18"
46
46
  },
47
47
  "resolutions": {
48
+ "@isaacs/brace-expansion": "^5.0.1",
49
+ "minimatch": "^10.1.2",
48
50
  "tar": "^7.5.7"
49
51
  }
50
52
  }