@speakai/mcp-server 1.1.1 → 1.3.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.
Files changed (3) hide show
  1. package/README.md +250 -80
  2. package/dist/index.js +703 -97
  3. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -119,17 +119,35 @@ function createSpeakClient(options) {
119
119
  timeout: 6e4
120
120
  });
121
121
  }
122
+ function redactValue(value, depth = 0) {
123
+ if (depth > 4) return "[truncated]";
124
+ if (typeof value === "string") {
125
+ return value.length > MAX_STRING_LEN ? value.slice(0, MAX_STRING_LEN) + "\u2026" : value;
126
+ }
127
+ if (Array.isArray(value)) {
128
+ return value.slice(0, 20).map((v) => redactValue(v, depth + 1));
129
+ }
130
+ if (value && typeof value === "object") {
131
+ const out = {};
132
+ for (const [k, v] of Object.entries(value)) {
133
+ out[k] = SENSITIVE_KEY_PATTERN.test(k) ? "[redacted]" : redactValue(v, depth + 1);
134
+ }
135
+ return out;
136
+ }
137
+ return value;
138
+ }
122
139
  function formatAxiosError(error) {
123
140
  if (import_axios.default.isAxiosError(error)) {
124
141
  const status = error.response?.status;
125
142
  const data = error.response?.data;
126
- const message = typeof data === "object" && data !== null ? JSON.stringify(data, null, 2) : String(data ?? error.message);
143
+ const safe = redactValue(data);
144
+ const message = typeof safe === "object" && safe !== null ? JSON.stringify(safe, null, 2) : String(safe ?? error.message);
127
145
  return status ? `HTTP ${status}: ${message}` : `Request failed: ${message}`;
128
146
  }
129
147
  if (error instanceof Error) return error.message;
130
148
  return String(error);
131
149
  }
132
- var import_axios, accessToken, refreshToken, tokenExpiresAt, speakClient;
150
+ var import_axios, accessToken, refreshToken, tokenExpiresAt, speakClient, SENSITIVE_KEY_PATTERN, MAX_STRING_LEN;
133
151
  var init_client = __esm({
134
152
  "src/client.ts"() {
135
153
  "use strict";
@@ -179,6 +197,8 @@ var init_client = __esm({
179
197
  return Promise.reject(error);
180
198
  }
181
199
  );
200
+ SENSITIVE_KEY_PATTERN = /(token|secret|password|cookie|authorization|jwt|apikey|api[_-]?key|bearer|signature)/i;
201
+ MAX_STRING_LEN = 500;
182
202
  }
183
203
  });
184
204
 
@@ -1090,6 +1110,13 @@ function register(server, client) {
1090
1110
  filename: import_zod.z.string().min(1).describe("Original filename including extension"),
1091
1111
  mimeType: import_zod.z.string().describe('MIME type of the file, e.g. "audio/mp4" or "video/mp4"')
1092
1112
  },
