@speakai/mcp-server 1.0.10 → 1.0.11
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 +70 -9
- package/dist/index.js +83 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
Connect Claude, Cursor, Windsurf, and other AI assistants to your <a href="https://speakai.co">Speak AI</a> workspace.<br/>
|
|
9
|
-
|
|
9
|
+
82 tools, 5 resources, 3 prompts, 28 CLI commands — transcribe, analyze, search, and manage media at scale.
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
@@ -160,10 +160,10 @@ SPEAK_API_KEY=your-key npx @speakai/mcp-server
|
|
|
160
160
|
|
|
161
161
|
---
|
|
162
162
|
|
|
163
|
-
## MCP Tools (
|
|
163
|
+
## MCP Tools (82)
|
|
164
164
|
|
|
165
165
|
<details>
|
|
166
|
-
<summary>Media (
|
|
166
|
+
<summary>Media (15 tools)</summary>
|
|
167
167
|
|
|
168
168
|
| Tool | Description |
|
|
169
169
|
|---|---|
|
|
@@ -181,6 +181,7 @@ SPEAK_API_KEY=your-key npx @speakai/mcp-server
|
|
|
181
181
|
| `delete_media` | Permanently delete a media file |
|
|
182
182
|
| `toggle_media_favorite` | Mark or unmark media as a favorite |
|
|
183
183
|
| `reanalyze_media` | Re-run AI analysis with latest models |
|
|
184
|
+
| `bulk_move_media` | Move multiple media files to a folder in one call |
|
|
184
185
|
|
|
185
186
|
</details>
|
|
186
187
|
|
|
@@ -331,7 +332,7 @@ SPEAK_API_KEY=your-key npx @speakai/mcp-server
|
|
|
331
332
|
|
|
332
333
|
| Tool | Description |
|
|
333
334
|
|---|---|
|
|
334
|
-
| `export_media` | Export as PDF, DOCX, SRT, VTT, TXT,
|
|
335
|
+
| `export_media` | Export as PDF, DOCX, SRT, VTT, TXT, or CSV |
|
|
335
336
|
| `export_multiple_media` | Batch export with optional merge into one file |
|
|
336
337
|
|
|
337
338
|
</details>
|
|
@@ -407,7 +408,7 @@ Parameters: days (optional, default: 7), folder (optional)
|
|
|
407
408
|
|
|
408
409
|
---
|
|
409
410
|
|
|
410
|
-
## CLI (
|
|
411
|
+
## CLI (28 Commands)
|
|
411
412
|
|
|
412
413
|
Install globally and configure once:
|
|
413
414
|
|
|
@@ -436,12 +437,12 @@ npx @speakai/mcp-server config set-key
|
|
|
436
437
|
|
|
437
438
|
| Command | Description |
|
|
438
439
|
|---|---|
|
|
439
|
-
| `list-media` / `ls` | List media files with filtering and pagination |
|
|
440
|
+
| `list-media` / `ls` | List media files with filtering, date ranges, and pagination |
|
|
440
441
|
| `upload <source>` | Upload media from URL or local file (`--wait` to poll) |
|
|
441
442
|
| `get-transcript` / `transcript <id>` | Get transcript (`--plain` or `--json`) |
|
|
442
443
|
| `get-insights` / `insights <id>` | Get AI insights (topics, sentiment, keywords) |
|
|
443
444
|
| `status <id>` | Check media processing status |
|
|
444
|
-
| `export <id>` | Export transcript (`-f pdf\|docx\|srt\|vtt\|txt\|csv
|
|
445
|
+
| `export <id>` | Export transcript (`-f pdf\|docx\|srt\|vtt\|txt\|csv`) |
|
|
445
446
|
| `update <id>` | Update media metadata (name, description, tags, folder) |
|
|
446
447
|
| `delete <id>` | Delete a media file |
|
|
447
448
|
| `favorites <id>` | Toggle favorite status |
|
|
@@ -461,6 +462,7 @@ npx @speakai/mcp-server config set-key
|
|
|
461
462
|
| Command | Description |
|
|
462
463
|
|---|---|
|
|
463
464
|
| `list-folders` / `folders` | List all folders |
|
|
465
|
+
| `move <folderId> <mediaIds...>` | Move media files to a folder |
|
|
464
466
|
| `create-folder <name>` | Create a new folder |
|
|
465
467
|
| `clips` | List clips (filter by media or folder) |
|
|
466
468
|
| `clip <mediaId>` | Create a clip (`--start` and `--end` in seconds) |
|
|
@@ -515,6 +517,12 @@ speakai-mcp schedule-meeting "https://zoom.us/j/123456" -t "Weekly Standup"
|
|
|
515
517
|
|
|
516
518
|
# List videos as JSON for scripting
|
|
517
519
|
speakai-mcp ls --type video --json | jq '.mediaList[].name'
|
|
520
|
+
|
|
521
|
+
# List media from the last week
|
|
522
|
+
speakai-mcp ls --from 2026-03-19 --to 2026-03-26
|
|
523
|
+
|
|
524
|
+
# Move 3 files to a folder
|
|
525
|
+
speakai-mcp move folder123 media1 media2 media3
|
|
518
526
|
```
|
|
519
527
|
|
|
520
528
|
---
|
|
@@ -589,14 +597,67 @@ AI: -> list_media(from: "2026-03-18", mediaType: "audio")
|
|
|
589
597
|
|
|
590
598
|
### Authentication
|
|
591
599
|
|
|
592
|
-
|
|
600
|
+
The MCP server and CLI handle token management automatically. If you're calling the REST API directly, here's the full auth flow:
|
|
601
|
+
|
|
602
|
+
**Step 1 — Get an access token:**
|
|
603
|
+
|
|
604
|
+
```bash
|
|
605
|
+
curl -X POST https://api.speakai.co/v1/auth/accessToken \
|
|
606
|
+
-H "Content-Type: application/json" \
|
|
607
|
+
-H "x-speakai-key: YOUR_API_KEY"
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
Response:
|
|
611
|
+
```json
|
|
612
|
+
{
|
|
613
|
+
"data": {
|
|
614
|
+
"email": "you@example.com",
|
|
615
|
+
"accessToken": "eyJhbG...",
|
|
616
|
+
"refreshToken": "eyJhbG..."
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
**Step 2 — Use the token on all subsequent requests:**
|
|
622
|
+
|
|
623
|
+
```bash
|
|
624
|
+
curl https://api.speakai.co/v1/media \
|
|
625
|
+
-H "x-speakai-key: YOUR_API_KEY" \
|
|
626
|
+
-H "x-access-token: ACCESS_TOKEN_FROM_STEP_1"
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
**Step 3 — Refresh before expiry:**
|
|
630
|
+
|
|
631
|
+
```bash
|
|
632
|
+
curl -X POST https://api.speakai.co/v1/auth/refreshToken \
|
|
633
|
+
-H "Content-Type: application/json" \
|
|
634
|
+
-H "x-speakai-key: YOUR_API_KEY" \
|
|
635
|
+
-H "x-access-token: CURRENT_ACCESS_TOKEN" \
|
|
636
|
+
-d '{"refreshToken": "REFRESH_TOKEN_FROM_STEP_1"}'
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
**Token Lifetimes:**
|
|
640
|
+
|
|
641
|
+
| Token | Expiry | How to Renew |
|
|
642
|
+
|---|---|---|
|
|
643
|
+
| Access token | 80 minutes | Refresh endpoint or re-authenticate |
|
|
644
|
+
| Refresh token | 24 hours | Re-authenticate with API key |
|
|
645
|
+
|
|
646
|
+
**Auth Rate Limits:** 5 requests per 60 seconds on both `/v1/auth/accessToken` and `/v1/auth/refreshToken`.
|
|
647
|
+
|
|
648
|
+
### Data Model Notes
|
|
649
|
+
|
|
650
|
+
- **Folder IDs:** Folders have both `_id` (MongoDB ObjectId) and `folderId` (string). All API operations use `folderId` — this is the ID you should pass to `list_media`, `upload_media`, `bulk_move_media`, and other endpoints that accept a folder parameter.
|
|
651
|
+
- **Media IDs:** Media items use `mediaId` (returned in list responses as `_id`).
|
|
593
652
|
|
|
594
653
|
### Rate Limits
|
|
595
654
|
|
|
596
|
-
-
|
|
655
|
+
- The MCP client automatically retries on `429` with exponential backoff
|
|
656
|
+
- For direct API usage, implement exponential backoff and respect `Retry-After` headers
|
|
597
657
|
- Cache stable data (folder lists, field definitions, supported languages)
|
|
598
658
|
- Use `export_multiple_media` over individual exports for batch operations
|
|
599
659
|
- Use `upload_and_analyze` instead of manual upload + poll + fetch loops
|
|
660
|
+
- Use `bulk_move_media` to move multiple items at once instead of updating one by one
|
|
600
661
|
|
|
601
662
|
### Error Format
|
|
602
663
|
|
package/dist/index.js
CHANGED
|
@@ -166,6 +166,16 @@ var init_client = __esm({
|
|
|
166
166
|
originalRequest.headers["x-access-token"] = accessToken;
|
|
167
167
|
return speakClient(originalRequest);
|
|
168
168
|
}
|
|
169
|
+
if (error.response?.status === 429 && retryCount < 3) {
|
|
170
|
+
const retryAfter = error.response.headers["retry-after"];
|
|
171
|
+
const delaySeconds = retryAfter ? parseInt(retryAfter, 10) : Math.pow(2, retryCount + 1);
|
|
172
|
+
const delayMs = (Number.isFinite(delaySeconds) ? delaySeconds : 2) * 1e3;
|
|
173
|
+
process.stderr.write(`[speakai-mcp] Rate limited, retrying in ${delayMs / 1e3}s...
|
|
174
|
+
`);
|
|
175
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
176
|
+
originalRequest._retryCount = retryCount + 1;
|
|
177
|
+
return speakClient(originalRequest);
|
|
178
|
+
}
|
|
169
179
|
return Promise.reject(error);
|
|
170
180
|
}
|
|
171
181
|
);
|
|
@@ -1418,6 +1428,29 @@ function register(server, client) {
|
|
|
1418
1428
|
}
|
|
1419
1429
|
}
|
|
1420
1430
|
);
|
|
1431
|
+
server.tool(
|
|
1432
|
+
"bulk_move_media",
|
|
1433
|
+
"Move multiple media files to a folder in a single operation. Use this for batch reorganization instead of updating media one by one.",
|
|
1434
|
+
{
|
|
1435
|
+
folderId: import_zod.z.string().min(1).describe("Target folder ID to move media into"),
|
|
1436
|
+
mediaIds: import_zod.z.array(import_zod.z.string().min(1)).min(1).describe("Array of media IDs to move")
|
|
1437
|
+
},
|
|
1438
|
+
async (body) => {
|
|
1439
|
+
try {
|
|
1440
|
+
const result = await api.put("/v1/media/move", body);
|
|
1441
|
+
return {
|
|
1442
|
+
content: [
|
|
1443
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) }
|
|
1444
|
+
]
|
|
1445
|
+
};
|
|
1446
|
+
} catch (err) {
|
|
1447
|
+
return {
|
|
1448
|
+
content: [{ type: "text", text: `Error: ${formatAxiosError(err)}` }],
|
|
1449
|
+
isError: true
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
);
|
|
1421
1454
|
}
|
|
1422
1455
|
var import_zod;
|
|
1423
1456
|
var init_media3 = __esm({
|
|
@@ -1562,10 +1595,10 @@ function register3(server, client) {
|
|
|
1562
1595
|
const api = client ?? speakClient;
|
|
1563
1596
|
server.tool(
|
|
1564
1597
|
"export_media",
|
|
1565
|
-
"Export a media file's transcript or insights in various formats (pdf, docx, srt, vtt, txt, csv
|
|
1598
|
+
"Export a media file's transcript or insights in various formats (pdf, docx, srt, vtt, txt, csv).",
|
|
1566
1599
|
{
|
|
1567
1600
|
mediaId: import_zod3.z.string().min(1).describe("Unique identifier of the media file"),
|
|
1568
|
-
fileType: import_zod3.z.enum(["pdf", "docx", "srt", "vtt", "txt", "csv"
|
|
1601
|
+
fileType: import_zod3.z.enum(["pdf", "docx", "srt", "vtt", "txt", "csv"]).describe("Desired export format"),
|
|
1569
1602
|
isSpeakerNames: import_zod3.z.boolean().optional().describe("Include speaker names in export"),
|
|
1570
1603
|
isSpeakerEmail: import_zod3.z.boolean().optional().describe("Include speaker emails in export"),
|
|
1571
1604
|
isTimeStamps: import_zod3.z.boolean().optional().describe("Include timestamps in export"),
|
|
@@ -1573,12 +1606,11 @@ function register3(server, client) {
|
|
|
1573
1606
|
isRedacted: import_zod3.z.boolean().optional().describe("Apply PII redaction to export"),
|
|
1574
1607
|
redactedCategories: import_zod3.z.array(import_zod3.z.string()).optional().describe("Specific categories to redact")
|
|
1575
1608
|
},
|
|
1576
|
-
async ({ mediaId, fileType, ...
|
|
1609
|
+
async ({ mediaId, fileType, ...body }) => {
|
|
1577
1610
|
try {
|
|
1578
1611
|
const result = await api.post(
|
|
1579
1612
|
`/v1/media/export/${mediaId}/${fileType}`,
|
|
1580
|
-
|
|
1581
|
-
{ params: query }
|
|
1613
|
+
body
|
|
1582
1614
|
);
|
|
1583
1615
|
return {
|
|
1584
1616
|
content: [
|
|
@@ -1598,7 +1630,7 @@ function register3(server, client) {
|
|
|
1598
1630
|
"Export multiple media files at once, optionally merged into a single file.",
|
|
1599
1631
|
{
|
|
1600
1632
|
mediaIds: import_zod3.z.array(import_zod3.z.string()).describe("Array of media IDs to export"),
|
|
1601
|
-
fileType: import_zod3.z.enum(["pdf", "docx", "srt", "vtt", "txt", "csv"
|
|
1633
|
+
fileType: import_zod3.z.enum(["pdf", "docx", "srt", "vtt", "txt", "csv"]).describe("Desired export format"),
|
|
1602
1634
|
isSpeakerNames: import_zod3.z.boolean().optional().describe("Include speaker names in export"),
|
|
1603
1635
|
isSpeakerEmail: import_zod3.z.boolean().optional().describe("Include speaker emails in export"),
|
|
1604
1636
|
isTimeStamps: import_zod3.z.boolean().optional().describe("Include timestamps in export"),
|
|
@@ -3957,7 +3989,7 @@ function createCli() {
|
|
|
3957
3989
|
rl.close();
|
|
3958
3990
|
printSuccess("Setup complete! You're ready to go.");
|
|
3959
3991
|
});
|
|
3960
|
-
program.command("list-media").alias("ls").description("List media files").option("-t, --type <type>", "Filter by type (audio, video, text)").option("-p, --page <n>", "Page number (0-based)", "0").option("-s, --page-size <n>", "Results per page", "20").option("--sort <field>", "Sort field", "createdAt:desc").option("-f, --folder <id>", "Filter by folder ID").option("-n, --name <filter>", "Filter by name").option("--favorites", "Show only favorites").option("--json", "Output raw JSON").action(async (opts) => {
|
|
3992
|
+
program.command("list-media").alias("ls").description("List media files").option("-t, --type <type>", "Filter by type (audio, video, text)").option("-p, --page <n>", "Page number (0-based)", "0").option("-s, --page-size <n>", "Results per page", "20").option("--sort <field>", "Sort field", "createdAt:desc").option("-f, --folder <id>", "Filter by folder ID").option("-n, --name <filter>", "Filter by name").option("--from <date>", "Start date filter (ISO 8601, e.g. 2026-01-01)").option("--to <date>", "End date filter (ISO 8601)").option("--favorites", "Show only favorites").option("--json", "Output raw JSON").action(async (opts) => {
|
|
3961
3993
|
requireApiKey();
|
|
3962
3994
|
const client = await getClient();
|
|
3963
3995
|
try {
|
|
@@ -3971,6 +4003,8 @@ function createCli() {
|
|
|
3971
4003
|
if (opts.type) params.mediaType = opts.type;
|
|
3972
4004
|
if (opts.folder) params.folderId = opts.folder;
|
|
3973
4005
|
if (opts.name) params.filterName = opts.name;
|
|
4006
|
+
if (opts.from) params.from = opts.from;
|
|
4007
|
+
if (opts.to) params.to = opts.to;
|
|
3974
4008
|
if (opts.favorites) params.isFavorites = true;
|
|
3975
4009
|
const res = await client.get("/v1/media", { params });
|
|
3976
4010
|
const data = res.data?.data;
|
|
@@ -4164,20 +4198,19 @@ function createCli() {
|
|
|
4164
4198
|
});
|
|
4165
4199
|
program.command("export").description("Export media transcript/insights").argument("<mediaId>", "Media file ID").option(
|
|
4166
4200
|
"-f, --format <type>",
|
|
4167
|
-
"Export format (pdf, docx, srt, vtt, txt, csv
|
|
4201
|
+
"Export format (pdf, docx, srt, vtt, txt, csv)",
|
|
4168
4202
|
"txt"
|
|
4169
4203
|
).option("--speakers", "Include speaker names").option("--timestamps", "Include timestamps").option("--redacted", "Apply PII redaction").option("--json", "Output raw JSON").action(async (mediaId, opts) => {
|
|
4170
4204
|
requireApiKey();
|
|
4171
4205
|
const client = await getClient();
|
|
4172
4206
|
try {
|
|
4173
|
-
const
|
|
4174
|
-
if (opts.speakers)
|
|
4175
|
-
if (opts.timestamps)
|
|
4176
|
-
if (opts.redacted)
|
|
4207
|
+
const body = {};
|
|
4208
|
+
if (opts.speakers) body.isSpeakerNames = true;
|
|
4209
|
+
if (opts.timestamps) body.isTimeStamps = true;
|
|
4210
|
+
if (opts.redacted) body.isRedacted = true;
|
|
4177
4211
|
const res = await client.post(
|
|
4178
4212
|
`/v1/media/export/${mediaId}/${opts.format}`,
|
|
4179
|
-
|
|
4180
|
-
{ params }
|
|
4213
|
+
body
|
|
4181
4214
|
);
|
|
4182
4215
|
if (opts.json) {
|
|
4183
4216
|
printJson(res.data);
|
|
@@ -4256,8 +4289,8 @@ function createCli() {
|
|
|
4256
4289
|
}
|
|
4257
4290
|
const folders = Array.isArray(data) ? data : data?.folderList ?? data?.folders ?? [];
|
|
4258
4291
|
printTable(folders, [
|
|
4259
|
-
{ key: "
|
|
4260
|
-
{ key: "name", label: "Name", width:
|
|
4292
|
+
{ key: "folderId", label: "Folder ID", width: 20 },
|
|
4293
|
+
{ key: "name", label: "Name", width: 34 },
|
|
4261
4294
|
{ key: "createdAt", label: "Created", width: 20 }
|
|
4262
4295
|
]);
|
|
4263
4296
|
} catch (err) {
|
|
@@ -4441,6 +4474,22 @@ function createCli() {
|
|
|
4441
4474
|
process.exit(1);
|
|
4442
4475
|
}
|
|
4443
4476
|
});
|
|
4477
|
+
program.command("move").description("Move one or more media files to a folder").argument("<folderId>", "Target folder ID").argument("<mediaIds...>", "Media file IDs to move").option("--json", "Output raw JSON").action(async (folderId, mediaIds, opts) => {
|
|
4478
|
+
requireApiKey();
|
|
4479
|
+
const client = await getClient();
|
|
4480
|
+
try {
|
|
4481
|
+
const res = await client.put("/v1/media/move", { folderId, mediaIds });
|
|
4482
|
+
const data = res.data?.data;
|
|
4483
|
+
if (opts.json) {
|
|
4484
|
+
printJson(data);
|
|
4485
|
+
} else {
|
|
4486
|
+
printSuccess(`Moved ${mediaIds.length} item(s) to folder ${folderId}`);
|
|
4487
|
+
}
|
|
4488
|
+
} catch (err) {
|
|
4489
|
+
printError(err.response?.data?.message ?? err.message);
|
|
4490
|
+
process.exit(1);
|
|
4491
|
+
}
|
|
4492
|
+
});
|
|
4444
4493
|
program.command("create-folder").description("Create a new folder").argument("<name>", "Folder name").option("--json", "Output raw JSON").action(async (name, opts) => {
|
|
4445
4494
|
requireApiKey();
|
|
4446
4495
|
const client = await getClient();
|
|
@@ -4450,7 +4499,7 @@ function createCli() {
|
|
|
4450
4499
|
if (opts.json) {
|
|
4451
4500
|
printJson(data);
|
|
4452
4501
|
} else {
|
|
4453
|
-
printSuccess(`Folder created: ${data?._id ?? "OK"} \u2014 ${name}`);
|
|
4502
|
+
printSuccess(`Folder created: ${data?.folderId ?? data?._id ?? "OK"} \u2014 ${name}`);
|
|
4454
4503
|
}
|
|
4455
4504
|
} catch (err) {
|
|
4456
4505
|
printError(err.response?.data?.message ?? err.message);
|
|
@@ -4479,21 +4528,23 @@ function createCli() {
|
|
|
4479
4528
|
printJson(data);
|
|
4480
4529
|
return;
|
|
4481
4530
|
}
|
|
4482
|
-
const total = data?.
|
|
4483
|
-
const
|
|
4484
|
-
const
|
|
4485
|
-
|
|
4486
|
-
console.log(`
|
|
4487
|
-
console.log(`
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4531
|
+
const total = data?.totalMedia ?? "\u2014";
|
|
4532
|
+
const analyzed = data?.analyzedMedia ?? "\u2014";
|
|
4533
|
+
const notAnalyzed = data?.notAnalyzedMedia ?? "\u2014";
|
|
4534
|
+
console.log(`Total media: ${total}`);
|
|
4535
|
+
console.log(` Analyzed: ${analyzed}`);
|
|
4536
|
+
console.log(` Not analyzed: ${notAnalyzed}`);
|
|
4537
|
+
if (data?.duration) {
|
|
4538
|
+
const hrs = Math.round(data.duration / 3600 * 10) / 10;
|
|
4539
|
+
console.log(`Duration: ${hrs}h total`);
|
|
4540
|
+
}
|
|
4541
|
+
if (data?.analyzedMinutes) {
|
|
4542
|
+
const hrs = Math.round(data.analyzedMinutes / 60 * 10) / 10;
|
|
4543
|
+
console.log(`Analyzed: ${hrs}h (${data.analyzedMinutes} min)`);
|
|
4544
|
+
}
|
|
4545
|
+
if (data?.fileSize) {
|
|
4546
|
+
const gb = Math.round(data.fileSize / (1024 * 1024 * 1024) * 100) / 100;
|
|
4547
|
+
console.log(`Storage: ${gb} GB`);
|
|
4497
4548
|
}
|
|
4498
4549
|
} catch (err) {
|
|
4499
4550
|
printError(err.response?.data?.message ?? err.message);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@speakai/mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "Official Speak AI MCP Server — connect Claude and other AI assistants to Speak AI's transcription, insights, and media management API",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|