forked from deshartman/twilio-messaging-mcp-server
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
190 lines (166 loc) · 6.03 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/usr/bin/env node
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { TwilioMessagingServer } from "./servers/TwilioMessagingServer.js";
// Get configuration parameters from the command line arguments
/****************************************************
*
* Twilio API Credentials
*
****************************************************/
// NOTE: we are enforcing use of API Keys here instead of Auth Token, as it is a better posture for message level sends
const accountSid = process.argv[2] || '';
const apiKey = process.argv[3] || '';
const apiSecret = process.argv[4] || '';
const number = process.argv[5] || '';
// Validate required configuration
if (!accountSid || !apiKey || !apiSecret || !number) {
console.error("Usage: twilio-messaging-mcp-server <accountSid> <apiKey> <apiSecret> <number>");
process.exit(1);
}
// Validate Twilio Account SID format
if (!accountSid.startsWith('AC')) {
console.error(`TwilioMessagingServer: Invalid Account SID format. Twilio Account SID must start with 'AC'`);
process.exit(1);
}
// Create Twilio service with provided credentials
const twilioMessagingServer = new TwilioMessagingServer(accountSid, apiKey, apiSecret, number);
// Set up the callback handler to log messages and also set the callback Data
twilioMessagingServer.on('log', (data: { level: string, message: string }) => {
// Forward logs to the MCP server
// Only use valid log levels: info, error, debug. If level is 'warn', treat it as 'info'
const mcpLevel = data.level === 'warn' ? 'info' : data.level as "info" | "error" | "debug";
// Send the log message to the MCP server's underlying Server instance
mcpServer.server.sendLoggingMessage({
level: mcpLevel,
data: data.message,
});
});
let callbackData: any = null;
// Set up the callback handler to log messages and also set the callback Data
twilioMessagingServer.on('callback', (callbackMessage) => {
callbackData = JSON.stringify(callbackMessage, null, 2);
});
/****************************************************
*
* MCP server
*
****************************************************/
// Server configuration with clear naming for the messaging service
const SERVER_CONFIG = {
name: "TwilioMessagingServer",
description: "MCP server for sending SMS messages via Twilio API",
version: "1.0.0"
};
const MCP_CAPABILITIES = {
capabilities: {
tools: {},
resources: {},
prompts: {},
logging: {} // Add logging capability
}
}
const mcpServer = new McpServer(SERVER_CONFIG, MCP_CAPABILITIES);
// Define schemas for Twilio messaging
const messageSchema = z.object({
to: z.string().describe("The Twilio To number in +E.164 format (+XXXXXXXXXX)"),
message: z.string().describe("The message to send")
});
// Register the SMS sending tool
mcpServer.tool(
"send-sms",
"Send an SMS message via Twilio",
messageSchema.shape,
async ({ to, message }) => {
try {
const response = await twilioMessagingServer.sendSMS(to, message);
const sid = response?.sid;
if (sid) {
return {
content: [{
type: "text",
text: `Message sent successfully. SID: ${sid}`
}]
};
} else {
return {
content: [{
type: "text",
text: "Failed to send message. Check logs for details."
}],
isError: true
};
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error(`TwilioMessagingServer: Error sending SMS: ${errorMessage}`);
return {
content: [{
type: "text",
text: `Error sending message: ${errorMessage}`
}],
isError: true
};
}
}
);
// Create a resource template for the status callback for the Messaging API to uri: Accounts/{AccountSid}/Messages.json
mcpServer.resource(
"twilio-status-callback", // name
"twilio://statuscallback", // Resource URI
{ description: "Get the last raw status callback data from Twilio" }, // ResourceMetadata
async (uri) => {
// Get the latest data from Twilio
return {
contents: [
{
uri: uri.toString(),
text: callbackData,
mimeType: "application/json"
}
]
};
}
)
// create a new mcpServer.prompt to tell the LLM how to use the tool and how to call the resource for status updates
// Register prompts using the built-in prompt method
mcpServer.prompt(
"SendSMS",
"Prompt for sending an SMS using Twilio Messaging MCP Server",
messageSchema.shape,
(args, extra) => {
const { to, message } = args;
return {
messages: [
{
role: "assistant",
content: {
type: "text",
text: `To send an SMS message to ${to}, use the 'send-sms' tool with the following parameters:\n\n- to: ${to}\n- message: ${message}`
}
}
]
};
}
);
// Start the server
async function main() {
try {
const transport = new StdioServerTransport();
await mcpServer.connect(transport);
} catch (error) {
console.error(`TwilioMessagingServer: Error starting server: ${error}`);
process.exit(1);
}
}
// Handle clean shutdown
process.on("SIGINT", async () => {
await mcpServer.close();
process.exit(0);
});
// Start the server
main().catch(error => {
console.error(`TwilioMessagingServer: Fatal error: ${error}`);
process.exit(1);
});