Skip to main content
Glama
yshk-mrt
by yshk-mrt

GetSceneList

Retrieve a list of available OBS scenes to facilitate automated streaming setup and management within the Presentation Buddy MCP Server.

Instructions

Gets the list of available OBS scenes.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • Core handler logic for the GetSceneList tool: sends 'GetSceneList' request to OBS WebSocket API with empty parameters.
    case "GetSceneList": obsResponseData = await sendToObs("GetSceneList", {}, context, action.name); break;
  • Handler response formatting specific to GetSceneList: stringifies the OBS response data as text content.
    }; } else if (action.name === "GetSceneList") { // For GetSceneList, return the full response as content return { content: [ { type: "text", text: JSON.stringify(obsResponseData, null, 2) } ]
  • src/index.ts:360-991 (registration)
    Dynamic registration of MCP tools from obs_mcp_tool_def.json, including 'GetSceneList' via server.tool() with shared handler containing case-specific logic.
    if (toolDefinitions && toolDefinitions.actions) { toolDefinitions.actions.forEach(action => { const requestSchema = action.requestDataSchema ? zodSchemaFromMcpSchema(action.requestDataSchema) : undefined; server.tool( action.name, action.description || "", requestSchema ? { params: requestSchema } : {}, async (params: any, context: any) => { // Using any for context if ToolContext is not easily available console.log(`Tool '${action.name}' called with params:`, JSON.stringify(params, null, 2)); try { // Ensure OBS is connected before attempting to send a command if (!obsClient || obsClient.readyState !== WebSocket.OPEN) { if (!isObsConnecting) { console.log("OBS not connected. Attempting to connect before processing tool action."); await connectToObs(); // This will throw if it fails if (!obsClient || obsClient.readyState !== WebSocket.OPEN) { throw new Error("Failed to connect to OBS."); } } else { // Wait for existing connection attempt await obsConnectionPromise; if (!obsClient || obsClient.readyState !== WebSocket.OPEN) { throw new Error("Failed to connect to OBS after waiting."); } } } let obsResponseData: any; // Map MCP actions to OBS requestTypes and params switch (action.name) { case "SwitchScene": console.log("SwitchScene params:", JSON.stringify(params, null, 2)); // Extract the scene name from the parameters const sceneName = params.scene || (params.params && params.params.scene); console.log("Extracted sceneName:", sceneName); if (!sceneName) { throw new Error("No scene name provided"); } try { // NOTE: Changed from SetCurrentProgramScene to use the direct parameters expected by OBS obsResponseData = await sendToObs( "SetCurrentProgramScene", { "sceneName": sceneName }, // Make sure we use the exact field name OBS expects context, action.name ); console.log("OBS response:", JSON.stringify(obsResponseData, null, 2)); } catch (error) { console.error("OBS error:", error); throw error; } break; case "StartStream": obsResponseData = await sendToObs("StartStream", {}, context, action.name); break; case "StopStream": obsResponseData = await sendToObs("StopStream", {}, context, action.name); break; case "StartRecording": obsResponseData = await sendToObs("StartRecord", {}, context, action.name); break; case "StopRecording": obsResponseData = await sendToObs("StopRecord", {}, context, action.name); break; case "SetSourceVisibility": console.log("SetSourceVisibility params:", JSON.stringify(params, null, 2)); // Try different parameter access patterns const sourceScene = params.scene || (params.params && params.params.scene); const sourceName = params.source || (params.params && params.params.source); const visible = params.visible !== undefined ? params.visible : (params.params && params.params.visible !== undefined ? params.params.visible : null); if (!sourceScene || !sourceName || visible === null) { throw new Error("Missing required parameters: scene, source, or visible"); } const sceneItemIdResponse = await sendToObs<{ sceneItemId: number }>( "GetSceneItemId", { sceneName: sourceScene, sourceName: sourceName }, context, action.name ); obsResponseData = await sendToObs( "SetSceneItemEnabled", { sceneName: sourceScene, sceneItemId: sceneItemIdResponse.sceneItemId, sceneItemEnabled: visible }, context, action.name ); break; case "GetSceneItemList": // Extract sceneName from params const sceneNameForItems = params.sceneName || params.scene || (params.params && (params.params.sceneName || params.params.scene)); if (!sceneNameForItems) { throw new Error("Missing required parameter: sceneName"); } obsResponseData = await sendToObs( "GetSceneItemList", { sceneName: sceneNameForItems }, context, action.name ); // Transform isGroup: null to isGroup: false to match schema if (obsResponseData && Array.isArray(obsResponseData.sceneItems)) { obsResponseData.sceneItems.forEach((item: any) => { if (item.isGroup === null) { item.isGroup = false; } }); } break; case "GetSceneList": obsResponseData = await sendToObs("GetSceneList", {}, context, action.name); break; case "SetTextContent": console.log("SetTextContent params:", JSON.stringify(params, null, 2)); // Extract parameters const textSourceName = params.source || (params.params && params.params.source); const textContent = params.text || (params.params && params.params.text); if (!textSourceName || textContent === undefined) { throw new Error("Missing required parameters: source or text"); } console.log(`Setting text content for source "${textSourceName}" to: ${textContent}`); try { // Use SetInputSettings to update the text property obsResponseData = await sendToObs( "SetInputSettings", { inputName: textSourceName, inputSettings: { text: textContent } }, context, action.name ); console.log("SetTextContent response:", JSON.stringify(obsResponseData, null, 2)); } catch (error) { console.error("Error setting text content:", error); throw error; } break; case "SetAudioMute": console.log("SetAudioMute params:", JSON.stringify(params, null, 2)); // Extract parameters const audioSourceName = params.source || (params.params && params.params.source); const muteState = params.mute !== undefined ? params.mute : (params.params && params.params.mute !== undefined ? params.params.mute : null); if (!audioSourceName || muteState === null) { throw new Error("Missing required parameters: source or mute"); } console.log(`Setting mute state for source "${audioSourceName}" to: ${muteState}`); try { obsResponseData = await sendToObs( "SetInputMute", { inputName: audioSourceName, inputMuted: muteState }, context, action.name ); console.log("SetAudioMute response:", JSON.stringify(obsResponseData, null, 2)); } catch (error) { console.error("Error setting audio mute state:", error); throw error; } break; case "SetAudioVolume": console.log("SetAudioVolume params:", JSON.stringify(params, null, 2)); // Extract parameters const volumeSourceName = params.source || (params.params && params.params.source); const volumeLevel = params.volume !== undefined ? params.volume : (params.params && params.params.volume !== undefined ? params.params.volume : null); if (!volumeSourceName || volumeLevel === null) { throw new Error("Missing required parameters: source or volume"); } if (volumeLevel < 0 || volumeLevel > 1) { throw new Error("Volume must be between 0.0 and 1.0"); } console.log(`Setting volume for source "${volumeSourceName}" to: ${volumeLevel}`); try { obsResponseData = await sendToObs( "SetInputVolume", { inputName: volumeSourceName, inputVolumeMul: volumeLevel // Using multiplier (0.0 to 1.0) format }, context, action.name ); console.log("SetAudioVolume response:", JSON.stringify(obsResponseData, null, 2)); } catch (error) { console.error("Error setting audio volume:", error); throw error; } break; case "SetSourcePosition": console.log("SetSourcePosition params:", JSON.stringify(params, null, 2)); // Extract parameters const posSceneName = params.scene || (params.params && params.params.scene); const posSourceName = params.source || (params.params && params.params.source); const xPos = params.x !== undefined ? params.x : (params.params && params.params.x !== undefined ? params.params.x : null); const yPos = params.y !== undefined ? params.y : (params.params && params.params.y !== undefined ? params.params.y : null); if (!posSceneName || !posSourceName || xPos === null || yPos === null) { throw new Error("Missing required parameters: scene, source, x, or y"); } console.log(`Setting position for source "${posSourceName}" in scene "${posSceneName}" to: x=${xPos}, y=${yPos}`); try { // First get the scene item ID const posItemIdResponse = await sendToObs<{ sceneItemId: number }>( "GetSceneItemId", { sceneName: posSceneName, sourceName: posSourceName }, context, action.name ); // Then set the position obsResponseData = await sendToObs( "SetSceneItemTransform", { sceneName: posSceneName, sceneItemId: posItemIdResponse.sceneItemId, sceneItemTransform: { positionX: xPos, positionY: yPos } }, context, action.name ); console.log("SetSourcePosition response:", JSON.stringify(obsResponseData, null, 2)); } catch (error) { console.error("Error setting source position:", error); throw error; } break; case "SetSourceScale": console.log("SetSourceScale params:", JSON.stringify(params, null, 2)); // Extract parameters const scaleSceneName = params.scene || (params.params && params.params.scene); const scaleSourceName = params.source || (params.params && params.params.source); const scaleX = params.scaleX !== undefined ? params.scaleX : (params.params && params.params.scaleX !== undefined ? params.params.scaleX : null); const scaleY = params.scaleY !== undefined ? params.scaleY : (params.params && params.params.scaleY !== undefined ? params.params.scaleY : null); if (!scaleSceneName || !scaleSourceName || scaleX === null || scaleY === null) { throw new Error("Missing required parameters: scene, source, scaleX, or scaleY"); } console.log(`Setting scale for source "${scaleSourceName}" in scene "${scaleSceneName}" to: scaleX=${scaleX}, scaleY=${scaleY}`); try { // First get the scene item ID const scaleItemIdResponse = await sendToObs<{ sceneItemId: number }>( "GetSceneItemId", { sceneName: scaleSceneName, sourceName: scaleSourceName }, context, action.name ); // Then set the scale obsResponseData = await sendToObs( "SetSceneItemTransform", { sceneName: scaleSceneName, sceneItemId: scaleItemIdResponse.sceneItemId, sceneItemTransform: { scaleX: scaleX, scaleY: scaleY } }, context, action.name ); console.log("SetSourceScale response:", JSON.stringify(obsResponseData, null, 2)); } catch (error) { console.error("Error setting source scale:", error); throw error; } break; case "TakeSourceScreenshot": // Try to get source from params or params.params const mcpInputParams = params.params || params; const sourceNameForScreenshot = mcpInputParams.source; // Assuming 'source' is the key from MCP if (!sourceNameForScreenshot) { console.error("TakeSourceScreenshot Error: Missing required parameter 'source'. Params received:", JSON.stringify(params, null, 2)); throw new Error("Missing required parameter: source"); } // Ensure sourceNameForScreenshot is a string before calling replace if (typeof sourceNameForScreenshot !== 'string') { console.error("TakeSourceScreenshot Error: 'source' parameter is not a string. Value:", sourceNameForScreenshot); throw new Error("'source' parameter must be a string."); } console.log(`Executing TakeSourceScreenshot for source: ${sourceNameForScreenshot}`); const format = mcpInputParams.imageFormat || "png"; const timestamp = Date.now(); const filename = `screenshot-${sourceNameForScreenshot.replace(/[^a-z0-9]/gi, '_')}-${timestamp}.${format}`; const absoluteFilePath = path.join(SCREENSHOTS_DIR_ABS, filename); // URI that will be returned to the client const resourceUri = `file://${absoluteFilePath}`; console.log(`Attempting to save screenshot to: ${absoluteFilePath}`); try { const obsRequestData: any = { sourceName: sourceNameForScreenshot, // OBS uses sourceName for SaveSourceScreenshot imageFormat: format, imageFilePath: absoluteFilePath, }; if (mcpInputParams.width !== undefined) obsRequestData.imageWidth = mcpInputParams.width; if (mcpInputParams.height !== undefined) obsRequestData.imageHeight = mcpInputParams.height; if (mcpInputParams.compressionQuality !== undefined) obsRequestData.imageCompressionQuality = mcpInputParams.compressionQuality; // Use SaveSourceScreenshot OBS request const obsResponse = await sendToObs("SaveSourceScreenshot", obsRequestData, context, "TakeSourceScreenshot"); console.log(`SaveSourceScreenshot OBS response:`, obsResponse); console.log(`Screenshot for source ${sourceNameForScreenshot} saved to ${absoluteFilePath}. Returning URI: ${resourceUri}`); // Return the URI to the client as a resource return { content: [{ type: "resource", resource: { uri: resourceUri, text: `Screenshot for ${sourceNameForScreenshot} available at ${filename}`, // mimeType: `image/${format}` // Optional: include mimeType if known } }], filename: filename // Additional top-level info }; } catch (e: any) { console.error(`Error in TakeSourceScreenshot for source ${sourceNameForScreenshot}:`, e.message); throw new Error(`Failed to take screenshot for ${sourceNameForScreenshot}: ${e.message}`); } break; case "SetTransitionSettings": const transitionParams = params as { transitionName: string; transitionDuration: number }; console.log(`Executing SetTransitionSettings with params:`, transitionParams); try { await sendToObs("SetCurrentSceneTransition", { transitionName: transitionParams.transitionName }, context, action.name); await sendToObs("SetCurrentSceneTransitionSettings", { transitionSettings: { duration: transitionParams.transitionDuration }, overlay: false }, context, action.name); return { structuredContent: { success: true } }; } catch (e: any) { console.error(`Error in SetTransitionSettings for OBS:`, e.message); return { structuredContent: { success: false, error: e.message } }; } case "TriggerStudioModeTransition": console.log(`Executing TriggerStudioModeTransition`); try { await sendToObs("TriggerStudioModeTransition", {}, context, action.name); return { structuredContent: { success: true } }; } catch (e: any) { console.error(`Error in TriggerStudioModeTransition for OBS:`, e.message); return { structuredContent: { success: false, error: e.message } }; } case "PlayPauseMedia": const mediaParams = params as { sourceName: string; mediaAction: string }; console.log(`Executing PlayPauseMedia with params:`, mediaParams); try { await sendToObs("TriggerMediaInputAction", { inputName: mediaParams.sourceName, mediaAction: mediaParams.mediaAction.toUpperCase() }, context, action.name); return { structuredContent: { success: true } }; } catch (e: any) { console.error(`Error in PlayPauseMedia for OBS:`, e.message); return { structuredContent: { success: false, error: e.message } }; } case "SetMediaTime": const timeParams = params as { sourceName: string; mediaTime: number }; console.log(`Executing SetMediaTime with params:`, timeParams); try { await sendToObs("SetMediaInputCursor", { inputName: timeParams.sourceName, mediaCursor: timeParams.mediaTime }, context, action.name); return { structuredContent: { success: true } }; } catch (e: any) { console.error(`Error in SetMediaTime for OBS:`, e.message); return { structuredContent: { success: false, error: e.message } }; } case "SaveReplayBuffer": console.log(`Executing SaveReplayBuffer`); try { await sendToObs("SaveReplayBuffer", {}, context, action.name); return { structuredContent: { success: true } }; } catch (e: any) { console.error(`Error in SaveReplayBuffer for OBS:`, e.message); return { structuredContent: { success: false, error: e.message } }; } case "SaveReplayBufferAndAdd": const replayParams = params as { sceneName: string; sourceName: string; replayFolder?: string }; console.log(`Executing SaveReplayBufferAndAdd with params:`, replayParams); try { // 1. Save the replay buffer await sendToObs("SaveReplayBuffer", {}, context, action.name); // 2. Wait a bit for the file to be written to disk await new Promise(resolve => setTimeout(resolve, 2000)); // 3. Find the latest replay file const defaultRecordingPath = process.env.OBS_REPLAY_PATH || path.join(os.homedir(), 'Movies'); const replayFolder = replayParams.replayFolder || defaultRecordingPath; console.log(`Looking for the latest replay file in: ${replayFolder}`); // Get a list of replay files in the directory let files: string[] = []; try { files = fs.readdirSync(replayFolder) .filter(file => file.toLowerCase().includes('replay') && (file.endsWith('.mp4') || file.endsWith('.mov'))) .map(file => path.join(replayFolder, file)); // Sort by modification time, newest first files.sort((a, b) => { return fs.statSync(b).mtime.getTime() - fs.statSync(a).mtime.getTime(); }); console.log(`Found ${files.length} replay files`); } catch (fsError: any) { console.error(`Error reading replay directory ${replayFolder}:`, fsError.message); return { structuredContent: { success: false, error: `Failed to read replay directory: ${fsError.message}` } }; } if (files.length === 0) { return { structuredContent: { success: false, error: "No replay files found" } }; } // Get the most recent file const latestReplayFile = files[0]; console.log(`Latest replay file: ${latestReplayFile}`); // 4. Create a media source with this file const mediaSourceResponse = await sendToObs<{ sceneItemId: number }>("CreateInput", { sceneName: replayParams.sceneName, inputName: replayParams.sourceName, inputKind: "ffmpeg_source", inputSettings: { local_file: latestReplayFile, looping: true }, sceneItemEnabled: true }, context, action.name); console.log(`Created media source with ID: ${mediaSourceResponse.sceneItemId}`); return { structuredContent: { success: true, filePath: latestReplayFile, sceneItemId: mediaSourceResponse.sceneItemId } }; } catch (e: any) { console.error(`Error in SaveReplayBufferAndAdd for OBS:`, e.message); return { structuredContent: { success: false, error: e.message } }; } case "CreateSource": const sourceParams = params as { sceneName: string; sourceName: string; sourceKind: string; sourceSettings: object; setVisible?: boolean }; console.log(`Executing CreateSource with params:`, sourceParams); try { const response = await sendToObs<{ sceneItemId: number }>("CreateInput", { sceneName: sourceParams.sceneName, inputName: sourceParams.sourceName, inputKind: sourceParams.sourceKind, inputSettings: sourceParams.sourceSettings, sceneItemEnabled: typeof sourceParams.setVisible === 'boolean' ? sourceParams.setVisible : true }, context, action.name); return { structuredContent: { success: true, sceneItemId: response.sceneItemId } }; } catch (e: any) { console.error(`Error in CreateSource for OBS:`, e.message); return { structuredContent: { success: false, error: e.message } }; } case "SetShaderFilter": const shaderParams = params as { sourceName: string; filterName: string; shaderCode?: string; shaderParameters?: Record<string, any> }; console.log(`Executing SetShaderFilter with params:`, shaderParams); try { // Directly construct filterSettings without GetSourceFilterInfo const filterSettings: Record<string, any> = {}; if (shaderParams.shaderCode) { // The actual key for shader code might depend on the specific shader filter plugin. // Common ones are 'shader_text', 'glsl', or 'code'. // Assuming 'shader_text' based on common OBS shader filter plugins. filterSettings.shader_text = shaderParams.shaderCode; } if (shaderParams.shaderParameters) { // Shader parameters are often applied directly at the root of filterSettings // or under a specific key like 'defaults'. // This assumes they are direct key-value pairs. for (const [key, value] of Object.entries(shaderParams.shaderParameters)) { filterSettings[key] = value; } } // Apply the constructed filter settings await sendToObs( "SetSourceFilterSettings", { sourceName: shaderParams.sourceName, filterName: shaderParams.filterName, filterSettings: filterSettings }, context, action.name ); console.log(`Successfully attempted to update shader filter settings for ${shaderParams.filterName} on ${shaderParams.sourceName}`); return { structuredContent: { success: true } }; } catch (e: any) { console.error(`Error in SetShaderFilter for OBS:`, e.message); return { structuredContent: { success: false, error: e.message } }; } case "SetLutFilter": console.log(`Executing SetLutFilter with params:`, JSON.stringify(params, null, 2)); try { // Parse the input parameters // Check if params are directly in params or in a nested params object const paramsObj = params.params || params; const sourceName = paramsObj.sourceName; const filterName = paramsObj.filterName; const amount = paramsObj.amount; const path = paramsObj.path; console.log(`SetLutFilter: Parsed parameters - sourceName: "${sourceName}", filterName: "${filterName}", amount: ${amount}, path: ${path || "undefined"}`); if (!sourceName || !filterName) { throw new Error("Missing required parameters: sourceName or filterName"); } console.log(`SetLutFilter: Setting LUT filter "${filterName}" on source "${sourceName}"`); // Direct approach - skip getting current settings console.log("Using direct filter update approach"); const filterSettings: Record<string, any> = {}; if (amount !== undefined) { filterSettings.amount = amount; } if (path) { filterSettings.path = path; } console.log(`Filter settings to apply:`, JSON.stringify(filterSettings, null, 2)); const setFilterResponse = await sendToObs( "SetSourceFilterSettings", { sourceName: sourceName, filterName: filterName, filterSettings: filterSettings }, context, action.name ); console.log(`SetSourceFilterSettings response:`, JSON.stringify(setFilterResponse, null, 2)); console.log(`Successfully updated LUT filter settings directly for ${filterName} on ${sourceName}`); return { structuredContent: { success: true } }; } catch (e: any) { console.error(`Error in SetLutFilter for OBS:`, e.message); console.error(`Error details:`, e); return { structuredContent: { success: false, error: e.message } }; } default: console.error(`Unknown MCP action: ${action.name}`); throw new Error(`Action '${action.name}' is not implemented.`); } // ★★★ デバッグログ追加箇所 ★★★ console.log(`DEBUG: Formatting response for action: '${action.name}'`); console.log(`DEBUG: obsResponseData content for MCP: ${JSON.stringify(obsResponseData, null, 2)}`); // ★★★ここまで★★★ // Return the appropriate response format based on the action if (action.name === "GetSceneItemList") { // EXPERIMENT: Return as content to test Claude client behavior console.log(`DEBUG: Formatting GetSceneItemList response as 'content' for Claude client test.`); return { content: [{ type: "text", text: JSON.stringify(obsResponseData, null, 2) }] }; } else if (action.name === "GetSceneList") { // For GetSceneList, return the full response as content return { content: [ { type: "text", text: JSON.stringify(obsResponseData, null, 2) } ] }; } else { // For other actions, return a simple success message return { content: [ { type: "text", text: `Action '${action.name}' executed successfully.` } ] }; } } catch (error: any) { console.error(`Error executing tool '${action.name}':`, error); throw error; } } ); console.log(`Registered tool: ${action.name}`); });
  • Key helper function invoked by GetSceneList handler to communicate with OBS WebSocket: handles connection check, request sending, response resolution, and errors.
    async function sendToObs<T = any>(requestType: string, requestData?: any, mcpContext?: any, actionName?: string): Promise<T> { if (!obsClient || obsClient.readyState !== WebSocket.OPEN) { if (!isObsConnecting) { // Avoid multiple concurrent connection attempts from sendToObs console.log("OBS not connected. Attempting to connect before sending."); try { await connectToObs(); // Attempt to connect first if (!obsClient || obsClient.readyState !== WebSocket.OPEN) { // Check again after attempt throw new Error("OBS WebSocket is not connected after attempt."); } } catch (connectError) { console.error("Failed to connect to OBS for sendToObs:", connectError); throw new Error(`OBS Connection Error: ${(connectError as Error).message}`); } } else { // If it's already connecting, we should ideally wait for obsConnectionPromise // For now, throwing an error or queuing the request might be options. // Let's throw, as the logic to queue and wait can get complex quickly here. console.warn("OBS is currently connecting. Request will likely fail or be delayed."); // Fall through to try sending, but it might fail if connection isn't ready. } } // Re-check after potential connectToObs call if (!obsClient || obsClient.readyState !== WebSocket.OPEN) { throw new Error("OBS WebSocket is not connected or ready."); } const obsRequestId = await generateRequestId(); const payload: ObsRequest = { op: 6, // Request d: { requestType, requestId: obsRequestId, requestData, }, }; console.log("Full OBS request payload:", JSON.stringify(payload, null, 2)); return new Promise((resolve, reject) => { if (mcpContext && actionName) { // Only store if it's an MCP-initiated request needing response mapping pendingObsRequests.set(obsRequestId, { resolve, reject, mcpContext, actionName }); } else { // If not from MCP tool (e.g. internal calls), handle response directly or differently // For now, still use pendingObsRequests for simplicity pendingObsRequests.set(obsRequestId, { resolve, reject, mcpContext: mcpContext!, actionName: actionName || requestType }); } // console.log("OBS TX:", JSON.stringify(payload, null, 2)); obsClient!.send(JSON.stringify(payload), (err) => { if (err) { console.error(`Error sending to OBS for request '${requestType}':`, err); pendingObsRequests.delete(obsRequestId); reject(err); } }); // Timeout for OBS requests setTimeout(() => { if (pendingObsRequests.has(obsRequestId)) { console.warn(`OBS request '${requestType}' (ID: ${obsRequestId}) timed out.`); pendingObsRequests.get(obsRequestId)?.reject(new Error(`OBS request '${requestType}' timed out.`)); pendingObsRequests.delete(obsRequestId); } }, 10000); // 10 seconds timeout }); }
  • src/index.ts:93-101 (registration)
    Loads obs_mcp_tool_def.json containing tool definitions (name, schema, etc.) used to register GetSceneList and other tools.
    const TOOL_DEF_FILE = path.join(__dirname, "obs_mcp_tool_def.json"); let toolDefinitions: { name: string, version: string, description: string, actions: any[] } = { name: 'obs-mcp-default', version: '0.0.1', description: 'OBS MCP Default', actions: []}; try { const toolDefContent = fs.readFileSync(TOOL_DEF_FILE, "utf-8"); toolDefinitions = JSON.parse(toolDefContent); } catch (error) { console.error(`Error loading tool definitions from ${TOOL_DEF_FILE}:`, error); // Proceed with empty actions or default }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yshk-mrt/obs-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server