@codespar/mcp-twilio 0.1.0 → 0.2.1
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 +24 -14
- package/dist/index.js +270 -18
- package/package.json +2 -2
- package/server.json +2 -2
- package/src/index.ts +249 -18
package/README.md
CHANGED
|
@@ -4,22 +4,32 @@ MCP server for [Twilio](https://www.twilio.com) — the global standard for prog
|
|
|
4
4
|
|
|
5
5
|
SMS, WhatsApp, and Voice across 180+ countries. Verify (2FA) and Lookup (phone validation) included. Fills the global messaging gap in a catalog otherwise tilted to Brazil-specific providers (Z-API, Take Blip, Zenvia, Evolution API).
|
|
6
6
|
|
|
7
|
-
## Tools
|
|
7
|
+
## Tools (22)
|
|
8
8
|
|
|
9
9
|
| Tool | Purpose |
|
|
10
|
-
|
|
11
|
-
| `send_message` | Send an SMS or WhatsApp message
|
|
12
|
-
| `get_message` | Retrieve a message by SID |
|
|
13
|
-
| `list_messages` | List messages with optional filters
|
|
14
|
-
| `delete_message` | Delete a message from history |
|
|
15
|
-
| `make_call` | Place an outbound voice call
|
|
16
|
-
| `get_call` | Retrieve a call by SID |
|
|
17
|
-
| `update_call` |
|
|
18
|
-
| `start_verification` |
|
|
19
|
-
| `check_verification` | Check a Verify (2FA) code |
|
|
20
|
-
| `lookup_phone` | Validate
|
|
21
|
-
| `list_incoming_numbers` | List
|
|
22
|
-
| `buy_phone_number` | Provision a new phone number |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `send_message` | Send an SMS or WhatsApp message. |
|
|
12
|
+
| `get_message` | Retrieve a message resource by SID (SM... |
|
|
13
|
+
| `list_messages` | List messages with optional filters. |
|
|
14
|
+
| `delete_message` | Delete a message from history. |
|
|
15
|
+
| `make_call` | Place an outbound voice call. |
|
|
16
|
+
| `get_call` | Retrieve a call resource by SID (CA...). |
|
|
17
|
+
| `update_call` | Modify an in-progress call. |
|
|
18
|
+
| `start_verification` | Start a Verify (2FA) challenge. |
|
|
19
|
+
| `check_verification` | Check a Verify (2FA) code against a Service SID. |
|
|
20
|
+
| `lookup_phone` | Validate and normalize a phone number via Lookups v2. |
|
|
21
|
+
| `list_incoming_numbers` | List Twilio-provisioned phone numbers on this account. |
|
|
22
|
+
| `buy_phone_number` | Provision a new phone number. |
|
|
23
|
+
| `list_recordings` | List call recordings on this account. |
|
|
24
|
+
| `create_verify_service` | Create a Verify Service (VA...). |
|
|
25
|
+
| `create_conversation` | Create a Twilio Conversation. |
|
|
26
|
+
| `list_conversations` | List Conversations. |
|
|
27
|
+
| `add_conversation_participant` | Add a participant to a Conversation. |
|
|
28
|
+
| `send_conversation_message` | Post a message into a Conversation. |
|
|
29
|
+
| `list_messaging_services` | List Messaging Services (MG...). |
|
|
30
|
+
| `execute_studio_flow` | Trigger a Studio Flow Execution for a contact. |
|
|
31
|
+
| `create_taskrouter_task` | Create a TaskRouter Task on a Workspace. |
|
|
32
|
+
| `list_taskrouter_workers` | List Workers on a TaskRouter Workspace. |
|
|
23
33
|
|
|
24
34
|
## Install
|
|
25
35
|
|
package/dist/index.js
CHANGED
|
@@ -11,28 +11,42 @@
|
|
|
11
11
|
* providers (Z-API, Take Blip, Zenvia, Evolution API). Twilio ships in 180+
|
|
12
12
|
* countries on day one.
|
|
13
13
|
*
|
|
14
|
-
* Tools (
|
|
15
|
-
* send_message
|
|
16
|
-
* get_message
|
|
17
|
-
* list_messages
|
|
18
|
-
* delete_message
|
|
19
|
-
* make_call
|
|
20
|
-
* get_call
|
|
21
|
-
* update_call
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
14
|
+
* Tools (22):
|
|
15
|
+
* send_message — send SMS or WhatsApp (prefix `To` with `whatsapp:+E164`)
|
|
16
|
+
* get_message — retrieve a message by SID
|
|
17
|
+
* list_messages — list messages with optional To/From/DateSent filters
|
|
18
|
+
* delete_message — delete a message from history
|
|
19
|
+
* make_call — place an outbound voice call driven by a TwiML Url
|
|
20
|
+
* get_call — retrieve a call by SID
|
|
21
|
+
* update_call — hang up or redirect an in-progress call
|
|
22
|
+
* list_recordings — list call recordings (optionally filtered by CallSid)
|
|
23
|
+
* start_verification — send a Verify (2FA) code via sms / whatsapp / call
|
|
24
|
+
* check_verification — check a Verify (2FA) code
|
|
25
|
+
* create_verify_service — provision a new Verify Service (VA...)
|
|
26
|
+
* lookup_phone — validate + format + classify a phone number
|
|
27
|
+
* list_incoming_numbers — list provisioned Twilio phone numbers
|
|
28
|
+
* buy_phone_number — provision a new phone number
|
|
29
|
+
* create_conversation — create a Conversation (Conversations API)
|
|
30
|
+
* list_conversations — list Conversations
|
|
31
|
+
* add_conversation_participant — add an SMS / WhatsApp / chat participant to a Conversation
|
|
32
|
+
* send_conversation_message — post a message into a Conversation
|
|
33
|
+
* list_messaging_services — list Messaging Services (MG...)
|
|
34
|
+
* execute_studio_flow — trigger a Studio Flow execution for a contact
|
|
35
|
+
* create_taskrouter_task — create a TaskRouter task on a Workspace
|
|
36
|
+
* list_taskrouter_workers — list TaskRouter Workers on a Workspace
|
|
27
37
|
*
|
|
28
38
|
* Authentication
|
|
29
39
|
* HTTP Basic with AccountSid:AuthToken.
|
|
30
40
|
* Authorization: Basic <base64(AccountSid:AuthToken)>
|
|
31
41
|
*
|
|
32
42
|
* API surface
|
|
33
|
-
* Accounts API
|
|
34
|
-
* Verify API
|
|
35
|
-
* Lookups API
|
|
43
|
+
* Accounts API : https://api.twilio.com/2010-04-01/Accounts/{AccountSid}
|
|
44
|
+
* Verify API : https://verify.twilio.com/v2
|
|
45
|
+
* Lookups API : https://lookups.twilio.com/v2
|
|
46
|
+
* Conversations API : https://conversations.twilio.com/v1
|
|
47
|
+
* Messaging API : https://messaging.twilio.com/v1
|
|
48
|
+
* Studio API : https://studio.twilio.com/v2
|
|
49
|
+
* TaskRouter API : https://taskrouter.twilio.com/v1
|
|
36
50
|
*
|
|
37
51
|
* Request bodies are application/x-www-form-urlencoded. Responses are JSON
|
|
38
52
|
* (Accounts endpoints use the `.json` suffix).
|
|
@@ -96,7 +110,7 @@ function buildQuery(params) {
|
|
|
96
110
|
const s = q.toString();
|
|
97
111
|
return s ? `?${s}` : "";
|
|
98
112
|
}
|
|
99
|
-
const server = new Server({ name: "mcp-twilio", version: "0.1
|
|
113
|
+
const server = new Server({ name: "mcp-twilio", version: "0.2.1" }, { capabilities: { tools: {} } });
|
|
100
114
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
101
115
|
tools: [
|
|
102
116
|
{
|
|
@@ -260,6 +274,144 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
260
274
|
},
|
|
261
275
|
},
|
|
262
276
|
},
|
|
277
|
+
{
|
|
278
|
+
name: "list_recordings",
|
|
279
|
+
description: "List call recordings on this account. Optionally filter by CallSid, or by DateCreated range. Returns Twilio's paginated list.",
|
|
280
|
+
inputSchema: {
|
|
281
|
+
type: "object",
|
|
282
|
+
properties: {
|
|
283
|
+
CallSid: { type: "string", description: "Restrict to recordings produced by this Call SID (CA...)" },
|
|
284
|
+
DateCreatedAfter: { type: "string", description: "Return recordings created on/after this date (YYYY-MM-DD)" },
|
|
285
|
+
DateCreatedBefore: { type: "string", description: "Return recordings created on/before this date (YYYY-MM-DD)" },
|
|
286
|
+
PageSize: { type: "number", description: "Max rows per page (default 50, max 1000)" },
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
name: "create_verify_service",
|
|
292
|
+
description: "Create a Verify Service (VA...). A service groups verification attempts and holds per-service config (code length, friendly name).",
|
|
293
|
+
inputSchema: {
|
|
294
|
+
type: "object",
|
|
295
|
+
properties: {
|
|
296
|
+
FriendlyName: { type: "string", description: "Human-readable name for the service (e.g. your app name)" },
|
|
297
|
+
CodeLength: { type: "number", description: "OTP code length, 4-10 (default 6)" },
|
|
298
|
+
LookupEnabled: { type: "boolean", description: "Run Lookup on targets before sending (blocks invalid numbers)" },
|
|
299
|
+
DefaultTemplateSid: { type: "string", description: "Default message template SID" },
|
|
300
|
+
},
|
|
301
|
+
required: ["FriendlyName"],
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
name: "create_conversation",
|
|
306
|
+
description: "Create a Twilio Conversation. Conversations host multi-channel (SMS / WhatsApp / chat) threads with server-side history.",
|
|
307
|
+
inputSchema: {
|
|
308
|
+
type: "object",
|
|
309
|
+
properties: {
|
|
310
|
+
FriendlyName: { type: "string", description: "Human-readable label for the conversation" },
|
|
311
|
+
UniqueName: { type: "string", description: "Developer-defined unique identifier (alternative addressable key)" },
|
|
312
|
+
MessagingServiceSid: { type: "string", description: "Messaging Service SID (MG...) used for outbound SMS/WhatsApp in this conversation" },
|
|
313
|
+
Attributes: { type: "string", description: "JSON string of custom attributes stored on the conversation" },
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
name: "list_conversations",
|
|
319
|
+
description: "List Conversations. Returns a paginated list; pass PageSize to cap.",
|
|
320
|
+
inputSchema: {
|
|
321
|
+
type: "object",
|
|
322
|
+
properties: {
|
|
323
|
+
StartDate: { type: "string", description: "Only include conversations created on/after this ISO-8601 date" },
|
|
324
|
+
EndDate: { type: "string", description: "Only include conversations created on/before this ISO-8601 date" },
|
|
325
|
+
State: { type: "string", enum: ["active", "inactive", "closed"], description: "Filter by conversation state" },
|
|
326
|
+
PageSize: { type: "number", description: "Max rows per page (default 50, max 1000)" },
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
name: "add_conversation_participant",
|
|
332
|
+
description: "Add a participant to a Conversation. For SMS / WhatsApp, pass MessagingBinding.Address (+E164 or whatsapp:+E164) + MessagingBinding.ProxyAddress (your Twilio number). For chat participants, pass Identity.",
|
|
333
|
+
inputSchema: {
|
|
334
|
+
type: "object",
|
|
335
|
+
properties: {
|
|
336
|
+
ConversationSid: { type: "string", description: "Conversation SID (CH...) or UniqueName" },
|
|
337
|
+
Identity: { type: "string", description: "Chat identity (use for in-app chat participants)" },
|
|
338
|
+
"MessagingBinding.Address": { type: "string", description: "Participant's address: +E164 for SMS, whatsapp:+E164 for WhatsApp" },
|
|
339
|
+
"MessagingBinding.ProxyAddress": { type: "string", description: "Your Twilio number the participant messages with (E.164 or whatsapp:+E164)" },
|
|
340
|
+
Attributes: { type: "string", description: "JSON string of custom participant attributes" },
|
|
341
|
+
},
|
|
342
|
+
required: ["ConversationSid"],
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
name: "send_conversation_message",
|
|
347
|
+
description: "Post a message into a Conversation. Fanned out to all participants via their channel (SMS / WhatsApp / chat).",
|
|
348
|
+
inputSchema: {
|
|
349
|
+
type: "object",
|
|
350
|
+
properties: {
|
|
351
|
+
ConversationSid: { type: "string", description: "Conversation SID (CH...) or UniqueName" },
|
|
352
|
+
Body: { type: "string", description: "Message text (UTF-8)" },
|
|
353
|
+
Author: { type: "string", description: "Identity of the author (defaults to `system` if omitted)" },
|
|
354
|
+
MediaSid: { type: "string", description: "Media SID (ME...) from a prior MCS upload" },
|
|
355
|
+
Attributes: { type: "string", description: "JSON string of custom message attributes" },
|
|
356
|
+
},
|
|
357
|
+
required: ["ConversationSid"],
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
name: "list_messaging_services",
|
|
362
|
+
description: "List Messaging Services (MG...). A Messaging Service bundles sender pools, templates, and routing rules.",
|
|
363
|
+
inputSchema: {
|
|
364
|
+
type: "object",
|
|
365
|
+
properties: {
|
|
366
|
+
PageSize: { type: "number", description: "Max rows per page (default 50, max 1000)" },
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
name: "execute_studio_flow",
|
|
372
|
+
description: "Trigger a Studio Flow Execution for a contact. Studio flows are visual IVR / workflow builders — this kicks one off for a specific To/From pair.",
|
|
373
|
+
inputSchema: {
|
|
374
|
+
type: "object",
|
|
375
|
+
properties: {
|
|
376
|
+
FlowSid: { type: "string", description: "Studio Flow SID (FW...)" },
|
|
377
|
+
To: { type: "string", description: "Contact address (E.164 or whatsapp:+E164)" },
|
|
378
|
+
From: { type: "string", description: "Your Twilio number (E.164 or whatsapp:+E164)" },
|
|
379
|
+
Parameters: { type: "string", description: "JSON string of variables injected into the flow's execution context" },
|
|
380
|
+
},
|
|
381
|
+
required: ["FlowSid", "To", "From"],
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
name: "create_taskrouter_task",
|
|
386
|
+
description: "Create a TaskRouter Task on a Workspace. TaskRouter routes work (calls, chats, tickets) to eligible Workers based on attributes.",
|
|
387
|
+
inputSchema: {
|
|
388
|
+
type: "object",
|
|
389
|
+
properties: {
|
|
390
|
+
WorkspaceSid: { type: "string", description: "TaskRouter Workspace SID (WS...)" },
|
|
391
|
+
Attributes: { type: "string", description: "JSON string of task attributes used for routing (e.g. {\"selected_language\":\"pt-br\"})" },
|
|
392
|
+
WorkflowSid: { type: "string", description: "Workflow SID (WW...) that decides how this task is routed" },
|
|
393
|
+
TaskChannel: { type: "string", description: "Task channel unique_name or SID (e.g. voice, chat, sms, default)" },
|
|
394
|
+
Priority: { type: "number", description: "Priority (higher = more urgent)" },
|
|
395
|
+
Timeout: { type: "number", description: "Seconds before the task times out (default 86400)" },
|
|
396
|
+
},
|
|
397
|
+
required: ["WorkspaceSid"],
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
name: "list_taskrouter_workers",
|
|
402
|
+
description: "List Workers on a TaskRouter Workspace. Optionally filter by ActivityName, Available, or TargetWorkersExpression.",
|
|
403
|
+
inputSchema: {
|
|
404
|
+
type: "object",
|
|
405
|
+
properties: {
|
|
406
|
+
WorkspaceSid: { type: "string", description: "TaskRouter Workspace SID (WS...)" },
|
|
407
|
+
ActivityName: { type: "string", description: "Filter by activity name (e.g. Available, Idle, Offline)" },
|
|
408
|
+
Available: { type: "string", description: "Filter by availability: 'true' or 'false'" },
|
|
409
|
+
TargetWorkersExpression: { type: "string", description: "TaskRouter expression to filter workers (e.g. languages HAS \"pt-br\")" },
|
|
410
|
+
PageSize: { type: "number", description: "Max rows per page (default 50, max 1000)" },
|
|
411
|
+
},
|
|
412
|
+
required: ["WorkspaceSid"],
|
|
413
|
+
},
|
|
414
|
+
},
|
|
263
415
|
],
|
|
264
416
|
}));
|
|
265
417
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -370,6 +522,106 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
370
522
|
body.StatusCallback = a.StatusCallback;
|
|
371
523
|
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("POST", "/IncomingPhoneNumbers.json", body), null, 2) }] };
|
|
372
524
|
}
|
|
525
|
+
case "list_recordings": {
|
|
526
|
+
const q = buildQuery({
|
|
527
|
+
CallSid: a.CallSid,
|
|
528
|
+
"DateCreated>": a.DateCreatedAfter,
|
|
529
|
+
"DateCreated<": a.DateCreatedBefore,
|
|
530
|
+
PageSize: a.PageSize,
|
|
531
|
+
});
|
|
532
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("GET", `/Recordings.json${q}`), null, 2) }] };
|
|
533
|
+
}
|
|
534
|
+
case "create_verify_service": {
|
|
535
|
+
const body = { FriendlyName: a.FriendlyName };
|
|
536
|
+
if (a.CodeLength !== undefined)
|
|
537
|
+
body.CodeLength = a.CodeLength;
|
|
538
|
+
if (a.LookupEnabled !== undefined)
|
|
539
|
+
body.LookupEnabled = a.LookupEnabled;
|
|
540
|
+
if (a.DefaultTemplateSid)
|
|
541
|
+
body.DefaultTemplateSid = a.DefaultTemplateSid;
|
|
542
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("POST", "https://verify.twilio.com/v2/Services", body), null, 2) }] };
|
|
543
|
+
}
|
|
544
|
+
case "create_conversation": {
|
|
545
|
+
const body = {};
|
|
546
|
+
if (a.FriendlyName)
|
|
547
|
+
body.FriendlyName = a.FriendlyName;
|
|
548
|
+
if (a.UniqueName)
|
|
549
|
+
body.UniqueName = a.UniqueName;
|
|
550
|
+
if (a.MessagingServiceSid)
|
|
551
|
+
body.MessagingServiceSid = a.MessagingServiceSid;
|
|
552
|
+
if (a.Attributes)
|
|
553
|
+
body.Attributes = a.Attributes;
|
|
554
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("POST", "https://conversations.twilio.com/v1/Conversations", body), null, 2) }] };
|
|
555
|
+
}
|
|
556
|
+
case "list_conversations": {
|
|
557
|
+
const q = buildQuery({
|
|
558
|
+
StartDate: a.StartDate,
|
|
559
|
+
EndDate: a.EndDate,
|
|
560
|
+
State: a.State,
|
|
561
|
+
PageSize: a.PageSize,
|
|
562
|
+
});
|
|
563
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("GET", `https://conversations.twilio.com/v1/Conversations${q}`), null, 2) }] };
|
|
564
|
+
}
|
|
565
|
+
case "add_conversation_participant": {
|
|
566
|
+
const body = {};
|
|
567
|
+
if (a.Identity)
|
|
568
|
+
body.Identity = a.Identity;
|
|
569
|
+
if (a["MessagingBinding.Address"])
|
|
570
|
+
body["MessagingBinding.Address"] = a["MessagingBinding.Address"];
|
|
571
|
+
if (a["MessagingBinding.ProxyAddress"])
|
|
572
|
+
body["MessagingBinding.ProxyAddress"] = a["MessagingBinding.ProxyAddress"];
|
|
573
|
+
if (a.Attributes)
|
|
574
|
+
body.Attributes = a.Attributes;
|
|
575
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("POST", `https://conversations.twilio.com/v1/Conversations/${a.ConversationSid}/Participants`, body), null, 2) }] };
|
|
576
|
+
}
|
|
577
|
+
case "send_conversation_message": {
|
|
578
|
+
const body = {};
|
|
579
|
+
if (a.Body)
|
|
580
|
+
body.Body = a.Body;
|
|
581
|
+
if (a.Author)
|
|
582
|
+
body.Author = a.Author;
|
|
583
|
+
if (a.MediaSid)
|
|
584
|
+
body.MediaSid = a.MediaSid;
|
|
585
|
+
if (a.Attributes)
|
|
586
|
+
body.Attributes = a.Attributes;
|
|
587
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("POST", `https://conversations.twilio.com/v1/Conversations/${a.ConversationSid}/Messages`, body), null, 2) }] };
|
|
588
|
+
}
|
|
589
|
+
case "list_messaging_services": {
|
|
590
|
+
const q = buildQuery({ PageSize: a.PageSize });
|
|
591
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("GET", `https://messaging.twilio.com/v1/Services${q}`), null, 2) }] };
|
|
592
|
+
}
|
|
593
|
+
case "execute_studio_flow": {
|
|
594
|
+
const body = {
|
|
595
|
+
To: a.To,
|
|
596
|
+
From: a.From,
|
|
597
|
+
};
|
|
598
|
+
if (a.Parameters)
|
|
599
|
+
body.Parameters = a.Parameters;
|
|
600
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("POST", `https://studio.twilio.com/v2/Flows/${a.FlowSid}/Executions`, body), null, 2) }] };
|
|
601
|
+
}
|
|
602
|
+
case "create_taskrouter_task": {
|
|
603
|
+
const body = {};
|
|
604
|
+
if (a.Attributes)
|
|
605
|
+
body.Attributes = a.Attributes;
|
|
606
|
+
if (a.WorkflowSid)
|
|
607
|
+
body.WorkflowSid = a.WorkflowSid;
|
|
608
|
+
if (a.TaskChannel)
|
|
609
|
+
body.TaskChannel = a.TaskChannel;
|
|
610
|
+
if (a.Priority !== undefined)
|
|
611
|
+
body.Priority = a.Priority;
|
|
612
|
+
if (a.Timeout !== undefined)
|
|
613
|
+
body.Timeout = a.Timeout;
|
|
614
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("POST", `https://taskrouter.twilio.com/v1/Workspaces/${a.WorkspaceSid}/Tasks`, body), null, 2) }] };
|
|
615
|
+
}
|
|
616
|
+
case "list_taskrouter_workers": {
|
|
617
|
+
const q = buildQuery({
|
|
618
|
+
ActivityName: a.ActivityName,
|
|
619
|
+
Available: a.Available,
|
|
620
|
+
TargetWorkersExpression: a.TargetWorkersExpression,
|
|
621
|
+
PageSize: a.PageSize,
|
|
622
|
+
});
|
|
623
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("GET", `https://taskrouter.twilio.com/v1/Workspaces/${a.WorkspaceSid}/Workers${q}`), null, 2) }] };
|
|
624
|
+
}
|
|
373
625
|
default:
|
|
374
626
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
375
627
|
}
|
|
@@ -396,7 +648,7 @@ async function main() {
|
|
|
396
648
|
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
397
649
|
t.onclose = () => { if (t.sessionId)
|
|
398
650
|
transports.delete(t.sessionId); };
|
|
399
|
-
const s = new Server({ name: "mcp-twilio", version: "0.1
|
|
651
|
+
const s = new Server({ name: "mcp-twilio", version: "0.2.1" }, { capabilities: { tools: {} } });
|
|
400
652
|
server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
|
|
401
653
|
server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
|
|
402
654
|
await s.connect(t);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codespar/mcp-twilio",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "MCP server for Twilio
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "MCP server for Twilio \u2014 global SMS, WhatsApp, Voice, Verify, and Lookup across 180+ countries",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"bin": {
|
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"source": "github",
|
|
8
8
|
"subfolder": "packages/communication/twilio"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.1
|
|
10
|
+
"version": "0.2.1",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "@codespar/mcp-twilio",
|
|
15
|
-
"version": "0.1
|
|
15
|
+
"version": "0.2.1",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
package/src/index.ts
CHANGED
|
@@ -12,28 +12,42 @@
|
|
|
12
12
|
* providers (Z-API, Take Blip, Zenvia, Evolution API). Twilio ships in 180+
|
|
13
13
|
* countries on day one.
|
|
14
14
|
*
|
|
15
|
-
* Tools (
|
|
16
|
-
* send_message
|
|
17
|
-
* get_message
|
|
18
|
-
* list_messages
|
|
19
|
-
* delete_message
|
|
20
|
-
* make_call
|
|
21
|
-
* get_call
|
|
22
|
-
* update_call
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
15
|
+
* Tools (22):
|
|
16
|
+
* send_message — send SMS or WhatsApp (prefix `To` with `whatsapp:+E164`)
|
|
17
|
+
* get_message — retrieve a message by SID
|
|
18
|
+
* list_messages — list messages with optional To/From/DateSent filters
|
|
19
|
+
* delete_message — delete a message from history
|
|
20
|
+
* make_call — place an outbound voice call driven by a TwiML Url
|
|
21
|
+
* get_call — retrieve a call by SID
|
|
22
|
+
* update_call — hang up or redirect an in-progress call
|
|
23
|
+
* list_recordings — list call recordings (optionally filtered by CallSid)
|
|
24
|
+
* start_verification — send a Verify (2FA) code via sms / whatsapp / call
|
|
25
|
+
* check_verification — check a Verify (2FA) code
|
|
26
|
+
* create_verify_service — provision a new Verify Service (VA...)
|
|
27
|
+
* lookup_phone — validate + format + classify a phone number
|
|
28
|
+
* list_incoming_numbers — list provisioned Twilio phone numbers
|
|
29
|
+
* buy_phone_number — provision a new phone number
|
|
30
|
+
* create_conversation — create a Conversation (Conversations API)
|
|
31
|
+
* list_conversations — list Conversations
|
|
32
|
+
* add_conversation_participant — add an SMS / WhatsApp / chat participant to a Conversation
|
|
33
|
+
* send_conversation_message — post a message into a Conversation
|
|
34
|
+
* list_messaging_services — list Messaging Services (MG...)
|
|
35
|
+
* execute_studio_flow — trigger a Studio Flow execution for a contact
|
|
36
|
+
* create_taskrouter_task — create a TaskRouter task on a Workspace
|
|
37
|
+
* list_taskrouter_workers — list TaskRouter Workers on a Workspace
|
|
28
38
|
*
|
|
29
39
|
* Authentication
|
|
30
40
|
* HTTP Basic with AccountSid:AuthToken.
|
|
31
41
|
* Authorization: Basic <base64(AccountSid:AuthToken)>
|
|
32
42
|
*
|
|
33
43
|
* API surface
|
|
34
|
-
* Accounts API
|
|
35
|
-
* Verify API
|
|
36
|
-
* Lookups API
|
|
44
|
+
* Accounts API : https://api.twilio.com/2010-04-01/Accounts/{AccountSid}
|
|
45
|
+
* Verify API : https://verify.twilio.com/v2
|
|
46
|
+
* Lookups API : https://lookups.twilio.com/v2
|
|
47
|
+
* Conversations API : https://conversations.twilio.com/v1
|
|
48
|
+
* Messaging API : https://messaging.twilio.com/v1
|
|
49
|
+
* Studio API : https://studio.twilio.com/v2
|
|
50
|
+
* TaskRouter API : https://taskrouter.twilio.com/v1
|
|
37
51
|
*
|
|
38
52
|
* Request bodies are application/x-www-form-urlencoded. Responses are JSON
|
|
39
53
|
* (Accounts endpoints use the `.json` suffix).
|
|
@@ -109,7 +123,7 @@ function buildQuery(params: Record<string, unknown>): string {
|
|
|
109
123
|
}
|
|
110
124
|
|
|
111
125
|
const server = new Server(
|
|
112
|
-
{ name: "mcp-twilio", version: "0.1
|
|
126
|
+
{ name: "mcp-twilio", version: "0.2.1" },
|
|
113
127
|
{ capabilities: { tools: {} } }
|
|
114
128
|
);
|
|
115
129
|
|
|
@@ -276,6 +290,144 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
276
290
|
},
|
|
277
291
|
},
|
|
278
292
|
},
|
|
293
|
+
{
|
|
294
|
+
name: "list_recordings",
|
|
295
|
+
description: "List call recordings on this account. Optionally filter by CallSid, or by DateCreated range. Returns Twilio's paginated list.",
|
|
296
|
+
inputSchema: {
|
|
297
|
+
type: "object",
|
|
298
|
+
properties: {
|
|
299
|
+
CallSid: { type: "string", description: "Restrict to recordings produced by this Call SID (CA...)" },
|
|
300
|
+
DateCreatedAfter: { type: "string", description: "Return recordings created on/after this date (YYYY-MM-DD)" },
|
|
301
|
+
DateCreatedBefore: { type: "string", description: "Return recordings created on/before this date (YYYY-MM-DD)" },
|
|
302
|
+
PageSize: { type: "number", description: "Max rows per page (default 50, max 1000)" },
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
name: "create_verify_service",
|
|
308
|
+
description: "Create a Verify Service (VA...). A service groups verification attempts and holds per-service config (code length, friendly name).",
|
|
309
|
+
inputSchema: {
|
|
310
|
+
type: "object",
|
|
311
|
+
properties: {
|
|
312
|
+
FriendlyName: { type: "string", description: "Human-readable name for the service (e.g. your app name)" },
|
|
313
|
+
CodeLength: { type: "number", description: "OTP code length, 4-10 (default 6)" },
|
|
314
|
+
LookupEnabled: { type: "boolean", description: "Run Lookup on targets before sending (blocks invalid numbers)" },
|
|
315
|
+
DefaultTemplateSid: { type: "string", description: "Default message template SID" },
|
|
316
|
+
},
|
|
317
|
+
required: ["FriendlyName"],
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
name: "create_conversation",
|
|
322
|
+
description: "Create a Twilio Conversation. Conversations host multi-channel (SMS / WhatsApp / chat) threads with server-side history.",
|
|
323
|
+
inputSchema: {
|
|
324
|
+
type: "object",
|
|
325
|
+
properties: {
|
|
326
|
+
FriendlyName: { type: "string", description: "Human-readable label for the conversation" },
|
|
327
|
+
UniqueName: { type: "string", description: "Developer-defined unique identifier (alternative addressable key)" },
|
|
328
|
+
MessagingServiceSid: { type: "string", description: "Messaging Service SID (MG...) used for outbound SMS/WhatsApp in this conversation" },
|
|
329
|
+
Attributes: { type: "string", description: "JSON string of custom attributes stored on the conversation" },
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
name: "list_conversations",
|
|
335
|
+
description: "List Conversations. Returns a paginated list; pass PageSize to cap.",
|
|
336
|
+
inputSchema: {
|
|
337
|
+
type: "object",
|
|
338
|
+
properties: {
|
|
339
|
+
StartDate: { type: "string", description: "Only include conversations created on/after this ISO-8601 date" },
|
|
340
|
+
EndDate: { type: "string", description: "Only include conversations created on/before this ISO-8601 date" },
|
|
341
|
+
State: { type: "string", enum: ["active", "inactive", "closed"], description: "Filter by conversation state" },
|
|
342
|
+
PageSize: { type: "number", description: "Max rows per page (default 50, max 1000)" },
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
name: "add_conversation_participant",
|
|
348
|
+
description: "Add a participant to a Conversation. For SMS / WhatsApp, pass MessagingBinding.Address (+E164 or whatsapp:+E164) + MessagingBinding.ProxyAddress (your Twilio number). For chat participants, pass Identity.",
|
|
349
|
+
inputSchema: {
|
|
350
|
+
type: "object",
|
|
351
|
+
properties: {
|
|
352
|
+
ConversationSid: { type: "string", description: "Conversation SID (CH...) or UniqueName" },
|
|
353
|
+
Identity: { type: "string", description: "Chat identity (use for in-app chat participants)" },
|
|
354
|
+
"MessagingBinding.Address": { type: "string", description: "Participant's address: +E164 for SMS, whatsapp:+E164 for WhatsApp" },
|
|
355
|
+
"MessagingBinding.ProxyAddress": { type: "string", description: "Your Twilio number the participant messages with (E.164 or whatsapp:+E164)" },
|
|
356
|
+
Attributes: { type: "string", description: "JSON string of custom participant attributes" },
|
|
357
|
+
},
|
|
358
|
+
required: ["ConversationSid"],
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
name: "send_conversation_message",
|
|
363
|
+
description: "Post a message into a Conversation. Fanned out to all participants via their channel (SMS / WhatsApp / chat).",
|
|
364
|
+
inputSchema: {
|
|
365
|
+
type: "object",
|
|
366
|
+
properties: {
|
|
367
|
+
ConversationSid: { type: "string", description: "Conversation SID (CH...) or UniqueName" },
|
|
368
|
+
Body: { type: "string", description: "Message text (UTF-8)" },
|
|
369
|
+
Author: { type: "string", description: "Identity of the author (defaults to `system` if omitted)" },
|
|
370
|
+
MediaSid: { type: "string", description: "Media SID (ME...) from a prior MCS upload" },
|
|
371
|
+
Attributes: { type: "string", description: "JSON string of custom message attributes" },
|
|
372
|
+
},
|
|
373
|
+
required: ["ConversationSid"],
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
name: "list_messaging_services",
|
|
378
|
+
description: "List Messaging Services (MG...). A Messaging Service bundles sender pools, templates, and routing rules.",
|
|
379
|
+
inputSchema: {
|
|
380
|
+
type: "object",
|
|
381
|
+
properties: {
|
|
382
|
+
PageSize: { type: "number", description: "Max rows per page (default 50, max 1000)" },
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
name: "execute_studio_flow",
|
|
388
|
+
description: "Trigger a Studio Flow Execution for a contact. Studio flows are visual IVR / workflow builders — this kicks one off for a specific To/From pair.",
|
|
389
|
+
inputSchema: {
|
|
390
|
+
type: "object",
|
|
391
|
+
properties: {
|
|
392
|
+
FlowSid: { type: "string", description: "Studio Flow SID (FW...)" },
|
|
393
|
+
To: { type: "string", description: "Contact address (E.164 or whatsapp:+E164)" },
|
|
394
|
+
From: { type: "string", description: "Your Twilio number (E.164 or whatsapp:+E164)" },
|
|
395
|
+
Parameters: { type: "string", description: "JSON string of variables injected into the flow's execution context" },
|
|
396
|
+
},
|
|
397
|
+
required: ["FlowSid", "To", "From"],
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
name: "create_taskrouter_task",
|
|
402
|
+
description: "Create a TaskRouter Task on a Workspace. TaskRouter routes work (calls, chats, tickets) to eligible Workers based on attributes.",
|
|
403
|
+
inputSchema: {
|
|
404
|
+
type: "object",
|
|
405
|
+
properties: {
|
|
406
|
+
WorkspaceSid: { type: "string", description: "TaskRouter Workspace SID (WS...)" },
|
|
407
|
+
Attributes: { type: "string", description: "JSON string of task attributes used for routing (e.g. {\"selected_language\":\"pt-br\"})" },
|
|
408
|
+
WorkflowSid: { type: "string", description: "Workflow SID (WW...) that decides how this task is routed" },
|
|
409
|
+
TaskChannel: { type: "string", description: "Task channel unique_name or SID (e.g. voice, chat, sms, default)" },
|
|
410
|
+
Priority: { type: "number", description: "Priority (higher = more urgent)" },
|
|
411
|
+
Timeout: { type: "number", description: "Seconds before the task times out (default 86400)" },
|
|
412
|
+
},
|
|
413
|
+
required: ["WorkspaceSid"],
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
name: "list_taskrouter_workers",
|
|
418
|
+
description: "List Workers on a TaskRouter Workspace. Optionally filter by ActivityName, Available, or TargetWorkersExpression.",
|
|
419
|
+
inputSchema: {
|
|
420
|
+
type: "object",
|
|
421
|
+
properties: {
|
|
422
|
+
WorkspaceSid: { type: "string", description: "TaskRouter Workspace SID (WS...)" },
|
|
423
|
+
ActivityName: { type: "string", description: "Filter by activity name (e.g. Available, Idle, Offline)" },
|
|
424
|
+
Available: { type: "string", description: "Filter by availability: 'true' or 'false'" },
|
|
425
|
+
TargetWorkersExpression: { type: "string", description: "TaskRouter expression to filter workers (e.g. languages HAS \"pt-br\")" },
|
|
426
|
+
PageSize: { type: "number", description: "Max rows per page (default 50, max 1000)" },
|
|
427
|
+
},
|
|
428
|
+
required: ["WorkspaceSid"],
|
|
429
|
+
},
|
|
430
|
+
},
|
|
279
431
|
],
|
|
280
432
|
}));
|
|
281
433
|
|
|
@@ -370,6 +522,85 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
370
522
|
if (a.StatusCallback) body.StatusCallback = a.StatusCallback;
|
|
371
523
|
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("POST", "/IncomingPhoneNumbers.json", body), null, 2) }] };
|
|
372
524
|
}
|
|
525
|
+
case "list_recordings": {
|
|
526
|
+
const q = buildQuery({
|
|
527
|
+
CallSid: a.CallSid,
|
|
528
|
+
"DateCreated>": a.DateCreatedAfter,
|
|
529
|
+
"DateCreated<": a.DateCreatedBefore,
|
|
530
|
+
PageSize: a.PageSize,
|
|
531
|
+
});
|
|
532
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("GET", `/Recordings.json${q}`), null, 2) }] };
|
|
533
|
+
}
|
|
534
|
+
case "create_verify_service": {
|
|
535
|
+
const body: Record<string, unknown> = { FriendlyName: a.FriendlyName };
|
|
536
|
+
if (a.CodeLength !== undefined) body.CodeLength = a.CodeLength;
|
|
537
|
+
if (a.LookupEnabled !== undefined) body.LookupEnabled = a.LookupEnabled;
|
|
538
|
+
if (a.DefaultTemplateSid) body.DefaultTemplateSid = a.DefaultTemplateSid;
|
|
539
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("POST", "https://verify.twilio.com/v2/Services", body), null, 2) }] };
|
|
540
|
+
}
|
|
541
|
+
case "create_conversation": {
|
|
542
|
+
const body: Record<string, unknown> = {};
|
|
543
|
+
if (a.FriendlyName) body.FriendlyName = a.FriendlyName;
|
|
544
|
+
if (a.UniqueName) body.UniqueName = a.UniqueName;
|
|
545
|
+
if (a.MessagingServiceSid) body.MessagingServiceSid = a.MessagingServiceSid;
|
|
546
|
+
if (a.Attributes) body.Attributes = a.Attributes;
|
|
547
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("POST", "https://conversations.twilio.com/v1/Conversations", body), null, 2) }] };
|
|
548
|
+
}
|
|
549
|
+
case "list_conversations": {
|
|
550
|
+
const q = buildQuery({
|
|
551
|
+
StartDate: a.StartDate,
|
|
552
|
+
EndDate: a.EndDate,
|
|
553
|
+
State: a.State,
|
|
554
|
+
PageSize: a.PageSize,
|
|
555
|
+
});
|
|
556
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("GET", `https://conversations.twilio.com/v1/Conversations${q}`), null, 2) }] };
|
|
557
|
+
}
|
|
558
|
+
case "add_conversation_participant": {
|
|
559
|
+
const body: Record<string, unknown> = {};
|
|
560
|
+
if (a.Identity) body.Identity = a.Identity;
|
|
561
|
+
if (a["MessagingBinding.Address"]) body["MessagingBinding.Address"] = a["MessagingBinding.Address"];
|
|
562
|
+
if (a["MessagingBinding.ProxyAddress"]) body["MessagingBinding.ProxyAddress"] = a["MessagingBinding.ProxyAddress"];
|
|
563
|
+
if (a.Attributes) body.Attributes = a.Attributes;
|
|
564
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("POST", `https://conversations.twilio.com/v1/Conversations/${a.ConversationSid}/Participants`, body), null, 2) }] };
|
|
565
|
+
}
|
|
566
|
+
case "send_conversation_message": {
|
|
567
|
+
const body: Record<string, unknown> = {};
|
|
568
|
+
if (a.Body) body.Body = a.Body;
|
|
569
|
+
if (a.Author) body.Author = a.Author;
|
|
570
|
+
if (a.MediaSid) body.MediaSid = a.MediaSid;
|
|
571
|
+
if (a.Attributes) body.Attributes = a.Attributes;
|
|
572
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("POST", `https://conversations.twilio.com/v1/Conversations/${a.ConversationSid}/Messages`, body), null, 2) }] };
|
|
573
|
+
}
|
|
574
|
+
case "list_messaging_services": {
|
|
575
|
+
const q = buildQuery({ PageSize: a.PageSize });
|
|
576
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("GET", `https://messaging.twilio.com/v1/Services${q}`), null, 2) }] };
|
|
577
|
+
}
|
|
578
|
+
case "execute_studio_flow": {
|
|
579
|
+
const body: Record<string, unknown> = {
|
|
580
|
+
To: a.To,
|
|
581
|
+
From: a.From,
|
|
582
|
+
};
|
|
583
|
+
if (a.Parameters) body.Parameters = a.Parameters;
|
|
584
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("POST", `https://studio.twilio.com/v2/Flows/${a.FlowSid}/Executions`, body), null, 2) }] };
|
|
585
|
+
}
|
|
586
|
+
case "create_taskrouter_task": {
|
|
587
|
+
const body: Record<string, unknown> = {};
|
|
588
|
+
if (a.Attributes) body.Attributes = a.Attributes;
|
|
589
|
+
if (a.WorkflowSid) body.WorkflowSid = a.WorkflowSid;
|
|
590
|
+
if (a.TaskChannel) body.TaskChannel = a.TaskChannel;
|
|
591
|
+
if (a.Priority !== undefined) body.Priority = a.Priority;
|
|
592
|
+
if (a.Timeout !== undefined) body.Timeout = a.Timeout;
|
|
593
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("POST", `https://taskrouter.twilio.com/v1/Workspaces/${a.WorkspaceSid}/Tasks`, body), null, 2) }] };
|
|
594
|
+
}
|
|
595
|
+
case "list_taskrouter_workers": {
|
|
596
|
+
const q = buildQuery({
|
|
597
|
+
ActivityName: a.ActivityName,
|
|
598
|
+
Available: a.Available,
|
|
599
|
+
TargetWorkersExpression: a.TargetWorkersExpression,
|
|
600
|
+
PageSize: a.PageSize,
|
|
601
|
+
});
|
|
602
|
+
return { content: [{ type: "text", text: JSON.stringify(await twilioRequest("GET", `https://taskrouter.twilio.com/v1/Workspaces/${a.WorkspaceSid}/Workers${q}`), null, 2) }] };
|
|
603
|
+
}
|
|
373
604
|
default:
|
|
374
605
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
375
606
|
}
|
|
@@ -392,7 +623,7 @@ async function main() {
|
|
|
392
623
|
if (!sid && isInitializeRequest(req.body)) {
|
|
393
624
|
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
394
625
|
t.onclose = () => { if (t.sessionId) transports.delete(t.sessionId); };
|
|
395
|
-
const s = new Server({ name: "mcp-twilio", version: "0.1
|
|
626
|
+
const s = new Server({ name: "mcp-twilio", version: "0.2.1" }, { capabilities: { tools: {} } });
|
|
396
627
|
(server as unknown as { _requestHandlers: Map<unknown, unknown> })._requestHandlers.forEach((v, k) => (s as unknown as { _requestHandlers: Map<unknown, unknown> })._requestHandlers.set(k, v));
|
|
397
628
|
(server as unknown as { _notificationHandlers?: Map<unknown, unknown> })._notificationHandlers?.forEach((v, k) => (s as unknown as { _notificationHandlers: Map<unknown, unknown> })._notificationHandlers.set(k, v));
|
|
398
629
|
await s.connect(t);
|