@mentagen/mcp 0.4.1 → 0.5.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.
@@ -64,9 +64,10 @@ export class MentagenClient {
64
64
  /**
65
65
  * List boards the user has access to.
66
66
  */
67
- async listBoards() {
68
- const result = await this.call('boards.list', {});
69
- return result.data || [];
67
+ async listBoards(params) {
68
+ return this.call('boards.listSimple', {
69
+ orgId: params?.orgId,
70
+ });
70
71
  }
71
72
  /**
72
73
  * Search boards by name.
@@ -84,6 +85,18 @@ export class MentagenClient {
84
85
  async searchNodes(params) {
85
86
  return this.call('boards.searchNodesText', params);
86
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
+ }
87
100
  /**
88
101
  * Add a new node to a board.
89
102
  */
package/dist/index.js CHANGED
File without changes
@@ -3,7 +3,12 @@ import { z } from 'zod';
3
3
  import { formatShortDate } from '../utils/ejson.js';
4
4
  import { formatError } from '../utils/errors.js';
5
5
  import { getBoardUrl } from '../utils/urls.js';
6
- export const boardsSchema = z.object({});
6
+ export const boardsSchema = z.object({
7
+ orgId: z
8
+ .string()
9
+ .optional()
10
+ .describe('Optional organization ID to filter boards'),
11
+ });
7
12
  /**
8
13
  * Format boards as TSV for compact output.
9
14
  * Uses papaparse for proper escaping of special characters.
@@ -13,18 +18,29 @@ function formatBoardsTsv(boards) {
13
18
  delimiter: '\t',
14
19
  header: true,
15
20
  newline: '\n',
16
- columns: ['id', 'name', 'description', 'nodeCount', 'updatedAt', 'link'],
21
+ columns: [
22
+ 'id',
23
+ 'name',
24
+ 'description',
25
+ 'nodeCount',
26
+ 'updatedAt',
27
+ 'org',
28
+ 'teams',
29
+ 'link',
30
+ ],
17
31
  });
18
32
  }
19
- export async function handleBoards(client, _params, baseUrl) {
33
+ export async function handleBoards(client, params, baseUrl) {
20
34
  try {
21
- const boards = await client.listBoards();
35
+ const boards = await client.listBoards({ orgId: params.orgId });
22
36
  const formatted = boards.map(board => ({
23
37
  id: board._id,
24
38
  name: board.name,
25
39
  description: board.description || '',
26
40
  nodeCount: board.nodeCount ?? 0,
27
41
  updatedAt: formatShortDate(board.updatedAt),
42
+ org: board.org || '',
43
+ teams: board.teams?.join(',') || '',
28
44
  link: getBoardUrl(baseUrl, board._id),
29
45
  }));
30
46
  return {
@@ -4,6 +4,11 @@ import { formatError } from '../utils/errors.js';
4
4
  import { getBoardUrl } from '../utils/urls.js';
5
5
  export const createBoardSchema = z.object({
6
6
  name: z.string().min(1).describe('The name for the new board'),
7
+ orgId: z
8
+ .string()
9
+ .length(24)
10
+ .optional()
11
+ .describe('Optional organization ID to create the board in'),
7
12
  });
8
13
  function generateObjectId() {
9
14
  const timestamp = Math.floor(Date.now() / 1000)
@@ -18,6 +23,7 @@ export async function handleCreateBoard(client, params, baseUrl) {
18
23
  const board = await client.createBoard({
19
24
  _id: boardId,
20
25
  name: params.name,
26
+ org: params.orgId,
21
27
  });
22
28
  const result = {
23
29
  success: true,
@@ -19,7 +19,9 @@ import { gridCalcSchema, handleGridCalc } from './grid-calc.js';
19
19
  import { handleLink, linkSchema } from './link.js';
20
20
  import { handleListEdges, listEdgesSchema } from './list-edges.js';
21
21
  import { handleListNodes, listNodesSchema } from './list-nodes.js';
22
+ import { handleListOrganizations, listOrganizationsSchema, } from './list-organizations.js';
22
23
  import { handleListPositions, listPositionsSchema } from './list-positions.js';
24
+ import { handleListTeams, listTeamsSchema } from './list-teams.js';
23
25
  import { handleNodeTypes, nodeTypesSchema } from './node-types.js';
24
26
  import { handlePatchContent, patchContentSchema } from './patch-content.js';
25
27
  import { handleRead, readSchema } from './read.js';
@@ -40,7 +42,12 @@ export function registerTools(server, client, config) {
40
42
  description: 'List all boards you have access to in Mentagen. Returns board IDs, names, node counts, and direct links.',
41
43
  inputSchema: {
42
44
  type: 'object',
43
- properties: {},
45
+ properties: {
46
+ orgId: {
47
+ type: 'string',
48
+ description: 'Optional organization ID to filter boards',
49
+ },
50
+ },
44
51
  },
45
52
  },
46
53
  {
@@ -67,13 +74,17 @@ export function registerTools(server, client, config) {
67
74
  type: 'string',
68
75
  description: 'The name for the new board',
69
76
  },
77
+ orgId: {
78
+ type: 'string',
79
+ description: 'Optional organization ID to create the board in. Board will belong to the user without team assignment.',
80
+ },
70
81
  },
71
82
  required: ['name'],
72
83
  },
73
84
  },
74
85
  {
75
86
  name: 'mentagen_update_board',
76
- description: 'Update a board name in Mentagen.',
87
+ description: 'Update a board in Mentagen. Can update name, org, and team assignments.',
77
88
  inputSchema: {
78
89
  type: 'object',
79
90
  properties: {
@@ -85,8 +96,17 @@ export function registerTools(server, client, config) {
85
96
  type: 'string',
86
97
  description: 'New name for the board',
87
98
  },
99
+ org: {
100
+ type: ['string', 'null'],
101
+ description: 'Organization ID to assign the board to. Set to null to remove from organization.',
102
+ },
103
+ teams: {
104
+ type: 'array',
105
+ items: { type: 'string' },
106
+ description: 'Array of team IDs to assign to this board. Use mentagen_list_teams to get team IDs for an org.',
107
+ },
88
108
  },
89
- required: ['boardId', 'name'],
109
+ required: ['boardId'],
90
110
  },
91
111
  },
92
112
  {
@@ -103,6 +123,29 @@ export function registerTools(server, client, config) {
103
123
  required: ['boardId'],
104
124
  },
105
125
  },
126
+ // Organization tools
127
+ {
128
+ name: 'mentagen_list_organizations',
129
+ description: 'List all organizations you are a member of. Returns organization IDs, names, your role, member counts, and direct links.',
130
+ inputSchema: {
131
+ type: 'object',
132
+ properties: {},
133
+ },
134
+ },
135
+ {
136
+ name: 'mentagen_list_teams',
137
+ description: 'List all teams within an organization. Returns team IDs, names, member counts, and whether it is a system team.',
138
+ inputSchema: {
139
+ type: 'object',
140
+ properties: {
141
+ orgId: {
142
+ type: 'string',
143
+ description: 'The organization ID to list teams from',
144
+ },
145
+ },
146
+ required: ['orgId'],
147
+ },
148
+ },
106
149
  // Node tools
107
150
  {
108
151
  name: 'mentagen_list_nodes',
@@ -173,6 +216,10 @@ export function registerTools(server, client, config) {
173
216
  type: 'string',
174
217
  description: 'Optional board ID to limit search scope',
175
218
  },
219
+ org: {
220
+ type: 'string',
221
+ description: 'Optional organization ID to limit search to boards in that organization',
222
+ },
176
223
  limit: {
177
224
  type: 'number',
178
225
  description: 'Maximum number of results (default: 10)',
@@ -893,6 +940,15 @@ export function registerTools(server, client, config) {
893
940
  const params = deleteBoardSchema.parse(args);
894
941
  return handleDeleteBoard(client, params);
895
942
  },
943
+ // Organization handlers
944
+ mentagen_list_organizations: async (args) => {
945
+ listOrganizationsSchema.parse(args);
946
+ return handleListOrganizations(client, config.mentagenUrl);
947
+ },
948
+ mentagen_list_teams: async (args) => {
949
+ const params = listTeamsSchema.parse(args);
950
+ return handleListTeams(client, params);
951
+ },
896
952
  // Node handlers
897
953
  mentagen_list_nodes: async (args) => {
898
954
  const params = listNodesSchema.parse(args);
@@ -0,0 +1,46 @@
1
+ import Papa from 'papaparse';
2
+ import { z } from 'zod';
3
+ import { formatError } from '../utils/errors.js';
4
+ export const listOrganizationsSchema = z.object({});
5
+ export async function handleListOrganizations(client, baseUrl) {
6
+ try {
7
+ const organizations = await client.listOrganizations();
8
+ if (organizations.length === 0) {
9
+ return {
10
+ content: [
11
+ {
12
+ type: 'text',
13
+ text: 'No organizations found. You are not a member of any organization.',
14
+ },
15
+ ],
16
+ };
17
+ }
18
+ const rows = organizations.map(org => ({
19
+ id: org._id,
20
+ name: org.name,
21
+ role: org.role?.type ?? '',
22
+ memberCount: org.members ?? 0,
23
+ link: `${baseUrl}/org/${org._id}`,
24
+ }));
25
+ const tsv = Papa.unparse(rows, {
26
+ delimiter: '\t',
27
+ header: true,
28
+ newline: '\n',
29
+ columns: ['id', 'name', 'role', 'memberCount', 'link'],
30
+ });
31
+ return {
32
+ content: [{ type: 'text', text: tsv }],
33
+ };
34
+ }
35
+ catch (error) {
36
+ return {
37
+ content: [
38
+ {
39
+ type: 'text',
40
+ text: `Failed to list organizations: ${formatError(error)}`,
41
+ },
42
+ ],
43
+ isError: true,
44
+ };
45
+ }
46
+ }
@@ -0,0 +1,47 @@
1
+ import Papa from 'papaparse';
2
+ import { z } from 'zod';
3
+ import { formatError } from '../utils/errors.js';
4
+ export const listTeamsSchema = z.object({
5
+ orgId: z.string().describe('The organization ID to list teams from'),
6
+ });
7
+ export async function handleListTeams(client, params) {
8
+ try {
9
+ const teams = await client.listTeams({ orgId: params.orgId });
10
+ if (teams.length === 0) {
11
+ return {
12
+ content: [
13
+ {
14
+ type: 'text',
15
+ text: 'No teams found in this organization.',
16
+ },
17
+ ],
18
+ };
19
+ }
20
+ const rows = teams.map(team => ({
21
+ id: team._id,
22
+ name: team.name,
23
+ memberCount: team.members?.length ?? 0,
24
+ isSystemTeam: team.systemType === 'general',
25
+ }));
26
+ const tsv = Papa.unparse(rows, {
27
+ delimiter: '\t',
28
+ header: true,
29
+ newline: '\n',
30
+ columns: ['id', 'name', 'memberCount', 'isSystemTeam'],
31
+ });
32
+ return {
33
+ content: [{ type: 'text', text: tsv }],
34
+ };
35
+ }
36
+ catch (error) {
37
+ return {
38
+ content: [
39
+ {
40
+ type: 'text',
41
+ text: `Failed to list teams: ${formatError(error)}`,
42
+ },
43
+ ],
44
+ isError: true,
45
+ };
46
+ }
47
+ }
@@ -11,6 +11,10 @@ export const searchSchema = z.object({
11
11
  .string()
12
12
  .optional()
13
13
  .describe('Optional board ID to limit search scope'),
14
+ org: z
15
+ .string()
16
+ .optional()
17
+ .describe('Optional organization ID to limit search to boards in that organization'),
14
18
  limit: z
15
19
  .number()
16
20
  .default(10)
@@ -59,6 +63,7 @@ export async function handleSearch(client, params, baseUrl) {
59
63
  const results = await client.searchNodes({
60
64
  query: params.query,
61
65
  board: params.board,
66
+ org: params.org,
62
67
  semanticRatio: params.semanticRatio,
63
68
  });
64
69
  const formatted = formatSearchResults(results.hits.slice(0, params.limit));
@@ -14,11 +14,23 @@ export const updateBoardSchema = z
14
14
  .url()
15
15
  .optional()
16
16
  .describe('Pull request URL for this board'),
17
+ org: z
18
+ .string()
19
+ .length(24)
20
+ .optional()
21
+ .nullable()
22
+ .describe('Organization ID. Set to null to remove from organization.'),
23
+ teams: z
24
+ .array(z.string().length(24))
25
+ .optional()
26
+ .describe('Array of team IDs to assign to this board'),
17
27
  })
18
28
  .refine(data => data.name !== undefined ||
19
29
  data.description !== undefined ||
20
- data.pullRequestUrl !== undefined, {
21
- message: 'At least one field (name, description, or pullRequestUrl) must be provided',
30
+ data.pullRequestUrl !== undefined ||
31
+ data.org !== undefined ||
32
+ data.teams !== undefined, {
33
+ message: 'At least one field (name, description, pullRequestUrl, org, or teams) must be provided',
22
34
  });
23
35
  export async function handleUpdateBoard(client, params, baseUrl) {
24
36
  try {
@@ -29,6 +41,10 @@ export async function handleUpdateBoard(client, params, baseUrl) {
29
41
  data.description = params.description;
30
42
  if (params.pullRequestUrl !== undefined)
31
43
  data.pullRequestUrl = params.pullRequestUrl;
44
+ if (params.org !== undefined)
45
+ data.org = params.org;
46
+ if (params.teams !== undefined)
47
+ data.teams = params.teams;
32
48
  const board = await client.updateBoard({
33
49
  boardId: params.boardId,
34
50
  data,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentagen/mcp",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "MCP server for Mentagen knowledge base integration with Cursor",
5
5
  "type": "module",
6
6
  "bin": "dist/index.js",