@mentagen/mcp 0.2.0 → 0.4.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.
@@ -1,10 +1,14 @@
1
1
  import { z } from 'zod';
2
2
  import { formatError } from '../utils/errors.js';
3
- import { generateId } from './create.js';
3
+ import { applyExtension, generateId } from './create.js';
4
4
  import { calculateNodeSize } from './size-calc.js';
5
5
  const nodeInputSchema = z.object({
6
6
  tempId: z.string().describe('Temporary ID for referencing in edges'),
7
7
  name: z.string().describe('The node title/name'),
8
+ extension: z
9
+ .string()
10
+ .optional()
11
+ .describe('File extension to append to name (e.g., ".ts", ".py"). Only applies to code nodes. Defaults to .js.'),
8
12
  content: z.string().optional().describe('Node content'),
9
13
  type: z
10
14
  .enum(['text', 'markdown', 'code', 'url'])
@@ -43,8 +47,9 @@ const DEFAULT_NODE_COLORS = {
43
47
  async function createNode(client, boardId, nodeInput, idMap) {
44
48
  const realId = generateId();
45
49
  const type = nodeInput.type ?? 'text';
50
+ const finalName = applyExtension(nodeInput.name, type, nodeInput.extension);
46
51
  const color = nodeInput.color ?? DEFAULT_NODE_COLORS[type];
47
- const autoSize = calculateNodeSize(nodeInput.name, type);
52
+ const autoSize = calculateNodeSize(finalName, type);
48
53
  const x = nodeInput.x ?? 100;
49
54
  const y = nodeInput.y ?? 100;
50
55
  const width = nodeInput.width ?? autoSize.width;
@@ -57,7 +62,7 @@ async function createNode(client, boardId, nodeInput, idMap) {
57
62
  _id: realId,
58
63
  boardId,
59
64
  node: {
60
- name: nodeInput.name,
65
+ name: finalName,
61
66
  ...(isCodeNode ? { code: nodeContent } : { content: nodeContent }),
62
67
  type,
63
68
  color,
@@ -27,13 +27,32 @@ const DEFAULT_NODE_COLORS = {
27
27
  excalidraw: 'teal',
28
28
  teleprompter: 'rose',
29
29
  };
30
+ const DEFAULT_CODE_EXTENSION = '.js';
31
+ /**
32
+ * Append extension to name for code nodes only.
33
+ * Defaults to .js if no extension provided.
34
+ * Avoids double extensions if name already ends with the extension.
35
+ */
36
+ export function applyExtension(name, type, extension) {
37
+ if (type !== 'code')
38
+ return name;
39
+ const ext = extension ?? DEFAULT_CODE_EXTENSION;
40
+ if (name.endsWith(ext))
41
+ return name;
42
+ return `${name}${ext}`;
43
+ }
30
44
  export const createSchema = z.object({
31
45
  boardId: z.string().describe('The board ID to create the node on'),
32
46
  name: z.string().describe('The node title/name'),
33
- content: z
47
+ extension: z
48
+ .string()
49
+ .optional()
50
+ .describe('File extension to append to name (e.g., ".ts", ".py"). Only applies to code nodes. Defaults to .js.'),
51
+ content: z.string().optional().describe('Content for text/markdown nodes.'),
52
+ code: z
34
53
  .string()
35
54
  .optional()
36
- .describe('Content for text/markdown nodes. Use "url" field for url-type nodes.'),
55
+ .describe('Code content for code-type nodes. Takes precedence over content.'),
37
56
  url: z
38
57
  .string()
39
58
  .optional()
@@ -77,6 +96,7 @@ export function generateId() {
77
96
  }
78
97
  /**
79
98
  * Build node fields object with content mapped to the correct field based on type.
99
+ * Expects params.name to already have extension applied.
80
100
  */
81
101
  function buildNodeFields(params, color, position) {
82
102
  const nodeFields = {
@@ -86,7 +106,7 @@ function buildNodeFields(params, color, position) {
86
106
  ...position,
87
107
  };
88
108
  if (params.type === 'code') {
89
- nodeFields.code = params.content || '';
109
+ nodeFields.code = params.code ?? params.content ?? '';
90
110
  }
91
111
  else if (params.type === 'url') {
92
112
  if (!params.url) {
@@ -105,15 +125,16 @@ function buildNodeFields(params, color, position) {
105
125
  export async function handleCreate(client, params, mentagenUrl) {
106
126
  try {
107
127
  const _id = generateId();
128
+ const finalName = applyExtension(params.name, params.type, params.extension);
108
129
  const color = params.color || DEFAULT_NODE_COLORS[params.type];
109
- const autoSize = calculateNodeSize(params.name, params.type);
130
+ const autoSize = calculateNodeSize(finalName, params.type);
110
131
  const position = {
111
132
  x: snapToGrid(params.x ?? 96),
112
133
  y: snapToGrid(params.y ?? 96),
113
134
  width: snapToGrid(params.width ?? autoSize.width),
114
135
  height: snapToGrid(params.height ?? autoSize.height),
115
136
  };
116
- const nodeFields = buildNodeFields(params, color, position);
137
+ const nodeFields = buildNodeFields({ ...params, name: finalName }, color, position);
117
138
  const node = await client.addNode({
118
139
  _id,
119
140
  boardId: params.boardId,
@@ -231,7 +231,11 @@ export function registerTools(server, client, config) {
231
231
  },
232
232
  name: {
233
233
  type: 'string',
234
- description: 'The node title/name (used for auto-sizing). For code nodes, use a filename with extension (e.g., "utils.ts", "app.py") for syntax highlighting.',
234
+ description: 'The node title/name (used for auto-sizing). Extension is appended automatically via the extension parameter.',
235
+ },
236
+ extension: {
237
+ type: 'string',
238
+ description: 'File extension to append to name (e.g., ".ts", ".py"). Only applies to code nodes. Defaults to .js.',
235
239
  },
236
240
  content: {
237
241
  type: 'string',
@@ -240,7 +244,7 @@ export function registerTools(server, client, config) {
240
244
  type: {
241
245
  type: 'string',
242
246
  enum: ['text', 'markdown', 'code', 'url'],
243
- description: 'Node type: text (default), markdown, code, or url. Code nodes get syntax highlighting based on the file extension in the name.',
247
+ description: 'Node type: text (default), markdown, code, or url.',
244
248
  },
245
249
  color: {
246
250
  type: 'string',
@@ -478,7 +482,11 @@ export function registerTools(server, client, config) {
478
482
  },
479
483
  name: {
480
484
  type: 'string',
481
- description: 'Node title/name (used for auto-sizing). For code nodes, use a filename with extension for syntax highlighting.',
485
+ description: 'Node title/name (used for auto-sizing). Extension is appended automatically via the extension parameter.',
486
+ },
487
+ extension: {
488
+ type: 'string',
489
+ description: 'File extension to append to name (e.g., ".ts", ".py"). Only applies to code nodes. Defaults to .js.',
482
490
  },
483
491
  content: {
484
492
  type: 'string',
@@ -487,7 +495,7 @@ export function registerTools(server, client, config) {
487
495
  type: {
488
496
  type: 'string',
489
497
  enum: ['text', 'markdown', 'code', 'url'],
490
- description: 'Node type (default: text). Code nodes get syntax highlighting based on file extension in name.',
498
+ description: 'Node type (default: text).',
491
499
  },
492
500
  color: { type: 'string', description: 'Node color' },
493
501
  x: { type: 'number', description: 'X position' },
@@ -7,10 +7,11 @@ export const updateSchema = z.object({
7
7
  boardId: z.string().describe('The board ID containing the node'),
8
8
  nodeId: z.string().describe('The node ID to update'),
9
9
  name: z.string().optional().describe('New name for the node'),
10
- content: z
10
+ content: z.string().optional().describe('Content for text/markdown nodes.'),
11
+ code: z
11
12
  .string()
12
13
  .optional()
13
- .describe('Content for text/markdown nodes. Use "url" field for url-type nodes.'),
14
+ .describe('Code content for code-type nodes. Takes precedence over content.'),
14
15
  url: z
15
16
  .string()
16
17
  .optional()
@@ -46,8 +47,11 @@ const POSITION_FIELDS = ['x', 'y', 'width', 'height'];
46
47
  * Add content fields to update data based on node type.
47
48
  */
48
49
  function addContentFields(data, params, nodeType) {
49
- if (nodeType === NodeType.Code && params.content !== undefined) {
50
- data.code = params.content;
50
+ if (nodeType === NodeType.Code) {
51
+ const codeContent = params.code ?? params.content;
52
+ if (codeContent !== undefined) {
53
+ data.code = codeContent;
54
+ }
51
55
  }
52
56
  else if (nodeType === NodeType.URL) {
53
57
  if (params.url !== undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentagen/mcp",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "MCP server for Mentagen knowledge base integration with Cursor",
5
5
  "type": "module",
6
6
  "bin": "dist/index.js",