UNPKG

@digitalsamba/embedded-api-mcp-server

Version:

Digital Samba Embedded API MCP Server - Model Context Protocol server for Digital Samba's Embedded API

659 lines 23.1 kB
/** * Digital Samba MCP Server - Poll Management Tools * * This module implements tools for managing polls within Digital Samba sessions. * It provides MCP tools for creating, updating, deleting, and managing poll results. * * Tools provided: * - create-poll: Create a new poll in a session * - update-poll: Update an existing poll * - delete-poll: Delete a specific poll * - delete-session-polls: Delete all polls for a session * - delete-room-polls: Delete all polls for all sessions in a room * - publish-poll-results: Publish poll results to participants * * @module tools/poll-management * @author Digital Samba Team * @version 1.0.0 */ // External dependencies // import { z } from "zod"; // Removed: unused // MCP SDK imports // import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; // TODO: Direct MCP server integration import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js"; import logger from "../../logger.js"; // Removed Zod schema - using JSON Schema directly in tool definitions /** * Register poll management tools with the MCP SDK * * @returns {ToolDefinition[]} Array of tool definitions */ export function registerPollTools() { return [ { name: "create-poll", description: '[Poll Management] Create a new poll/survey in a room. Use when users say: "create a poll", "add a survey", "make a poll", "create voting question", "add multiple choice question". Requires roomId, question, and at least 2 options. Returns poll ID for tracking.', inputSchema: { type: "object", properties: { roomId: { type: "string", description: "The ID of the room to create the poll in", }, question: { type: "string", description: "The poll question", }, options: { type: "array", items: { type: "object", properties: { text: { type: "string", description: "The text of the poll option", }, id: { type: "string", description: "Optional ID for the option", }, }, required: ["text"], }, minItems: 2, description: "Array of poll options (minimum 2)", }, type: { type: "string", enum: ["single", "multiple"], description: "Poll type: single choice or multiple choice", }, anonymous: { type: "boolean", description: "Whether the poll is anonymous", }, showResults: { type: "boolean", description: "Whether to show results to participants", }, }, required: ["roomId", "question", "options"], }, }, { name: "update-poll", description: '[Poll Management] Update an existing poll\'s question, options, or settings. Use when users say: "change poll question", "update poll", "edit poll options", "modify survey", "change poll settings". Requires roomId and pollId. Can update question, options, type, or visibility settings.', inputSchema: { type: "object", properties: { roomId: { type: "string", description: "The ID of the room containing the poll", }, pollId: { type: "string", description: "The ID of the poll to update", }, question: { type: "string", description: "Updated poll question", }, options: { type: "array", items: { type: "object", properties: { text: { type: "string", description: "The text of the poll option", }, id: { type: "string", description: "Optional ID for the option", }, }, required: ["text"], }, description: "Updated poll options", }, type: { type: "string", enum: ["single", "multiple"], description: "Updated poll type", }, anonymous: { type: "boolean", description: "Updated anonymous setting", }, showResults: { type: "boolean", description: "Updated show results setting", }, }, required: ["roomId", "pollId"], }, }, { name: "delete-poll", description: '[Poll Management] Delete a specific poll from a room. Use when users say: "delete poll", "remove poll", "delete survey", "remove voting question", "cancel poll". Requires roomId and pollId. This action cannot be undone.', inputSchema: { type: "object", properties: { roomId: { type: "string", description: "The ID of the room containing the poll", }, pollId: { type: "string", description: "The ID of the poll to delete", }, }, required: ["roomId", "pollId"], }, }, { name: "delete-session-polls", description: '[Poll Management] Delete ALL polls from a specific session. Use when users say: "delete all session polls", "remove all polls from session", "clear session polls", "delete all surveys from meeting". Requires sessionId. Removes all poll data from that session.', inputSchema: { type: "object", properties: { sessionId: { type: "string", description: "The ID of the session to delete polls from", }, }, required: ["sessionId"], }, }, { name: "delete-room-polls", description: '[Poll Management] Delete ALL polls from ALL sessions in a room. Use when users say: "delete all room polls", "remove all polls from room", "clear room poll history", "wipe all room surveys". Requires roomId. Affects all past and current session polls.', inputSchema: { type: "object", properties: { roomId: { type: "string", description: "The ID of the room to delete polls from", }, }, required: ["roomId"], }, }, { name: "publish-poll-results", description: '[Poll Management] Publish/share poll results with participants. Use when users say: "show poll results", "publish poll results", "share voting results", "display poll outcome", "reveal survey results". Requires roomId, pollId, and sessionId. Makes results visible to all participants.', inputSchema: { type: "object", properties: { roomId: { type: "string", description: "The ID of the room containing the poll", }, pollId: { type: "string", description: "The ID of the poll to publish results for", }, sessionId: { type: "string", description: "The ID of the specific session (optional)", }, }, required: ["roomId", "pollId"], }, }, ]; } /** * Execute a poll management tool * * @param {string} toolName - Name of the tool to execute * @param {any} params - Tool parameters * @param {DigitalSambaApiClient} apiClient - API client instance * @returns {Promise<any>} Tool execution result */ export async function executePollTool(toolName, params, apiClient) { switch (toolName) { case "create-poll": return handleCreatePoll(params, apiClient); case "update-poll": return handleUpdatePoll(params, apiClient); case "delete-poll": return handleDeletePoll(params, apiClient); case "delete-session-polls": return handleDeleteSessionPolls(params, apiClient); case "delete-room-polls": return handleDeleteRoomPolls(params, apiClient); case "publish-poll-results": return handlePublishPollResults(params, apiClient); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${toolName}`); } } /** * Handle create poll */ async function handleCreatePoll(params, apiClient) { const { roomId, question, options, type = "single", anonymous = false, showResults = true, } = params; if (!roomId || roomId.trim() === "") { return { content: [ { type: "text", text: "Room ID is required to create a poll.", }, ], isError: true, }; } if (!question || question.trim() === "") { return { content: [ { type: "text", text: "Poll question is required.", }, ], isError: true, }; } if (!options || options.length < 2) { return { content: [ { type: "text", text: "At least 2 poll options are required.", }, ], isError: true, }; } logger.info("Creating poll", { roomId, question, optionCount: options.length, }); try { const pollData = { question, options, type, anonymous, show_results: showResults, }; const result = await apiClient.createPoll(roomId, pollData); return { content: [ { type: "text", text: `Successfully created poll "${question}" with ${options.length} options in room ${roomId}. Poll ID: ${result.id}`, }, ], }; } catch (error) { logger.error("Error creating poll", { roomId, error: error instanceof Error ? error.message : String(error), }); const errorMessage = error instanceof Error ? error.message : String(error); let displayMessage = `Error creating poll: ${errorMessage}`; if (errorMessage.includes("Room not found") || errorMessage.includes("404")) { displayMessage = `Room with ID ${roomId} not found`; } return { content: [ { type: "text", text: displayMessage, }, ], isError: true, }; } } /** * Handle update poll */ async function handleUpdatePoll(params, apiClient) { const { roomId, pollId, ...updateData } = params; if (!roomId || roomId.trim() === "") { return { content: [ { type: "text", text: "Room ID is required to update a poll.", }, ], isError: true, }; } if (!pollId || pollId.trim() === "") { return { content: [ { type: "text", text: "Poll ID is required to update a poll.", }, ], isError: true, }; } // Check if there's anything to update const hasUpdates = Object.keys(updateData).length > 0; if (!hasUpdates) { return { content: [ { type: "text", text: "No updates provided for the poll.", }, ], isError: true, }; } logger.info("Updating poll", { pollId, updates: Object.keys(updateData) }); try { // Transform showResults to show_results for API const apiUpdateData = { ...updateData }; if ("showResults" in apiUpdateData) { apiUpdateData.show_results = apiUpdateData.showResults; delete apiUpdateData.showResults; } await apiClient.updatePoll(roomId, pollId, apiUpdateData); const updateSummary = Object.keys(updateData).join(", "); return { content: [ { type: "text", text: `Successfully updated poll ${pollId}. Updated fields: ${updateSummary}`, }, ], }; } catch (error) { logger.error("Error updating poll", { pollId, error: error instanceof Error ? error.message : String(error), }); const errorMessage = error instanceof Error ? error.message : String(error); let displayMessage = `Error updating poll: ${errorMessage}`; if (errorMessage.includes("Poll not found") || errorMessage.includes("404")) { displayMessage = `Poll with ID ${pollId} not found`; } return { content: [ { type: "text", text: displayMessage, }, ], isError: true, }; } } /** * Handle delete poll */ async function handleDeletePoll(params, apiClient) { const { roomId, pollId } = params; if (!roomId || roomId.trim() === "") { return { content: [ { type: "text", text: "Room ID is required to delete a poll.", }, ], isError: true, }; } if (!pollId || pollId.trim() === "") { return { content: [ { type: "text", text: "Poll ID is required to delete a poll.", }, ], isError: true, }; } logger.info("Deleting poll", { roomId, pollId }); try { await apiClient.deletePoll(roomId, pollId); return { content: [ { type: "text", text: `Successfully deleted poll ${pollId}`, }, ], }; } catch (error) { logger.error("Error deleting poll", { pollId, error: error instanceof Error ? error.message : String(error), }); const errorMessage = error instanceof Error ? error.message : String(error); let displayMessage = `Error deleting poll: ${errorMessage}`; if (errorMessage.includes("Poll not found") || errorMessage.includes("404")) { displayMessage = `Poll with ID ${pollId} not found`; } return { content: [ { type: "text", text: displayMessage, }, ], isError: true, }; } } /** * Handle delete session polls */ async function handleDeleteSessionPolls(params, apiClient) { const { sessionId } = params; if (!sessionId || sessionId.trim() === "") { return { content: [ { type: "text", text: "Session ID is required to delete polls.", }, ], isError: true, }; } logger.info("Deleting session polls", { sessionId }); try { await apiClient.deleteSessionPolls(sessionId); return { content: [ { type: "text", text: `Successfully deleted all polls for session ${sessionId}`, }, ], }; } catch (error) { logger.error("Error deleting session polls", { sessionId, error: error instanceof Error ? error.message : String(error), }); const errorMessage = error instanceof Error ? error.message : String(error); let displayMessage = `Error deleting session polls: ${errorMessage}`; if (errorMessage.includes("Session not found") || errorMessage.includes("404")) { displayMessage = `Session with ID ${sessionId} not found`; } return { content: [ { type: "text", text: displayMessage, }, ], isError: true, }; } } /** * Handle delete room polls */ async function handleDeleteRoomPolls(params, apiClient) { const { roomId } = params; if (!roomId || roomId.trim() === "") { return { content: [ { type: "text", text: "Room ID is required to delete polls.", }, ], isError: true, }; } logger.info("Deleting room polls", { roomId }); try { // Get all sessions for the room const sessionsResponse = await apiClient.listSessions({ room_id: roomId }); const sessions = sessionsResponse.data; if (!sessions || sessions.length === 0) { return { content: [ { type: "text", text: `No sessions found for room ${roomId}`, }, ], }; } // Delete polls for each session let deletedCount = 0; for (const session of sessions) { try { await apiClient.deleteSessionPolls(session.id); deletedCount++; } catch (error) { logger.warn("Failed to delete polls for session", { sessionId: session.id, error: error instanceof Error ? error.message : String(error), }); } } return { content: [ { type: "text", text: `Successfully deleted polls from ${deletedCount} sessions in room ${roomId}`, }, ], }; } catch (error) { logger.error("Error deleting room polls", { roomId, error: error instanceof Error ? error.message : String(error), }); return { content: [ { type: "text", text: `Error deleting room polls: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } /** * Handle publish poll results */ async function handlePublishPollResults(params, apiClient) { const { roomId, pollId, sessionId } = params; if (!roomId || roomId.trim() === "") { return { content: [ { type: "text", text: "Room ID is required to publish results.", }, ], isError: true, }; } if (!pollId || pollId.trim() === "") { return { content: [ { type: "text", text: "Poll ID is required to publish results.", }, ], isError: true, }; } logger.info("Publishing poll results", { roomId, pollId, sessionId }); try { // The API method expects sessionId as a required parameter // If not provided, we'll need to get the current session or return an error if (!sessionId) { return { content: [ { type: "text", text: "Session ID is required to publish poll results. Please provide the session ID.", }, ], isError: true, }; } await apiClient.publishPollResults(pollId, sessionId); return { content: [ { type: "text", text: `Successfully published results for poll ${pollId} in room ${roomId} (session ${sessionId})`, }, ], }; } catch (error) { logger.error("Error publishing poll results", { roomId, pollId, sessionId, error: error instanceof Error ? error.message : String(error), }); const errorMessage = error instanceof Error ? error.message : String(error); let displayMessage = `Error publishing poll results: ${errorMessage}`; if (errorMessage.includes("Poll not found") || errorMessage.includes("404")) { displayMessage = `Poll with ID ${pollId} not found`; } else if (errorMessage.includes("Session not found")) { displayMessage = `Session with ID ${sessionId} not found`; } else if (errorMessage.includes("Room not found")) { displayMessage = `Room with ID ${roomId} not found`; } return { content: [ { type: "text", text: displayMessage, }, ], isError: true, }; } } //# sourceMappingURL=index.js.map