@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
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1866 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// src/index.ts
|
|
5
|
+
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
6
|
+
|
|
7
|
+
// src/config.ts
|
|
8
|
+
var ConfigError = class extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "ConfigError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
function loadConfig() {
|
|
15
|
+
const errors = [];
|
|
16
|
+
const apiBaseUrl = process.env.IMBOARD_API_BASE_URL?.trim();
|
|
17
|
+
const apiToken = process.env.IMBOARD_API_TOKEN?.trim();
|
|
18
|
+
if (!apiBaseUrl) {
|
|
19
|
+
errors.push("IMBOARD_API_BASE_URL is required");
|
|
20
|
+
}
|
|
21
|
+
if (!apiToken) {
|
|
22
|
+
errors.push("IMBOARD_API_TOKEN is required");
|
|
23
|
+
}
|
|
24
|
+
if (apiBaseUrl && !isValidUrl(apiBaseUrl)) {
|
|
25
|
+
errors.push(`IMBOARD_API_BASE_URL is not a valid URL: ${apiBaseUrl}`);
|
|
26
|
+
}
|
|
27
|
+
if (apiBaseUrl && isValidUrl(apiBaseUrl) && isPrivateAddress(apiBaseUrl)) {
|
|
28
|
+
errors.push("IMBOARD_API_BASE_URL must not point to a private/internal address");
|
|
29
|
+
}
|
|
30
|
+
if (errors.length > 0) {
|
|
31
|
+
throw new ConfigError(
|
|
32
|
+
`Configuration errors:
|
|
33
|
+
- ${errors.join("\n - ")}`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
apiBaseUrl,
|
|
38
|
+
apiToken
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function isValidUrl(value) {
|
|
42
|
+
try {
|
|
43
|
+
const url = new URL(value);
|
|
44
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
var PRIVATE_HOSTNAMES = ["localhost", "127.0.0.1", "0.0.0.0", "169.254.169.254", "[::1]"];
|
|
50
|
+
var PRIVATE_IP_PATTERN = /^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.)/;
|
|
51
|
+
function isPrivateAddress(value) {
|
|
52
|
+
const { hostname } = new URL(value);
|
|
53
|
+
return PRIVATE_HOSTNAMES.includes(hostname) || PRIVATE_IP_PATTERN.test(hostname);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/server.ts
|
|
57
|
+
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
58
|
+
|
|
59
|
+
// src/utils/redact.ts
|
|
60
|
+
var TOKEN_PATTERN = /\b([a-zA-Z0-9_-]{8})[a-zA-Z0-9_-]{24,}\b/g;
|
|
61
|
+
var BEARER_PATTERN = /(Bearer\s+)[a-zA-Z0-9_.\-/+=]{8,}/gi;
|
|
62
|
+
function redact(value) {
|
|
63
|
+
return value.replace(BEARER_PATTERN, "$1[REDACTED]").replace(TOKEN_PATTERN, "$1***");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/utils/logging.ts
|
|
67
|
+
var LOG_LEVELS = {
|
|
68
|
+
debug: 0,
|
|
69
|
+
info: 1,
|
|
70
|
+
warn: 2,
|
|
71
|
+
error: 3
|
|
72
|
+
};
|
|
73
|
+
var minLevel = "info";
|
|
74
|
+
function setLogLevel(level) {
|
|
75
|
+
minLevel = level;
|
|
76
|
+
}
|
|
77
|
+
function shouldLog(level) {
|
|
78
|
+
return LOG_LEVELS[level] >= LOG_LEVELS[minLevel];
|
|
79
|
+
}
|
|
80
|
+
function formatMessage(level, message, context) {
|
|
81
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
82
|
+
const redacted = redact(message);
|
|
83
|
+
const parts = [`[${timestamp}] [${level.toUpperCase()}] ${redacted}`];
|
|
84
|
+
if (context && Object.keys(context).length > 0) {
|
|
85
|
+
const safeContext = redact(JSON.stringify(context));
|
|
86
|
+
parts.push(safeContext);
|
|
87
|
+
}
|
|
88
|
+
return parts.join(" ");
|
|
89
|
+
}
|
|
90
|
+
var logger = {
|
|
91
|
+
debug(message, context) {
|
|
92
|
+
if (shouldLog("debug")) {
|
|
93
|
+
process.stderr.write(formatMessage("debug", message, context) + "\n");
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
info(message, context) {
|
|
97
|
+
if (shouldLog("info")) {
|
|
98
|
+
process.stderr.write(formatMessage("info", message, context) + "\n");
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
warn(message, context) {
|
|
102
|
+
if (shouldLog("warn")) {
|
|
103
|
+
process.stderr.write(formatMessage("warn", message, context) + "\n");
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
error(message, context) {
|
|
107
|
+
if (shouldLog("error")) {
|
|
108
|
+
process.stderr.write(formatMessage("error", message, context) + "\n");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// src/api-client/errors.ts
|
|
114
|
+
var ImboardApiError = class extends Error {
|
|
115
|
+
code;
|
|
116
|
+
status;
|
|
117
|
+
requestId;
|
|
118
|
+
details;
|
|
119
|
+
constructor(payload) {
|
|
120
|
+
super(payload.message);
|
|
121
|
+
this.name = "ImboardApiError";
|
|
122
|
+
this.code = payload.code;
|
|
123
|
+
this.status = payload.status;
|
|
124
|
+
this.requestId = payload.requestId;
|
|
125
|
+
this.details = payload.details;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
var ImboardApiTimeoutError = class extends Error {
|
|
129
|
+
constructor(timeoutMs) {
|
|
130
|
+
super(`Request timed out after ${timeoutMs}ms`);
|
|
131
|
+
this.name = "ImboardApiTimeoutError";
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
var ImboardApiNetworkError = class extends Error {
|
|
135
|
+
constructor(message) {
|
|
136
|
+
super(message);
|
|
137
|
+
this.name = "ImboardApiNetworkError";
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// src/api-client/imboardApiClient.ts
|
|
142
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
143
|
+
var MAX_RETRIES = 2;
|
|
144
|
+
var RETRY_BASE_MS = 500;
|
|
145
|
+
var ImboardApiClient = class {
|
|
146
|
+
baseUrl;
|
|
147
|
+
token;
|
|
148
|
+
timeoutMs;
|
|
149
|
+
constructor(config, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
150
|
+
this.baseUrl = config.apiBaseUrl.replace(/\/+$/, "");
|
|
151
|
+
this.token = config.apiToken;
|
|
152
|
+
this.timeoutMs = timeoutMs;
|
|
153
|
+
}
|
|
154
|
+
async get(path, params) {
|
|
155
|
+
return this.request("GET", path, void 0, params);
|
|
156
|
+
}
|
|
157
|
+
async getCollection(path, params) {
|
|
158
|
+
return this.requestCollection("GET", path, params);
|
|
159
|
+
}
|
|
160
|
+
async post(path, body) {
|
|
161
|
+
return this.request("POST", path, body);
|
|
162
|
+
}
|
|
163
|
+
async put(path, body) {
|
|
164
|
+
return this.request("PUT", path, body);
|
|
165
|
+
}
|
|
166
|
+
async patch(path, body) {
|
|
167
|
+
return this.request("PATCH", path, body);
|
|
168
|
+
}
|
|
169
|
+
async delete(path) {
|
|
170
|
+
return this.request("DELETE", path);
|
|
171
|
+
}
|
|
172
|
+
async request(method, path, body, params) {
|
|
173
|
+
const response = await this.executeWithRetry(method, path, body, params);
|
|
174
|
+
const requestId = response.headers.get("x-request-id");
|
|
175
|
+
const json = await response.json();
|
|
176
|
+
if (!json || typeof json !== "object" || !("data" in json)) {
|
|
177
|
+
throw new ImboardApiError({
|
|
178
|
+
code: "INTERNAL_ERROR",
|
|
179
|
+
message: 'Response body missing "data" field',
|
|
180
|
+
status: response.status,
|
|
181
|
+
requestId,
|
|
182
|
+
details: null
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
data: json.data,
|
|
187
|
+
requestId
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
async requestCollection(method, path, params) {
|
|
191
|
+
const response = await this.executeWithRetry(method, path, void 0, params);
|
|
192
|
+
const requestId = response.headers.get("x-request-id");
|
|
193
|
+
const json = await response.json();
|
|
194
|
+
if (!json || typeof json !== "object" || !("data" in json) || !("meta" in json)) {
|
|
195
|
+
throw new ImboardApiError({
|
|
196
|
+
code: "INTERNAL_ERROR",
|
|
197
|
+
message: 'Response body missing "data" or "meta" field',
|
|
198
|
+
status: response.status,
|
|
199
|
+
requestId,
|
|
200
|
+
details: null
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
data: json.data,
|
|
205
|
+
meta: json.meta,
|
|
206
|
+
requestId
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
async executeWithRetry(method, path, body, params) {
|
|
210
|
+
const url = this.buildUrl(path, params);
|
|
211
|
+
const isIdempotent = method === "GET";
|
|
212
|
+
const maxAttempts = isIdempotent ? MAX_RETRIES + 1 : 1;
|
|
213
|
+
let lastError;
|
|
214
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
215
|
+
if (attempt > 0) {
|
|
216
|
+
const delayMs = RETRY_BASE_MS * Math.pow(2, attempt - 1);
|
|
217
|
+
logger.info(`Retrying ${method} ${path} (attempt ${attempt + 1}/${maxAttempts})`, { delayMs });
|
|
218
|
+
await sleep(delayMs);
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
const response = await this.executeSingle(method, url, body);
|
|
222
|
+
if (response.ok) {
|
|
223
|
+
return response;
|
|
224
|
+
}
|
|
225
|
+
const errorBody = await this.tryParseErrorBody(response);
|
|
226
|
+
const requestId = response.headers.get("x-request-id");
|
|
227
|
+
const apiError = new ImboardApiError({
|
|
228
|
+
code: errorBody?.code ?? "INTERNAL_ERROR",
|
|
229
|
+
message: errorBody?.message ?? `HTTP ${response.status}`,
|
|
230
|
+
status: response.status,
|
|
231
|
+
requestId,
|
|
232
|
+
details: errorBody?.details ?? null
|
|
233
|
+
});
|
|
234
|
+
if (isIdempotent && response.status >= 500 && attempt < maxAttempts - 1) {
|
|
235
|
+
logger.warn(`Server error ${response.status} on ${method} ${path}, will retry`, { requestId });
|
|
236
|
+
lastError = apiError;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
throw apiError;
|
|
240
|
+
} catch (error) {
|
|
241
|
+
if (error instanceof ImboardApiError) {
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
if (error instanceof ImboardApiTimeoutError || error instanceof ImboardApiNetworkError) {
|
|
245
|
+
if (isIdempotent && attempt < maxAttempts - 1) {
|
|
246
|
+
lastError = error;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
throw error;
|
|
250
|
+
}
|
|
251
|
+
throw new ImboardApiNetworkError(
|
|
252
|
+
error instanceof Error ? error.message : "Unknown network error"
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
throw lastError ?? new ImboardApiNetworkError("All retries exhausted");
|
|
257
|
+
}
|
|
258
|
+
async executeSingle(method, url, body) {
|
|
259
|
+
const controller = new AbortController();
|
|
260
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
261
|
+
const headers = {
|
|
262
|
+
"Authorization": `Bearer ${this.token}`,
|
|
263
|
+
"Accept": "application/json"
|
|
264
|
+
};
|
|
265
|
+
if (body !== void 0) {
|
|
266
|
+
headers["Content-Type"] = "application/json";
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
const response = await fetch(url, {
|
|
270
|
+
method,
|
|
271
|
+
headers,
|
|
272
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
273
|
+
signal: controller.signal
|
|
274
|
+
});
|
|
275
|
+
return response;
|
|
276
|
+
} catch (error) {
|
|
277
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
278
|
+
throw new ImboardApiTimeoutError(this.timeoutMs);
|
|
279
|
+
}
|
|
280
|
+
throw new ImboardApiNetworkError(
|
|
281
|
+
error instanceof Error ? error.message : "Network request failed"
|
|
282
|
+
);
|
|
283
|
+
} finally {
|
|
284
|
+
clearTimeout(timer);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
buildUrl(path, params) {
|
|
288
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
289
|
+
const url = new URL(`${this.baseUrl}${normalizedPath}`);
|
|
290
|
+
if (params) {
|
|
291
|
+
for (const [key, value] of Object.entries(params)) {
|
|
292
|
+
url.searchParams.set(key, value);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return url.toString();
|
|
296
|
+
}
|
|
297
|
+
async tryParseErrorBody(response) {
|
|
298
|
+
try {
|
|
299
|
+
const text = await response.text();
|
|
300
|
+
const json = JSON.parse(text);
|
|
301
|
+
return json.error;
|
|
302
|
+
} catch {
|
|
303
|
+
return void 0;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
function sleep(ms) {
|
|
308
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// src/tools/shared.ts
|
|
312
|
+
var import_zod = require("zod");
|
|
313
|
+
var objectIdSchema = import_zod.z.string().regex(/^[a-f0-9]{24}$/, "Must be a 24-character hex ID");
|
|
314
|
+
var boardIdParam = objectIdSchema.describe("The board ID");
|
|
315
|
+
var resourceIdParam = objectIdSchema;
|
|
316
|
+
var paginationParams = {
|
|
317
|
+
limit: import_zod.z.number().int().min(1).max(100).optional().describe("Number of results per page (1-100, default 25)"),
|
|
318
|
+
cursor: import_zod.z.string().optional().describe("Pagination cursor from a previous response"),
|
|
319
|
+
sort: import_zod.z.enum(["updatedAt"]).optional().describe("Sort field (default: updatedAt)"),
|
|
320
|
+
order: import_zod.z.enum(["asc", "desc"]).optional().describe("Sort order (default: desc)")
|
|
321
|
+
};
|
|
322
|
+
function buildQueryParams(args, filterKeys) {
|
|
323
|
+
const params = {};
|
|
324
|
+
for (const key of ["limit", "cursor", "sort", "order", ...filterKeys]) {
|
|
325
|
+
const value = args[key];
|
|
326
|
+
if (value !== void 0 && value !== null) {
|
|
327
|
+
params[key] = String(value);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return params;
|
|
331
|
+
}
|
|
332
|
+
function formatResult(data) {
|
|
333
|
+
return {
|
|
334
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function errorResult(code, message, extra) {
|
|
338
|
+
const payload = { error: code, message, ...extra };
|
|
339
|
+
return {
|
|
340
|
+
isError: true,
|
|
341
|
+
content: [{ type: "text", text: JSON.stringify(payload) }]
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
function handleToolError(error) {
|
|
345
|
+
if (error instanceof ImboardApiError) {
|
|
346
|
+
logger.warn(`API error: ${error.code} ${error.status}`, { requestId: error.requestId });
|
|
347
|
+
const message = error.status >= 500 ? "Internal server error" : error.message;
|
|
348
|
+
const extra = { status: error.status };
|
|
349
|
+
if (error.requestId) extra.requestId = error.requestId;
|
|
350
|
+
if (error.details !== void 0 && error.details !== null) extra.details = error.details;
|
|
351
|
+
return errorResult(error.code, message, extra);
|
|
352
|
+
}
|
|
353
|
+
if (error instanceof ImboardApiTimeoutError) {
|
|
354
|
+
logger.warn(`API timeout: ${error.message}`);
|
|
355
|
+
return errorResult("TIMEOUT", error.message);
|
|
356
|
+
}
|
|
357
|
+
if (error instanceof ImboardApiNetworkError) {
|
|
358
|
+
logger.warn(`Network error: ${error.message}`);
|
|
359
|
+
return errorResult("NETWORK_ERROR", error.message);
|
|
360
|
+
}
|
|
361
|
+
logger.error("Unexpected tool error", {
|
|
362
|
+
error: error instanceof Error ? error.message : String(error),
|
|
363
|
+
stack: error instanceof Error ? error.stack : void 0
|
|
364
|
+
});
|
|
365
|
+
return errorResult("INTERNAL_ERROR", "An unexpected error occurred");
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/tools/getMe.tool.ts
|
|
369
|
+
var registerGetMeTool = (server, client) => {
|
|
370
|
+
server.tool(
|
|
371
|
+
"get_me",
|
|
372
|
+
"Returns the currently authenticated imboard user's profile including name, email, and timezone.",
|
|
373
|
+
{},
|
|
374
|
+
async () => {
|
|
375
|
+
try {
|
|
376
|
+
const response = await client.get("/api/me");
|
|
377
|
+
return formatResult({ data: response.data });
|
|
378
|
+
} catch (error) {
|
|
379
|
+
return handleToolError(error);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
);
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
// src/tools/boards.tools.ts
|
|
386
|
+
var import_zod2 = require("zod");
|
|
387
|
+
var registerBoardsTools = (server, client) => {
|
|
388
|
+
server.tool(
|
|
389
|
+
"list_boards",
|
|
390
|
+
"Lists all boards the authenticated user has access to. Returns board name, billing state, and the user's role on each board.",
|
|
391
|
+
{
|
|
392
|
+
...paginationParams,
|
|
393
|
+
sort: import_zod2.z.enum(["updatedAt", "createdAt"]).optional().describe("Sort field (default: updatedAt)")
|
|
394
|
+
},
|
|
395
|
+
async ({ ...rest }) => {
|
|
396
|
+
try {
|
|
397
|
+
const params = buildQueryParams(rest, []);
|
|
398
|
+
const response = await client.getCollection("/api/boards", params);
|
|
399
|
+
return formatResult({ data: response.data, meta: response.meta });
|
|
400
|
+
} catch (error) {
|
|
401
|
+
return handleToolError(error);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
);
|
|
405
|
+
server.tool(
|
|
406
|
+
"get_board",
|
|
407
|
+
"Returns details for a specific board including name, description, billing state, and the authenticated user's role.",
|
|
408
|
+
{
|
|
409
|
+
boardId: boardIdParam
|
|
410
|
+
},
|
|
411
|
+
async ({ boardId }) => {
|
|
412
|
+
try {
|
|
413
|
+
const response = await client.get(`/api/boards/${boardId}`);
|
|
414
|
+
return formatResult({ data: response.data });
|
|
415
|
+
} catch (error) {
|
|
416
|
+
return handleToolError(error);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
server.tool(
|
|
421
|
+
"create_board",
|
|
422
|
+
"Creates a new board. Returns the created board details.",
|
|
423
|
+
{
|
|
424
|
+
name: import_zod2.z.string().describe("Board name"),
|
|
425
|
+
description: import_zod2.z.string().optional().describe("Board description (BlockNote JSON or plain text)")
|
|
426
|
+
},
|
|
427
|
+
async ({ name, description }) => {
|
|
428
|
+
try {
|
|
429
|
+
const body = { name };
|
|
430
|
+
if (description !== void 0) body.description = description;
|
|
431
|
+
const response = await client.post("/api/board", body);
|
|
432
|
+
return formatResult({ data: response.data });
|
|
433
|
+
} catch (error) {
|
|
434
|
+
return handleToolError(error);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
);
|
|
438
|
+
server.tool(
|
|
439
|
+
"update_board",
|
|
440
|
+
"Updates board details (name, description, settings). Requires admin access.",
|
|
441
|
+
{
|
|
442
|
+
boardId: boardIdParam,
|
|
443
|
+
name: import_zod2.z.string().describe("Board name"),
|
|
444
|
+
description: import_zod2.z.string().optional().describe("Board description (BlockNote JSON or plain text)"),
|
|
445
|
+
companyType: import_zod2.z.string().optional().describe("Company type"),
|
|
446
|
+
boardType: import_zod2.z.string().optional().describe("Board type"),
|
|
447
|
+
companyWebsiteUrl: import_zod2.z.string().optional().describe("Company website URL (must include protocol)"),
|
|
448
|
+
defaultTimezone: import_zod2.z.string().optional().describe("Default timezone (IANA format)")
|
|
449
|
+
},
|
|
450
|
+
async ({ boardId, name, description, companyType, boardType, companyWebsiteUrl, defaultTimezone }) => {
|
|
451
|
+
try {
|
|
452
|
+
const body = { name };
|
|
453
|
+
if (description !== void 0) body.description = description;
|
|
454
|
+
if (companyType !== void 0) body.companyType = companyType;
|
|
455
|
+
if (boardType !== void 0) body.boardType = boardType;
|
|
456
|
+
if (companyWebsiteUrl !== void 0) body.companyWebsiteUrl = companyWebsiteUrl;
|
|
457
|
+
if (defaultTimezone !== void 0) body.defaultTimezone = defaultTimezone;
|
|
458
|
+
const response = await client.put(`/api/board/${boardId}`, body);
|
|
459
|
+
return formatResult({ data: response.data });
|
|
460
|
+
} catch (error) {
|
|
461
|
+
return handleToolError(error);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
);
|
|
465
|
+
server.tool(
|
|
466
|
+
"assign_member_roles",
|
|
467
|
+
"Assigns roles, positions, and access types to a board member.",
|
|
468
|
+
{
|
|
469
|
+
boardId: boardIdParam,
|
|
470
|
+
userId: resourceIdParam.describe("The user ID of the board member"),
|
|
471
|
+
set_admin_role: import_zod2.z.enum(["admin", "guest"]).optional().describe("Admin role to assign"),
|
|
472
|
+
set_access_type: import_zod2.z.enum(["collaborator", "viewer"]).optional().describe("Access type to assign"),
|
|
473
|
+
board_position: import_zod2.z.array(import_zod2.z.string()).optional().describe("Board positions to assign"),
|
|
474
|
+
appointingParty: import_zod2.z.string().optional().describe("Name of the appointing party")
|
|
475
|
+
},
|
|
476
|
+
async ({ boardId, userId, ...fields }) => {
|
|
477
|
+
try {
|
|
478
|
+
const body = {};
|
|
479
|
+
if (fields.set_admin_role !== void 0) body.set_admin_role = fields.set_admin_role;
|
|
480
|
+
if (fields.set_access_type !== void 0) body.set_access_type = fields.set_access_type;
|
|
481
|
+
if (fields.board_position !== void 0) body.board_position = fields.board_position;
|
|
482
|
+
if (fields.appointingParty !== void 0) body.appointingParty = fields.appointingParty;
|
|
483
|
+
const response = await client.put(`/api/board/${boardId}/members/${userId}/roles`, body);
|
|
484
|
+
return formatResult({ data: response.data });
|
|
485
|
+
} catch (error) {
|
|
486
|
+
return handleToolError(error);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
);
|
|
490
|
+
server.tool(
|
|
491
|
+
"search_board_documents",
|
|
492
|
+
"Semantic search across all documents in a board. Returns matching document snippets ranked by relevance.",
|
|
493
|
+
{
|
|
494
|
+
boardId: boardIdParam,
|
|
495
|
+
query: import_zod2.z.string().describe("Search query (max 1000 characters)"),
|
|
496
|
+
limit: import_zod2.z.number().int().min(1).max(50).optional().describe("Max results (1-50, default 10)"),
|
|
497
|
+
minScore: import_zod2.z.number().min(0).max(1).optional().describe("Minimum relevance score (0-1)")
|
|
498
|
+
},
|
|
499
|
+
async ({ boardId, query, limit, minScore }) => {
|
|
500
|
+
try {
|
|
501
|
+
const body = { query };
|
|
502
|
+
if (limit !== void 0) body.limit = limit;
|
|
503
|
+
if (minScore !== void 0) body.minScore = minScore;
|
|
504
|
+
const response = await client.post(`/api/board/${boardId}/search`, body);
|
|
505
|
+
return formatResult({ data: response.data });
|
|
506
|
+
} catch (error) {
|
|
507
|
+
return handleToolError(error);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
);
|
|
511
|
+
server.tool(
|
|
512
|
+
"list_board_action_items",
|
|
513
|
+
"Lists action items for a board. Returns open/in-progress items by default.",
|
|
514
|
+
{
|
|
515
|
+
boardId: boardIdParam,
|
|
516
|
+
includeCompleted: import_zod2.z.enum(["true", "false"]).optional().describe("Include completed items (default: false)"),
|
|
517
|
+
status: import_zod2.z.string().optional().describe("Filter by status (open, in_progress, completed, cancelled)")
|
|
518
|
+
},
|
|
519
|
+
async ({ boardId, includeCompleted, status }) => {
|
|
520
|
+
try {
|
|
521
|
+
const params = {};
|
|
522
|
+
if (includeCompleted !== void 0) params.includeCompleted = includeCompleted;
|
|
523
|
+
if (status !== void 0) params.status = status;
|
|
524
|
+
const response = await client.get(`/api/board/${boardId}/action-items`, params);
|
|
525
|
+
return formatResult({ data: response.data });
|
|
526
|
+
} catch (error) {
|
|
527
|
+
return handleToolError(error);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
);
|
|
531
|
+
server.tool(
|
|
532
|
+
"update_board_action_item",
|
|
533
|
+
"Updates an action item status (in_progress, completed, cancelled).",
|
|
534
|
+
{
|
|
535
|
+
boardId: boardIdParam,
|
|
536
|
+
actionItemId: resourceIdParam.describe("The action item ID"),
|
|
537
|
+
status: import_zod2.z.string().describe("New status (open, in_progress, completed, cancelled)")
|
|
538
|
+
},
|
|
539
|
+
async ({ boardId, actionItemId, status }) => {
|
|
540
|
+
try {
|
|
541
|
+
const response = await client.patch(`/api/board/${boardId}/action-items/${actionItemId}`, { status });
|
|
542
|
+
return formatResult({ data: response.data });
|
|
543
|
+
} catch (error) {
|
|
544
|
+
return handleToolError(error);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
);
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
// src/tools/members.tools.ts
|
|
551
|
+
var registerMemberTools = (server, client) => {
|
|
552
|
+
server.tool(
|
|
553
|
+
"list_board_members",
|
|
554
|
+
"Lists members of a board including their roles, access types, and board positions.",
|
|
555
|
+
{
|
|
556
|
+
boardId: boardIdParam,
|
|
557
|
+
...paginationParams
|
|
558
|
+
},
|
|
559
|
+
async ({ boardId, ...rest }) => {
|
|
560
|
+
try {
|
|
561
|
+
const params = buildQueryParams(rest, []);
|
|
562
|
+
const result = await client.getCollection(
|
|
563
|
+
`/api/boards/${boardId}/members`,
|
|
564
|
+
params
|
|
565
|
+
);
|
|
566
|
+
return formatResult({ data: result.data, meta: result.meta });
|
|
567
|
+
} catch (error) {
|
|
568
|
+
return handleToolError(error);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
);
|
|
572
|
+
server.tool(
|
|
573
|
+
"get_board_member",
|
|
574
|
+
"Returns details for a specific board member including their role, access type, and board positions.",
|
|
575
|
+
{
|
|
576
|
+
boardId: boardIdParam,
|
|
577
|
+
memberId: resourceIdParam.describe("The member ID")
|
|
578
|
+
},
|
|
579
|
+
async ({ boardId, memberId }) => {
|
|
580
|
+
try {
|
|
581
|
+
const result = await client.get(
|
|
582
|
+
`/api/boards/${boardId}/members/${memberId}`
|
|
583
|
+
);
|
|
584
|
+
return formatResult({ data: result.data });
|
|
585
|
+
} catch (error) {
|
|
586
|
+
return handleToolError(error);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
);
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
// src/tools/meetings.tools.ts
|
|
593
|
+
var import_zod3 = require("zod");
|
|
594
|
+
var registerMeetingTools = (server, client) => {
|
|
595
|
+
server.tool(
|
|
596
|
+
"list_board_meetings",
|
|
597
|
+
"Lists meetings for a board. Supports filtering by status and date range, and sorting by start time.",
|
|
598
|
+
{
|
|
599
|
+
boardId: boardIdParam,
|
|
600
|
+
...paginationParams,
|
|
601
|
+
sort: import_zod3.z.enum(["updatedAt", "startTime"]).optional().describe("Sort field (default: startTime)"),
|
|
602
|
+
status: import_zod3.z.string().optional().describe("Filter by meeting status"),
|
|
603
|
+
startAfter: import_zod3.z.string().optional().describe("ISO-8601 date \u2014 only meetings starting after this"),
|
|
604
|
+
startBefore: import_zod3.z.string().optional().describe("ISO-8601 date \u2014 only meetings starting before this")
|
|
605
|
+
},
|
|
606
|
+
async ({ boardId, ...rest }) => {
|
|
607
|
+
try {
|
|
608
|
+
const params = buildQueryParams(rest, ["status", "startAfter", "startBefore"]);
|
|
609
|
+
const result = await client.getCollection(
|
|
610
|
+
`/api/boards/${boardId}/meetings`,
|
|
611
|
+
params
|
|
612
|
+
);
|
|
613
|
+
return formatResult({ data: result.data, meta: result.meta });
|
|
614
|
+
} catch (error) {
|
|
615
|
+
return handleToolError(error);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
);
|
|
619
|
+
server.tool(
|
|
620
|
+
"get_meeting",
|
|
621
|
+
"Returns details for a specific meeting including title, status, location, and scheduled time.",
|
|
622
|
+
{
|
|
623
|
+
boardId: boardIdParam,
|
|
624
|
+
meetingId: resourceIdParam.describe("The meeting ID")
|
|
625
|
+
},
|
|
626
|
+
async ({ boardId, meetingId }) => {
|
|
627
|
+
try {
|
|
628
|
+
const result = await client.get(
|
|
629
|
+
`/api/boards/${boardId}/meetings/${meetingId}`
|
|
630
|
+
);
|
|
631
|
+
return formatResult({ data: result.data });
|
|
632
|
+
} catch (error) {
|
|
633
|
+
return handleToolError(error);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
);
|
|
637
|
+
server.tool(
|
|
638
|
+
"create_meeting",
|
|
639
|
+
"Creates a new meeting on a board. Requires write access. The authenticated user is set as the creator.",
|
|
640
|
+
{
|
|
641
|
+
boardId: boardIdParam,
|
|
642
|
+
title: import_zod3.z.string().describe("Meeting title (max 200 characters)"),
|
|
643
|
+
status: import_zod3.z.string().describe("Meeting status: draft, planned, scheduled, completed, or finalized"),
|
|
644
|
+
locationType: import_zod3.z.string().describe("Location type: physical, virtual, or hybrid"),
|
|
645
|
+
startTime: import_zod3.z.string().describe("Meeting start time as ISO-8601 date string"),
|
|
646
|
+
endTime: import_zod3.z.string().describe("Meeting end time as ISO-8601 date string"),
|
|
647
|
+
location: import_zod3.z.string().optional().describe("Physical location (max 255 characters)"),
|
|
648
|
+
virtualMeetingUrl: import_zod3.z.string().optional().describe("Virtual meeting URL (max 255 characters)")
|
|
649
|
+
},
|
|
650
|
+
async ({ boardId, title, status, locationType, startTime, endTime, location, virtualMeetingUrl }) => {
|
|
651
|
+
try {
|
|
652
|
+
const body = {
|
|
653
|
+
title,
|
|
654
|
+
status,
|
|
655
|
+
locationType,
|
|
656
|
+
startTime,
|
|
657
|
+
endTime
|
|
658
|
+
};
|
|
659
|
+
if (location !== void 0) body.location = location;
|
|
660
|
+
if (virtualMeetingUrl !== void 0) body.virtualMeetingUrl = virtualMeetingUrl;
|
|
661
|
+
const result = await client.post(
|
|
662
|
+
`/api/boards/${boardId}/meetings`,
|
|
663
|
+
body
|
|
664
|
+
);
|
|
665
|
+
return formatResult({ data: result.data });
|
|
666
|
+
} catch (error) {
|
|
667
|
+
return handleToolError(error);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
);
|
|
671
|
+
server.tool(
|
|
672
|
+
"update_meeting",
|
|
673
|
+
"Updates an existing meeting. Only the provided fields are changed. Requires write access to the board.",
|
|
674
|
+
{
|
|
675
|
+
boardId: boardIdParam,
|
|
676
|
+
meetingId: resourceIdParam.describe("The meeting ID"),
|
|
677
|
+
title: import_zod3.z.string().optional().describe("Meeting title (max 200 characters)"),
|
|
678
|
+
status: import_zod3.z.string().optional().describe("Meeting status: draft, planned, scheduled, completed, or finalized"),
|
|
679
|
+
locationType: import_zod3.z.string().optional().describe("Location type: physical, virtual, or hybrid"),
|
|
680
|
+
location: import_zod3.z.string().optional().describe("Physical location (max 255 characters)"),
|
|
681
|
+
virtualMeetingUrl: import_zod3.z.string().optional().describe("Virtual meeting URL (max 255 characters)"),
|
|
682
|
+
startTime: import_zod3.z.string().optional().describe("Meeting start time as ISO-8601 date string"),
|
|
683
|
+
endTime: import_zod3.z.string().optional().describe("Meeting end time as ISO-8601 date string")
|
|
684
|
+
},
|
|
685
|
+
async ({ boardId, meetingId, title, status, locationType, location, virtualMeetingUrl, startTime, endTime }) => {
|
|
686
|
+
try {
|
|
687
|
+
const body = {};
|
|
688
|
+
if (title !== void 0) body.title = title;
|
|
689
|
+
if (status !== void 0) body.status = status;
|
|
690
|
+
if (locationType !== void 0) body.locationType = locationType;
|
|
691
|
+
if (location !== void 0) body.location = location;
|
|
692
|
+
if (virtualMeetingUrl !== void 0) body.virtualMeetingUrl = virtualMeetingUrl;
|
|
693
|
+
if (startTime !== void 0) body.startTime = startTime;
|
|
694
|
+
if (endTime !== void 0) body.endTime = endTime;
|
|
695
|
+
const result = await client.patch(
|
|
696
|
+
`/api/boards/${boardId}/meetings/${meetingId}`,
|
|
697
|
+
body
|
|
698
|
+
);
|
|
699
|
+
return formatResult({ data: result.data });
|
|
700
|
+
} catch (error) {
|
|
701
|
+
return handleToolError(error);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
);
|
|
705
|
+
server.tool(
|
|
706
|
+
"delete_meeting",
|
|
707
|
+
"Deletes a meeting from a board.",
|
|
708
|
+
{
|
|
709
|
+
boardId: boardIdParam,
|
|
710
|
+
meetingId: resourceIdParam.describe("The meeting ID")
|
|
711
|
+
},
|
|
712
|
+
async ({ boardId, meetingId }) => {
|
|
713
|
+
try {
|
|
714
|
+
const result = await client.delete(
|
|
715
|
+
`/api/meetings/${boardId}/${meetingId}/`
|
|
716
|
+
);
|
|
717
|
+
return formatResult({ data: result.data });
|
|
718
|
+
} catch (error) {
|
|
719
|
+
return handleToolError(error);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
);
|
|
723
|
+
server.tool(
|
|
724
|
+
"change_meeting_status",
|
|
725
|
+
"Changes meeting status (draft, planned, scheduled, completed, finalized).",
|
|
726
|
+
{
|
|
727
|
+
boardId: boardIdParam,
|
|
728
|
+
meetingId: resourceIdParam.describe("The meeting ID"),
|
|
729
|
+
meetingStatus: import_zod3.z.string().describe("New meeting status: draft, planned, scheduled, completed, or finalized")
|
|
730
|
+
},
|
|
731
|
+
async ({ boardId, meetingId, meetingStatus }) => {
|
|
732
|
+
try {
|
|
733
|
+
const result = await client.put(
|
|
734
|
+
`/api/meetings/${boardId}/${meetingId}/status`,
|
|
735
|
+
{ meetingStatus }
|
|
736
|
+
);
|
|
737
|
+
return formatResult({ data: result.data });
|
|
738
|
+
} catch (error) {
|
|
739
|
+
return handleToolError(error);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
);
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
// src/tools/documents.tools.ts
|
|
746
|
+
var import_zod4 = require("zod");
|
|
747
|
+
var registerDocumentTools = (server, client) => {
|
|
748
|
+
server.tool(
|
|
749
|
+
"list_board_documents",
|
|
750
|
+
"Lists document metadata for a board. Supports filtering by meeting or document type. Does not provide file download in V1.",
|
|
751
|
+
{
|
|
752
|
+
boardId: boardIdParam,
|
|
753
|
+
...paginationParams,
|
|
754
|
+
meetingId: import_zod4.z.string().optional().describe("Filter by meeting ID"),
|
|
755
|
+
documentType: import_zod4.z.string().optional().describe("Filter by document type (e.g. boardResolutions, minutesOfMeetings, capTable)")
|
|
756
|
+
},
|
|
757
|
+
async ({ boardId, ...rest }) => {
|
|
758
|
+
try {
|
|
759
|
+
const params = buildQueryParams(rest, ["meetingId", "documentType"]);
|
|
760
|
+
const result = await client.getCollection(
|
|
761
|
+
`/api/boards/${boardId}/documents`,
|
|
762
|
+
params
|
|
763
|
+
);
|
|
764
|
+
return formatResult({ data: result.data, meta: result.meta });
|
|
765
|
+
} catch (error) {
|
|
766
|
+
return handleToolError(error);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
);
|
|
770
|
+
server.tool(
|
|
771
|
+
"get_document",
|
|
772
|
+
"Returns metadata for a specific document including title, type, and associated meeting. Does not provide file download in V1.",
|
|
773
|
+
{
|
|
774
|
+
boardId: boardIdParam,
|
|
775
|
+
documentId: resourceIdParam.describe("The document ID")
|
|
776
|
+
},
|
|
777
|
+
async ({ boardId, documentId }) => {
|
|
778
|
+
try {
|
|
779
|
+
const result = await client.get(
|
|
780
|
+
`/api/boards/${boardId}/documents/${documentId}`
|
|
781
|
+
);
|
|
782
|
+
return formatResult({ data: result.data });
|
|
783
|
+
} catch (error) {
|
|
784
|
+
return handleToolError(error);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
);
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
// src/tools/documents-write.tools.ts
|
|
791
|
+
var import_zod5 = require("zod");
|
|
792
|
+
var registerDocumentWriteTools = (server, client) => {
|
|
793
|
+
server.tool(
|
|
794
|
+
"create_document",
|
|
795
|
+
"Creates a new document with an initial version. Requires a title and optionally a document type and external file link.",
|
|
796
|
+
{
|
|
797
|
+
boardId: boardIdParam,
|
|
798
|
+
title: import_zod5.z.string().describe("Document title"),
|
|
799
|
+
documentType: import_zod5.z.string().optional().describe("Document type (e.g. boardResolutions, minutesOfMeetings, capTable)"),
|
|
800
|
+
externalFileLink: import_zod5.z.string().optional().describe("HTTPS URL to an external file"),
|
|
801
|
+
meetingIds: import_zod5.z.array(import_zod5.z.string()).optional().describe("Array of meeting IDs to associate with")
|
|
802
|
+
},
|
|
803
|
+
async ({ boardId, title, documentType, externalFileLink, meetingIds }) => {
|
|
804
|
+
try {
|
|
805
|
+
const body = { title };
|
|
806
|
+
if (documentType !== void 0) body.documentType = documentType;
|
|
807
|
+
if (externalFileLink !== void 0) body.externalFileLink = externalFileLink;
|
|
808
|
+
if (meetingIds !== void 0) body.meetingIds = meetingIds;
|
|
809
|
+
const response = await client.post(`/api/documents/${boardId}/`, body);
|
|
810
|
+
return formatResult({ data: response.data });
|
|
811
|
+
} catch (error) {
|
|
812
|
+
return handleToolError(error);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
);
|
|
816
|
+
server.tool(
|
|
817
|
+
"update_document",
|
|
818
|
+
"Updates document details (title, meeting associations, working copy link).",
|
|
819
|
+
{
|
|
820
|
+
boardId: boardIdParam,
|
|
821
|
+
documentId: resourceIdParam.describe("The document ID"),
|
|
822
|
+
title: import_zod5.z.string().optional().describe("New document title"),
|
|
823
|
+
documentType: import_zod5.z.string().optional().describe("Document type"),
|
|
824
|
+
workingCopyLink: import_zod5.z.string().optional().describe("URL to working copy"),
|
|
825
|
+
meetingIds: import_zod5.z.array(import_zod5.z.string()).optional().describe("Array of meeting IDs to associate with")
|
|
826
|
+
},
|
|
827
|
+
async ({ boardId, documentId, title, documentType, workingCopyLink, meetingIds }) => {
|
|
828
|
+
try {
|
|
829
|
+
const body = {};
|
|
830
|
+
if (title !== void 0) body.title = title;
|
|
831
|
+
if (documentType !== void 0) body.documentType = documentType;
|
|
832
|
+
if (workingCopyLink !== void 0) body.workingCopyLink = workingCopyLink;
|
|
833
|
+
if (meetingIds !== void 0) body.meetingIds = meetingIds;
|
|
834
|
+
const response = await client.put(`/api/documents/${boardId}/${documentId}/`, body);
|
|
835
|
+
return formatResult({ data: response.data });
|
|
836
|
+
} catch (error) {
|
|
837
|
+
return handleToolError(error);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
);
|
|
841
|
+
server.tool(
|
|
842
|
+
"update_document_status",
|
|
843
|
+
"Changes document status (draft, review, approved, etc.).",
|
|
844
|
+
{
|
|
845
|
+
boardId: boardIdParam,
|
|
846
|
+
documentId: resourceIdParam.describe("The document ID"),
|
|
847
|
+
documentStatus: import_zod5.z.string().describe("New document status")
|
|
848
|
+
},
|
|
849
|
+
async ({ boardId, documentId, documentStatus }) => {
|
|
850
|
+
try {
|
|
851
|
+
const response = await client.put(`/api/documents/${boardId}/${documentId}/status`, { documentStatus });
|
|
852
|
+
return formatResult({ data: response.data });
|
|
853
|
+
} catch (error) {
|
|
854
|
+
return handleToolError(error);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
);
|
|
858
|
+
server.tool(
|
|
859
|
+
"delete_document",
|
|
860
|
+
"Deletes a document from a board.",
|
|
861
|
+
{
|
|
862
|
+
boardId: boardIdParam,
|
|
863
|
+
documentId: resourceIdParam.describe("The document ID")
|
|
864
|
+
},
|
|
865
|
+
async ({ boardId, documentId }) => {
|
|
866
|
+
try {
|
|
867
|
+
const response = await client.delete(`/api/documents/${boardId}/${documentId}/`);
|
|
868
|
+
return formatResult({ data: response.data });
|
|
869
|
+
} catch (error) {
|
|
870
|
+
return handleToolError(error);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
);
|
|
874
|
+
server.tool(
|
|
875
|
+
"create_document_version",
|
|
876
|
+
"Creates a new version of a document. Requires an external file link (file upload not supported via MCP).",
|
|
877
|
+
{
|
|
878
|
+
boardId: boardIdParam,
|
|
879
|
+
documentId: resourceIdParam.describe("The document ID"),
|
|
880
|
+
externalFileLink: import_zod5.z.string().describe("HTTPS URL to the new version file"),
|
|
881
|
+
notes: import_zod5.z.string().optional().describe("Version notes")
|
|
882
|
+
},
|
|
883
|
+
async ({ boardId, documentId, externalFileLink, notes }) => {
|
|
884
|
+
try {
|
|
885
|
+
const body = { externalFileLink };
|
|
886
|
+
if (notes !== void 0) body.notes = notes;
|
|
887
|
+
const response = await client.post(`/api/documents/${boardId}/${documentId}/version`, body);
|
|
888
|
+
return formatResult({ data: response.data });
|
|
889
|
+
} catch (error) {
|
|
890
|
+
return handleToolError(error);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
);
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
// src/tools/reports.tools.ts
|
|
897
|
+
var import_zod6 = require("zod");
|
|
898
|
+
var registerReportTools = (server, client) => {
|
|
899
|
+
server.tool(
|
|
900
|
+
"list_board_reports",
|
|
901
|
+
"Lists reports for a board. Supports filtering by status.",
|
|
902
|
+
{
|
|
903
|
+
boardId: boardIdParam,
|
|
904
|
+
...paginationParams,
|
|
905
|
+
status: import_zod6.z.enum(["draft", "publishedForReview", "finalized", "archived", "ongoing"]).optional().describe("Filter by report status")
|
|
906
|
+
},
|
|
907
|
+
async ({ boardId, ...rest }) => {
|
|
908
|
+
try {
|
|
909
|
+
const params = buildQueryParams(rest, ["status"]);
|
|
910
|
+
const result = await client.getCollection(
|
|
911
|
+
`/api/boards/${boardId}/reports`,
|
|
912
|
+
params
|
|
913
|
+
);
|
|
914
|
+
return formatResult({ data: result.data, meta: result.meta });
|
|
915
|
+
} catch (error) {
|
|
916
|
+
return handleToolError(error);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
);
|
|
920
|
+
server.tool(
|
|
921
|
+
"get_report",
|
|
922
|
+
"Returns details for a specific board report including title, status, and publication date.",
|
|
923
|
+
{
|
|
924
|
+
boardId: boardIdParam,
|
|
925
|
+
reportId: resourceIdParam.describe("The report ID")
|
|
926
|
+
},
|
|
927
|
+
async ({ boardId, reportId }) => {
|
|
928
|
+
try {
|
|
929
|
+
const result = await client.get(
|
|
930
|
+
`/api/boards/${boardId}/reports/${reportId}`
|
|
931
|
+
);
|
|
932
|
+
return formatResult({ data: result.data });
|
|
933
|
+
} catch (error) {
|
|
934
|
+
return handleToolError(error);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
);
|
|
938
|
+
server.tool(
|
|
939
|
+
"create_report",
|
|
940
|
+
"Creates a new report for a board.",
|
|
941
|
+
{
|
|
942
|
+
boardId: boardIdParam,
|
|
943
|
+
name: import_zod6.z.string().describe("Report name"),
|
|
944
|
+
description: import_zod6.z.string().optional().describe("Report description"),
|
|
945
|
+
meetingIds: import_zod6.z.array(import_zod6.z.string()).optional().describe("Meeting IDs to associate with"),
|
|
946
|
+
periodStart: import_zod6.z.string().optional().describe("Period start date (YYYY-MM-DD)"),
|
|
947
|
+
periodEnd: import_zod6.z.string().optional().describe("Period end date (YYYY-MM-DD)")
|
|
948
|
+
},
|
|
949
|
+
async ({ boardId, name, description, meetingIds, periodStart, periodEnd }) => {
|
|
950
|
+
try {
|
|
951
|
+
const body = { name };
|
|
952
|
+
if (description !== void 0) body.description = description;
|
|
953
|
+
if (meetingIds !== void 0) body.meetingIds = meetingIds;
|
|
954
|
+
if (periodStart !== void 0) body.periodStart = periodStart;
|
|
955
|
+
if (periodEnd !== void 0) body.periodEnd = periodEnd;
|
|
956
|
+
const result = await client.post(`/api/reports/${boardId}`, body);
|
|
957
|
+
return formatResult({ data: result.data });
|
|
958
|
+
} catch (error) {
|
|
959
|
+
return handleToolError(error);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
);
|
|
963
|
+
server.tool(
|
|
964
|
+
"update_report",
|
|
965
|
+
"Updates report details (name, description, status, period dates).",
|
|
966
|
+
{
|
|
967
|
+
boardId: boardIdParam,
|
|
968
|
+
reportId: resourceIdParam.describe("The report ID"),
|
|
969
|
+
name: import_zod6.z.string().optional().describe("Report name"),
|
|
970
|
+
description: import_zod6.z.string().optional().describe("Report description"),
|
|
971
|
+
status: import_zod6.z.string().optional().describe("Report status"),
|
|
972
|
+
meetingIds: import_zod6.z.array(import_zod6.z.string()).optional().describe("Meeting IDs to associate with"),
|
|
973
|
+
periodStart: import_zod6.z.string().optional().describe("Period start date (YYYY-MM-DD)"),
|
|
974
|
+
periodEnd: import_zod6.z.string().optional().describe("Period end date (YYYY-MM-DD)")
|
|
975
|
+
},
|
|
976
|
+
async ({ boardId, reportId, name, description, status, meetingIds, periodStart, periodEnd }) => {
|
|
977
|
+
try {
|
|
978
|
+
const body = {};
|
|
979
|
+
if (name !== void 0) body.name = name;
|
|
980
|
+
if (description !== void 0) body.description = description;
|
|
981
|
+
if (status !== void 0) body.status = status;
|
|
982
|
+
if (meetingIds !== void 0) body.meetingIds = meetingIds;
|
|
983
|
+
if (periodStart !== void 0) body.periodStart = periodStart;
|
|
984
|
+
if (periodEnd !== void 0) body.periodEnd = periodEnd;
|
|
985
|
+
const result = await client.put(`/api/reports/${boardId}/${reportId}`, body);
|
|
986
|
+
return formatResult({ data: result.data });
|
|
987
|
+
} catch (error) {
|
|
988
|
+
return handleToolError(error);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
);
|
|
992
|
+
server.tool(
|
|
993
|
+
"promote_report_status",
|
|
994
|
+
"Promotes report status (draft -> review -> published).",
|
|
995
|
+
{
|
|
996
|
+
boardId: boardIdParam,
|
|
997
|
+
reportId: resourceIdParam.describe("The report ID"),
|
|
998
|
+
status: import_zod6.z.string().describe("Target status (e.g. publishedForReview, finalized)")
|
|
999
|
+
},
|
|
1000
|
+
async ({ boardId, reportId, status }) => {
|
|
1001
|
+
try {
|
|
1002
|
+
const result = await client.put(`/api/reports/${boardId}/${reportId}/promote`, { status });
|
|
1003
|
+
return formatResult({ data: result.data });
|
|
1004
|
+
} catch (error) {
|
|
1005
|
+
return handleToolError(error);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
);
|
|
1009
|
+
server.tool(
|
|
1010
|
+
"create_followup_report",
|
|
1011
|
+
"Creates a follow-up report from an existing report, copying dashboards and settings.",
|
|
1012
|
+
{
|
|
1013
|
+
boardId: boardIdParam,
|
|
1014
|
+
reportId: resourceIdParam.describe("The source report ID"),
|
|
1015
|
+
name: import_zod6.z.string().describe("Name for the follow-up report"),
|
|
1016
|
+
description: import_zod6.z.string().optional().describe("Report description"),
|
|
1017
|
+
meetingIds: import_zod6.z.array(import_zod6.z.string()).optional().describe("Meeting IDs to associate with"),
|
|
1018
|
+
periodStart: import_zod6.z.string().optional().describe("Period start date (YYYY-MM-DD)"),
|
|
1019
|
+
periodEnd: import_zod6.z.string().optional().describe("Period end date (YYYY-MM-DD)")
|
|
1020
|
+
},
|
|
1021
|
+
async ({ boardId, reportId, name, description, meetingIds, periodStart, periodEnd }) => {
|
|
1022
|
+
try {
|
|
1023
|
+
const body = { name };
|
|
1024
|
+
if (description !== void 0) body.description = description;
|
|
1025
|
+
if (meetingIds !== void 0) body.meetingIds = meetingIds;
|
|
1026
|
+
if (periodStart !== void 0) body.periodStart = periodStart;
|
|
1027
|
+
if (periodEnd !== void 0) body.periodEnd = periodEnd;
|
|
1028
|
+
const result = await client.post(`/api/reports/${boardId}/${reportId}/followup`, body);
|
|
1029
|
+
return formatResult({ data: result.data });
|
|
1030
|
+
} catch (error) {
|
|
1031
|
+
return handleToolError(error);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
);
|
|
1035
|
+
server.tool(
|
|
1036
|
+
"create_dashboard",
|
|
1037
|
+
"Creates a custom dashboard in a report.",
|
|
1038
|
+
{
|
|
1039
|
+
boardId: boardIdParam,
|
|
1040
|
+
reportId: resourceIdParam.describe("The report ID"),
|
|
1041
|
+
customName: import_zod6.z.string().describe("Dashboard name (1-100 characters)"),
|
|
1042
|
+
displayOrder: import_zod6.z.number().optional().describe("Display order position")
|
|
1043
|
+
},
|
|
1044
|
+
async ({ boardId, reportId, customName, displayOrder }) => {
|
|
1045
|
+
try {
|
|
1046
|
+
const body = { customName };
|
|
1047
|
+
if (displayOrder !== void 0) body.displayOrder = displayOrder;
|
|
1048
|
+
const result = await client.post(`/api/reports/${boardId}/${reportId}/dashboards`, body);
|
|
1049
|
+
return formatResult({ data: result.data });
|
|
1050
|
+
} catch (error) {
|
|
1051
|
+
return handleToolError(error);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
);
|
|
1055
|
+
server.tool(
|
|
1056
|
+
"create_dashboard_version",
|
|
1057
|
+
"Creates a new version of a dashboard with updated content.",
|
|
1058
|
+
{
|
|
1059
|
+
boardId: boardIdParam,
|
|
1060
|
+
reportId: resourceIdParam.describe("The report ID"),
|
|
1061
|
+
dashboardId: resourceIdParam.describe("The dashboard ID"),
|
|
1062
|
+
content: import_zod6.z.record(import_zod6.z.string(), import_zod6.z.unknown()).describe("Dashboard content object (must include _type field)"),
|
|
1063
|
+
notes: import_zod6.z.array(import_zod6.z.string()).optional().describe("Version notes")
|
|
1064
|
+
},
|
|
1065
|
+
async ({ boardId, reportId, dashboardId, content, notes }) => {
|
|
1066
|
+
try {
|
|
1067
|
+
const body = { content };
|
|
1068
|
+
if (notes !== void 0) body.notes = notes;
|
|
1069
|
+
const result = await client.post(
|
|
1070
|
+
`/api/reports/${boardId}/${reportId}/dashboards/${dashboardId}/versions`,
|
|
1071
|
+
body
|
|
1072
|
+
);
|
|
1073
|
+
return formatResult({ data: result.data });
|
|
1074
|
+
} catch (error) {
|
|
1075
|
+
return handleToolError(error);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
);
|
|
1079
|
+
server.tool(
|
|
1080
|
+
"update_dashboard_review_status",
|
|
1081
|
+
"Updates the review status of a dashboard.",
|
|
1082
|
+
{
|
|
1083
|
+
boardId: boardIdParam,
|
|
1084
|
+
reportId: resourceIdParam.describe("The report ID"),
|
|
1085
|
+
dashboardId: resourceIdParam.describe("The dashboard ID"),
|
|
1086
|
+
newStatus: import_zod6.z.string().describe("New review status")
|
|
1087
|
+
},
|
|
1088
|
+
async ({ boardId, reportId, dashboardId, newStatus }) => {
|
|
1089
|
+
try {
|
|
1090
|
+
const result = await client.put(
|
|
1091
|
+
`/api/reports/${boardId}/${reportId}/dashboards/${dashboardId}/review-status`,
|
|
1092
|
+
{ newStatus }
|
|
1093
|
+
);
|
|
1094
|
+
return formatResult({ data: result.data });
|
|
1095
|
+
} catch (error) {
|
|
1096
|
+
return handleToolError(error);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
);
|
|
1100
|
+
server.tool(
|
|
1101
|
+
"get_dashboard_chat",
|
|
1102
|
+
"Gets discussion comments on a dashboard.",
|
|
1103
|
+
{
|
|
1104
|
+
boardId: boardIdParam,
|
|
1105
|
+
reportId: resourceIdParam.describe("The report ID"),
|
|
1106
|
+
dashboardId: resourceIdParam.describe("The dashboard ID"),
|
|
1107
|
+
chatType: import_zod6.z.enum(["internal", "boardFeedback"]).optional().describe("Chat type filter")
|
|
1108
|
+
},
|
|
1109
|
+
async ({ boardId, reportId, dashboardId, chatType }) => {
|
|
1110
|
+
try {
|
|
1111
|
+
const params = {};
|
|
1112
|
+
if (chatType !== void 0) params.chatType = chatType;
|
|
1113
|
+
const result = await client.get(
|
|
1114
|
+
`/api/reports/${boardId}/${reportId}/dashboards/${dashboardId}/chat`,
|
|
1115
|
+
params
|
|
1116
|
+
);
|
|
1117
|
+
return formatResult({ data: result.data });
|
|
1118
|
+
} catch (error) {
|
|
1119
|
+
return handleToolError(error);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
);
|
|
1123
|
+
server.tool(
|
|
1124
|
+
"post_dashboard_chat",
|
|
1125
|
+
"Posts a comment on a dashboard.",
|
|
1126
|
+
{
|
|
1127
|
+
boardId: boardIdParam,
|
|
1128
|
+
reportId: resourceIdParam.describe("The report ID"),
|
|
1129
|
+
dashboardId: resourceIdParam.describe("The dashboard ID"),
|
|
1130
|
+
message: import_zod6.z.string().describe("Comment text (1-5000 characters)"),
|
|
1131
|
+
chatType: import_zod6.z.enum(["internal", "boardFeedback"]).describe("Chat type: internal or boardFeedback"),
|
|
1132
|
+
recipientId: import_zod6.z.string().optional().describe("Recipient user ID (for directed feedback)")
|
|
1133
|
+
},
|
|
1134
|
+
async ({ boardId, reportId, dashboardId, message, chatType, recipientId }) => {
|
|
1135
|
+
try {
|
|
1136
|
+
const body = { message, chatType };
|
|
1137
|
+
if (recipientId !== void 0) body.recipientId = recipientId;
|
|
1138
|
+
const result = await client.post(
|
|
1139
|
+
`/api/reports/${boardId}/${reportId}/dashboards/${dashboardId}/chat`,
|
|
1140
|
+
body
|
|
1141
|
+
);
|
|
1142
|
+
return formatResult({ data: result.data });
|
|
1143
|
+
} catch (error) {
|
|
1144
|
+
return handleToolError(error);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
);
|
|
1148
|
+
server.tool(
|
|
1149
|
+
"get_board_feedback",
|
|
1150
|
+
"Gets board-level feedback on a report.",
|
|
1151
|
+
{
|
|
1152
|
+
boardId: boardIdParam,
|
|
1153
|
+
reportId: resourceIdParam.describe("The report ID"),
|
|
1154
|
+
recipientId: import_zod6.z.string().optional().describe("Filter by recipient user ID")
|
|
1155
|
+
},
|
|
1156
|
+
async ({ boardId, reportId, recipientId }) => {
|
|
1157
|
+
try {
|
|
1158
|
+
const params = {};
|
|
1159
|
+
if (recipientId !== void 0) params.recipientId = recipientId;
|
|
1160
|
+
const result = await client.get(
|
|
1161
|
+
`/api/reports/${boardId}/${reportId}/board-feedback`,
|
|
1162
|
+
params
|
|
1163
|
+
);
|
|
1164
|
+
return formatResult({ data: result.data });
|
|
1165
|
+
} catch (error) {
|
|
1166
|
+
return handleToolError(error);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
);
|
|
1170
|
+
server.tool(
|
|
1171
|
+
"post_board_feedback",
|
|
1172
|
+
"Posts board-level feedback on a report.",
|
|
1173
|
+
{
|
|
1174
|
+
boardId: boardIdParam,
|
|
1175
|
+
reportId: resourceIdParam.describe("The report ID"),
|
|
1176
|
+
message: import_zod6.z.string().describe("Feedback text (1-5000 characters)"),
|
|
1177
|
+
recipientId: resourceIdParam.describe("Recipient user ID")
|
|
1178
|
+
},
|
|
1179
|
+
async ({ boardId, reportId, message, recipientId }) => {
|
|
1180
|
+
try {
|
|
1181
|
+
const result = await client.post(
|
|
1182
|
+
`/api/reports/${boardId}/${reportId}/board-feedback`,
|
|
1183
|
+
{ message, recipientId }
|
|
1184
|
+
);
|
|
1185
|
+
return formatResult({ data: result.data });
|
|
1186
|
+
} catch (error) {
|
|
1187
|
+
return handleToolError(error);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
);
|
|
1191
|
+
server.tool(
|
|
1192
|
+
"trigger_report_audit",
|
|
1193
|
+
"Triggers an AI audit of a report.",
|
|
1194
|
+
{
|
|
1195
|
+
boardId: boardIdParam,
|
|
1196
|
+
reportId: resourceIdParam.describe("The report ID"),
|
|
1197
|
+
includeAiAnalysis: import_zod6.z.boolean().optional().describe("Include AI analysis (default: true)")
|
|
1198
|
+
},
|
|
1199
|
+
async ({ boardId, reportId, includeAiAnalysis }) => {
|
|
1200
|
+
try {
|
|
1201
|
+
const body = {};
|
|
1202
|
+
if (includeAiAnalysis !== void 0) body.includeAiAnalysis = includeAiAnalysis;
|
|
1203
|
+
const result = await client.post(`/api/reports/${boardId}/${reportId}/audit`, body);
|
|
1204
|
+
return formatResult({ data: result.data });
|
|
1205
|
+
} catch (error) {
|
|
1206
|
+
return handleToolError(error);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
);
|
|
1210
|
+
server.tool(
|
|
1211
|
+
"list_report_audits",
|
|
1212
|
+
"Lists audit history for a report.",
|
|
1213
|
+
{
|
|
1214
|
+
boardId: boardIdParam,
|
|
1215
|
+
reportId: resourceIdParam.describe("The report ID")
|
|
1216
|
+
},
|
|
1217
|
+
async ({ boardId, reportId }) => {
|
|
1218
|
+
try {
|
|
1219
|
+
const result = await client.get(`/api/reports/${boardId}/${reportId}/audits`);
|
|
1220
|
+
return formatResult({ data: result.data });
|
|
1221
|
+
} catch (error) {
|
|
1222
|
+
return handleToolError(error);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
);
|
|
1226
|
+
server.tool(
|
|
1227
|
+
"get_report_audit",
|
|
1228
|
+
"Gets details of a specific report audit.",
|
|
1229
|
+
{
|
|
1230
|
+
boardId: boardIdParam,
|
|
1231
|
+
reportId: resourceIdParam.describe("The report ID"),
|
|
1232
|
+
auditId: resourceIdParam.describe("The audit ID")
|
|
1233
|
+
},
|
|
1234
|
+
async ({ boardId, reportId, auditId }) => {
|
|
1235
|
+
try {
|
|
1236
|
+
const result = await client.get(`/api/reports/${boardId}/${reportId}/audits/${auditId}`);
|
|
1237
|
+
return formatResult({ data: result.data });
|
|
1238
|
+
} catch (error) {
|
|
1239
|
+
return handleToolError(error);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
);
|
|
1243
|
+
server.tool(
|
|
1244
|
+
"get_historical_metrics",
|
|
1245
|
+
"Gets historical KPI data for a board. Supports filtering by period type, date range, and number of periods.",
|
|
1246
|
+
{
|
|
1247
|
+
boardId: boardIdParam,
|
|
1248
|
+
periodType: import_zod6.z.enum(["monthly", "quarterly", "yearly"]).optional().describe("Period type (default: monthly)"),
|
|
1249
|
+
periods: import_zod6.z.number().int().min(1).max(48).optional().describe("Number of periods (1-48)"),
|
|
1250
|
+
startDate: import_zod6.z.string().optional().describe("Start date (ISO 8601)"),
|
|
1251
|
+
endDate: import_zod6.z.string().optional().describe("End date (ISO 8601)"),
|
|
1252
|
+
includeDerived: import_zod6.z.boolean().optional().describe("Include derived metrics")
|
|
1253
|
+
},
|
|
1254
|
+
async ({ boardId, periodType, periods, startDate, endDate, includeDerived }) => {
|
|
1255
|
+
try {
|
|
1256
|
+
const params = {};
|
|
1257
|
+
if (periodType !== void 0) params.periodType = periodType;
|
|
1258
|
+
if (periods !== void 0) params.periods = String(periods);
|
|
1259
|
+
if (startDate !== void 0) params.startDate = startDate;
|
|
1260
|
+
if (endDate !== void 0) params.endDate = endDate;
|
|
1261
|
+
if (includeDerived !== void 0) params.includeDerived = String(includeDerived);
|
|
1262
|
+
const result = await client.get(`/api/boards/${boardId}/historical-metrics`, params);
|
|
1263
|
+
return formatResult({ data: result.data });
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
return handleToolError(error);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
);
|
|
1269
|
+
server.tool(
|
|
1270
|
+
"get_metrics_comparison",
|
|
1271
|
+
"Compares metrics across time periods for a board.",
|
|
1272
|
+
{
|
|
1273
|
+
boardId: boardIdParam,
|
|
1274
|
+
metric: import_zod6.z.string().describe("Metric key to compare"),
|
|
1275
|
+
basePeriod: import_zod6.z.string().describe("Base period (e.g. 2025-01)"),
|
|
1276
|
+
comparisonPeriod: import_zod6.z.string().describe("Comparison period (e.g. 2025-02)"),
|
|
1277
|
+
periodType: import_zod6.z.enum(["monthly", "quarterly", "yearly"]).optional().describe("Period type")
|
|
1278
|
+
},
|
|
1279
|
+
async ({ boardId, metric, basePeriod, comparisonPeriod, periodType }) => {
|
|
1280
|
+
try {
|
|
1281
|
+
const params = { metric, basePeriod, comparisonPeriod };
|
|
1282
|
+
if (periodType !== void 0) params.periodType = periodType;
|
|
1283
|
+
const result = await client.get(`/api/boards/${boardId}/metrics-comparison`, params);
|
|
1284
|
+
return formatResult({ data: result.data });
|
|
1285
|
+
} catch (error) {
|
|
1286
|
+
return handleToolError(error);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
);
|
|
1290
|
+
};
|
|
1291
|
+
|
|
1292
|
+
// src/tools/dashboards.tools.ts
|
|
1293
|
+
var registerDashboardTools = (server, client) => {
|
|
1294
|
+
server.tool(
|
|
1295
|
+
"list_report_dashboards",
|
|
1296
|
+
"Lists dashboards under a specific report. Dashboards are nested under reports in imboard's domain model.",
|
|
1297
|
+
{
|
|
1298
|
+
boardId: boardIdParam,
|
|
1299
|
+
reportId: resourceIdParam.describe("The report ID (required \u2014 dashboards are nested under reports)"),
|
|
1300
|
+
...paginationParams
|
|
1301
|
+
},
|
|
1302
|
+
async ({ boardId, reportId, ...rest }) => {
|
|
1303
|
+
try {
|
|
1304
|
+
const params = buildQueryParams(rest, []);
|
|
1305
|
+
const result = await client.getCollection(
|
|
1306
|
+
`/api/boards/${boardId}/reports/${reportId}/dashboards`,
|
|
1307
|
+
params
|
|
1308
|
+
);
|
|
1309
|
+
return formatResult({ data: result.data, meta: result.meta });
|
|
1310
|
+
} catch (error) {
|
|
1311
|
+
return handleToolError(error);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
);
|
|
1315
|
+
server.tool(
|
|
1316
|
+
"get_dashboard",
|
|
1317
|
+
"Returns details for a specific dashboard including type, title, and associated report.",
|
|
1318
|
+
{
|
|
1319
|
+
boardId: boardIdParam,
|
|
1320
|
+
reportId: resourceIdParam.describe("The report ID"),
|
|
1321
|
+
dashboardId: resourceIdParam.describe("The dashboard ID")
|
|
1322
|
+
},
|
|
1323
|
+
async ({ boardId, reportId, dashboardId }) => {
|
|
1324
|
+
try {
|
|
1325
|
+
const result = await client.get(
|
|
1326
|
+
`/api/boards/${boardId}/reports/${reportId}/dashboards/${dashboardId}`
|
|
1327
|
+
);
|
|
1328
|
+
return formatResult({ data: result.data });
|
|
1329
|
+
} catch (error) {
|
|
1330
|
+
return handleToolError(error);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
);
|
|
1334
|
+
};
|
|
1335
|
+
|
|
1336
|
+
// src/tools/slots.tools.ts
|
|
1337
|
+
var import_zod7 = require("zod");
|
|
1338
|
+
var registerSlotTools = (server, client) => {
|
|
1339
|
+
server.tool(
|
|
1340
|
+
"list_meeting_slots",
|
|
1341
|
+
"Lists proposed time slots with vote tallies for a meeting. Each slot has feedbacks showing how each member voted.",
|
|
1342
|
+
{
|
|
1343
|
+
boardId: boardIdParam,
|
|
1344
|
+
meetingId: resourceIdParam.describe("The meeting ID")
|
|
1345
|
+
},
|
|
1346
|
+
async ({ boardId, meetingId }) => {
|
|
1347
|
+
try {
|
|
1348
|
+
const result = await client.getCollection(
|
|
1349
|
+
`/api/boards/${boardId}/meetings/${meetingId}/slots`
|
|
1350
|
+
);
|
|
1351
|
+
return formatResult({ data: result.data, meta: result.meta });
|
|
1352
|
+
} catch (error) {
|
|
1353
|
+
return handleToolError(error);
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
);
|
|
1357
|
+
server.tool(
|
|
1358
|
+
"create_meeting_slot",
|
|
1359
|
+
'Proposes a new time slot for a meeting. The creator is automatically marked as "ok". All other board members start as "didNotVote".',
|
|
1360
|
+
{
|
|
1361
|
+
boardId: boardIdParam,
|
|
1362
|
+
meetingId: resourceIdParam.describe("The meeting ID"),
|
|
1363
|
+
startTime: import_zod7.z.string().describe('Proposed start time as ISO-8601 UTC string (e.g. "2025-07-15T14:00:00.000Z")'),
|
|
1364
|
+
timezone: import_zod7.z.string().describe('IANA timezone for display (e.g. "America/New_York")'),
|
|
1365
|
+
durationInMinutes: import_zod7.z.number().int().min(15).max(180).optional().describe("Duration in minutes (15-180, default 120)")
|
|
1366
|
+
},
|
|
1367
|
+
async ({ boardId, meetingId, startTime, timezone, durationInMinutes }) => {
|
|
1368
|
+
try {
|
|
1369
|
+
const body = { startTime, timezone };
|
|
1370
|
+
if (durationInMinutes !== void 0) body.durationInMinutes = durationInMinutes;
|
|
1371
|
+
const result = await client.post(
|
|
1372
|
+
`/api/boards/${boardId}/meetings/${meetingId}/slots`,
|
|
1373
|
+
body
|
|
1374
|
+
);
|
|
1375
|
+
return formatResult({ data: result.data });
|
|
1376
|
+
} catch (error) {
|
|
1377
|
+
return handleToolError(error);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
);
|
|
1381
|
+
server.tool(
|
|
1382
|
+
"vote_on_slot",
|
|
1383
|
+
"Casts or updates the authenticated user's vote on a proposed meeting time slot.",
|
|
1384
|
+
{
|
|
1385
|
+
boardId: boardIdParam,
|
|
1386
|
+
meetingId: resourceIdParam.describe("The meeting ID"),
|
|
1387
|
+
slotId: resourceIdParam.describe("The proposed slot ID"),
|
|
1388
|
+
vote: import_zod7.z.enum(["ok", "challenging", "cant"]).describe('Vote: "ok" (can attend), "challenging" (maybe), "cant" (cannot attend)'),
|
|
1389
|
+
comment: import_zod7.z.string().max(500).optional().describe("Optional comment explaining the vote")
|
|
1390
|
+
},
|
|
1391
|
+
async ({ boardId, meetingId, slotId, vote, comment }) => {
|
|
1392
|
+
try {
|
|
1393
|
+
const body = { vote };
|
|
1394
|
+
if (comment !== void 0) body.comment = comment;
|
|
1395
|
+
const result = await client.put(
|
|
1396
|
+
`/api/boards/${boardId}/meetings/${meetingId}/slots/${slotId}/vote`,
|
|
1397
|
+
body
|
|
1398
|
+
);
|
|
1399
|
+
return formatResult({ data: result.data });
|
|
1400
|
+
} catch (error) {
|
|
1401
|
+
return handleToolError(error);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
);
|
|
1405
|
+
server.tool(
|
|
1406
|
+
"delete_meeting_slot",
|
|
1407
|
+
"Removes a proposed time slot from a meeting.",
|
|
1408
|
+
{
|
|
1409
|
+
boardId: boardIdParam,
|
|
1410
|
+
meetingId: resourceIdParam.describe("The meeting ID"),
|
|
1411
|
+
slotId: resourceIdParam.describe("The proposed slot ID")
|
|
1412
|
+
},
|
|
1413
|
+
async ({ boardId, meetingId, slotId }) => {
|
|
1414
|
+
try {
|
|
1415
|
+
const result = await client.delete(
|
|
1416
|
+
`/api/boards/${boardId}/meetings/${meetingId}/slots/${slotId}`
|
|
1417
|
+
);
|
|
1418
|
+
return formatResult({ data: result.data });
|
|
1419
|
+
} catch (error) {
|
|
1420
|
+
return handleToolError(error);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
);
|
|
1424
|
+
server.tool(
|
|
1425
|
+
"confirm_meeting_slot",
|
|
1426
|
+
"Confirms a proposed slot as the meeting's scheduled time. Copies the slot's start time, duration, and timezone to the meeting.",
|
|
1427
|
+
{
|
|
1428
|
+
boardId: boardIdParam,
|
|
1429
|
+
meetingId: resourceIdParam.describe("The meeting ID"),
|
|
1430
|
+
slotId: resourceIdParam.describe("The proposed slot ID to confirm")
|
|
1431
|
+
},
|
|
1432
|
+
async ({ boardId, meetingId, slotId }) => {
|
|
1433
|
+
try {
|
|
1434
|
+
const result = await client.post(
|
|
1435
|
+
`/api/boards/${boardId}/meetings/${meetingId}/slots/${slotId}/confirm`
|
|
1436
|
+
);
|
|
1437
|
+
return formatResult({ data: result.data });
|
|
1438
|
+
} catch (error) {
|
|
1439
|
+
return handleToolError(error);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
);
|
|
1443
|
+
};
|
|
1444
|
+
|
|
1445
|
+
// src/tools/invites.tools.ts
|
|
1446
|
+
var import_zod8 = require("zod");
|
|
1447
|
+
var registerInviteTools = (server, client) => {
|
|
1448
|
+
server.tool(
|
|
1449
|
+
"list_board_invites",
|
|
1450
|
+
"Lists pending invites for a board.",
|
|
1451
|
+
{
|
|
1452
|
+
boardId: boardIdParam
|
|
1453
|
+
},
|
|
1454
|
+
async ({ boardId }) => {
|
|
1455
|
+
try {
|
|
1456
|
+
const response = await client.get(`/api/invite/${boardId}/invites`);
|
|
1457
|
+
return formatResult({ data: response.data });
|
|
1458
|
+
} catch (error) {
|
|
1459
|
+
return handleToolError(error);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
);
|
|
1463
|
+
server.tool(
|
|
1464
|
+
"create_invite",
|
|
1465
|
+
"Invites a user to a board by email. Optionally include a personal message and board positions.",
|
|
1466
|
+
{
|
|
1467
|
+
boardId: boardIdParam,
|
|
1468
|
+
inviteeEmail: import_zod8.z.string().email().describe("Email address of the person to invite"),
|
|
1469
|
+
message: import_zod8.z.string().optional().describe("Optional personal message included in the invite email"),
|
|
1470
|
+
boardPositions: import_zod8.z.array(import_zod8.z.string()).optional().describe("Board positions to assign to the invitee")
|
|
1471
|
+
},
|
|
1472
|
+
async ({ boardId, inviteeEmail, message, boardPositions }) => {
|
|
1473
|
+
try {
|
|
1474
|
+
const body = { inviteeEmail };
|
|
1475
|
+
if (message !== void 0) body.message = message;
|
|
1476
|
+
if (boardPositions !== void 0) body.boardPositions = boardPositions;
|
|
1477
|
+
const response = await client.post(`/api/invite/${boardId}`, body);
|
|
1478
|
+
return formatResult({ data: response.data });
|
|
1479
|
+
} catch (error) {
|
|
1480
|
+
return handleToolError(error);
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
);
|
|
1484
|
+
server.tool(
|
|
1485
|
+
"accept_invite",
|
|
1486
|
+
"Accepts a board invite.",
|
|
1487
|
+
{
|
|
1488
|
+
boardInviteId: resourceIdParam.describe("The invite ID")
|
|
1489
|
+
},
|
|
1490
|
+
async ({ boardInviteId }) => {
|
|
1491
|
+
try {
|
|
1492
|
+
const response = await client.put(`/api/invite/accept/${boardInviteId}`);
|
|
1493
|
+
return formatResult({ data: response.data });
|
|
1494
|
+
} catch (error) {
|
|
1495
|
+
return handleToolError(error);
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
);
|
|
1499
|
+
server.tool(
|
|
1500
|
+
"decline_invite",
|
|
1501
|
+
"Declines a board invite.",
|
|
1502
|
+
{
|
|
1503
|
+
boardInviteId: resourceIdParam.describe("The invite ID")
|
|
1504
|
+
},
|
|
1505
|
+
async ({ boardInviteId }) => {
|
|
1506
|
+
try {
|
|
1507
|
+
const response = await client.put(`/api/invite/decline/${boardInviteId}`);
|
|
1508
|
+
return formatResult({ data: response.data });
|
|
1509
|
+
} catch (error) {
|
|
1510
|
+
return handleToolError(error);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
);
|
|
1514
|
+
server.tool(
|
|
1515
|
+
"retract_invite",
|
|
1516
|
+
"Retracts a sent board invite.",
|
|
1517
|
+
{
|
|
1518
|
+
boardInviteId: resourceIdParam.describe("The invite ID")
|
|
1519
|
+
},
|
|
1520
|
+
async ({ boardInviteId }) => {
|
|
1521
|
+
try {
|
|
1522
|
+
const response = await client.put(`/api/invite/retract/${boardInviteId}`);
|
|
1523
|
+
return formatResult({ data: response.data });
|
|
1524
|
+
} catch (error) {
|
|
1525
|
+
return handleToolError(error);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
);
|
|
1529
|
+
};
|
|
1530
|
+
|
|
1531
|
+
// src/tools/notifications.tools.ts
|
|
1532
|
+
var registerNotificationTools = (server, client) => {
|
|
1533
|
+
server.tool(
|
|
1534
|
+
"list_notifications",
|
|
1535
|
+
"Lists notifications for the authenticated user.",
|
|
1536
|
+
{},
|
|
1537
|
+
async () => {
|
|
1538
|
+
try {
|
|
1539
|
+
const response = await client.get("/api/notification/");
|
|
1540
|
+
return formatResult({ data: response.data });
|
|
1541
|
+
} catch (error) {
|
|
1542
|
+
return handleToolError(error);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
);
|
|
1546
|
+
server.tool(
|
|
1547
|
+
"mark_all_notifications_read",
|
|
1548
|
+
"Marks all notifications as read.",
|
|
1549
|
+
{},
|
|
1550
|
+
async () => {
|
|
1551
|
+
try {
|
|
1552
|
+
const response = await client.put("/api/notification/read/all");
|
|
1553
|
+
return formatResult({ data: response.data });
|
|
1554
|
+
} catch (error) {
|
|
1555
|
+
return handleToolError(error);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
);
|
|
1559
|
+
server.tool(
|
|
1560
|
+
"mark_notification_read",
|
|
1561
|
+
"Marks a single notification as read.",
|
|
1562
|
+
{
|
|
1563
|
+
notificationId: resourceIdParam.describe("The notification ID")
|
|
1564
|
+
},
|
|
1565
|
+
async ({ notificationId }) => {
|
|
1566
|
+
try {
|
|
1567
|
+
const response = await client.put(`/api/notification/read/${notificationId}`);
|
|
1568
|
+
return formatResult({ data: response.data });
|
|
1569
|
+
} catch (error) {
|
|
1570
|
+
return handleToolError(error);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
);
|
|
1574
|
+
};
|
|
1575
|
+
|
|
1576
|
+
// src/tools/action-items.tools.ts
|
|
1577
|
+
var import_zod9 = require("zod");
|
|
1578
|
+
var registerActionItemTools = (server, client) => {
|
|
1579
|
+
server.tool(
|
|
1580
|
+
"list_my_action_items",
|
|
1581
|
+
"Lists action items assigned to the authenticated user across all boards.",
|
|
1582
|
+
{
|
|
1583
|
+
includeCompleted: import_zod9.z.enum(["true", "false"]).optional().describe("Include completed items (default: false)"),
|
|
1584
|
+
status: import_zod9.z.string().optional().describe("Filter by status (open, in_progress, completed, cancelled)")
|
|
1585
|
+
},
|
|
1586
|
+
async ({ includeCompleted, status }) => {
|
|
1587
|
+
try {
|
|
1588
|
+
const params = {};
|
|
1589
|
+
if (includeCompleted !== void 0) params.includeCompleted = includeCompleted;
|
|
1590
|
+
if (status !== void 0) params.status = status;
|
|
1591
|
+
const response = await client.get("/api/action-items/my", params);
|
|
1592
|
+
return formatResult({ data: response.data });
|
|
1593
|
+
} catch (error) {
|
|
1594
|
+
return handleToolError(error);
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
);
|
|
1598
|
+
server.tool(
|
|
1599
|
+
"update_action_item_status",
|
|
1600
|
+
"Updates an action item status (in_progress, completed, cancelled).",
|
|
1601
|
+
{
|
|
1602
|
+
actionItemId: resourceIdParam.describe("The action item ID"),
|
|
1603
|
+
status: import_zod9.z.string().describe("New status (open, in_progress, completed, cancelled)")
|
|
1604
|
+
},
|
|
1605
|
+
async ({ actionItemId, status }) => {
|
|
1606
|
+
try {
|
|
1607
|
+
const response = await client.patch(`/api/action-items/${actionItemId}/status`, { status });
|
|
1608
|
+
return formatResult({ data: response.data });
|
|
1609
|
+
} catch (error) {
|
|
1610
|
+
return handleToolError(error);
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
);
|
|
1614
|
+
};
|
|
1615
|
+
|
|
1616
|
+
// src/tools/user.tools.ts
|
|
1617
|
+
var registerUserTools = (server, client) => {
|
|
1618
|
+
server.tool(
|
|
1619
|
+
"list_my_boards",
|
|
1620
|
+
"Lists all board memberships and pending invites for the authenticated user.",
|
|
1621
|
+
{},
|
|
1622
|
+
async () => {
|
|
1623
|
+
try {
|
|
1624
|
+
const response = await client.get("/api/user/allboards");
|
|
1625
|
+
return formatResult({ data: response.data });
|
|
1626
|
+
} catch (error) {
|
|
1627
|
+
return handleToolError(error);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
);
|
|
1631
|
+
server.tool(
|
|
1632
|
+
"get_storage_usage",
|
|
1633
|
+
"Returns storage usage for unassigned email attachments.",
|
|
1634
|
+
{},
|
|
1635
|
+
async () => {
|
|
1636
|
+
try {
|
|
1637
|
+
const response = await client.get("/api/user/storage");
|
|
1638
|
+
return formatResult({ data: response.data });
|
|
1639
|
+
} catch (error) {
|
|
1640
|
+
return handleToolError(error);
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
);
|
|
1644
|
+
};
|
|
1645
|
+
|
|
1646
|
+
// src/tools/supporting.tools.ts
|
|
1647
|
+
var import_zod10 = require("zod");
|
|
1648
|
+
var registerSupportingTools = (server, client) => {
|
|
1649
|
+
server.tool(
|
|
1650
|
+
"get_board_audit_log",
|
|
1651
|
+
"Gets the audit trail for a board. Returns a chronological list of actions taken on the board.",
|
|
1652
|
+
{
|
|
1653
|
+
boardId: boardIdParam
|
|
1654
|
+
},
|
|
1655
|
+
async ({ boardId }) => {
|
|
1656
|
+
try {
|
|
1657
|
+
const response = await client.get(`/api/auditlog/${boardId}`);
|
|
1658
|
+
return formatResult({ data: response.data });
|
|
1659
|
+
} catch (error) {
|
|
1660
|
+
return handleToolError(error);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
);
|
|
1664
|
+
server.tool(
|
|
1665
|
+
"get_portfolio_data",
|
|
1666
|
+
"Gets portfolio data aggregated across all boards. Useful for cross-board analytics.",
|
|
1667
|
+
{
|
|
1668
|
+
getOnlyMyPortfolio: import_zod10.z.enum(["true", "false"]).optional().describe("Only include boards where user is a member (default: false)"),
|
|
1669
|
+
includeArchived: import_zod10.z.enum(["true", "false"]).optional().describe("Include archived boards (default: false)")
|
|
1670
|
+
},
|
|
1671
|
+
async ({ getOnlyMyPortfolio, includeArchived }) => {
|
|
1672
|
+
try {
|
|
1673
|
+
const params = {};
|
|
1674
|
+
if (getOnlyMyPortfolio !== void 0) params.getOnlyMyPortfolio = getOnlyMyPortfolio;
|
|
1675
|
+
if (includeArchived !== void 0) params.includeArchived = includeArchived;
|
|
1676
|
+
const response = await client.get("/api/portfolio/data", params);
|
|
1677
|
+
return formatResult({ data: response.data });
|
|
1678
|
+
} catch (error) {
|
|
1679
|
+
return handleToolError(error);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
);
|
|
1683
|
+
server.tool(
|
|
1684
|
+
"get_fx_rates",
|
|
1685
|
+
"Gets current foreign exchange rates. Optionally specify base currency, targets, and date.",
|
|
1686
|
+
{
|
|
1687
|
+
base: import_zod10.z.string().length(3).optional().describe("Base currency (3-letter ISO 4217 code, e.g. USD)"),
|
|
1688
|
+
targets: import_zod10.z.array(import_zod10.z.string().length(3)).optional().describe("Target currencies to convert to"),
|
|
1689
|
+
date: import_zod10.z.string().optional().describe("Historical date in YYYY-MM-DD format")
|
|
1690
|
+
},
|
|
1691
|
+
async ({ base, targets, date }) => {
|
|
1692
|
+
try {
|
|
1693
|
+
const params = {};
|
|
1694
|
+
if (base !== void 0) params.base = base;
|
|
1695
|
+
if (targets !== void 0) params["targets[]"] = targets.join(",");
|
|
1696
|
+
if (date !== void 0) params.date = date;
|
|
1697
|
+
const response = await client.get("/api/fx/rates", params);
|
|
1698
|
+
return formatResult({ data: response.data });
|
|
1699
|
+
} catch (error) {
|
|
1700
|
+
return handleToolError(error);
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
);
|
|
1704
|
+
server.tool(
|
|
1705
|
+
"list_unassigned_attachments",
|
|
1706
|
+
"Lists email attachments awaiting assignment to documents.",
|
|
1707
|
+
{},
|
|
1708
|
+
async () => {
|
|
1709
|
+
try {
|
|
1710
|
+
const response = await client.get("/api/unassigned-emails/");
|
|
1711
|
+
return formatResult({ data: response.data });
|
|
1712
|
+
} catch (error) {
|
|
1713
|
+
return handleToolError(error);
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
);
|
|
1717
|
+
server.tool(
|
|
1718
|
+
"reject_attachment",
|
|
1719
|
+
"Rejects an unassigned email attachment.",
|
|
1720
|
+
{
|
|
1721
|
+
attachmentId: resourceIdParam.describe("The attachment ID")
|
|
1722
|
+
},
|
|
1723
|
+
async ({ attachmentId }) => {
|
|
1724
|
+
try {
|
|
1725
|
+
const response = await client.put(`/api/unassigned-emails/attachment/${attachmentId}`);
|
|
1726
|
+
return formatResult({ data: response.data });
|
|
1727
|
+
} catch (error) {
|
|
1728
|
+
return handleToolError(error);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
);
|
|
1732
|
+
};
|
|
1733
|
+
|
|
1734
|
+
// src/resources/docs.resources.ts
|
|
1735
|
+
var DOCS_BASE_URL = "https://docs.imboard.ai";
|
|
1736
|
+
var STATIC_RESOURCES = [
|
|
1737
|
+
{
|
|
1738
|
+
uri: `${DOCS_BASE_URL}/openapi.json`,
|
|
1739
|
+
name: "OpenAPI Specification",
|
|
1740
|
+
description: "Complete OpenAPI 3.1 spec for the imboard REST API \u2014 all endpoints, schemas, and examples",
|
|
1741
|
+
mimeType: "application/json"
|
|
1742
|
+
},
|
|
1743
|
+
{
|
|
1744
|
+
uri: `${DOCS_BASE_URL}/llms.txt`,
|
|
1745
|
+
name: "LLMs.txt Index",
|
|
1746
|
+
description: "Structured index of all documentation pages \u2014 optimized for LLM consumption",
|
|
1747
|
+
mimeType: "text/plain"
|
|
1748
|
+
},
|
|
1749
|
+
{
|
|
1750
|
+
uri: `${DOCS_BASE_URL}/llms-full.txt`,
|
|
1751
|
+
name: "Full Documentation (LLM)",
|
|
1752
|
+
description: "Complete documentation content in a single text file \u2014 all guides, references, and examples",
|
|
1753
|
+
mimeType: "text/plain"
|
|
1754
|
+
},
|
|
1755
|
+
{
|
|
1756
|
+
uri: `${DOCS_BASE_URL}/api/schemas`,
|
|
1757
|
+
name: "Schema Index",
|
|
1758
|
+
description: "JSON index of all resource schemas (board, meeting, document, report, dashboard)",
|
|
1759
|
+
mimeType: "application/json"
|
|
1760
|
+
},
|
|
1761
|
+
{
|
|
1762
|
+
uri: `${DOCS_BASE_URL}/api/recipes`,
|
|
1763
|
+
name: "Recipe Index",
|
|
1764
|
+
description: "JSON index of available dossier recipes (financial-update, sales-pipeline-update)",
|
|
1765
|
+
mimeType: "application/json"
|
|
1766
|
+
}
|
|
1767
|
+
];
|
|
1768
|
+
function registerDocsResources(server) {
|
|
1769
|
+
for (const resource of STATIC_RESOURCES) {
|
|
1770
|
+
server.resource(resource.name, resource.uri, {
|
|
1771
|
+
description: resource.description,
|
|
1772
|
+
mimeType: resource.mimeType
|
|
1773
|
+
}, async () => {
|
|
1774
|
+
try {
|
|
1775
|
+
const response = await fetch(resource.uri);
|
|
1776
|
+
if (!response.ok) {
|
|
1777
|
+
return {
|
|
1778
|
+
contents: [
|
|
1779
|
+
{
|
|
1780
|
+
uri: resource.uri,
|
|
1781
|
+
mimeType: "text/plain",
|
|
1782
|
+
text: `Failed to fetch ${resource.uri}: ${response.status} ${response.statusText}`
|
|
1783
|
+
}
|
|
1784
|
+
]
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
const text = await response.text();
|
|
1788
|
+
return {
|
|
1789
|
+
contents: [
|
|
1790
|
+
{
|
|
1791
|
+
uri: resource.uri,
|
|
1792
|
+
mimeType: resource.mimeType,
|
|
1793
|
+
text
|
|
1794
|
+
}
|
|
1795
|
+
]
|
|
1796
|
+
};
|
|
1797
|
+
} catch (err) {
|
|
1798
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1799
|
+
return {
|
|
1800
|
+
contents: [
|
|
1801
|
+
{
|
|
1802
|
+
uri: resource.uri,
|
|
1803
|
+
mimeType: "text/plain",
|
|
1804
|
+
text: `Error fetching ${resource.uri}: ${message}`
|
|
1805
|
+
}
|
|
1806
|
+
]
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
logger.info(`Registered ${STATIC_RESOURCES.length} docs resources`);
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
// src/server.ts
|
|
1815
|
+
var SERVER_NAME = "imboard-mcp-server";
|
|
1816
|
+
var SERVER_VERSION = "0.1.0";
|
|
1817
|
+
function createServer(config) {
|
|
1818
|
+
logger.info(`Creating ${SERVER_NAME} v${SERVER_VERSION}`);
|
|
1819
|
+
const server = new import_mcp.McpServer({
|
|
1820
|
+
name: SERVER_NAME,
|
|
1821
|
+
version: SERVER_VERSION
|
|
1822
|
+
});
|
|
1823
|
+
const api = new ImboardApiClient(config);
|
|
1824
|
+
registerGetMeTool(server, api);
|
|
1825
|
+
registerBoardsTools(server, api);
|
|
1826
|
+
registerMemberTools(server, api);
|
|
1827
|
+
registerMeetingTools(server, api);
|
|
1828
|
+
registerDocumentTools(server, api);
|
|
1829
|
+
registerDocumentWriteTools(server, api);
|
|
1830
|
+
registerReportTools(server, api);
|
|
1831
|
+
registerDashboardTools(server, api);
|
|
1832
|
+
registerSlotTools(server, api);
|
|
1833
|
+
registerInviteTools(server, api);
|
|
1834
|
+
registerNotificationTools(server, api);
|
|
1835
|
+
registerActionItemTools(server, api);
|
|
1836
|
+
registerUserTools(server, api);
|
|
1837
|
+
registerSupportingTools(server, api);
|
|
1838
|
+
registerDocsResources(server);
|
|
1839
|
+
logger.info("All tools and resources registered");
|
|
1840
|
+
return server;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
// src/index.ts
|
|
1844
|
+
async function main() {
|
|
1845
|
+
if (process.env.LOG_LEVEL) {
|
|
1846
|
+
setLogLevel(process.env.LOG_LEVEL);
|
|
1847
|
+
}
|
|
1848
|
+
logger.info("Starting imboard MCP server...");
|
|
1849
|
+
const config = loadConfig();
|
|
1850
|
+
logger.info("Configuration loaded", { apiBaseUrl: config.apiBaseUrl });
|
|
1851
|
+
const server = createServer(config);
|
|
1852
|
+
const transport = new import_stdio.StdioServerTransport();
|
|
1853
|
+
await server.connect(transport);
|
|
1854
|
+
logger.info("MCP server connected via stdio transport");
|
|
1855
|
+
}
|
|
1856
|
+
main().catch((err) => {
|
|
1857
|
+
if (err instanceof ConfigError) {
|
|
1858
|
+
logger.error(err.message);
|
|
1859
|
+
process.exit(1);
|
|
1860
|
+
}
|
|
1861
|
+
logger.error("Fatal error during startup", {
|
|
1862
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1863
|
+
});
|
|
1864
|
+
process.exit(1);
|
|
1865
|
+
});
|
|
1866
|
+
//# sourceMappingURL=index.cjs.map
|