1113
+ {
1114
+ title: "Get Signed Upload URL",
1115
+ readOnlyHint: true,
1116
+ destructiveHint: false,
1117
+ idempotentHint: false,
1118
+ openWorldHint: true
1119
+ },
1093
1120
  async ({ isVideo, filename, mimeType }) => {
1094
1121
  try {
1095
1122
  const result = await api.get("/v1/media/upload/signedurl", {
@@ -1127,6 +1154,13 @@ function register(server, client) {
1127
1154
  })
1128
1155
  ).optional().describe("Custom field values to attach to the media")
1129
1156
  },
1157
+ {
1158
+ title: "Upload Media from URL",
1159
+ readOnlyHint: false,
1160
+ destructiveHint: false,
1161
+ idempotentHint: false,
1162
+ openWorldHint: true
1163
+ },
1130
1164
  async (body) => {
1131
1165
  try {
1132
1166
  const result = await api.post("/v1/media/upload", body);
@@ -1145,7 +1179,7 @@ function register(server, client) {
1145
1179
  );
1146
1180
  server.tool(
1147
1181
  "list_media",
1148
- "List and search media files in the workspace with filtering, pagination, and sorting. Use filterName for text search, mediaType to filter by audio/video/text, folderId for folder-specific results, and from/to for date ranges. Returns mediaIds you can pass to get_transcript, get_media_insights, or ask_magic_prompt. For deep full-text search across transcripts, use search_media instead.",
1182
+ "List and search media files in the workspace with filtering, pagination, and sorting. Use filterName for text search, mediaType to filter by audio/video/text, folderId for folder-specific results, and from/to for date ranges. Use the include param to embed additional data (transcripts, speakers, keywords) inline with each result, avoiding N+1 API calls. Returns mediaIds you can pass to get_transcript, get_media_insights, or ask_magic_prompt. For deep full-text search across transcripts, use search_media instead.",
1149
1183
  {
1150
1184
  mediaType: import_zod.z.enum([MediaType.AUDIO, MediaType.VIDEO, MediaType.TEXT]).optional().describe('Filter by media type: "audio", "video", or "text"'),
1151
1185
  page: import_zod.z.number().int().min(0).optional().describe("Page number for pagination (0-based, default: 0)"),
@@ -1156,11 +1190,34 @@ function register(server, client) {
1156
1190
  folderId: import_zod.z.string().optional().describe("Filter media within a specific folder"),
1157
1191
  from: import_zod.z.string().optional().describe("Start date for date range filter (ISO 8601)"),
1158
1192
  to: import_zod.z.string().optional().describe("End date for date range filter (ISO 8601)"),
1159
- isFavorites: import_zod.z.boolean().optional().describe("Filter to only show favorited media")
1193
+ isFavorites: import_zod.z.boolean().optional().describe("Filter to only show favorited media"),
1194
+ include: import_zod.z.array(
1195
+ import_zod.z.enum([
1196
+ "transcription",
1197
+ "keywords",
1198
+ "speakers",
1199
+ "sentiment",
1200
+ "custom",
1201
+ "fields"
1202
+ ])
1203
+ ).optional().describe(
1204
+ "Additional data to include with each media item. Without this, only metadata is returned. Use 'transcription' to include full transcripts inline, 'speakers' for speaker details, 'keywords' for extracted keywords, etc. Avoids N+1 API calls when you need data for multiple files."
1205
+ )
1160
1206
  },
1161
- async (params) => {
1207
+ {
1208
+ title: "List Media Files",
1209
+ readOnlyHint: true,
1210
+ destructiveHint: false,
1211
+ idempotentHint: true,
1212
+ openWorldHint: true
1213
+ },
1214
+ async ({ include, ...params }) => {
1162
1215
  try {
1163
- const result = await api.get("/v1/media", { params });
1216
+ const queryParams = { ...params };
1217
+ if (include?.length) {
1218
+ queryParams.requestTypes = include.join(",");
1219
+ }
1220
+ const result = await api.get("/v1/media", { params: queryParams });
1164
1221
  return {
1165
1222
  content: [
1166
1223
  { type: "text", text: JSON.stringify(result.data, null, 2) }
@@ -1180,6 +1237,13 @@ function register(server, client) {
1180
1237
  {
1181
1238
  mediaId: import_zod.z.string().min(1).describe("Unique identifier of the media file")
1182
1239
  },
1240
+ {
1241
+ title: "Get Media Insights",
1242
+ readOnlyHint: true,
1243
+ destructiveHint: false,
1244
+ idempotentHint: true,
1245
+ openWorldHint: true
1246
+ },
1183
1247
  async ({ mediaId }) => {
1184
1248
  try {
1185
1249
  const result = await api.get(`/v1/media/insight/${mediaId}`);
@@ -1202,6 +1266,13 @@ function register(server, client) {
1202
1266
  {
1203
1267
  mediaId: import_zod.z.string().min(1).describe("Unique identifier of the media file")
1204
1268
  },
1269
+ {
1270
+ title: "Get Transcript",
1271
+ readOnlyHint: true,
1272
+ destructiveHint: false,
1273
+ idempotentHint: true,
1274
+ openWorldHint: true
1275
+ },
1205
1276
  async ({ mediaId }) => {
1206
1277
  try {
1207
1278
  const result = await api.get(`/v1/media/transcript/${mediaId}`);
@@ -1230,6 +1301,13 @@ function register(server, client) {
1230
1301
  })
1231
1302
  ).describe("Array of speaker ID to name mappings")
1232
1303
  },
1304
+ {
1305
+ title: "Rename Transcript Speakers",
1306
+ readOnlyHint: false,
1307
+ destructiveHint: true,
1308
+ idempotentHint: true,
1309
+ openWorldHint: true
1310
+ },
1233
1311
  async ({ mediaId, speakers }) => {
1234
1312
  try {
1235
1313
  const result = await api.put(
@@ -1255,6 +1333,13 @@ function register(server, client) {
1255
1333
  {
1256
1334
  mediaId: import_zod.z.string().min(1).describe("Unique identifier of the media file")
1257
1335
  },
1336
+ {
1337
+ title: "Get Media Status",
1338
+ readOnlyHint: true,
1339
+ destructiveHint: false,
1340
+ idempotentHint: true,
1341
+ openWorldHint: true
1342
+ },
1258
1343
  async ({ mediaId }) => {
1259
1344
  try {
1260
1345
  const result = await api.get(`/v1/media/status/${mediaId}`);
@@ -1284,6 +1369,13 @@ function register(server, client) {
1284
1369
  remark: import_zod.z.string().optional().describe("Internal remark or note"),
1285
1370
  manageBy: import_zod.z.string().optional().describe("User ID to assign management of this media to")
1286
1371
  },
1372
+ {
1373
+ title: "Update Media Metadata",
1374
+ readOnlyHint: false,
1375
+ destructiveHint: false,
1376
+ idempotentHint: true,
1377
+ openWorldHint: true
1378
+ },
1287
1379
  async ({ mediaId, ...body }) => {
1288
1380
  try {
1289
1381
  const result = await api.put(`/v1/media/${mediaId}`, body);
@@ -1306,6 +1398,13 @@ function register(server, client) {
1306
1398
  {
1307
1399
  mediaId: import_zod.z.string().min(1).describe("Unique identifier of the media file to delete")
1308
1400
  },
1401
+ {
1402
+ title: "Delete Media File",
1403
+ readOnlyHint: false,
1404
+ destructiveHint: true,
1405
+ idempotentHint: true,
1406
+ openWorldHint: true
1407
+ },
1309
1408
  async ({ mediaId }) => {
1310
1409
  try {
1311
1410
  const result = await api.delete(`/v1/media/${mediaId}`);
@@ -1328,6 +1427,13 @@ function register(server, client) {
1328
1427
  {
1329
1428
  mediaId: import_zod.z.string().min(1).describe("Unique identifier of the media file")
1330
1429
  },
1430
+ {
1431
+ title: "Get Captions",
1432
+ readOnlyHint: true,
1433
+ destructiveHint: false,
1434
+ idempotentHint: true,
1435
+ openWorldHint: true
1436
+ },
1331
1437
  async ({ mediaId }) => {
1332
1438
  try {
1333
1439
  const result = await api.get(`/v1/media/caption/${mediaId}`);
@@ -1348,6 +1454,13 @@ function register(server, client) {
1348
1454
  "list_supported_languages",
1349
1455
  "List all languages supported for transcription. Use the language codes when uploading media with a specific sourceLanguage.",
1350
1456
  {},
1457
+ {
1458
+ title: "List Supported Languages",
1459
+ readOnlyHint: true,
1460
+ destructiveHint: false,
1461
+ idempotentHint: true,
1462
+ openWorldHint: true
1463
+ },
1351
1464
  async () => {
1352
1465
  try {
1353
1466
  const result = await api.get("/v1/media/supportedLanguages");
@@ -1368,6 +1481,13 @@ function register(server, client) {
1368
1481
  "get_media_statistics",
1369
1482
  "Get workspace-level media statistics \u2014 total counts, processing status breakdown, storage usage, etc.",
1370
1483
  {},
1484
+ {
1485
+ title: "Get Media Statistics",
1486
+ readOnlyHint: true,
1487
+ destructiveHint: false,
1488
+ idempotentHint: true,
1489
+ openWorldHint: true
1490
+ },
1371
1491
  async () => {
1372
1492
  try {
1373
1493
  const result = await api.get("/v1/media/statistics");
@@ -1390,6 +1510,13 @@ function register(server, client) {
1390
1510
  {
1391
1511
  mediaId: import_zod.z.string().min(1).describe("Unique identifier of the media file")
1392
1512
  },
1513
+ {
1514
+ title: "Toggle Media Favorite",
1515
+ readOnlyHint: false,
1516
+ destructiveHint: false,
1517
+ idempotentHint: true,
1518
+ openWorldHint: true
1519
+ },
1393
1520
  async (body) => {
1394
1521
  try {
1395
1522
  const result = await api.post("/v1/media/favorites", body);
@@ -1412,6 +1539,13 @@ function register(server, client) {
1412
1539
  {
1413
1540
  mediaId: import_zod.z.string().min(1).describe("Unique identifier of the media file to re-analyze")
1414
1541
  },
1542
+ {
1543
+ title: "Re-analyze Media",
1544
+ readOnlyHint: false,
1545
+ destructiveHint: false,
1546
+ idempotentHint: false,
1547
+ openWorldHint: true
1548
+ },
1415
1549
  async ({ mediaId }) => {
1416
1550
  try {
1417
1551
  const result = await api.post(`/v1/media/reanalyze/${mediaId}`, {});
@@ -1428,6 +1562,52 @@ function register(server, client) {
1428
1562
  }
1429
1563
  }
1430
1564
  );
1565
+ server.tool(
1566
+ "bulk_update_transcript_speakers",
1567
+ "Update or rename speaker labels across multiple media files in a single operation. Applies the same speaker mappings to every specified media file. Use this instead of calling update_transcript_speakers repeatedly when renaming speakers across a project or folder.",
1568
+ {
1569
+ mediaIds: import_zod.z.array(import_zod.z.string().min(1)).min(1).max(500).describe("Array of media IDs to update speakers for (max 500 per call)"),
1570
+ speakers: import_zod.z.array(
1571
+ import_zod.z.object({
1572
+ id: import_zod.z.string().min(1).describe("Speaker identifier from the transcript"),
1573
+ name: import_zod.z.string().min(1).describe("Display name to assign to the speaker")
1574
+ })
1575
+ ).describe("Array of speaker ID to name mappings to apply to all specified media files")
1576
+ },
1577
+ {
1578
+ title: "Bulk Rename Speakers Across Files",
1579
+ readOnlyHint: false,
1580
+ destructiveHint: true,
1581
+ idempotentHint: true,
1582
+ openWorldHint: true
1583
+ },
1584
+ async ({ mediaIds, speakers }) => {
1585
+ const results = [];
1586
+ for (const mediaId of mediaIds) {
1587
+ try {
1588
+ await api.put(`/v1/media/speakers/${mediaId}`, { speakers });
1589
+ results.push({ mediaId, success: true });
1590
+ } catch (err) {
1591
+ results.push({ mediaId, success: false, error: formatAxiosError(err) });
1592
+ }
1593
+ }
1594
+ const succeeded = results.filter((r) => r.success).length;
1595
+ const failed = results.filter((r) => !r.success).length;
1596
+ return {
1597
+ content: [
1598
+ {
1599
+ type: "text",
1600
+ text: JSON.stringify(
1601
+ { summary: { total: mediaIds.length, succeeded, failed }, results },
1602
+ null,
1603
+ 2
1604
+ )
1605
+ }
1606
+ ],
1607
+ isError: failed === mediaIds.length
1608
+ };
1609
+ }
1610
+ );
1431
1611
  server.tool(
1432
1612
  "bulk_move_media",
1433
1613
  "Move multiple media files to a folder in a single operation. Use this for batch reorganization instead of updating media one by one.",
@@ -1435,6 +1615,13 @@ function register(server, client) {
1435
1615
  folderId: import_zod.z.string().min(1).describe("Target folder ID to move media into"),
1436
1616
  mediaIds: import_zod.z.array(import_zod.z.string().min(1)).min(1).describe("Array of media IDs to move")
1437
1617
  },
1618
+ {
1619
+ title: "Bulk Move Media Files",
1620
+ readOnlyHint: false,
1621
+ destructiveHint: true,
1622
+ idempotentHint: false,
1623
+ openWorldHint: true
1624
+ },
1438
1625
  async (body) => {
1439
1626
  try {
1440
1627
  const result = await api.put("/v1/media/move", body);
@@ -1486,6 +1673,13 @@ function register2(server, client) {
1486
1673
  })
1487
1674
  ).optional().describe("Custom field values to attach to the text note")
1488
1675
  },
1676
+ {
1677
+ title: "Create Text Note",
1678
+ readOnlyHint: false,
1679
+ destructiveHint: false,
1680
+ idempotentHint: false,
1681
+ openWorldHint: true
1682
+ },
1489
1683
  async (body) => {
1490
1684
  try {
1491
1685
  const result = await api.post("/v1/text/create", body);
@@ -1508,6 +1702,13 @@ function register2(server, client) {
1508
1702
  {
1509
1703
  mediaId: import_zod2.z.string().min(1).describe("Unique identifier of the text note")
1510
1704
  },
1705
+ {
1706
+ title: "Get Text Note Insights",
1707
+ readOnlyHint: true,
1708
+ destructiveHint: false,
1709
+ idempotentHint: true,
1710
+ openWorldHint: true
1711
+ },
1511
1712
  async ({ mediaId }) => {
1512
1713
  try {
1513
1714
  const result = await api.get(`/v1/text/insight/${mediaId}`);
@@ -1530,6 +1731,13 @@ function register2(server, client) {
1530
1731
  {
1531
1732
  mediaId: import_zod2.z.string().describe("Unique identifier of the text note to reanalyze")
1532
1733
  },
1734
+ {
1735
+ title: "Re-analyze Text Note",
1736
+ readOnlyHint: false,
1737
+ destructiveHint: false,
1738
+ idempotentHint: false,
1739
+ openWorldHint: true
1740
+ },
1533
1741
  async ({ mediaId }) => {
1534
1742
  try {
1535
1743
  const result = await api.get(`/v1/text/reanalyze/${mediaId}`);
@@ -1557,6 +1765,13 @@ function register2(server, client) {
1557
1765
  folderId: import_zod2.z.string().optional().describe("Move to a different folder"),
1558
1766
  tags: import_zod2.z.string().optional().describe("Updated comma-separated tags")
1559
1767
  },
1768
+ {
1769
+ title: "Update Text Note",
1770
+ readOnlyHint: false,
1771
+ destructiveHint: true,
1772
+ idempotentHint: true,
1773
+ openWorldHint: true
1774
+ },
1560
1775
  async ({ mediaId, ...body }) => {
1561
1776
  try {
1562
1777
  const result = await api.put(
@@ -1606,6 +1821,13 @@ function register3(server, client) {
1606
1821
  isRedacted: import_zod3.z.boolean().optional().describe("Apply PII redaction to export"),
1607
1822
  redactedCategories: import_zod3.z.array(import_zod3.z.string()).optional().describe("Specific categories to redact")
1608
1823
  },
1824
+ {
1825
+ title: "Export Media Transcript",
1826
+ readOnlyHint: true,
1827
+ destructiveHint: false,
1828
+ idempotentHint: true,
1829
+ openWorldHint: true
1830
+ },
1609
1831
  async ({ mediaId, fileType, ...body }) => {
1610
1832
  try {
1611
1833
  const result = await api.post(
@@ -1639,6 +1861,13 @@ function register3(server, client) {
1639
1861
  isMerged: import_zod3.z.boolean().optional().describe("Merge all exports into a single file"),
1640
1862
  folderId: import_zod3.z.string().optional().describe("Folder ID for the merged export")
1641
1863
  },
1864
+ {
1865
+ title: "Export Multiple Media Files",
1866
+ readOnlyHint: true,
1867
+ destructiveHint: false,
1868
+ idempotentHint: true,
1869
+ openWorldHint: true
1870
+ },
1642
1871
  async (body) => {
1643
1872
  try {
1644
1873
  const result = await api.post(
@@ -1679,6 +1908,13 @@ function register4(server, client) {
1679
1908
  "get_all_folder_views",
1680
1909
  "Retrieve all saved views across all folders.",
1681
1910
  {},
1911
+ {
1912
+ title: "Get All Folder Views",
1913
+ readOnlyHint: true,
1914
+ destructiveHint: false,
1915
+ idempotentHint: true,
1916
+ openWorldHint: true
1917
+ },
1682
1918
  async () => {
1683
1919
  try {
1684
1920
  const result = await api.get("/v1/folders/views");
@@ -1701,6 +1937,13 @@ function register4(server, client) {
1701
1937
  {
1702
1938
  folderId: import_zod4.z.string().min(1).describe("Unique identifier of the folder")
1703
1939
  },
1940
+ {
1941
+ title: "Get Folder Views",
1942
+ readOnlyHint: true,
1943
+ destructiveHint: false,
1944
+ idempotentHint: true,
1945
+ openWorldHint: true
1946
+ },
1704
1947
  async ({ folderId }) => {
1705
1948
  try {
1706
1949
  const result = await api.get(`/v1/folders/${folderId}/views`);
@@ -1725,6 +1968,13 @@ function register4(server, client) {
1725
1968
  name: import_zod4.z.string().optional().describe("Display name for the view"),
1726
1969
  filters: import_zod4.z.record(import_zod4.z.unknown()).optional().describe("Filter configuration object")
1727
1970
  },
1971
+ {
1972
+ title: "Create Folder View",
1973
+ readOnlyHint: false,
1974
+ destructiveHint: false,
1975
+ idempotentHint: false,
1976
+ openWorldHint: true
1977
+ },
1728
1978
  async ({ folderId, ...body }) => {
1729
1979
  try {
1730
1980
  const result = await api.post(
@@ -1753,6 +2003,13 @@ function register4(server, client) {
1753
2003
  name: import_zod4.z.string().optional().describe("New display name for the view"),
1754
2004
  filters: import_zod4.z.record(import_zod4.z.unknown()).optional().describe("Updated filter configuration")
1755
2005
  },
2006
+ {
2007
+ title: "Update Folder View",
2008
+ readOnlyHint: false,
2009
+ destructiveHint: false,
2010
+ idempotentHint: true,
2011
+ openWorldHint: true
2012
+ },
1756
2013
  async ({ folderId, viewId, ...body }) => {
1757
2014
  try {
1758
2015
  const result = await api.put(
@@ -1778,6 +2035,13 @@ function register4(server, client) {
1778
2035
  {
1779
2036
  viewId: import_zod4.z.string().min(1).describe("Unique identifier of the view to clone")
1780
2037
  },
2038
+ {
2039
+ title: "Clone Folder View",
2040
+ readOnlyHint: false,
2041
+ destructiveHint: false,
2042
+ idempotentHint: false,
2043
+ openWorldHint: true
2044
+ },
1781
2045
  async (body) => {
1782
2046
  try {
1783
2047
  const result = await api.post("/v1/folders/views/clone", body);
@@ -1802,6 +2066,13 @@ function register4(server, client) {
1802
2066
  pageSize: import_zod4.z.number().int().min(1).max(500).optional().describe("Results per page (default: 20, max: 500)"),
1803
2067
  sortBy: import_zod4.z.string().optional().describe('Sort field and direction, e.g. "createdAt:desc"')
1804
2068
  },
2069
+ {
2070
+ title: "List Folders",
2071
+ readOnlyHint: true,
2072
+ destructiveHint: false,
2073
+ idempotentHint: true,
2074
+ openWorldHint: true
2075
+ },
1805
2076
  async (params) => {
1806
2077
  try {
1807
2078
  const result = await api.get("/v1/folder", { params });
@@ -1824,6 +2095,13 @@ function register4(server, client) {
1824
2095
  {
1825
2096
  folderId: import_zod4.z.string().min(1).describe("Unique identifier of the folder")
1826
2097
  },
2098
+ {
2099
+ title: "Get Folder Info",
2100
+ readOnlyHint: true,
2101
+ destructiveHint: false,
2102
+ idempotentHint: true,
2103
+ openWorldHint: true
2104
+ },
1827
2105
  async ({ folderId }) => {
1828
2106
  try {
1829
2107
  const result = await api.get(`/v1/folder/${folderId}`);
@@ -1847,6 +2125,13 @@ function register4(server, client) {
1847
2125
  name: import_zod4.z.string().min(1).describe("Display name for the new folder"),
1848
2126
  parentFolderId: import_zod4.z.string().optional().describe("ID of the parent folder for nesting")
1849
2127
  },
2128
+ {
2129
+ title: "Create Folder",
2130
+ readOnlyHint: false,
2131
+ destructiveHint: false,
2132
+ idempotentHint: false,
2133
+ openWorldHint: true
2134
+ },
1850
2135
  async (body) => {
1851
2136
  try {
1852
2137
  const result = await api.post("/v1/folder", body);
@@ -1869,6 +2154,13 @@ function register4(server, client) {
1869
2154
  {
1870
2155
  folderId: import_zod4.z.string().min(1).describe("ID of the folder to clone")
1871
2156
  },
2157
+ {
2158
+ title: "Clone Folder",
2159
+ readOnlyHint: false,
2160
+ destructiveHint: false,
2161
+ idempotentHint: false,
2162
+ openWorldHint: true
2163
+ },
1872
2164
  async (body) => {
1873
2165
  try {
1874
2166
  const result = await api.post("/v1/folder/clone", body);
@@ -1892,6 +2184,13 @@ function register4(server, client) {
1892
2184
  folderId: import_zod4.z.string().min(1).describe("Unique identifier of the folder"),
1893
2185
  name: import_zod4.z.string().optional().describe("New display name for the folder")
1894
2186
  },
2187
+ {
2188
+ title: "Update Folder",
2189
+ readOnlyHint: false,
2190
+ destructiveHint: false,
2191
+ idempotentHint: true,
2192
+ openWorldHint: true
2193
+ },
1895
2194
  async ({ folderId, ...body }) => {
1896
2195
  try {
1897
2196
  const result = await api.put(`/v1/folder/${folderId}`, body);
@@ -1914,6 +2213,13 @@ function register4(server, client) {
1914
2213
  {
1915
2214
  folderId: import_zod4.z.string().min(1).describe("Unique identifier of the folder to delete")
1916
2215
  },
2216
+ {
2217
+ title: "Delete Folder",
2218
+ readOnlyHint: false,
2219
+ destructiveHint: true,
2220
+ idempotentHint: true,
2221
+ openWorldHint: true
2222
+ },
1917
2223
  async ({ folderId }) => {
1918
2224
  try {
1919
2225
  const result = await api.delete(`/v1/folder/${folderId}`);
@@ -1953,6 +2259,13 @@ function register5(server, client) {
1953
2259
  {
1954
2260
  token: import_zod5.z.string().min(1).describe("Unique token identifying the recorder")
1955
2261
  },
2262
+ {
2263
+ title: "Check Recorder Status",
2264
+ readOnlyHint: true,
2265
+ destructiveHint: false,
2266
+ idempotentHint: true,
2267
+ openWorldHint: true
2268
+ },
1956
2269
  async ({ token }) => {
1957
2270
  try {
1958
2271
  const result = await api.get(`/v1/recorder/status/${token}`);
@@ -1975,6 +2288,13 @@ function register5(server, client) {
1975
2288
  folderId: import_zod5.z.string().optional().describe("Folder to store recordings in"),
1976
2289
  settings: import_zod5.z.record(import_zod5.z.unknown()).optional().describe("Recorder configuration settings")
1977
2290
  },
2291
+ {
2292
+ title: "Create Recorder",
2293
+ readOnlyHint: false,
2294
+ destructiveHint: false,
2295
+ idempotentHint: false,
2296
+ openWorldHint: true
2297
+ },
1978
2298
  async (body) => {
1979
2299
  try {
1980
2300
  const result = await api.post("/v1/recorder/create", body);
@@ -1997,6 +2317,13 @@ function register5(server, client) {
1997
2317
  pageSize: import_zod5.z.number().int().min(1).max(500).optional().describe("Results per page (default: 20, max: 500)"),
1998
2318
  sortBy: import_zod5.z.string().optional().describe('Sort field, e.g. "createdAt:desc"')
1999
2319
  },
2320
+ {
2321
+ title: "List Recorders",
2322
+ readOnlyHint: true,
2323
+ destructiveHint: false,
2324
+ idempotentHint: true,
2325
+ openWorldHint: true
2326
+ },
2000
2327
  async (params) => {
2001
2328
  try {
2002
2329
  const result = await api.get("/v1/recorder", { params });
@@ -2017,6 +2344,13 @@ function register5(server, client) {
2017
2344
  {
2018
2345
  recorderId: import_zod5.z.string().min(1).describe("ID of the recorder to clone")
2019
2346
  },
2347
+ {
2348
+ title: "Clone Recorder",
2349
+ readOnlyHint: false,
2350
+ destructiveHint: false,
2351
+ idempotentHint: false,
2352
+ openWorldHint: true
2353
+ },
2020
2354
  async (body) => {
2021
2355
  try {
2022
2356
  const result = await api.post("/v1/recorder/clone", body);
@@ -2037,6 +2371,13 @@ function register5(server, client) {
2037
2371
  {
2038
2372
  recorderId: import_zod5.z.string().min(1).describe("Unique identifier of the recorder")
2039
2373
  },
2374
+ {
2375
+ title: "Get Recorder Info",
2376
+ readOnlyHint: true,
2377
+ destructiveHint: false,
2378
+ idempotentHint: true,
2379
+ openWorldHint: true
2380
+ },
2040
2381
  async ({ recorderId }) => {
2041
2382
  try {
2042
2383
  const result = await api.get(`/v1/recorder/${recorderId}`);
@@ -2057,6 +2398,13 @@ function register5(server, client) {
2057
2398
  {
2058
2399
  recorderId: import_zod5.z.string().min(1).describe("Unique identifier of the recorder")
2059
2400
  },
2401
+ {
2402
+ title: "Get Recorder Submissions",
2403
+ readOnlyHint: true,
2404
+ destructiveHint: false,
2405
+ idempotentHint: true,
2406
+ openWorldHint: true
2407
+ },
2060
2408
  async ({ recorderId }) => {
2061
2409
  try {
2062
2410
  const result = await api.get(`/v1/recorder/recordings/${recorderId}`);
@@ -2077,6 +2425,13 @@ function register5(server, client) {
2077
2425
  {
2078
2426
  recorderId: import_zod5.z.string().min(1).describe("Unique identifier of the recorder")
2079
2427
  },
2428
+ {
2429
+ title: "Generate Recorder Share URL",
2430
+ readOnlyHint: true,
2431
+ destructiveHint: false,
2432
+ idempotentHint: true,
2433
+ openWorldHint: true
2434
+ },
2080
2435
  async ({ recorderId }) => {
2081
2436
  try {
2082
2437
  const result = await api.get(`/v1/recorder/url/${recorderId}`);
@@ -2098,6 +2453,13 @@ function register5(server, client) {
2098
2453
  recorderId: import_zod5.z.string().min(1).describe("Unique identifier of the recorder"),
2099
2454
  settings: import_zod5.z.record(import_zod5.z.unknown()).describe("Settings object with updated values")
2100
2455
  },
2456
+ {
2457
+ title: "Update Recorder Settings",
2458
+ readOnlyHint: false,
2459
+ destructiveHint: false,
2460
+ idempotentHint: true,
2461
+ openWorldHint: true
2462
+ },
2101
2463
  async ({ recorderId, settings }) => {
2102
2464
  try {
2103
2465
  const result = await api.put(`/v1/recorder/settings/${recorderId}`, settings);
@@ -2119,6 +2481,13 @@ function register5(server, client) {
2119
2481
  recorderId: import_zod5.z.string().min(1).describe("Unique identifier of the recorder"),
2120
2482
  questions: import_zod5.z.array(import_zod5.z.record(import_zod5.z.unknown())).describe("Array of question objects")
2121
2483
  },
2484
+ {
2485
+ title: "Update Recorder Questions",
2486
+ readOnlyHint: false,
2487
+ destructiveHint: true,
2488
+ idempotentHint: true,
2489
+ openWorldHint: true
2490
+ },
2122
2491
  async ({ recorderId, questions }) => {
2123
2492
  try {
2124
2493
  const result = await api.put(`/v1/recorder/questions/${recorderId}`, { questions });
@@ -2139,6 +2508,13 @@ function register5(server, client) {
2139
2508
  {
2140
2509
  recorderId: import_zod5.z.string().min(1).describe("Unique identifier of the recorder to delete")
2141
2510
  },
2511
+ {
2512
+ title: "Delete Recorder",
2513
+ readOnlyHint: false,
2514
+ destructiveHint: true,
2515
+ idempotentHint: true,
2516
+ openWorldHint: true
2517
+ },
2142
2518
  async ({ recorderId }) => {
2143
2519
  try {
2144
2520
  const result = await api.delete(`/v1/recorder/${recorderId}`);
@@ -2177,6 +2553,13 @@ function register6(server, client) {
2177
2553
  mediaId: import_zod6.z.string().min(1).describe("Unique identifier of the media file"),
2178
2554
  settings: import_zod6.z.record(import_zod6.z.unknown()).optional().describe("Embed configuration settings")
2179
2555
  },
2556
+ {
2557
+ title: "Create Embed Widget",
2558
+ readOnlyHint: false,
2559
+ destructiveHint: false,
2560
+ idempotentHint: false,
2561
+ openWorldHint: true
2562
+ },
2180
2563
  async (body) => {
2181
2564
  try {
2182
2565
  const result = await api.post("/v1/embed", body);
@@ -2198,6 +2581,13 @@ function register6(server, client) {
2198
2581
  embedId: import_zod6.z.string().min(1).describe("Unique identifier of the embed"),
2199
2582
  settings: import_zod6.z.record(import_zod6.z.unknown()).optional().describe("Updated embed settings")
2200
2583
  },
2584
+ {
2585
+ title: "Update Embed Widget",
2586
+ readOnlyHint: false,
2587
+ destructiveHint: false,
2588
+ idempotentHint: true,
2589
+ openWorldHint: true
2590
+ },
2201
2591
  async ({ embedId, ...body }) => {
2202
2592
  try {
2203
2593
  const result = await api.put(`/v1/embed/${embedId}`, body);
@@ -2218,6 +2608,13 @@ function register6(server, client) {
2218
2608
  {
2219
2609
  mediaId: import_zod6.z.string().min(1).describe("Unique identifier of the media file")
2220
2610
  },
2611
+ {
2612
+ title: "Check Embed Exists",
2613
+ readOnlyHint: true,
2614
+ destructiveHint: false,
2615
+ idempotentHint: true,
2616
+ openWorldHint: true
2617
+ },
2221
2618
  async ({ mediaId }) => {
2222
2619
  try {
2223
2620
  const result = await api.get(`/v1/embed/${mediaId}`);
@@ -2238,6 +2635,13 @@ function register6(server, client) {
2238
2635
  {
2239
2636
  mediaId: import_zod6.z.string().min(1).describe("Unique identifier of the media file")
2240
2637
  },
2638
+ {
2639
+ title: "Get Embed Iframe URL",
2640
+ readOnlyHint: true,
2641
+ destructiveHint: false,
2642
+ idempotentHint: true,
2643
+ openWorldHint: true
2644
+ },
2241
2645
  async ({ mediaId }) => {
2242
2646
  try {
2243
2647
  const result = await api.get("/v1/embed/iframe", {
@@ -2295,6 +2699,13 @@ function register7(server, client) {
2295
2699
  endDate: import_zod7.z.string().optional().describe("End date for date range filter (ISO 8601, e.g., '2025-03-31')"),
2296
2700
  isIndividualPrompt: import_zod7.z.boolean().optional().describe("When true, processes each media file separately instead of combining context. Useful for comparing responses across files.")
2297
2701
  },
2702
+ {
2703
+ title: "Ask AI About Your Recordings",
2704
+ readOnlyHint: true,
2705
+ destructiveHint: false,
2706
+ idempotentHint: false,
2707
+ openWorldHint: true
2708
+ },
2298
2709
  async (params) => {
2299
2710
  try {
2300
2711
  const result = await api.post("/v1/prompt", params);
@@ -2316,6 +2727,13 @@ function register7(server, client) {
2316
2727
  promptId: import_zod7.z.string().min(1).describe("ID of the conversation containing the failed message"),
2317
2728
  messageId: import_zod7.z.string().min(1).describe("ID of the specific message to retry")
2318
2729
  },
2730
+ {
2731
+ title: "Retry AI Question",
2732
+ readOnlyHint: true,
2733
+ destructiveHint: false,
2734
+ idempotentHint: false,
2735
+ openWorldHint: true
2736
+ },
2319
2737
  async (body) => {
2320
2738
  try {
2321
2739
  const result = await api.post("/v1/prompt/retry", body);
@@ -2336,6 +2754,13 @@ function register7(server, client) {
2336
2754
  {
2337
2755
  limit: import_zod7.z.number().int().positive().optional().describe("Number of recent conversations to return (default: 10)")
2338
2756
  },
2757
+ {
2758
+ title: "Get Chat History",
2759
+ readOnlyHint: true,
2760
+ destructiveHint: false,
2761
+ idempotentHint: true,
2762
+ openWorldHint: true
2763
+ },
2339
2764
  async ({ limit }) => {
2340
2765
  try {
2341
2766
  const result = await api.get("/v1/prompt/history", {
@@ -2363,6 +2788,13 @@ function register7(server, client) {
2363
2788
  page: import_zod7.z.number().int().min(0).optional().describe("Page number for pagination (0-based, default: 0)"),
2364
2789
  pageSize: import_zod7.z.number().int().min(1).max(500).optional().describe("Results per page (default: 25, max: 500)")
2365
2790
  },
2791
+ {
2792
+ title: "Get Chat Messages",
2793
+ readOnlyHint: true,
2794
+ destructiveHint: false,
2795
+ idempotentHint: true,
2796
+ openWorldHint: true
2797
+ },
2366
2798
  async (params) => {
2367
2799
  try {
2368
2800
  const result = await api.get("/v1/prompt/messages", { params });
@@ -2383,6 +2815,13 @@ function register7(server, client) {
2383
2815
  {
2384
2816
  promptId: import_zod7.z.string().min(1).describe("ID of the message to delete")
2385
2817
  },
2818
+ {
2819
+ title: "Delete Chat Message",
2820
+ readOnlyHint: false,
2821
+ destructiveHint: true,
2822
+ idempotentHint: true,
2823
+ openWorldHint: true
2824
+ },
2386
2825
  async ({ promptId }) => {
2387
2826
  try {
2388
2827
  const result = await api.delete(`/v1/prompt/message/${promptId}`);
@@ -2401,6 +2840,13 @@ function register7(server, client) {
2401
2840
  "list_prompts",
2402
2841
  "List all available Magic Prompt templates. Use template IDs with ask_magic_prompt's assistantTemplateId parameter when using assistantType 'custom'.",
2403
2842
  {},
2843
+ {
2844
+ title: "List Prompt Templates",
2845
+ readOnlyHint: true,
2846
+ destructiveHint: false,
2847
+ idempotentHint: true,
2848
+ openWorldHint: true
2849
+ },
2404
2850
  async () => {
2405
2851
  try {
2406
2852
  const result = await api.get("/v1/prompt");
@@ -2419,6 +2865,13 @@ function register7(server, client) {
2419
2865
  "get_favorite_prompts",
2420
2866
  "Get all prompts and answers that have been marked as favorites. Useful for finding saved insights and important AI-generated analysis.",
2421
2867
  {},
2868
+ {
2869
+ title: "Get Favorite Prompts",
2870
+ readOnlyHint: true,
2871
+ destructiveHint: false,
2872
+ idempotentHint: true,
2873
+ openWorldHint: true
2874
+ },
2422
2875
  async () => {
2423
2876
  try {
2424
2877
  const result = await api.get("/v1/prompt/favorites");
@@ -2441,6 +2894,13 @@ function register7(server, client) {
2441
2894
  messageId: import_zod7.z.string().min(1).describe("ID of the specific message to favorite/unfavorite"),
2442
2895
  isFavorite: import_zod7.z.boolean().describe("true to mark as favorite, false to remove")
2443
2896
  },
2897
+ {
2898
+ title: "Toggle Prompt Favorite",
2899
+ readOnlyHint: false,
2900
+ destructiveHint: false,
2901
+ idempotentHint: true,
2902
+ openWorldHint: true
2903
+ },
2444
2904
  async (body) => {
2445
2905
  try {
2446
2906
  const result = await api.post("/v1/prompt/favorites", body);
@@ -2462,6 +2922,13 @@ function register7(server, client) {
2462
2922
  promptId: import_zod7.z.string().min(1).describe("ID of the conversation to rename"),
2463
2923
  title: import_zod7.z.string().min(1).describe("New title for the conversation")
2464
2924
  },
2925
+ {
2926
+ title: "Rename Chat",
2927
+ readOnlyHint: false,
2928
+ destructiveHint: false,
2929
+ idempotentHint: true,
2930
+ openWorldHint: true
2931
+ },
2465
2932
  async ({ promptId, title }) => {
2466
2933
  try {
2467
2934
  const result = await api.put(`/v1/prompt/${promptId}`, { title });
@@ -2485,6 +2952,13 @@ function register7(server, client) {
2485
2952
  score: import_zod7.z.number().describe("Feedback score: 1 for thumbs up, -1 for thumbs down"),
2486
2953
  reason: import_zod7.z.string().optional().describe("Optional explanation for the feedback")
2487
2954
  },
2955
+ {
2956
+ title: "Submit Chat Feedback",
2957
+ readOnlyHint: false,
2958
+ destructiveHint: false,
2959
+ idempotentHint: false,
2960
+ openWorldHint: true
2961
+ },
2488
2962
  async (body) => {
2489
2963
  try {
2490
2964
  const result = await api.post("/v1/prompt/feedback", body);
@@ -2506,6 +2980,13 @@ function register7(server, client) {
2506
2980
  startDate: import_zod7.z.string().optional().describe("Start date for stats (ISO 8601)"),
2507
2981
  endDate: import_zod7.z.string().optional().describe("End date for stats (ISO 8601)")
2508
2982
  },
2983
+ {
2984
+ title: "Get Chat Statistics",
2985
+ readOnlyHint: true,
2986
+ destructiveHint: false,
2987
+ idempotentHint: true,
2988
+ openWorldHint: true
2989
+ },
2509
2990
  async (params) => {
2510
2991
  try {
2511
2992
  const result = await api.get("/v1/prompt/statistics", { params });
@@ -2526,6 +3007,13 @@ function register7(server, client) {
2526
3007
  {
2527
3008
  promptId: import_zod7.z.string().min(1).describe("ID of the conversation to export")
2528
3009
  },
3010
+ {
3011
+ title: "Export Chat Answer",
3012
+ readOnlyHint: true,
3013
+ destructiveHint: false,
3014
+ idempotentHint: true,
3015
+ openWorldHint: true
3016
+ },
2529
3017
  async (body) => {
2530
3018
  try {
2531
3019
  const result = await api.post("/v1/prompt/export", body);
@@ -2567,6 +3055,13 @@ function register8(server, client) {
2567
3055
  page: import_zod8.z.number().int().min(0).optional().describe("Page number (0-based, default: 0)"),
2568
3056
  pageSize: import_zod8.z.number().int().min(1).max(500).optional().describe("Results per page (default: 20, max: 500)")
2569
3057
  },
3058
+ {
3059
+ title: "List Meeting Events",
3060
+ readOnlyHint: true,
3061
+ destructiveHint: false,
3062
+ idempotentHint: true,
3063
+ openWorldHint: true
3064
+ },
2570
3065
  async (params) => {
2571
3066
  try {
2572
3067
  const result = await api.get("/v1/meeting-assistant/events", {
@@ -2591,6 +3086,13 @@ function register8(server, client) {
2591
3086
  title: import_zod8.z.string().optional().describe("Display title for the event"),
2592
3087
  scheduledAt: import_zod8.z.string().optional().describe("ISO 8601 datetime for when the meeting starts")
2593
3088
  },
3089
+ {
3090
+ title: "Schedule AI Meeting Assistant",
3091
+ readOnlyHint: false,
3092
+ destructiveHint: false,
3093
+ idempotentHint: false,
3094
+ openWorldHint: true
3095
+ },
2594
3096
  async (body) => {
2595
3097
  try {
2596
3098
  const result = await api.post(
@@ -2614,6 +3116,13 @@ function register8(server, client) {
2614
3116
  {
2615
3117
  meetingAssistantEventId: import_zod8.z.string().describe("Unique identifier of the meeting assistant event")
2616
3118
  },
3119
+ {
3120
+ title: "Remove Assistant from Meeting",
3121
+ readOnlyHint: false,
3122
+ destructiveHint: false,
3123
+ idempotentHint: true,
3124
+ openWorldHint: true
3125
+ },
2617
3126
  async ({ meetingAssistantEventId }) => {
2618
3127
  try {
2619
3128
  const result = await api.put(
@@ -2638,6 +3147,13 @@ function register8(server, client) {
2638
3147
  {
2639
3148
  meetingAssistantEventId: import_zod8.z.string().describe("Unique identifier of the meeting assistant event to cancel")
2640
3149
  },
3150
+ {
3151
+ title: "Cancel Scheduled Meeting Assistant",
3152
+ readOnlyHint: false,
3153
+ destructiveHint: true,
3154
+ idempotentHint: true,
3155
+ openWorldHint: true
3156
+ },
2641
3157
  async ({ meetingAssistantEventId }) => {
2642
3158
  try {
2643
3159
  const result = await api.delete(
@@ -2676,6 +3192,13 @@ function register9(server, client) {
2676
3192
  "list_fields",
2677
3193
  "List all custom fields defined in the workspace.",
2678
3194
  {},
3195
+ {
3196
+ title: "List Custom Fields",
3197
+ readOnlyHint: true,
3198
+ destructiveHint: false,
3199
+ idempotentHint: true,
3200
+ openWorldHint: true
3201
+ },
2679
3202
  async () => {
2680
3203
  try {
2681
3204
  const result = await api.get("/v1/fields");
@@ -2698,6 +3221,13 @@ function register9(server, client) {
2698
3221
  type: import_zod9.z.string().optional().describe("Field type (text, number, select, etc.)"),
2699
3222
  options: import_zod9.z.array(import_zod9.z.string()).optional().describe("Options for select/multi-select field types")
2700
3223
  },
3224
+ {
3225
+ title: "Create Custom Field",
3226
+ readOnlyHint: false,
3227
+ destructiveHint: false,
3228
+ idempotentHint: false,
3229
+ openWorldHint: true
3230
+ },
2701
3231
  async (body) => {
2702
3232
  try {
2703
3233
  const result = await api.post("/v1/fields", body);
@@ -2718,6 +3248,13 @@ function register9(server, client) {
2718
3248
  {
2719
3249
  fields: import_zod9.z.array(import_zod9.z.record(import_zod9.z.unknown())).describe("Array of field objects to update")
2720
3250
  },
3251
+ {
3252
+ title: "Bulk Update Custom Fields",
3253
+ readOnlyHint: false,
3254
+ destructiveHint: false,
3255
+ idempotentHint: true,
3256
+ openWorldHint: true
3257
+ },
2721
3258
  async ({ fields }) => {
2722
3259
  try {
2723
3260
  const result = await api.post("/v1/fields/multi", { fields });
@@ -2741,6 +3278,13 @@ function register9(server, client) {
2741
3278
  type: import_zod9.z.string().optional().describe("New field type"),
2742
3279
  options: import_zod9.z.array(import_zod9.z.string()).optional().describe("Updated options for select types")
2743
3280
  },
3281
+ {
3282
+ title: "Update Custom Field",
3283
+ readOnlyHint: false,
3284
+ destructiveHint: false,
3285
+ idempotentHint: true,
3286
+ openWorldHint: true
3287
+ },
2744
3288
  async ({ id, ...body }) => {
2745
3289
  try {
2746
3290
  const result = await api.put(`/v1/fields/${id}`, body);
@@ -2776,6 +3320,13 @@ function register10(server, client) {
2776
3320
  "list_automations",
2777
3321
  "List all automation rules configured in the workspace.",
2778
3322
  {},
3323
+ {
3324
+ title: "List Automations",
3325
+ readOnlyHint: true,
3326
+ destructiveHint: false,
3327
+ idempotentHint: true,
3328
+ openWorldHint: true
3329
+ },
2779
3330
  async () => {
2780
3331
  try {
2781
3332
  const result = await api.get("/v1/automations");
@@ -2796,6 +3347,13 @@ function register10(server, client) {
2796
3347
  {
2797
3348
  automationId: import_zod10.z.string().min(1).describe("Unique identifier of the automation")
2798
3349
  },
3350
+ {
3351
+ title: "Get Automation Details",
3352
+ readOnlyHint: true,
3353
+ destructiveHint: false,
3354
+ idempotentHint: true,
3355
+ openWorldHint: true
3356
+ },
2799
3357
  async ({ automationId }) => {
2800
3358
  try {
2801
3359
  const result = await api.get(`/v1/automations/${automationId}`);
@@ -2819,6 +3377,13 @@ function register10(server, client) {
2819
3377
  actions: import_zod10.z.array(import_zod10.z.record(import_zod10.z.unknown())).optional().describe("Array of action configurations"),
2820
3378
  config: import_zod10.z.record(import_zod10.z.unknown()).optional().describe("Full automation configuration object")
2821
3379
  },
3380
+ {
3381
+ title: "Create Automation",
3382
+ readOnlyHint: false,
3383
+ destructiveHint: false,
3384
+ idempotentHint: false,
3385
+ openWorldHint: true
3386
+ },
2822
3387
  async (body) => {
2823
3388
  try {
2824
3389
  const result = await api.post("/v1/automations/", body);
@@ -2843,6 +3408,13 @@ function register10(server, client) {
2843
3408
  actions: import_zod10.z.array(import_zod10.z.record(import_zod10.z.unknown())).optional().describe("Updated action configurations"),
2844
3409
  config: import_zod10.z.record(import_zod10.z.unknown()).optional().describe("Full updated configuration object")
2845
3410
  },
3411
+ {
3412
+ title: "Update Automation",
3413
+ readOnlyHint: false,
3414
+ destructiveHint: false,
3415
+ idempotentHint: true,
3416
+ openWorldHint: true
3417
+ },
2846
3418
  async ({ automationId, ...body }) => {
2847
3419
  try {
2848
3420
  const result = await api.put(
@@ -2867,6 +3439,13 @@ function register10(server, client) {
2867
3439
  automationId: import_zod10.z.string().min(1).describe("Unique identifier of the automation"),
2868
3440
  enabled: import_zod10.z.boolean().describe("Set to true to enable, false to disable")
2869
3441
  },
3442
+ {
3443
+ title: "Enable or Disable Automation",
3444
+ readOnlyHint: false,
3445
+ destructiveHint: false,
3446
+ idempotentHint: true,
3447
+ openWorldHint: true
3448
+ },
2870
3449
  async ({ automationId, enabled }) => {
2871
3450
  try {
2872
3451
  const result = await api.put(
@@ -2908,6 +3487,13 @@ function register11(server, client) {
2908
3487
  url: import_zod11.z.string().url().describe("HTTPS endpoint URL to receive webhook payloads"),
2909
3488
  events: import_zod11.z.array(import_zod11.z.string()).optional().describe("Array of event types to subscribe to")
2910
3489
  },
3490
+ {
3491
+ title: "Create Webhook",
3492
+ readOnlyHint: false,
3493
+ destructiveHint: false,
3494
+ idempotentHint: false,
3495
+ openWorldHint: true
3496
+ },
2911
3497
  async (body) => {
2912
3498
  try {
2913
3499
  const result = await api.post("/v1/webhook", body);
@@ -2926,6 +3512,13 @@ function register11(server, client) {
2926
3512
  "list_webhooks",
2927
3513
  "List all configured webhooks in the workspace.",
2928
3514
  {},
3515
+ {
3516
+ title: "List Webhooks",
3517
+ readOnlyHint: true,
3518
+ destructiveHint: false,
3519
+ idempotentHint: true,
3520
+ openWorldHint: true
3521
+ },
2929
3522
  async () => {
2930
3523
  try {
2931
3524
  const result = await api.get("/v1/webhook");
@@ -2948,6 +3541,13 @@ function register11(server, client) {
2948
3541
  url: import_zod11.z.string().url().optional().describe("New endpoint URL"),
2949
3542
  events: import_zod11.z.array(import_zod11.z.string()).optional().describe("Updated array of event types")
2950
3543
  },
3544
+ {
3545
+ title: "Update Webhook",
3546
+ readOnlyHint: false,
3547
+ destructiveHint: false,
3548
+ idempotentHint: true,
3549
+ openWorldHint: true
3550
+ },
2951
3551
  async ({ webhookId, ...body }) => {
2952
3552
  try {
2953
3553
  const result = await api.put(`/v1/webhook/${webhookId}`, body);
@@ -2968,6 +3568,13 @@ function register11(server, client) {
2968
3568
  {
2969
3569
  webhookId: import_zod11.z.string().min(1).describe("Unique identifier of the webhook to delete")
2970
3570
  },
3571
+ {
3572
+ title: "Delete Webhook",
3573
+ readOnlyHint: false,
3574
+ destructiveHint: true,
3575
+ idempotentHint: true,
3576
+ openWorldHint: true
3577
+ },
2971
3578
  async ({ webhookId }) => {
2972
3579
  try {
2973
3580
  const result = await api.delete(`/v1/webhook/${webhookId}`);
@@ -3021,6 +3628,13 @@ function register12(server, client) {
3021
3628
  })
3022
3629
  ).optional().describe("Advanced filters for narrowing search results by tags, speakers, media type, sentiment, folder, etc.")
3023
3630
  },
3631
+ {
3632
+ title: "Search Media Library",
3633
+ readOnlyHint: true,
3634
+ destructiveHint: false,
3635
+ idempotentHint: true,
3636
+ openWorldHint: true
3637
+ },
3024
3638
  async (params) => {
3025
3639
  try {
3026
3640
  const result = await api.post("/v1/analytics/search", params);
@@ -3075,6 +3689,13 @@ function register13(server, client) {
3075
3689
  tags: import_zod13.z.array(import_zod13.z.string()).optional().describe("Tags for the clip"),
3076
3690
  mergeStrategy: import_zod13.z.enum(["CONCATENATE"]).optional().describe("How to merge multiple segments (default: CONCATENATE)")
3077
3691
  },
3692
+ {
3693
+ title: "Create Highlight Clip",
3694
+ readOnlyHint: false,
3695
+ destructiveHint: false,
3696
+ idempotentHint: false,
3697
+ openWorldHint: true
3698
+ },
3078
3699
  async (body) => {
3079
3700
  try {
3080
3701
  const result = await api.post("/v1/clips", body);
@@ -3097,6 +3718,13 @@ function register13(server, client) {
3097
3718
  folderId: import_zod13.z.string().optional().describe("Filter clips by folder ID"),
3098
3719
  mediaIds: import_zod13.z.array(import_zod13.z.string()).optional().describe("Filter clips by source media file IDs")
3099
3720
  },
3721
+ {
3722
+ title: "List Clips",
3723
+ readOnlyHint: true,
3724
+ destructiveHint: false,
3725
+ idempotentHint: true,
3726
+ openWorldHint: true
3727
+ },
3100
3728
  async ({ clipId, ...params }) => {
3101
3729
  try {
3102
3730
  const url = clipId ? `/v1/clips/${clipId}` : "/v1/clips";
@@ -3121,6 +3749,13 @@ function register13(server, client) {
3121
3749
  description: import_zod13.z.string().optional().describe("New description"),
3122
3750
  tags: import_zod13.z.array(import_zod13.z.string()).optional().describe("New tags")
3123
3751
  },
3752
+ {
3753
+ title: "Update Clip",
3754
+ readOnlyHint: false,
3755
+ destructiveHint: false,
3756
+ idempotentHint: true,
3757
+ openWorldHint: true
3758
+ },
3124
3759
  async ({ clipId, ...body }) => {
3125
3760
  try {
3126
3761
  const result = await api.put(`/v1/clips/${clipId}`, body);
@@ -3141,6 +3776,13 @@ function register13(server, client) {
3141
3776
  {
3142
3777
  clipId: import_zod13.z.string().min(1).describe("ID of the clip to delete")
3143
3778
  },
3779
+ {
3780
+ title: "Delete Clip",
3781
+ readOnlyHint: false,
3782
+ destructiveHint: true,
3783
+ idempotentHint: true,
3784
+ openWorldHint: true
3785
+ },
3144
3786
  async ({ clipId }) => {
3145
3787
  try {
3146
3788
  const result = await api.delete(`/v1/clips/${clipId}`);
@@ -3209,12 +3851,7 @@ function register14(server, client) {
3209
3851
  const api = client ?? speakClient;
3210
3852
  server.tool(
3211
3853
  "upload_and_analyze",
3212
- [
3213
- "Upload media from a URL, wait for processing to complete, then return the transcript and AI insights \u2014 all in one call.",
3214
- "This is a convenience tool that combines upload_media + polling get_media_status + get_transcript + get_media_insights.",
3215
- "Processing typically takes 1-3 minutes for audio under 60 minutes.",
3216
- "Use this when you want the full analysis result without managing the polling loop yourself."
3217
- ].join(" "),
3854
+ "Upload media and return media_id immediately. After this returns, poll get_media_status until state is 'processed' (typically 1-3 min for under 60min audio), then call get_media_insights for AI summaries. This async pattern is required for remote MCP transports \u2014 long blocking calls die at proxy idle timeouts.",
3218
3855
  {
3219
3856
  url: import_zod14.z.string().describe("Publicly accessible URL of the media file"),
3220
3857
  name: import_zod14.z.string().optional().describe("Display name for the media (defaults to filename from URL)"),
@@ -3223,6 +3860,13 @@ function register14(server, client) {
3223
3860
  folderId: import_zod14.z.string().optional().describe("Folder ID to place the media in"),
3224
3861
  tags: import_zod14.z.string().optional().describe("Comma-separated tags")
3225
3862
  },
3863
+ {
3864
+ title: "Upload and Analyze Media",
3865
+ readOnlyHint: false,
3866
+ destructiveHint: false,
3867
+ idempotentHint: false,
3868
+ openWorldHint: true
3869
+ },
3226
3870
  async (params) => {
3227
3871
  try {
3228
3872
  const uploadBody = {
@@ -3235,6 +3879,7 @@ function register14(server, client) {
3235
3879
  if (params.tags) uploadBody.tags = params.tags;
3236
3880
  const uploadRes = await api.post("/v1/media/upload", uploadBody);
3237
3881
  const mediaId = uploadRes.data?.data?.mediaId;
3882
+ const state = uploadRes.data?.data?.state ?? "pending";
3238
3883
  if (!mediaId) {
3239
3884
  return {
3240
3885
  content: [{ type: "text", text: `Error: Upload succeeded but no mediaId returned.
@@ -3242,35 +3887,15 @@ ${JSON.stringify(uploadRes.data, null, 2)}` }],
3242
3887
  isError: true
3243
3888
  };
3244
3889
  }
3245
- let state = uploadRes.data?.data?.state;
3246
- let attempts = 0;
3247
- while (state !== MediaState.PROCESSED && state !== MediaState.FAILED && attempts < MAX_POLL_ATTEMPTS) {
3248
- await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
3249
- const statusRes = await api.get(`/v1/media/status/${mediaId}`);
3250
- state = statusRes.data?.data?.state;
3251
- attempts++;
3252
- }
3253
- if (state === MediaState.FAILED) {
3254
- return {
3255
- content: [{ type: "text", text: `Error: Processing failed for media ${mediaId}` }],
3256
- isError: true
3257
- };
3258
- }
3259
- if (state !== MediaState.PROCESSED) {
3260
- return {
3261
- content: [{ type: "text", text: `Timeout: Media ${mediaId} is still processing (state: ${state}). Use get_media_status to check later.` }],
3262
- isError: true
3263
- };
3264
- }
3265
- const [transcriptRes, insightsRes] = await Promise.all([
3266
- api.get(`/v1/media/transcript/${mediaId}`),
3267
- api.get(`/v1/media/insight/${mediaId}`)
3268
- ]);
3269
3890
  const result = {
3270
3891
  mediaId,
3271
- state: "processed",
3272
- transcript: transcriptRes.data?.data,
3273
- insights: insightsRes.data?.data
3892
+ state,
3893
+ message: "Upload accepted. Processing has started in the background.",
3894
+ nextSteps: [
3895
+ `1. Poll get_media_status with mediaId="${mediaId}" every 10-30 seconds.`,
3896
+ `2. When state is "processed" (typically 1-3 min for audio under 60 min), call get_media_insights for the AI summary and get_transcript for the full transcript.`,
3897
+ `3. If state becomes "failed", processing did not complete \u2014 surface the error to the user.`
3898
+ ]
3274
3899
  };
3275
3900
  return {
3276
3901
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
@@ -3299,6 +3924,13 @@ ${JSON.stringify(uploadRes.data, null, 2)}` }],
3299
3924
  folderId: import_zod14.z.string().optional().describe("Folder ID to place the media in"),
3300
3925
  tags: import_zod14.z.string().optional().describe("Comma-separated tags")
3301
3926
  },
3927
+ {
3928
+ title: "Upload Local File",
3929
+ readOnlyHint: false,
3930
+ destructiveHint: false,
3931
+ idempotentHint: false,
3932
+ openWorldHint: true
3933
+ },
3302
3934
  async (params) => {
3303
3935
  try {
3304
3936
  const filePath = params.filePath;
@@ -3371,7 +4003,7 @@ ${JSON.stringify(signedRes.data, null, 2)}` }],
3371
4003
  }
3372
4004
  );
3373
4005
  }
3374
- var import_zod14, fs, path2, POLL_INTERVAL_MS, MAX_POLL_ATTEMPTS;
4006
+ var import_zod14, fs, path2;
3375
4007
  var init_workflows = __esm({
3376
4008
  "src/tools/workflows.ts"() {
3377
4009
  "use strict";
@@ -3381,8 +4013,6 @@ var init_workflows = __esm({
3381
4013
  fs = __toESM(require("fs"));
3382
4014
  path2 = __toESM(require("path"));
3383
4015
  init_media_utils();
3384
- POLL_INTERVAL_MS = 5e3;
3385
- MAX_POLL_ATTEMPTS = 120;
3386
4016
  }
3387
4017
  });
3388
4018
 
@@ -3438,6 +4068,21 @@ var resources_exports = {};
3438
4068
  __export(resources_exports, {
3439
4069
  registerResources: () => registerResources
3440
4070
  });
4071
+ function asJsonContent(uri, data) {
4072
+ return {
4073
+ contents: [
4074
+ {
4075
+ uri,
4076
+ mimeType: "application/json",
4077
+ text: JSON.stringify(data, null, 2)
4078
+ }
4079
+ ]
4080
+ };
4081
+ }
4082
+ function reportError(label, err) {
4083
+ const detail = formatAxiosError(err);
4084
+ throw new Error(`Speak AI resource '${label}' failed: ${detail}`);
4085
+ }
3441
4086
  function registerResources(server, client) {
3442
4087
  const api = client ?? speakClient;
3443
4088
  server.resource(
@@ -3449,17 +4094,9 @@ function registerResources(server, client) {
3449
4094
  const result = await api.get("/v1/media", {
3450
4095
  params: { page: 0, pageSize: 50, sortBy: "createdAt:desc", filterMedia: 2 }
3451
4096
  });
3452
- return {
3453
- contents: [
3454
- {
3455
- uri: "speakai://media",
3456
- mimeType: "application/json",
3457
- text: JSON.stringify(result.data?.data, null, 2)
3458
- }
3459
- ]
3460
- };
3461
- } catch {
3462
- return { contents: [] };
4097
+ return asJsonContent("speakai://media", result.data?.data);
4098
+ } catch (err) {
4099
+ reportError("media-library", err);
3463
4100
  }
3464
4101
  }
3465
4102
  );
@@ -3472,17 +4109,9 @@ function registerResources(server, client) {
3472
4109
  const result = await api.get("/v1/folder", {
3473
4110
  params: { page: 0, pageSize: 100, sortBy: "createdAt:desc" }
3474
4111
  });
3475
- return {
3476
- contents: [
3477
- {
3478
- uri: "speakai://folders",
3479
- mimeType: "application/json",
3480
- text: JSON.stringify(result.data?.data, null, 2)
3481
- }
3482
- ]
3483
- };
3484
- } catch {
3485
- return { contents: [] };
4112
+ return asJsonContent("speakai://folders", result.data?.data);
4113
+ } catch (err) {
4114
+ reportError("folders", err);
3486
4115
  }
3487
4116
  }
3488
4117
  );
@@ -3493,17 +4122,9 @@ function registerResources(server, client) {
3493
4122
  async () => {
3494
4123
  try {
3495
4124
  const result = await api.get("/v1/media/supportedLanguages");
3496
- return {
3497
- contents: [
3498
- {
3499
- uri: "speakai://languages",
3500
- mimeType: "application/json",
3501
- text: JSON.stringify(result.data?.data, null, 2)
3502
- }
3503
- ]
3504
- };
3505
- } catch {
3506
- return { contents: [] };
4125
+ return asJsonContent("speakai://languages", result.data?.data);
4126
+ } catch (err) {
4127
+ reportError("supported-languages", err);
3507
4128
  }
3508
4129
  }
3509
4130
  );
@@ -3514,17 +4135,9 @@ function registerResources(server, client) {
3514
4135
  async (uri, { mediaId }) => {
3515
4136
  try {
3516
4137
  const result = await api.get(`/v1/media/transcript/${mediaId}`);
3517
- return {
3518
- contents: [
3519
- {
3520
- uri: uri.href,
3521
- mimeType: "application/json",
3522
- text: JSON.stringify(result.data?.data, null, 2)
3523
- }
3524
- ]
3525
- };
3526
- } catch {
3527
- return { contents: [] };
4138
+ return asJsonContent(uri.href, result.data?.data);
4139
+ } catch (err) {
4140
+ reportError(`transcript(${mediaId})`, err);
3528
4141
  }
3529
4142
  }
3530
4143
  );
@@ -3535,17 +4148,9 @@ function registerResources(server, client) {
3535
4148
  async (uri, { mediaId }) => {
3536
4149
  try {
3537
4150
  const result = await api.get(`/v1/media/insight/${mediaId}`);
3538
- return {
3539
- contents: [
3540
- {
3541
- uri: uri.href,
3542
- mimeType: "application/json",
3543
- text: JSON.stringify(result.data?.data, null, 2)
3544
- }
3545
- ]
3546
- };
3547
- } catch {
3548
- return { contents: [] };
4151
+ return asJsonContent(uri.href, result.data?.data);
4152
+ } catch (err) {
4153
+ reportError(`insights(${mediaId})`, err);
3549
4154
  }
3550
4155
  }
3551
4156
  );
@@ -3556,6 +4161,7 @@ var init_resources = __esm({
3556
4161
  "use strict";
3557
4162
  import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
3558
4163
  init_client();
4164
+ init_client();
3559
4165
  }
3560
4166
  });
3561
4167