sipgateio-mcp
Version:
Sipgate API MCP server for AI code assistants
429 lines (428 loc) • 19.3 kB
JavaScript
#!/usr/bin/env node
/**
* This is an MCP server that implements access to the sipgate API.
* It provides tools and resources for interacting with sipgate services including:
* - Account information
* - Phone numbers and devices
* - SMS messaging
* - Call history and management
*/
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ErrorCode, ListResourcesRequestSchema, ListToolsRequestSchema, McpError, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
import { sipgateIO, createCallModule, createSMSModule, createHistoryModule, createNumbersModule, createDevicesModule } from "sipgateio";
// Get authentication credentials from environment variables
const TOKEN_ID = process.env.SIPGATE_TOKEN_ID || '';
const TOKEN = process.env.SIPGATE_TOKEN || '';
// Check if credentials are provided
if (!TOKEN_ID || !TOKEN) {
throw new Error("SIPGATE_TOKEN_ID and SIPGATE_TOKEN environment variables are required");
}
/**
* SipgateApiServer class that handles all the MCP server functionality
*/
class SipgateApiServer {
server;
client;
callModule;
smsModule;
historyModule;
numbersModule;
devicesModule;
cachedResources = {};
constructor() {
this.server = new Server({
name: "sipgate-api-server",
version: "0.1.0",
}, {
capabilities: {
resources: {},
tools: {},
},
});
// Initialize sipgate client with Personal Access Token
this.client = sipgateIO({
tokenId: TOKEN_ID,
token: TOKEN,
});
// Initialize modules
this.callModule = createCallModule(this.client);
this.smsModule = createSMSModule(this.client);
this.historyModule = createHistoryModule(this.client);
this.numbersModule = createNumbersModule(this.client);
this.devicesModule = createDevicesModule(this.client);
this.setupResourceHandlers();
this.setupToolHandlers();
// Error handling
this.server.onerror = (error) => console.error('[MCP Error]', error);
process.on('SIGINT', async () => {
await this.server.close();
process.exit(0);
});
}
/**
* Setup resource handlers for sipgate data
*/
setupResourceHandlers() {
// List available resources
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: "sipgate://account",
mimeType: "application/json",
name: "Account Information",
description: "Information about your sipgate account"
},
{
uri: "sipgate://numbers",
mimeType: "application/json",
name: "Phone Numbers",
description: "List of phone numbers associated with your sipgate account"
},
{
uri: "sipgate://history",
mimeType: "application/json",
name: "Call History",
description: "Recent call history from your sipgate account"
},
{
uri: "sipgate://devices",
mimeType: "application/json",
name: "Devices",
description: "List of devices associated with your sipgate account"
}
]
};
});
// Handle resource requests
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const uri = request.params.uri;
// Check if we have a cached version of this resource
if (this.cachedResources[uri]) {
return {
contents: [{
uri: uri,
mimeType: "application/json",
text: JSON.stringify(this.cachedResources[uri], null, 2)
}]
};
}
try {
let data;
if (uri === "sipgate://account") {
// Get account information using direct API call
const response = await this.client.get("/account");
data = response;
}
else if (uri === "sipgate://numbers") {
// Get all phone numbers
const numbers = await this.numbersModule.getAllNumbers();
data = numbers;
}
else if (uri === "sipgate://history") {
// Get call history
const history = await this.historyModule.fetchAll({ limit: 20 });
data = history;
}
else if (uri === "sipgate://devices") {
const webuserId = await this.client.getAuthenticatedWebuserId();
const devices = await this.devicesModule.getDevices(webuserId);
data = devices;
}
else {
throw new McpError(ErrorCode.InvalidRequest, `Invalid URI: ${uri}`);
}
// Cache the data
this.cachedResources[uri] = data;
return {
contents: [{
uri: uri,
mimeType: "application/json",
text: JSON.stringify(data, null, 2)
}]
};
}
catch (error) {
throw new McpError(ErrorCode.InternalError, `Sipgate API error: ${error.message}`);
}
});
}
/**
* Setup tool handlers for sipgate operations
*/
setupToolHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_account_info",
description: "Get information about your sipgate account",
inputSchema: {
type: "object",
properties: {},
required: []
}
},
{
name: "get_phone_numbers",
description: "Get a list of phone numbers associated with your sipgate account",
inputSchema: {
type: "object",
properties: {},
required: []
}
},
{
name: "send_sms",
description: "Send an SMS message",
inputSchema: {
type: "object",
properties: {
smsId: {
type: "string",
description: "The SMS ID to use for sending (e.g., 's0')"
},
recipient: {
type: "string",
description: "The recipient's phone number in E.164 format (e.g., +4915123456789)"
},
message: {
type: "string",
description: "The message to send"
}
},
required: ["smsId", "recipient", "message"]
}
},
{
name: "initiate_call",
description: "Initiate a phone call",
inputSchema: {
type: "object",
properties: {
from: {
type: "string",
description: "The extension ID or phone number to use for calling (e.g., 'e0' or '+4915123456789')"
},
to: {
type: "string",
description: "The recipient's phone number in E.164 format"
},
callerId: {
type: "string",
description: "Optional caller ID to display to the recipient"
}
},
required: ["from", "to"]
}
},
{
name: "get_call_history",
description: "Get call history",
inputSchema: {
type: "object",
properties: {
limit: {
type: "number",
description: "Maximum number of entries to return (default: 10)"
},
types: {
type: "array",
items: {
type: "string",
enum: ["CALL", "VOICEMAIL", "FAX", "SMS"]
},
description: "Types of entries to include"
}
},
required: []
}
},
{
name: "get_user_info",
description: "Get information about the authenticated user",
inputSchema: {
type: "object",
properties: {},
required: []
}
},
{
name: "get_devices",
description: "Get a list of devices associated with your sipgate account",
inputSchema: {
type: "object",
properties: {},
required: []
}
}
]
};
});
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
switch (request.params.name) {
case "get_account_info": {
try {
const response = await this.client.get("/account");
const responseText = JSON.stringify(response, null, 2);
return {
content: [{
type: "text",
text: responseText
}]
};
}
catch (error) {
throw new McpError(ErrorCode.InternalError, `Error getting account info: ${error.message}`);
}
}
case "get_phone_numbers": {
try {
const numbers = await this.numbersModule.getAllNumbers();
const responseText = numbers ? JSON.stringify(numbers, null, 2) : "No phone numbers available";
return {
content: [{
type: "text",
text: responseText
}]
};
}
catch (error) {
throw new McpError(ErrorCode.InternalError, `Error getting phone numbers: ${error.message}`);
}
}
case "send_sms": {
try {
const args = request.params.arguments;
if (!args.recipient || !args.message) {
throw new McpError(ErrorCode.InvalidParams, "Missing required parameters: recipient and message are required");
}
// Get WebUserID first
const webuserId = await this.client.getAuthenticatedWebuserId();
// Get SMS extensions
const extensionsResponse = await this.client.get(`/${webuserId}/sms`);
const smsExtensions = extensionsResponse.items || [];
if (smsExtensions.length === 0) {
throw new McpError(ErrorCode.InternalError, "No SMS extensions available for this account");
}
// Use the first available SMS extension
const smsExtension = smsExtensions[0];
const debugInfo = `Debug Info:
Web User ID: ${webuserId}
Using SMS Extension: ${JSON.stringify(smsExtension, null, 2)}`;
// Send SMS using the SMS module with the found extension
const smsData = {
smsId: smsExtension.id,
to: args.recipient.replace(/\s+/g, ''), // Remove any whitespace
message: args.message
};
try {
await this.smsModule.send(smsData);
return {
content: [{
type: "text",
text: `${debugInfo}\n\nSMS sent successfully to ${args.recipient}`
}]
};
}
catch (smsError) {
return {
content: [{
type: "text",
text: `${debugInfo}\n\nSMS Error: ${smsError.message}`,
}],
isError: true
};
}
}
catch (error) {
throw new McpError(ErrorCode.InternalError, `SMS sending failed: ${error.message}`);
}
}
case "initiate_call": {
const args = request.params.arguments;
if (!args.from || !args.to) {
throw new McpError(ErrorCode.InvalidParams, "Missing required parameters: from and to are required");
}
const callData = {
from: args.from,
to: args.to
};
if (args.callerId) {
callData.callerId = args.callerId;
}
await this.callModule.initiate(callData);
return {
content: [{
type: "text",
text: `Call initiated from ${args.from} to ${args.to}`
}]
};
}
case "get_call_history": {
const args = request.params.arguments;
const limit = args.limit || 10;
const types = args.types || ["CALL", "VOICEMAIL", "FAX", "SMS"];
const historyFilter = {
limit,
types
};
const history = await this.historyModule.fetchAll(historyFilter);
const responseText = history ? JSON.stringify(history, null, 2) : "No call history available";
return {
content: [{
type: "text",
text: responseText
}]
};
}
case "get_user_info": {
const webuserId = await this.client.getAuthenticatedWebuserId();
const response = await this.client.get(`/users/${webuserId}`);
const responseText = JSON.stringify(response, null, 2);
return {
content: [{
type: "text",
text: responseText
}]
};
}
case "get_devices": {
const webuserId = await this.client.getAuthenticatedWebuserId();
const devices = await this.devicesModule.getDevices(webuserId);
return {
content: [{
type: "text",
text: devices ? JSON.stringify(devices, null, 2) : "No devices available"
}]
};
}
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
}
}
catch (error) {
return {
content: [{
type: "text",
text: `Sipgate API error: ${error.message}`
}],
isError: true
};
}
});
}
/**
* Start the server
*/
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('Sipgate API MCP server running on stdio');
}
}
// Create and run the server
const server = new SipgateApiServer();
server.run().catch(console.error);