@digitalsamba/embedded-api-mcp-server
Version:
Digital Samba Embedded API MCP Server - Model Context Protocol server for Digital Samba's Embedded API
360 lines • 15.8 kB
JavaScript
/**
* Digital Samba MCP Server
*
* A Model Context Protocol server that provides AI assistants with tools
* to interact with Digital Samba's video conferencing API.
*/
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 { config as loadEnv } from "dotenv";
// Load environment variables
loadEnv();
// Local imports
import { DigitalSambaApiClient } from "./digital-samba-api.js";
import logger from "./logger.js";
// Import resources and tools
import { registerRoomResources, handleRoomResource, } from "./resources/rooms/index.js";
import { registerSessionResources, handleSessionResource, } from "./resources/sessions/index.js";
import { registerAnalyticsResources, handleAnalyticsResource, } from "./resources/analytics/index.js";
import { registerRecordingResources, handleRecordingResource, } from "./resources/recordings-adapter.js";
import { ExportResources, registerExportResources, } from "./resources/exports/index.js";
import { registerContentResources, handleContentResource, } from "./resources/content/index.js";
import { registerRoomTools, executeRoomTool, } from "./tools/room-management/index.js";
import { registerSessionTools, executeSessionTool, } from "./tools/session-management/index.js";
import { registerRecordingTools, executeRecordingTool, } from "./tools/recording-tools-adapter.js";
import { registerAnalyticsTools, executeAnalyticsTool, } from "./tools/analytics-tools/index.js";
import { registerLiveSessionTools, executeLiveSessionTool, } from "./tools/live-session-controls/index.js";
import { registerCommunicationTools, executeCommunicationTool, } from "./tools/communication-management/index.js";
import { registerPollTools, executePollTool, } from "./tools/poll-management/index.js";
import { registerLibraryTools, executeLibraryTool, } from "./tools/library-management/index.js";
import { registerRoleTools, executeRoleTool, } from "./tools/role-management/index.js";
import { registerWebhookTools, executeWebhookTool, } from "./tools/webhook-management/index.js";
import { registerExportTools, executeExportTool, } from "./tools/export-tools/index.js";
// Import version information
import { VERSION, PACKAGE_NAME, VERSION_INFO } from "./version.js";
// Create MCP server with actual version
const server = new Server({
name: "digital-samba",
version: VERSION,
}, {
capabilities: {
resources: {},
tools: {},
},
});
// API client instance (created per request with API key)
let apiClient = null;
/**
* Get or create API client with the provided API key
*/
function getApiClient(apiKey) {
if (!apiClient) {
const baseUrl = process.env.DIGITAL_SAMBA_API_URL ||
"https://api.digitalsamba.com/api/v1";
apiClient = new DigitalSambaApiClient(apiKey, baseUrl);
}
return apiClient;
}
// Add version resource
const versionResource = {
uri: "digitalsamba://version",
name: "server-version",
description: `[Server Info] Get the current version and build information of ${PACKAGE_NAME}. Use when users ask: "what version is this", "check version", "server version", "mcp version", "build info".`,
mimeType: "application/json",
};
// Register handlers
server.setRequestHandler(ListResourcesRequestSchema, async () => {
// For listing resources, we don't need an API key - just return the schema
// API key will be checked when actually reading resources
const apiKey = process.env.DIGITAL_SAMBA_DEVELOPER_KEY;
// Create a dummy client if needed for analytics registration
let client = null;
if (apiKey) {
client = getApiClient(apiKey);
}
return {
resources: [
versionResource,
...registerRoomResources(),
...registerSessionResources(),
...registerRecordingResources(),
...(client ? registerAnalyticsResources(client) : []),
...registerExportResources(),
...registerContentResources(),
],
};
});
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const apiKey = process.env.DIGITAL_SAMBA_DEVELOPER_KEY;
if (!apiKey) {
throw new McpError(ErrorCode.InvalidRequest, "DIGITAL_SAMBA_DEVELOPER_KEY not configured");
}
const client = getApiClient(apiKey);
const { uri } = request.params;
logger.debug(`Reading resource: ${uri}`);
// Handle version resource
if (uri === "digitalsamba://version") {
return {
contents: [
{
uri: uri,
text: JSON.stringify(VERSION_INFO, null, 2),
mimeType: "application/json",
},
],
};
}
// Route to appropriate handler based on URI prefix
if (uri.startsWith("digitalsamba://rooms")) {
return handleRoomResource(uri, {}, request, {
apiUrl: process.env.DIGITAL_SAMBA_API_URL ||
"https://api.digitalsamba.com/api/v1",
});
}
else if (uri.startsWith("digitalsamba://sessions")) {
return handleSessionResource(uri, {}, request, {
apiUrl: process.env.DIGITAL_SAMBA_API_URL ||
"https://api.digitalsamba.com/api/v1",
});
}
else if (uri.startsWith("digitalsamba://recordings")) {
return handleRecordingResource(uri, client);
}
else if (uri.startsWith("digitalsamba://analytics")) {
return handleAnalyticsResource(uri, client);
}
else if (uri.startsWith("digitalsamba://exports")) {
const exportResources = new ExportResources(client);
return exportResources.handleResourceRequest(uri);
}
else if (uri.startsWith("digitalsamba://content")) {
return handleContentResource(uri, client);
}
throw new McpError(ErrorCode.InvalidRequest, `Unknown resource URI: ${uri}`);
});
server.setRequestHandler(ListToolsRequestSchema, async () => {
// For listing tools, we don't need an API key - just return the schema
// API key will be checked when actually executing tools
const allTools = [
{
name: "get-server-version",
description: `[Server Info] Get the current version and build information of the MCP server. Use to check: "server version", "what version is running", "is this the latest version", "build time". Current version: ${VERSION}`,
inputSchema: {
type: "object",
properties: {},
},
},
...registerRoomTools(),
...registerSessionTools(),
...registerRecordingTools(),
...registerAnalyticsTools(),
...registerLiveSessionTools(),
...registerCommunicationTools(),
...registerPollTools(),
...registerLibraryTools(),
...registerRoleTools(),
...registerWebhookTools(),
...registerExportTools(),
];
// Log export tools for debugging
const exportTools = allTools.filter(t => t.name.includes('export'));
logger.debug(`ListToolsRequest: Registered ${allTools.length} total tools`);
logger.debug(`Export tools (${exportTools.length}): ${exportTools.map(t => t.name).join(', ')}`);
return {
tools: allTools,
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const apiKey = process.env.DIGITAL_SAMBA_DEVELOPER_KEY;
if (!apiKey) {
throw new McpError(ErrorCode.InvalidRequest, "DIGITAL_SAMBA_DEVELOPER_KEY not configured");
}
const client = getApiClient(apiKey);
const { name, arguments: args } = request.params;
logger.debug(`Executing tool: ${name}`);
try {
// Route to appropriate tool handler based on name
// Version tool
if (name === "get-server-version") {
return {
content: [
{
type: "text",
text: JSON.stringify(VERSION_INFO, null, 2),
},
],
};
}
// Room management tools
else if (name === "create-room" ||
name === "update-room" ||
name === "delete-room" ||
name === "generate-token" ||
name === "get-default-room-settings" ||
name === "update-default-room-settings" ||
name === "list-rooms" ||
name === "get-room-details" ||
name === "list-live-rooms" ||
name === "list-live-participants") {
return await executeRoomTool(name, args || {}, request, {
apiUrl: process.env.DIGITAL_SAMBA_API_URL ||
"https://api.digitalsamba.com/api/v1",
});
}
// Analytics tools
else if (name === "get-participant-statistics" ||
name === "get-room-analytics" ||
name === "get-usage-statistics" ||
name === "get-usage-analytics" ||
name === "get-live-analytics" ||
name === "get-live-room-analytics" ||
name === "get-session-analytics" ||
name === "get-participant-analytics") {
return await executeAnalyticsTool(name, args || {}, client);
}
// Session management tools
else if (name === "end-session" ||
name === "get-session-summary" ||
name === "get-all-room-sessions" ||
name === "hard-delete-session-resources" ||
name === "bulk-delete-session-data" ||
name === "get-session-statistics" ||
name === "list-sessions" ||
name === "get-session-details" ||
name === "list-session-participants" ||
name === "list-room-sessions") {
return await executeSessionTool(name, args || {}, client, request);
}
// Recording management tools
else if (name.includes("recording") ||
name === "get-recordings" ||
name === "archive-recording" ||
name === "unarchive-recording") {
return await executeRecordingTool(name, args || {}, client);
}
// Live session control tools
else if (name === "start-transcription" ||
name === "stop-transcription" ||
name === "phone-participants-joined" ||
name === "phone-participants-left") {
return await executeLiveSessionTool(name, args || {}, client);
}
// Export tools (moved before communication tools to avoid conflicts)
else if (name.includes("export-")) {
logger.debug(`Routing export tool: ${name} to executeExportTool`);
return await executeExportTool(name, args || {}, request, {
apiUrl: process.env.DIGITAL_SAMBA_API_URL ||
"https://api.digitalsamba.com/api/v1",
});
}
// Communication management tools
else if (name.includes("-chats") ||
name.includes("-qa") ||
name.includes("-transcripts") ||
name.includes("-summaries")) {
return await executeCommunicationTool(name, args || {}, client);
}
// Poll management tools
else if (name.includes("poll")) {
return await executePollTool(name, args || {}, client);
}
// Library management tools
else if (name.includes("library") ||
name.includes("libraries") ||
name.includes("webapp") ||
name.includes("whiteboard") ||
name === "get-file-links") {
return await executeLibraryTool(name, args || {}, client);
}
// Role management tools
else if (name.includes("role") || name === "get-permissions") {
return await executeRoleTool(name, args || {}, client);
}
// Webhook management tools
else if (name.includes("webhook") || name === "list-webhook-events") {
return await executeWebhookTool(name, args || {}, client);
}
logger.error(`Unknown tool requested: ${name}`, {
availablePatterns: [
'get-server-version',
'create-room, update-room, delete-room, etc.',
'get-participant-statistics, get-room-analytics, etc.',
'end-session, get-session-summary, etc.',
'delete-recording, update-recording, etc.',
'start-transcription, stop-transcription, etc.',
'delete-session-chats, delete-room-chats, etc.',
'create-poll, update-poll, etc.',
'includes("library") or includes("libraries")',
'includes("role") or get-permissions',
'includes("webhook") or list-webhook-events',
'includes("export-")'
]
});
throw new McpError(ErrorCode.InvalidRequest, `Unknown tool: ${name}`);
}
catch (error) {
logger.error(`Tool execution error: ${error.message}`, error);
if (error instanceof McpError) {
throw error;
}
throw new McpError(ErrorCode.InternalError, `Failed to execute tool ${name}: ${error.message}`);
}
});
/**
* Start the MCP server
*/
async function main() {
const args = process.argv.slice(2);
// Check for developer key in command line args
const apiKeyArgIndex = args.findIndex((arg) => arg === "--developer-key" || arg === "-k");
if (apiKeyArgIndex !== -1 && args[apiKeyArgIndex + 1]) {
process.env.DIGITAL_SAMBA_DEVELOPER_KEY = args[apiKeyArgIndex + 1];
}
// Log developer key status (don't exit if not found - it will be checked when needed)
if (!process.env.DIGITAL_SAMBA_DEVELOPER_KEY) {
logger.warn("DIGITAL_SAMBA_DEVELOPER_KEY not set. Developer key will be required for operations.");
}
else {
logger.info("Developer key configured");
}
// Create stdio transport
const transport = new StdioServerTransport();
// Connect server to transport
await server.connect(transport);
// Show version on start if enabled (default: true)
if (process.env.DS_SHOW_VERSION_ON_START !== "false") {
logger.info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
logger.info(`Digital Samba Embedded API MCP Server`);
logger.info(`Package: ${PACKAGE_NAME}`);
logger.info(`Version: ${VERSION}`);
if (VERSION_INFO.buildTime !== "development") {
logger.info(`Build: ${VERSION_INFO.buildTime}`);
}
logger.info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
logger.info("Server started and waiting for client connection...");
}
else {
logger.info(`${PACKAGE_NAME}@${VERSION} started`);
}
logger.debug(`Full build info:`, VERSION_INFO);
// Handle graceful shutdown
process.on("SIGINT", async () => {
logger.info("Shutting down Digital Samba MCP Server...");
await server.close();
process.exit(0);
});
}
// Export main for programmatic use
export { main };
// Run the server if this is the main module
// Skip this check during testing
if (typeof jest === "undefined" &&
import.meta.url === `file://${process.argv[1]}`) {
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});
}
//# sourceMappingURL=index.js.map