UNPKG

mcp-sms-ir

Version:

MCP Server for SMS.ir messaging services

350 lines (317 loc) 9.77 kB
#!/usr/bin/env node import axios, { AxiosInstance, AxiosError } from 'axios'; // Use type declarations to make TypeScript happy declare module '@modelcontextprotocol/sdk' { export class Server { constructor(info: any, options: any); connect(transport: any): Promise<void>; close(): Promise<void>; setRequestHandler(schema: any, handler: Function): void; onerror: (error: any) => void; } export class StdioServerTransport { constructor(); } export const CallToolRequestSchema: any; export const ListToolsRequestSchema: any; export const ErrorCode: { MethodNotFound: string; }; export class McpError extends Error { constructor(code: string, message: string); } } // Import dynamically to avoid TypeScript errors const MCP_SDK = '@modelcontextprotocol/sdk'; // SMS.ir API base URL const BASE_URL = 'https://api.sms.ir/v1'; // Parameter interfaces interface SendSmsParams { mobile: string; message: string; lineNumber?: string; sendDateTime?: string; } interface SendBulkSmsParams { mobiles: string[]; messageText: string; lineNumber?: string; sendDateTime?: string; } interface VerificationParams { mobile: string; templateId: string; parameters: Array<{name: string; value: string}>; } class SmsIrServer { private server: Server; private axiosInstance: AxiosInstance; constructor(apiKey: string) { if (!apiKey) { throw new Error('SMS_IR_API_KEY is required'); } this.server = new Server( { name: 'sms-ir-server', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); // Configure axios instance with default headers and base URL this.axiosInstance = axios.create({ baseURL: BASE_URL, headers: { 'Content-Type': 'application/json', 'Accept': 'text/plain', 'x-api-key': apiKey, } }); this.setupToolHandlers(); // Error handling this.server.onerror = (error: any) => console.error('[MCP Error]', error); process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } private setupToolHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'send_sms', description: 'Send a SMS message to a single recipient', inputSchema: { type: 'object', properties: { mobile: { type: 'string', description: 'Recipient mobile number (e.g., 09121234567)', }, message: { type: 'string', description: 'Message content', }, lineNumber: { type: 'string', description: 'Sender line number (optional)', }, sendDateTime: { type: 'string', description: 'Scheduled date and time for sending the message (optional, ISO format)', }, }, required: ['mobile', 'message'], }, }, { name: 'send_bulk_sms', description: 'Send the same SMS message to multiple recipients', inputSchema: { type: 'object', properties: { mobiles: { type: 'array', items: { type: 'string', }, description: 'Array of recipient mobile numbers', }, messageText: { type: 'string', description: 'Message content to send to all recipients', }, lineNumber: { type: 'string', description: 'Sender line number (optional)', }, sendDateTime: { type: 'string', description: 'Scheduled date and time for sending the message (optional, ISO format)', }, }, required: ['mobiles', 'messageText'], }, }, { name: 'send_verification_code', description: 'Send a verification code SMS using a template', inputSchema: { type: 'object', properties: { mobile: { type: 'string', description: 'Recipient mobile number', }, templateId: { type: 'string', description: 'Template ID from SMS.ir panel', }, parameters: { type: 'array', items: { type: 'object', properties: { name: { type: 'string', description: 'Parameter name as defined in the template', }, value: { type: 'string', description: 'Parameter value to replace in the template', }, }, required: ['name', 'value'], }, description: 'Array of parameters to substitute in the template', }, }, required: ['mobile', 'templateId', 'parameters'], }, }, { name: 'check_credit', description: 'Check the remaining credit in your SMS.ir account', inputSchema: { type: 'object', properties: {}, required: [], }, }, ], })); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request: any) => { try { switch (request.params.name) { case 'send_sms': { return await this.handleSendSms(request.params.arguments as SendSmsParams); } case 'send_bulk_sms': { return await this.handleSendBulkSms(request.params.arguments as SendBulkSmsParams); } case 'send_verification_code': { return await this.handleSendVerification(request.params.arguments as VerificationParams); } case 'check_credit': { return await this.handleCheckCredit(); } default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`); } } catch (error: any) { if (axios.isAxiosError(error)) { const axiosError = error as AxiosError; const statusCode = axiosError.response?.status; const responseData = axiosError.response?.data; return { content: [ { type: 'text', text: `SMS.ir API error (${statusCode}): ${JSON.stringify(responseData)}`, }, ], isError: true, }; } return { content: [ { type: 'text', text: `Unexpected error: ${error.message || JSON.stringify(error)}`, }, ], isError: true, }; } }); } private async handleSendSms(params: SendSmsParams) { const response = await this.axiosInstance.post('/send', { mobile: params.mobile, message: params.message, lineNumber: params.lineNumber, sendDateTime: params.sendDateTime, }); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], }; } private async handleSendBulkSms(params: SendBulkSmsParams) { const response = await this.axiosInstance.post('/send/bulk', { lineNumber: params.lineNumber, messageText: params.messageText, mobiles: params.mobiles, sendDateTime: params.sendDateTime, }); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], }; } private async handleSendVerification(params: VerificationParams) { const response = await this.axiosInstance.post('/send/verify', { mobile: params.mobile, templateId: params.templateId, parameters: params.parameters, }); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], }; } private async handleCheckCredit() { const response = await this.axiosInstance.get('/credit'); return { content: [ { type: 'text', text: JSON.stringify(response.data, null, 2), }, ], }; } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('SMS.ir MCP server running on stdio'); } } // Initialize and run the server const main = async () => { try { // Dynamically import the SDK const sdk = await import(MCP_SDK); const { Server, StdioServerTransport, CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError } = sdk; // Extend the SmsIrServer class with the imported SDK Object.assign(SmsIrServer.prototype, { Server, StdioServerTransport, CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError }); // Get API key from environment variables const API_KEY = process.env.SMS_IR_API_KEY; if (!API_KEY) { throw new Error('SMS_IR_API_KEY environment variable is required'); } const server = new SmsIrServer(API_KEY); await server.run(); } catch (error) { console.error('Failed to start SMS.ir MCP server:', error); process.exit(1); } }; main();