@mentagen/mcp 0.8.1 → 0.8.3

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.
@@ -0,0 +1,197 @@
1
+ import { randomUUID } from 'crypto';
2
+ /**
3
+ * Lightweight HTTP client for Mentagen's Bifrost API.
4
+ *
5
+ * Uses the HTTP transport endpoint (/__h) to call server methods.
6
+ * Bifrost expects text/plain with a specific payload/context structure.
7
+ */
8
+ export class MentagenClient {
9
+ baseUrl;
10
+ token;
11
+ constructor(config) {
12
+ this.baseUrl = config.mentagenUrl.replace(/\/$/, '');
13
+ this.token = config.token;
14
+ }
15
+ /**
16
+ * Call a Bifrost method via HTTP.
17
+ *
18
+ * Bifrost's HTTP transport expects:
19
+ * - Content-Type: text/plain
20
+ * - Body: JSON with { payload: { uuid, method, params }, context: { token } }
21
+ */
22
+ async call(method, params = {}) {
23
+ const url = `${this.baseUrl}/__h`;
24
+ const body = JSON.stringify({
25
+ payload: {
26
+ uuid: randomUUID(),
27
+ method,
28
+ params,
29
+ },
30
+ context: {
31
+ token: this.token,
32
+ },
33
+ });
34
+ const response = await fetch(url, {
35
+ method: 'POST',
36
+ headers: {
37
+ 'Content-Type': 'text/plain',
38
+ },
39
+ body,
40
+ });
41
+ if (!response.ok) {
42
+ const text = await response.text();
43
+ throw new Error(`HTTP ${response.status}: ${text}`);
44
+ }
45
+ const data = (await response.json());
46
+ if (data.type === 'error' || data.error) {
47
+ const errorMsg = data.message ||
48
+ (typeof data.error === 'string' ? data.error : data.error?.message);
49
+ throw new Error(errorMsg || 'Unknown error');
50
+ }
51
+ return data.result;
52
+ }
53
+ /**
54
+ * Verify the token and get the current user.
55
+ */
56
+ async getCurrentUser() {
57
+ try {
58
+ return await this.call('user.current');
59
+ }
60
+ catch {
61
+ return null;
62
+ }
63
+ }
64
+ /**
65
+ * List boards the user has access to.
66
+ */
67
+ async listBoards(params) {
68
+ return this.call('boards.listSimple', {
69
+ orgId: params?.orgId,
70
+ });
71
+ }
72
+ /**
73
+ * Search boards by name.
74
+ */
75
+ async searchBoards(query, limit = 20) {
76
+ const result = await this.call('boards.search', {
77
+ query,
78
+ limit,
79
+ });
80
+ return result.data || [];
81
+ }
82
+ /**
83
+ * Search nodes using hybrid semantic/keyword search.
84
+ */
85
+ async searchNodes(params) {
86
+ return this.call('boards.searchNodesText', params);
87
+ }
88
+ /**
89
+ * List organizations the user has access to.
90
+ */
91
+ async listOrganizations() {
92
+ return this.call('org.list', {});
93
+ }
94
+ /**
95
+ * List teams within an organization.
96
+ */
97
+ async listTeams(params) {
98
+ return this.call('team.list', params);
99
+ }
100
+ /**
101
+ * Add a new node to a board.
102
+ */
103
+ async addNode(params) {
104
+ return this.call('boards.addNode', params);
105
+ }
106
+ /**
107
+ * Update a node's properties.
108
+ */
109
+ async updateNode(params) {
110
+ return this.call('boards.updateNode', params);
111
+ }
112
+ /**
113
+ * Get a single node by ID.
114
+ * Uses getNodeForMcp which excludes large fields (chat, textDetections, _vectors)
115
+ * in favor of their computed alternatives (chatConcatenated, textDetectionsConcatenated).
116
+ */
117
+ async getNode(params) {
118
+ return this.call('boards.getNodeForMcp', params);
119
+ }
120
+ /**
121
+ * List all nodes in a board (basic fields only).
122
+ */
123
+ async listNodes(params) {
124
+ return this.call('boards.listNodesSimple', params);
125
+ }
126
+ /**
127
+ * List all nodes with full content fields for extraction.
128
+ * Includes: content, code, chatConcatenated, textDetectionsConcatenated, pdfText, article
129
+ * Note: Uses computed fields instead of raw chat/textDetections.
130
+ */
131
+ async listNodesWithContent(params) {
132
+ return this.call('boards.listNodesWithContent', params);
133
+ }
134
+ /**
135
+ * Soft-delete a node.
136
+ */
137
+ async deleteNode(params) {
138
+ await this.call('boards.deleteNode', params);
139
+ }
140
+ /**
141
+ * Create a new board.
142
+ */
143
+ async createBoard(params) {
144
+ return this.call('boards.create', params);
145
+ }
146
+ /**
147
+ * Get a single board by ID.
148
+ */
149
+ async getBoard(params) {
150
+ return this.call('boards.get', params);
151
+ }
152
+ /**
153
+ * Update a board's properties.
154
+ */
155
+ async updateBoard(params) {
156
+ return this.call('boards.update', params);
157
+ }
158
+ /**
159
+ * Soft-delete a board.
160
+ */
161
+ async deleteBoard(params) {
162
+ await this.call('boards.deleteBoard', params);
163
+ }
164
+ /**
165
+ * Add an edge between two nodes.
166
+ */
167
+ async addEdge(params) {
168
+ return this.call('boards.addEdge', params);
169
+ }
170
+ /**
171
+ * List all edges in a board.
172
+ */
173
+ async listEdges(params) {
174
+ return this.call('boards.listEdgesSimple', params);
175
+ }
176
+ /**
177
+ * Update an edge's metadata (direction and/or label).
178
+ */
179
+ async updateEdge(params) {
180
+ return this.call('boards.updateEdgeMetadata', params);
181
+ }
182
+ /**
183
+ * Soft-delete an edge.
184
+ */
185
+ async deleteEdge(params) {
186
+ await this.call('boards.deleteEdge', params);
187
+ }
188
+ }
189
+ export async function createMentagenClient(config) {
190
+ const client = new MentagenClient(config);
191
+ const user = await client.getCurrentUser();
192
+ if (!user) {
193
+ throw new Error('Authentication failed. Please check your MENTAGEN_TOKEN.\n' +
194
+ 'Generate a new token in Mentagen Settings > Developer if needed.');
195
+ }
196
+ return client;
197
+ }
@@ -8,7 +8,6 @@ export const EdgeDirection = {
8
8
  Target: 'target',
9
9
  };
