@imboard.ai/mcp-server 0.1.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/README.md +194 -0
- package/dist/index.cjs +1866 -0
- package/package.json +83 -0
- package/src/api-client/errors.ts +42 -0
- package/src/api-client/imboardApiClient.ts +256 -0
- package/src/config.ts +62 -0
- package/src/index.ts +32 -0
- package/src/resources/docs.resources.ts +97 -0
- package/src/server.ts +54 -0
- package/src/tools/action-items.tools.ts +41 -0
- package/src/tools/boards.tools.ts +172 -0
- package/src/tools/dashboards.tools.ts +45 -0
- package/src/tools/documents-write.tools.ts +110 -0
- package/src/tools/documents.tools.ts +46 -0
- package/src/tools/getMe.tool.ts +17 -0
- package/src/tools/invites.tools.ts +90 -0
- package/src/tools/meetings.tools.ts +160 -0
- package/src/tools/members.tools.ts +43 -0
- package/src/tools/notifications.tools.ts +47 -0
- package/src/tools/reports.tools.ts +414 -0
- package/src/tools/shared.ts +82 -0
- package/src/tools/slots.tools.ts +115 -0
- package/src/tools/supporting.tools.ts +92 -0
- package/src/tools/user.tools.ts +31 -0
- package/src/utils/logging.ts +59 -0
- package/src/utils/redact.ts +8 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { boardIdParam, resourceIdParam, paginationParams, buildQueryParams, formatResult, handleToolError, RegisterToolsFn } from './shared.js';
|
|
3
|
+
|
|
4
|
+
export const registerBoardsTools: RegisterToolsFn = (server, client) => {
|
|
5
|
+
server.tool(
|
|
6
|
+
'list_boards',
|
|
7
|
+
'Lists all boards the authenticated user has access to. Returns board name, billing state, and the user\'s role on each board.',
|
|
8
|
+
{
|
|
9
|
+
...paginationParams,
|
|
10
|
+
sort: z.enum(['updatedAt', 'createdAt']).optional().describe('Sort field (default: updatedAt)'),
|
|
11
|
+
},
|
|
12
|
+
async ({ ...rest }) => {
|
|
13
|
+
try {
|
|
14
|
+
const params = buildQueryParams(rest, []);
|
|
15
|
+
const response = await client.getCollection('/api/boards', params);
|
|
16
|
+
return formatResult({ data: response.data, meta: response.meta });
|
|
17
|
+
} catch (error) {
|
|
18
|
+
return handleToolError(error);
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
server.tool(
|
|
24
|
+
'get_board',
|
|
25
|
+
'Returns details for a specific board including name, description, billing state, and the authenticated user\'s role.',
|
|
26
|
+
{
|
|
27
|
+
boardId: boardIdParam,
|
|
28
|
+
},
|
|
29
|
+
async ({ boardId }) => {
|
|
30
|
+
try {
|
|
31
|
+
const response = await client.get(`/api/boards/${boardId}`);
|
|
32
|
+
return formatResult({ data: response.data });
|
|
33
|
+
} catch (error) {
|
|
34
|
+
return handleToolError(error);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
server.tool(
|
|
40
|
+
'create_board',
|
|
41
|
+
'Creates a new board. Returns the created board details.',
|
|
42
|
+
{
|
|
43
|
+
name: z.string().describe('Board name'),
|
|
44
|
+
description: z.string().optional().describe('Board description (BlockNote JSON or plain text)'),
|
|
45
|
+
},
|
|
46
|
+
async ({ name, description }) => {
|
|
47
|
+
try {
|
|
48
|
+
const body: Record<string, unknown> = { name };
|
|
49
|
+
if (description !== undefined) body.description = description;
|
|
50
|
+
const response = await client.post('/api/board', body);
|
|
51
|
+
return formatResult({ data: response.data });
|
|
52
|
+
} catch (error) {
|
|
53
|
+
return handleToolError(error);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
server.tool(
|
|
59
|
+
'update_board',
|
|
60
|
+
'Updates board details (name, description, settings). Requires admin access.',
|
|
61
|
+
{
|
|
62
|
+
boardId: boardIdParam,
|
|
63
|
+
name: z.string().describe('Board name'),
|
|
64
|
+
description: z.string().optional().describe('Board description (BlockNote JSON or plain text)'),
|
|
65
|
+
companyType: z.string().optional().describe('Company type'),
|
|
66
|
+
boardType: z.string().optional().describe('Board type'),
|
|
67
|
+
companyWebsiteUrl: z.string().optional().describe('Company website URL (must include protocol)'),
|
|
68
|
+
defaultTimezone: z.string().optional().describe('Default timezone (IANA format)'),
|
|
69
|
+
},
|
|
70
|
+
async ({ boardId, name, description, companyType, boardType, companyWebsiteUrl, defaultTimezone }) => {
|
|
71
|
+
try {
|
|
72
|
+
const body: Record<string, unknown> = { name };
|
|
73
|
+
if (description !== undefined) body.description = description;
|
|
74
|
+
if (companyType !== undefined) body.companyType = companyType;
|
|
75
|
+
if (boardType !== undefined) body.boardType = boardType;
|
|
76
|
+
if (companyWebsiteUrl !== undefined) body.companyWebsiteUrl = companyWebsiteUrl;
|
|
77
|
+
if (defaultTimezone !== undefined) body.defaultTimezone = defaultTimezone;
|
|
78
|
+
const response = await client.put(`/api/board/${boardId}`, body);
|
|
79
|
+
return formatResult({ data: response.data });
|
|
80
|
+
} catch (error) {
|
|
81
|
+
return handleToolError(error);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
server.tool(
|
|
87
|
+
'assign_member_roles',
|
|
88
|
+
'Assigns roles, positions, and access types to a board member.',
|
|
89
|
+
{
|
|
90
|
+
boardId: boardIdParam,
|
|
91
|
+
userId: resourceIdParam.describe('The user ID of the board member'),
|
|
92
|
+
set_admin_role: z.enum(['admin', 'guest']).optional().describe('Admin role to assign'),
|
|
93
|
+
set_access_type: z.enum(['collaborator', 'viewer']).optional().describe('Access type to assign'),
|
|
94
|
+
board_position: z.array(z.string()).optional().describe('Board positions to assign'),
|
|
95
|
+
appointingParty: z.string().optional().describe('Name of the appointing party'),
|
|
96
|
+
},
|
|
97
|
+
async ({ boardId, userId, ...fields }) => {
|
|
98
|
+
try {
|
|
99
|
+
const body: Record<string, unknown> = {};
|
|
100
|
+
if (fields.set_admin_role !== undefined) body.set_admin_role = fields.set_admin_role;
|
|
101
|
+
if (fields.set_access_type !== undefined) body.set_access_type = fields.set_access_type;
|
|
102
|
+
if (fields.board_position !== undefined) body.board_position = fields.board_position;
|
|
103
|
+
if (fields.appointingParty !== undefined) body.appointingParty = fields.appointingParty;
|
|
104
|
+
const response = await client.put(`/api/board/${boardId}/members/${userId}/roles`, body);
|
|
105
|
+
return formatResult({ data: response.data });
|
|
106
|
+
} catch (error) {
|
|
107
|
+
return handleToolError(error);
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
server.tool(
|
|
113
|
+
'search_board_documents',
|
|
114
|
+
'Semantic search across all documents in a board. Returns matching document snippets ranked by relevance.',
|
|
115
|
+
{
|
|
116
|
+
boardId: boardIdParam,
|
|
117
|
+
query: z.string().describe('Search query (max 1000 characters)'),
|
|
118
|
+
limit: z.number().int().min(1).max(50).optional().describe('Max results (1-50, default 10)'),
|
|
119
|
+
minScore: z.number().min(0).max(1).optional().describe('Minimum relevance score (0-1)'),
|
|
120
|
+
},
|
|
121
|
+
async ({ boardId, query, limit, minScore }) => {
|
|
122
|
+
try {
|
|
123
|
+
const body: Record<string, unknown> = { query };
|
|
124
|
+
if (limit !== undefined) body.limit = limit;
|
|
125
|
+
if (minScore !== undefined) body.minScore = minScore;
|
|
126
|
+
const response = await client.post(`/api/board/${boardId}/search`, body);
|
|
127
|
+
return formatResult({ data: response.data });
|
|
128
|
+
} catch (error) {
|
|
129
|
+
return handleToolError(error);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
server.tool(
|
|
135
|
+
'list_board_action_items',
|
|
136
|
+
'Lists action items for a board. Returns open/in-progress items by default.',
|
|
137
|
+
{
|
|
138
|
+
boardId: boardIdParam,
|
|
139
|
+
includeCompleted: z.enum(['true', 'false']).optional().describe('Include completed items (default: false)'),
|
|
140
|
+
status: z.string().optional().describe('Filter by status (open, in_progress, completed, cancelled)'),
|
|
141
|
+
},
|
|
142
|
+
async ({ boardId, includeCompleted, status }) => {
|
|
143
|
+
try {
|
|
144
|
+
const params: Record<string, string> = {};
|
|
145
|
+
if (includeCompleted !== undefined) params.includeCompleted = includeCompleted;
|
|
146
|
+
if (status !== undefined) params.status = status;
|
|
147
|
+
const response = await client.get(`/api/board/${boardId}/action-items`, params);
|
|
148
|
+
return formatResult({ data: response.data });
|
|
149
|
+
} catch (error) {
|
|
150
|
+
return handleToolError(error);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
server.tool(
|
|
156
|
+
'update_board_action_item',
|
|
157
|
+
'Updates an action item status (in_progress, completed, cancelled).',
|
|
158
|
+
{
|
|
159
|
+
boardId: boardIdParam,
|
|
160
|
+
actionItemId: resourceIdParam.describe('The action item ID'),
|
|
161
|
+
status: z.string().describe('New status (open, in_progress, completed, cancelled)'),
|
|
162
|
+
},
|
|
163
|
+
async ({ boardId, actionItemId, status }) => {
|
|
164
|
+
try {
|
|
165
|
+
const response = await client.patch(`/api/board/${boardId}/action-items/${actionItemId}`, { status });
|
|
166
|
+
return formatResult({ data: response.data });
|
|
167
|
+
} catch (error) {
|
|
168
|
+
return handleToolError(error);
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
);
|
|
172
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { boardIdParam, resourceIdParam, paginationParams, buildQueryParams, formatResult, handleToolError, RegisterToolsFn } from './shared.js';
|
|
2
|
+
|
|
3
|
+
export const registerDashboardTools: RegisterToolsFn = (server, client) => {
|
|
4
|
+
server.tool(
|
|
5
|
+
'list_report_dashboards',
|
|
6
|
+
'Lists dashboards under a specific report. Dashboards are nested under reports in imboard\'s domain model.',
|
|
7
|
+
{
|
|
8
|
+
boardId: boardIdParam,
|
|
9
|
+
reportId: resourceIdParam.describe('The report ID (required — dashboards are nested under reports)'),
|
|
10
|
+
...paginationParams,
|
|
11
|
+
},
|
|
12
|
+
async ({ boardId, reportId, ...rest }) => {
|
|
13
|
+
try {
|
|
14
|
+
const params = buildQueryParams(rest, []);
|
|
15
|
+
const result = await client.getCollection(
|
|
16
|
+
`/api/boards/${boardId}/reports/${reportId}/dashboards`,
|
|
17
|
+
params,
|
|
18
|
+
);
|
|
19
|
+
return formatResult({ data: result.data, meta: result.meta });
|
|
20
|
+
} catch (error) {
|
|
21
|
+
return handleToolError(error);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
server.tool(
|
|
27
|
+
'get_dashboard',
|
|
28
|
+
'Returns details for a specific dashboard including type, title, and associated report.',
|
|
29
|
+
{
|
|
30
|
+
boardId: boardIdParam,
|
|
31
|
+
reportId: resourceIdParam.describe('The report ID'),
|
|
32
|
+
dashboardId: resourceIdParam.describe('The dashboard ID'),
|
|
33
|
+
},
|
|
34
|
+
async ({ boardId, reportId, dashboardId }) => {
|
|
35
|
+
try {
|
|
36
|
+
const result = await client.get(
|
|
37
|
+
`/api/boards/${boardId}/reports/${reportId}/dashboards/${dashboardId}`,
|
|
38
|
+
);
|
|
39
|
+
return formatResult({ data: result.data });
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return handleToolError(error);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { boardIdParam, resourceIdParam, formatResult, handleToolError, RegisterToolsFn } from './shared.js';
|
|
3
|
+
|
|
4
|
+
export const registerDocumentWriteTools: RegisterToolsFn = (server, client) => {
|
|
5
|
+
server.tool(
|
|
6
|
+
'create_document',
|
|
7
|
+
'Creates a new document with an initial version. Requires a title and optionally a document type and external file link.',
|
|
8
|
+
{
|
|
9
|
+
boardId: boardIdParam,
|
|
10
|
+
title: z.string().describe('Document title'),
|
|
11
|
+
documentType: z.string().optional().describe('Document type (e.g. boardResolutions, minutesOfMeetings, capTable)'),
|
|
12
|
+
externalFileLink: z.string().optional().describe('HTTPS URL to an external file'),
|
|
13
|
+
meetingIds: z.array(z.string()).optional().describe('Array of meeting IDs to associate with'),
|
|
14
|
+
},
|
|
15
|
+
async ({ boardId, title, documentType, externalFileLink, meetingIds }) => {
|
|
16
|
+
try {
|
|
17
|
+
const body: Record<string, unknown> = { title };
|
|
18
|
+
if (documentType !== undefined) body.documentType = documentType;
|
|
19
|
+
if (externalFileLink !== undefined) body.externalFileLink = externalFileLink;
|
|
20
|
+
if (meetingIds !== undefined) body.meetingIds = meetingIds;
|
|
21
|
+
const response = await client.post(`/api/documents/${boardId}/`, body);
|
|
22
|
+
return formatResult({ data: response.data });
|
|
23
|
+
} catch (error) {
|
|
24
|
+
return handleToolError(error);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
server.tool(
|
|
30
|
+
'update_document',
|
|
31
|
+
'Updates document details (title, meeting associations, working copy link).',
|
|
32
|
+
{
|
|
33
|
+
boardId: boardIdParam,
|
|
34
|
+
documentId: resourceIdParam.describe('The document ID'),
|
|
35
|
+
title: z.string().optional().describe('New document title'),
|
|
36
|
+
documentType: z.string().optional().describe('Document type'),
|
|
37
|
+
workingCopyLink: z.string().optional().describe('URL to working copy'),
|
|
38
|
+
meetingIds: z.array(z.string()).optional().describe('Array of meeting IDs to associate with'),
|
|
39
|
+
},
|
|
40
|
+
async ({ boardId, documentId, title, documentType, workingCopyLink, meetingIds }) => {
|
|
41
|
+
try {
|
|
42
|
+
const body: Record<string, unknown> = {};
|
|
43
|
+
if (title !== undefined) body.title = title;
|
|
44
|
+
if (documentType !== undefined) body.documentType = documentType;
|
|
45
|
+
if (workingCopyLink !== undefined) body.workingCopyLink = workingCopyLink;
|
|
46
|
+
if (meetingIds !== undefined) body.meetingIds = meetingIds;
|
|
47
|
+
const response = await client.put(`/api/documents/${boardId}/${documentId}/`, body);
|
|
48
|
+
return formatResult({ data: response.data });
|
|
49
|
+
} catch (error) {
|
|
50
|
+
return handleToolError(error);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
server.tool(
|
|
56
|
+
'update_document_status',
|
|
57
|
+
'Changes document status (draft, review, approved, etc.).',
|
|
58
|
+
{
|
|
59
|
+
boardId: boardIdParam,
|
|
60
|
+
documentId: resourceIdParam.describe('The document ID'),
|
|
61
|
+
documentStatus: z.string().describe('New document status'),
|
|
62
|
+
},
|
|
63
|
+
async ({ boardId, documentId, documentStatus }) => {
|
|
64
|
+
try {
|
|
65
|
+
const response = await client.put(`/api/documents/${boardId}/${documentId}/status`, { documentStatus });
|
|
66
|
+
return formatResult({ data: response.data });
|
|
67
|
+
} catch (error) {
|
|
68
|
+
return handleToolError(error);
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
server.tool(
|
|
74
|
+
'delete_document',
|
|
75
|
+
'Deletes a document from a board.',
|
|
76
|
+
{
|
|
77
|
+
boardId: boardIdParam,
|
|
78
|
+
documentId: resourceIdParam.describe('The document ID'),
|
|
79
|
+
},
|
|
80
|
+
async ({ boardId, documentId }) => {
|
|
81
|
+
try {
|
|
82
|
+
const response = await client.delete(`/api/documents/${boardId}/${documentId}/`);
|
|
83
|
+
return formatResult({ data: response.data });
|
|
84
|
+
} catch (error) {
|
|
85
|
+
return handleToolError(error);
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
server.tool(
|
|
91
|
+
'create_document_version',
|
|
92
|
+
'Creates a new version of a document. Requires an external file link (file upload not supported via MCP).',
|
|
93
|
+
{
|
|
94
|
+
boardId: boardIdParam,
|
|
95
|
+
documentId: resourceIdParam.describe('The document ID'),
|
|
96
|
+
externalFileLink: z.string().describe('HTTPS URL to the new version file'),
|
|
97
|
+
notes: z.string().optional().describe('Version notes'),
|
|
98
|
+
},
|
|
99
|
+
async ({ boardId, documentId, externalFileLink, notes }) => {
|
|
100
|
+
try {
|
|
101
|
+
const body: Record<string, unknown> = { externalFileLink };
|
|
102
|
+
if (notes !== undefined) body.notes = notes;
|
|
103
|
+
const response = await client.post(`/api/documents/${boardId}/${documentId}/version`, body);
|
|
104
|
+
return formatResult({ data: response.data });
|
|
105
|
+
} catch (error) {
|
|
106
|
+
return handleToolError(error);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { boardIdParam, resourceIdParam, paginationParams, buildQueryParams, formatResult, handleToolError, RegisterToolsFn } from './shared.js';
|
|
3
|
+
|
|
4
|
+
export const registerDocumentTools: RegisterToolsFn = (server, client) => {
|
|
5
|
+
server.tool(
|
|
6
|
+
'list_board_documents',
|
|
7
|
+
'Lists document metadata for a board. Supports filtering by meeting or document type. Does not provide file download in V1.',
|
|
8
|
+
{
|
|
9
|
+
boardId: boardIdParam,
|
|
10
|
+
...paginationParams,
|
|
11
|
+
meetingId: z.string().optional().describe('Filter by meeting ID'),
|
|
12
|
+
documentType: z.string().optional().describe('Filter by document type (e.g. boardResolutions, minutesOfMeetings, capTable)'),
|
|
13
|
+
},
|
|
14
|
+
async ({ boardId, ...rest }) => {
|
|
15
|
+
try {
|
|
16
|
+
const params = buildQueryParams(rest, ['meetingId', 'documentType']);
|
|
17
|
+
const result = await client.getCollection(
|
|
18
|
+
`/api/boards/${boardId}/documents`,
|
|
19
|
+
params,
|
|
20
|
+
);
|
|
21
|
+
return formatResult({ data: result.data, meta: result.meta });
|
|
22
|
+
} catch (error) {
|
|
23
|
+
return handleToolError(error);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
server.tool(
|
|
29
|
+
'get_document',
|
|
30
|
+
'Returns metadata for a specific document including title, type, and associated meeting. Does not provide file download in V1.',
|
|
31
|
+
{
|
|
32
|
+
boardId: boardIdParam,
|
|
33
|
+
documentId: resourceIdParam.describe('The document ID'),
|
|
34
|
+
},
|
|
35
|
+
async ({ boardId, documentId }) => {
|
|
36
|
+
try {
|
|
37
|
+
const result = await client.get(
|
|
38
|
+
`/api/boards/${boardId}/documents/${documentId}`,
|
|
39
|
+
);
|
|
40
|
+
return formatResult({ data: result.data });
|
|
41
|
+
} catch (error) {
|
|
42
|
+
return handleToolError(error);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { formatResult, handleToolError, RegisterToolsFn } from './shared.js';
|
|
2
|
+
|
|
3
|
+
export const registerGetMeTool: RegisterToolsFn = (server, client) => {
|
|
4
|
+
server.tool(
|
|
5
|
+
'get_me',
|
|
6
|
+
'Returns the currently authenticated imboard user\'s profile including name, email, and timezone.',
|
|
7
|
+
{},
|
|
8
|
+
async () => {
|
|
9
|
+
try {
|
|
10
|
+
const response = await client.get('/api/me');
|
|
11
|
+
return formatResult({ data: response.data });
|
|
12
|
+
} catch (error) {
|
|
13
|
+
return handleToolError(error);
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { boardIdParam, resourceIdParam, formatResult, handleToolError, RegisterToolsFn } from './shared.js';
|
|
3
|
+
|
|
4
|
+
export const registerInviteTools: RegisterToolsFn = (server, client) => {
|
|
5
|
+
server.tool(
|
|
6
|
+
'list_board_invites',
|
|
7
|
+
'Lists pending invites for a board.',
|
|
8
|
+
{
|
|
9
|
+
boardId: boardIdParam,
|
|
10
|
+
},
|
|
11
|
+
async ({ boardId }) => {
|
|
12
|
+
try {
|
|
13
|
+
const response = await client.get(`/api/invite/${boardId}/invites`);
|
|
14
|
+
return formatResult({ data: response.data });
|
|
15
|
+
} catch (error) {
|
|
16
|
+
return handleToolError(error);
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
server.tool(
|
|
22
|
+
'create_invite',
|
|
23
|
+
'Invites a user to a board by email. Optionally include a personal message and board positions.',
|
|
24
|
+
{
|
|
25
|
+
boardId: boardIdParam,
|
|
26
|
+
inviteeEmail: z.string().email().describe('Email address of the person to invite'),
|
|
27
|
+
message: z.string().optional().describe('Optional personal message included in the invite email'),
|
|
28
|
+
boardPositions: z.array(z.string()).optional().describe('Board positions to assign to the invitee'),
|
|
29
|
+
},
|
|
30
|
+
async ({ boardId, inviteeEmail, message, boardPositions }) => {
|
|
31
|
+
try {
|
|
32
|
+
const body: Record<string, unknown> = { inviteeEmail };
|
|
33
|
+
if (message !== undefined) body.message = message;
|
|
34
|
+
if (boardPositions !== undefined) body.boardPositions = boardPositions;
|
|
35
|
+
const response = await client.post(`/api/invite/${boardId}`, body);
|
|
36
|
+
return formatResult({ data: response.data });
|
|
37
|
+
} catch (error) {
|
|
38
|
+
return handleToolError(error);
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
server.tool(
|
|
44
|
+
'accept_invite',
|
|
45
|
+
'Accepts a board invite.',
|
|
46
|
+
{
|
|
47
|
+
boardInviteId: resourceIdParam.describe('The invite ID'),
|
|
48
|
+
},
|
|
49
|
+
async ({ boardInviteId }) => {
|
|
50
|
+
try {
|
|
51
|
+
const response = await client.put(`/api/invite/accept/${boardInviteId}`);
|
|
52
|
+
return formatResult({ data: response.data });
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return handleToolError(error);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
server.tool(
|
|
60
|
+
'decline_invite',
|
|
61
|
+
'Declines a board invite.',
|
|
62
|
+
{
|
|
63
|
+
boardInviteId: resourceIdParam.describe('The invite ID'),
|
|
64
|
+
},
|
|
65
|
+
async ({ boardInviteId }) => {
|
|
66
|
+
try {
|
|
67
|
+
const response = await client.put(`/api/invite/decline/${boardInviteId}`);
|
|
68
|
+
return formatResult({ data: response.data });
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return handleToolError(error);
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
server.tool(
|
|
76
|
+
'retract_invite',
|
|
77
|
+
'Retracts a sent board invite.',
|
|
78
|
+
{
|
|
79
|
+
boardInviteId: resourceIdParam.describe('The invite ID'),
|
|
80
|
+
},
|
|
81
|
+
async ({ boardInviteId }) => {
|
|
82
|
+
try {
|
|
83
|
+
const response = await client.put(`/api/invite/retract/${boardInviteId}`);
|
|
84
|
+
return formatResult({ data: response.data });
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return handleToolError(error);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { boardIdParam, resourceIdParam, paginationParams, buildQueryParams, formatResult, handleToolError, RegisterToolsFn } from './shared.js';
|
|
3
|
+
|
|
4
|
+
export const registerMeetingTools: RegisterToolsFn = (server, client) => {
|
|
5
|
+
server.tool(
|
|
6
|
+
'list_board_meetings',
|
|
7
|
+
'Lists meetings for a board. Supports filtering by status and date range, and sorting by start time.',
|
|
8
|
+
{
|
|
9
|
+
boardId: boardIdParam,
|
|
10
|
+
...paginationParams,
|
|
11
|
+
sort: z.enum(['updatedAt', 'startTime']).optional().describe('Sort field (default: startTime)'),
|
|
12
|
+
status: z.string().optional().describe('Filter by meeting status'),
|
|
13
|
+
startAfter: z.string().optional().describe('ISO-8601 date — only meetings starting after this'),
|
|
14
|
+
startBefore: z.string().optional().describe('ISO-8601 date — only meetings starting before this'),
|
|
15
|
+
},
|
|
16
|
+
async ({ boardId, ...rest }) => {
|
|
17
|
+
try {
|
|
18
|
+
const params = buildQueryParams(rest, ['status', 'startAfter', 'startBefore']);
|
|
19
|
+
const result = await client.getCollection(
|
|
20
|
+
`/api/boards/${boardId}/meetings`,
|
|
21
|
+
params,
|
|
22
|
+
);
|
|
23
|
+
return formatResult({ data: result.data, meta: result.meta });
|
|
24
|
+
} catch (error) {
|
|
25
|
+
return handleToolError(error);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
server.tool(
|
|
31
|
+
'get_meeting',
|
|
32
|
+
'Returns details for a specific meeting including title, status, location, and scheduled time.',
|
|
33
|
+
{
|
|
34
|
+
boardId: boardIdParam,
|
|
35
|
+
meetingId: resourceIdParam.describe('The meeting ID'),
|
|
36
|
+
},
|
|
37
|
+
async ({ boardId, meetingId }) => {
|
|
38
|
+
try {
|
|
39
|
+
const result = await client.get(
|
|
40
|
+
`/api/boards/${boardId}/meetings/${meetingId}`,
|
|
41
|
+
);
|
|
42
|
+
return formatResult({ data: result.data });
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return handleToolError(error);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
server.tool(
|
|
50
|
+
'create_meeting',
|
|
51
|
+
'Creates a new meeting on a board. Requires write access. The authenticated user is set as the creator.',
|
|
52
|
+
{
|
|
53
|
+
boardId: boardIdParam,
|
|
54
|
+
title: z.string().describe('Meeting title (max 200 characters)'),
|
|
55
|
+
status: z.string().describe('Meeting status: draft, planned, scheduled, completed, or finalized'),
|
|
56
|
+
locationType: z.string().describe('Location type: physical, virtual, or hybrid'),
|
|
57
|
+
startTime: z.string().describe('Meeting start time as ISO-8601 date string'),
|
|
58
|
+
endTime: z.string().describe('Meeting end time as ISO-8601 date string'),
|
|
59
|
+
location: z.string().optional().describe('Physical location (max 255 characters)'),
|
|
60
|
+
virtualMeetingUrl: z.string().optional().describe('Virtual meeting URL (max 255 characters)'),
|
|
61
|
+
},
|
|
62
|
+
async ({ boardId, title, status, locationType, startTime, endTime, location, virtualMeetingUrl }) => {
|
|
63
|
+
try {
|
|
64
|
+
const body: Record<string, string> = {
|
|
65
|
+
title,
|
|
66
|
+
status,
|
|
67
|
+
locationType,
|
|
68
|
+
startTime,
|
|
69
|
+
endTime,
|
|
70
|
+
};
|
|
71
|
+
if (location !== undefined) body.location = location;
|
|
72
|
+
if (virtualMeetingUrl !== undefined) body.virtualMeetingUrl = virtualMeetingUrl;
|
|
73
|
+
|
|
74
|
+
const result = await client.post(
|
|
75
|
+
`/api/boards/${boardId}/meetings`,
|
|
76
|
+
body,
|
|
77
|
+
);
|
|
78
|
+
return formatResult({ data: result.data });
|
|
79
|
+
} catch (error) {
|
|
80
|
+
return handleToolError(error);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
server.tool(
|
|
86
|
+
'update_meeting',
|
|
87
|
+
'Updates an existing meeting. Only the provided fields are changed. Requires write access to the board.',
|
|
88
|
+
{
|
|
89
|
+
boardId: boardIdParam,
|
|
90
|
+
meetingId: resourceIdParam.describe('The meeting ID'),
|
|
91
|
+
title: z.string().optional().describe('Meeting title (max 200 characters)'),
|
|
92
|
+
status: z.string().optional().describe('Meeting status: draft, planned, scheduled, completed, or finalized'),
|
|
93
|
+
locationType: z.string().optional().describe('Location type: physical, virtual, or hybrid'),
|
|
94
|
+
location: z.string().optional().describe('Physical location (max 255 characters)'),
|
|
95
|
+
virtualMeetingUrl: z.string().optional().describe('Virtual meeting URL (max 255 characters)'),
|
|
96
|
+
startTime: z.string().optional().describe('Meeting start time as ISO-8601 date string'),
|
|
97
|
+
endTime: z.string().optional().describe('Meeting end time as ISO-8601 date string'),
|
|
98
|
+
},
|
|
99
|
+
async ({ boardId, meetingId, title, status, locationType, location, virtualMeetingUrl, startTime, endTime }) => {
|
|
100
|
+
try {
|
|
101
|
+
const body: Record<string, string> = {};
|
|
102
|
+
if (title !== undefined) body.title = title;
|
|
103
|
+
if (status !== undefined) body.status = status;
|
|
104
|
+
if (locationType !== undefined) body.locationType = locationType;
|
|
105
|
+
if (location !== undefined) body.location = location;
|
|
106
|
+
if (virtualMeetingUrl !== undefined) body.virtualMeetingUrl = virtualMeetingUrl;
|
|
107
|
+
if (startTime !== undefined) body.startTime = startTime;
|
|
108
|
+
if (endTime !== undefined) body.endTime = endTime;
|
|
109
|
+
|
|
110
|
+
const result = await client.patch(
|
|
111
|
+
`/api/boards/${boardId}/meetings/${meetingId}`,
|
|
112
|
+
body,
|
|
113
|
+
);
|
|
114
|
+
return formatResult({ data: result.data });
|
|
115
|
+
} catch (error) {
|
|
116
|
+
return handleToolError(error);
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
server.tool(
|
|
122
|
+
'delete_meeting',
|
|
123
|
+
'Deletes a meeting from a board.',
|
|
124
|
+
{
|
|
125
|
+
boardId: boardIdParam,
|
|
126
|
+
meetingId: resourceIdParam.describe('The meeting ID'),
|
|
127
|
+
},
|
|
128
|
+
async ({ boardId, meetingId }) => {
|
|
129
|
+
try {
|
|
130
|
+
const result = await client.delete(
|
|
131
|
+
`/api/meetings/${boardId}/${meetingId}/`,
|
|
132
|
+
);
|
|
133
|
+
return formatResult({ data: result.data });
|
|
134
|
+
} catch (error) {
|
|
135
|
+
return handleToolError(error);
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
server.tool(
|
|
141
|
+
'change_meeting_status',
|
|
142
|
+
'Changes meeting status (draft, planned, scheduled, completed, finalized).',
|
|
143
|
+
{
|
|
144
|
+
boardId: boardIdParam,
|
|
145
|
+
meetingId: resourceIdParam.describe('The meeting ID'),
|
|
146
|
+
meetingStatus: z.string().describe('New meeting status: draft, planned, scheduled, completed, or finalized'),
|
|
147
|
+
},
|
|
148
|
+
async ({ boardId, meetingId, meetingStatus }) => {
|
|
149
|
+
try {
|
|
150
|
+
const result = await client.put(
|
|
151
|
+
`/api/meetings/${boardId}/${meetingId}/status`,
|
|
152
|
+
{ meetingStatus },
|
|
153
|
+
);
|
|
154
|
+
return formatResult({ data: result.data });
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return handleToolError(error);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { boardIdParam, resourceIdParam, paginationParams, buildQueryParams, formatResult, handleToolError, RegisterToolsFn } from './shared.js';
|
|
2
|
+
|
|
3
|
+
export const registerMemberTools: RegisterToolsFn = (server, client) => {
|
|
4
|
+
server.tool(
|
|
5
|
+
'list_board_members',
|
|
6
|
+
'Lists members of a board including their roles, access types, and board positions.',
|
|
7
|
+
{
|
|
8
|
+
boardId: boardIdParam,
|
|
9
|
+
...paginationParams,
|
|
10
|
+
},
|
|
11
|
+
async ({ boardId, ...rest }) => {
|
|
12
|
+
try {
|
|
13
|
+
const params = buildQueryParams(rest, []);
|
|
14
|
+
const result = await client.getCollection(
|
|
15
|
+
`/api/boards/${boardId}/members`,
|
|
16
|
+
params,
|
|
17
|
+
);
|
|
18
|
+
return formatResult({ data: result.data, meta: result.meta });
|
|
19
|
+
} catch (error) {
|
|
20
|
+
return handleToolError(error);
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
server.tool(
|
|
26
|
+
'get_board_member',
|
|
27
|
+
'Returns details for a specific board member including their role, access type, and board positions.',
|
|
28
|
+
{
|
|
29
|
+
boardId: boardIdParam,
|
|
30
|
+
memberId: resourceIdParam.describe('The member ID'),
|
|
31
|
+
},
|
|
32
|
+
async ({ boardId, memberId }) => {
|
|
33
|
+
try {
|
|
34
|
+
const result = await client.get(
|
|
35
|
+
`/api/boards/${boardId}/members/${memberId}`,
|
|
36
|
+
);
|
|
37
|
+
return formatResult({ data: result.data });
|
|
38
|
+
} catch (error) {
|
|
39
|
+
return handleToolError(error);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
};
|