10
10
  export const NodeType = {
11
- Text: 'text',
12
11
  Markdown: 'markdown',
13
12
  Code: 'code',
14
13
  Image: 'image',
@@ -22,6 +21,5 @@ export const NodeType = {
22
21
  AIChat: 'ai-chat',
23
22
  AIQuiz: 'ai-quiz',
24
23
  File: 'file',
25
- Excalidraw: 'excalidraw',
26
24
  Teleprompter: 'teleprompter',
27
25
  };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
- import { createMentagenClient } from './client/helene.js';
4
+ import { createMentagenClient } from './client/bifrost.js';
5
5
  import { registerPrompts } from './prompts/index.js';
6
6
  import { registerResources } from './resources/index.js';
7
7
  import { registerTools } from './tools/index.js';
@@ -25,7 +25,6 @@ const DEFAULT_NODE_COLORS = {
25
25
  board: 'fuchsia',
26
26
  mermaid: 'violet',
27
27
  voice: undefined,
28
- excalidraw: 'teal',
29
28
  teleprompter: 'rose',
30
29
  };
31
30
  const DEFAULT_CODE_EXTENSION = '.js';
@@ -27,7 +27,6 @@ const NODE_TYPES_WITH_ICONS = new Set([
27
27
  'code',
28
28
  'markdown',
29
29
  'teleprompter',
30
- 'excalidraw',
31
30
  'url',
32
31
  'voice',
33
32
  'audio',
@@ -7,10 +7,8 @@ import { calculateNodeSize } from './size-calc.js';
7
7
  /**
8
8
  * Node types that can be converted between safely.
9
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
10
  */
12
11
  const CONVERTIBLE_TYPES = [
13
- NodeType.Text,
14
12
  NodeType.Markdown,
15
13
  NodeType.Code,
16
14
  NodeType.URL,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentagen/mcp",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
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.4"
48
+ "tar": "^7.5.7"
49
49
  }
50
50
  }