Skip to main content
Glama

Meeting BaaS MCP Server

by Meeting-BaaS
set_up_for_other_mcp.md20.8 kB
# Meeting BaaS MCP Server Setup Guide This guide explains how to set up a Meeting BaaS MCP server that connects AI assistants to the Meeting BaaS API for managing recordings, transcripts, and calendar data. ## Prerequisites - Node.js (v16 or higher) - npm (v8 or higher) - Redis (optional, for state management) ## Project Setup 1. Create a new directory for your project and initialize it: ```bash mkdir my-mcp-server cd my-mcp-server npm init -y ``` 2. Update your package.json with the necessary dependencies: ```json { "name": "meetingbaas-mcp", "version": "1.0.0", "description": "Meeting BaaS MCP Server", "type": "module", "main": "dist/index.js", "scripts": { "build": "tsc", "start": "node dist/index.js", "dev": "ts-node-esm src/index.ts", "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"", "prepare": "husky install" }, "dependencies": { "@meeting-baas/sdk": "^0.2.0", "axios": "^1.6.0", "express": "^4.18.2", "fastmcp": "^1.20.2", "redis": "^4.6.13", "zod": "^3.22.4" }, "devDependencies": { "@types/express": "^4.17.21", "@types/node": "^20.11.19", "@types/redis": "^4.0.11", "husky": "^8.0.3", "prettier": "^3.1.0", "ts-node": "^10.9.2", "typescript": "^5.3.3" } } ``` 3. Install the dependencies: ```bash npm install ``` 4. Create a TypeScript configuration file (tsconfig.json): ```json { "compilerOptions": { "target": "ES2020", "module": "NodeNext", "moduleResolution": "NodeNext", "esModuleInterop": true, "strict": true, "outDir": "dist", "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "declaration": true }, "include": ["src/**/*", "lib/**/*"], "exclude": ["node_modules", "dist"] } ``` ## File Structure Create the following directory structure: ``` my-mcp-server/ ├── lib/ │ ├── mcp-api-handler.ts │ └── tool-adapter.ts ├── src/ │ ├── api/ │ │ └── client.ts │ ├── tools/ │ │ ├── index.ts │ │ ├── calendar.ts │ │ ├── deleteData.ts │ │ ├── environment.ts │ │ ├── links.ts │ │ ├── listBots.ts │ │ ├── meeting.ts │ │ ├── qrcode.ts │ │ ├── retranscribe.ts │ │ └── search.ts │ ├── utils/ │ │ ├── logging.ts │ │ └── tool-types.ts │ └── index.ts └── tsconfig.json ``` ## Key Files Implementation ### 1. lib/mcp-api-handler.ts ```typescript import type { Request, Response } from 'express'; import type { Context, FastMCP } from 'fastmcp'; import { createClient } from 'redis'; interface McpApiHandlerOptions { capabilities?: { tools?: Record<string, { description: string }>; }; port?: number; transportType?: 'stdio' | 'sse'; sse?: { endpoint: `/${string}`; }; } export function initializeMcpApiHandler( setupServer: (server: FastMCP) => Promise<void>, options: McpApiHandlerOptions = {}, ): (req: Request, res: Response) => Promise<void> { const { capabilities = {}, port = 3000, transportType = 'sse', sse = { endpoint: '/mcp' }, } = options; // Initialize Redis client const redisClient = createClient({ url: process.env.REDIS_URL || 'redis://localhost:6379', }); // Initialize FastMCP server const server = new FastMCP({ name: 'Meeting BaaS MCP Server', version: '1.0.0' as const, authenticate: async (context: Context) => { const apiKey = context.request?.headers?.['x-api-key']; if (!apiKey) { throw new Error('API key required'); } return { apiKey: String(apiKey) }; }, }); // Set up server capabilities server.setCapabilities(capabilities); // Set up server tools setupServer(server).catch((error) => { console.error('Error setting up server:', error); }); // Handle SSE transport if (transportType === 'sse') { return async (req: Request, res: Response) => { if (req.path !== sse.endpoint) { res.status(404).send('Not found'); return; } res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); const transport = server.createTransport('sse', { request: req, response: res, }); await server.handleTransport(transport); }; } // Handle stdio transport if (transportType === 'stdio') { const transport = server.createTransport('stdio', { stdin: process.stdin, stdout: process.stdout, }); server.handleTransport(transport).catch((error) => { console.error('Error handling stdio transport:', error); }); } // Return a no-op handler for non-SSE requests return async (req: Request, res: Response) => { res.status(404).send('Not found'); }; } ``` ### 2. lib/tool-adapter.ts ```typescript import type { Context, Tool } from 'fastmcp'; import type { SessionAuth } from '../src/api/client.js'; import type { MeetingBaaSTool } from '../src/utils/tool-types.js'; // Adapter function to convert Meeting BaaS tools to FastMCP tools export function adaptTool<P extends Record<string, any>>( meetingTool: MeetingBaaSTool<any> ): Tool<SessionAuth, any> { return { name: meetingTool.name, description: meetingTool.description, parameters: meetingTool.parameters, execute: meetingTool.execute, }; } ``` ### 3. src/utils/tool-types.ts ```typescript import type { z } from 'zod'; import type { Context } from 'fastmcp'; import type { SessionAuth } from '../api/client.js'; /** * Type for tool execution result */ export type ToolResult = { content: { type: 'text'; text: string }[]; }; /** * Base type for Meeting BaaS tools */ export type MeetingBaaSTool<P extends z.ZodType> = { name: string; description: string; parameters: P; execute: (params: z.infer<P>, context: Context<SessionAuth>) => Promise<ToolResult>; }; /** * Helper function to create a Meeting BaaS tool with the correct type */ export function createTool<P extends z.ZodType>( name: string, description: string, parameters: P, execute: (params: z.infer<P>, context: Context<SessionAuth>) => Promise<ToolResult> ): MeetingBaaSTool<P> { return { name, description, parameters, execute, }; } ``` ### 4. src/utils/logging.ts ```typescript /** * Creates a server logger * * @param prefix - Prefix to add to log messages * @returns Logging function */ export function createServerLogger(prefix: string) { return function log(message: string) { const timestamp = new Date().toISOString(); console.error(`[${timestamp}] [${prefix}] ${message}`); }; } /** * Sets up filtering for ping messages * * This reduces log noise by filtering out the periodic ping messages */ export function setupPingFiltering() { const originalStdoutWrite = process.stdout.write.bind(process.stdout); const originalStderrWrite = process.stderr.write.bind(process.stderr); // Replace stdout.write process.stdout.write = (( chunk: string | Uint8Array, encoding?: BufferEncoding, callback?: (err?: Error) => void ) => { // Filter out ping messages if (typeof chunk === 'string' && chunk.includes('"method":"ping"')) { return true; } return originalStdoutWrite(chunk, encoding, callback); }) as typeof process.stdout.write; // Replace stderr.write process.stderr.write = (( chunk: string | Uint8Array, encoding?: BufferEncoding, callback?: (err?: Error) => void ) => { // Filter out ping messages if (typeof chunk === 'string' && chunk.includes('"method":"ping"')) { return true; } return originalStderrWrite(chunk, encoding, callback); }) as typeof process.stderr.write; } ``` ### 5. src/api/client.ts ```typescript import axios from 'axios'; // Session auth type for authenticating with the Meeting BaaS API export type SessionAuth = { apiKey: string }; // Create an API client with the session auth export function createApiClient(session: SessionAuth) { const apiUrl = process.env.MEETING_BAAS_API_URL || 'https://api.meetingbaas.com'; return axios.create({ baseURL: apiUrl, headers: { 'Content-Type': 'application/json', 'x-api-key': session.apiKey, }, }); } ``` ### 6. src/index.ts ```typescript /** * Meeting BaaS MCP Server * * Connects Claude and other AI assistants to Meeting BaaS API, * allowing them to manage recordings, transcripts, and calendar data. */ import { BaasClient, MpcClient } from '@meeting-baas/sdk'; import type { Context } from 'fastmcp'; import { FastMCP } from 'fastmcp'; import { z } from 'zod'; import { promises as fs } from 'fs'; import * as os from 'os'; import * as path from 'path'; import { initializeMcpApiHandler } from '../lib/mcp-api-handler.js'; import { adaptTool } from '../lib/tool-adapter.js'; import type { SessionAuth } from './api/client.js'; import { createTool } from './utils/tool-types.js'; // Set up proper error logging import { createServerLogger, setupPingFiltering } from './utils/logging.js'; // Set up ping message filtering to reduce log noise setupPingFiltering(); const serverLog = createServerLogger('MCP Server'); // Add global error handlers to prevent crashes process.on('unhandledRejection', (reason, promise) => { // Check if this is a connection closed error from the MCP protocol const error = reason as any; if (error && error.code === -32000 && error.message?.includes('Connection closed')) { serverLog(`Connection closed gracefully, ignoring error`); } else { serverLog(`Unhandled Rejection: ${error?.message || String(reason)}`); console.error('[MCP Server] Error details:', reason); } }); process.on('uncaughtException', (error) => { // Check if this is a connection closed error from the MCP protocol const err = error as any; if (err && err.code === 'ERR_UNHANDLED_ERROR' && err.context?.error?.code === -32000) { serverLog(`Connection closed gracefully, ignoring exception`); } else { serverLog(`Uncaught Exception: ${error?.message || String(error)}`); console.error('[MCP Server] Exception details:', error); } }); // Log startup information serverLog('========== SERVER STARTUP =========='); serverLog(`Node version: ${process.version}`); serverLog(`Running from Claude: ${process.env.MCP_FROM_CLAUDE === 'true' ? 'Yes' : 'No'}`); serverLog(`Process ID: ${process.pid}`); // Function to load and process the Claude Desktop config file async function loadClaudeDesktopConfig() { try { // Define the expected config path const configPath = path.join( os.homedir(), 'Library/Application Support/Claude/claude_desktop_config.json', ); const fileExists = await fs .stat(configPath) .then(() => true) .catch(() => false); if (fileExists) { serverLog(`Loading config from: ${configPath}`); try { const configContent = await fs.readFile(configPath, 'utf8'); const configJson = JSON.parse(configContent); // Check for meetingbaas server config if (configJson.mcpServers && configJson.mcpServers.meetingbaas) { const serverConfig = configJson.mcpServers.meetingbaas; // Check for headers if (serverConfig.headers) { // Check for API key header and set it as an environment variable if (serverConfig.headers['x-api-key']) { const apiKey = serverConfig.headers['x-api-key']; process.env.MEETING_BAAS_API_KEY = apiKey; serverLog(`API key loaded from config`); } // Check for QR code API key in headers if (serverConfig.headers['x-api-key']) { const qrCodeApiKey = serverConfig.headers['x-api-key']; process.env.QRCODE_API_KEY = qrCodeApiKey; serverLog(`QR code API key loaded from config`); } } // Check for bot configuration if (serverConfig.botConfig) { const botConfig = serverConfig.botConfig; let configItems = []; // Set bot name if available if (botConfig.name) { process.env.MEETING_BOT_NAME = botConfig.name; configItems.push('name'); } // Set bot image if available if (botConfig.image) { process.env.MEETING_BOT_IMAGE = botConfig.image; configItems.push('image'); } // Set bot entry message if available if (botConfig.entryMessage) { process.env.MEETING_BOT_ENTRY_MESSAGE = botConfig.entryMessage; configItems.push('message'); } // Set extra fields if available if (botConfig.extra) { process.env.MEETING_BOT_EXTRA = JSON.stringify(botConfig.extra); configItems.push('extra'); } if (configItems.length > 0) { serverLog(`Bot configuration loaded from config: ${configItems.join(', ')}`); } } } } catch (parseError) { serverLog(`Error parsing config file: ${parseError}`); } } else { serverLog(`Config file not found at ${configPath}`); } } catch (error) { serverLog(`Error loading config file: ${error}`); } } // Initialize the BaaS client const baasClient = new BaasClient({ apiKey: process.env.MEETING_BAAS_API_KEY || '', }); // Initialize MPC client for tool registration const mpcClient = new MpcClient({ serverUrl: process.env.MPC_SERVER_URL || 'http://localhost:7020', }); interface ToolParameter { name: string; required?: boolean; schema?: { type: string; }; } // Helper function to convert MPC parameter definition to Zod schema function convertToZodSchema(parameters: ToolParameter[]): z.ZodType { const schema: Record<string, z.ZodType> = {}; for (const param of parameters) { if (param.required) { schema[param.name] = z.string(); // Default to string for now, can be expanded based on param.schema.type } else { schema[param.name] = z.string().optional(); } } return z.object(schema); } const handler = initializeMcpApiHandler( async (server: FastMCP) => { // Register all Meeting BaaS tools automatically const tools = mpcClient.getRegisteredTools(); for (const tool of tools) { const paramsSchema = convertToZodSchema(tool.parameters || []); const meetingTool = createTool( tool.name, tool.description || 'Meeting BaaS tool', paramsSchema, async (params: Record<string, unknown>, context: Context<SessionAuth>) => { // Handle tool execution here return { content: [{ type: 'text', text: `Tool ${tool.name} executed` }], }; }, ); server.addTool(adaptTool(meetingTool)); } // Keep the existing echo tool as an example const echoTool = createTool( 'echo', 'Echo a message', z.object({ message: z.string() }), async ({ message }: { message: string }, context: Context<SessionAuth>) => ({ content: [{ type: 'text', text: `Tool echo: ${message}` }], }), ); server.addTool(adaptTool(echoTool)); }, { capabilities: { tools: { echo: { description: 'Echo a message', }, // Meeting BaaS tools will be automatically added to capabilities }, }, port: process.env.PORT ? parseInt(process.env.PORT, 10) : 3000, transportType: process.env.MCP_FROM_CLAUDE === 'true' ? 'stdio' : 'sse', sse: { endpoint: '/mcp' as `/${string}`, }, }, ); // Load the Claude Desktop config and start the server (async () => { // Load and log the Claude Desktop config await loadClaudeDesktopConfig(); // Start the server try { const port = process.env.PORT ? parseInt(process.env.PORT, 10) : 3000; const isClaudeDesktop = process.env.MCP_FROM_CLAUDE === 'true'; if (!isClaudeDesktop) { serverLog(`Meeting BaaS MCP Server starting on http://localhost:${port}/mcp`); } else { serverLog(`Meeting BaaS MCP Server starting in stdio mode for Claude Desktop`); } // Start the server using the appropriate method based on environment if (isClaudeDesktop) { // For Claude Desktop, we use stdio transport process.stdin.pipe(process.stdout); } else { // For web/HTTP interface, we use the handler directly const http = require('http'); const server = http.createServer(handler); server.listen(port, () => { serverLog(`Server listening on port ${port}`); }); } } catch (error) { serverLog(`Error starting server: ${error}`); } })(); export default handler; ``` ## Tool Examples Here are examples of tools that you can implement in the tools directory: ### src/tools/environment.ts ```typescript import { z } from 'zod'; import { createTool } from '../utils/tool-types.js'; /** * Tool to select the environment for API requests */ export const selectEnvironmentTool = createTool( 'selectEnvironment', 'Switch between API environments', z.object({ environment: z.enum(['production', 'staging', 'development']), }), async ({ environment }, context) => { // Set the environment for subsequent requests process.env.MEETING_BAAS_API_ENVIRONMENT = environment; return { content: [ { type: 'text', text: `Environment set to ${environment}` }, ], }; } ); ``` ### src/tools/deleteData.ts ```typescript import { z } from 'zod'; import { createApiClient } from '../api/client.js'; import { createTool } from '../utils/tool-types.js'; /** * Tool to delete data associated with a bot */ export const deleteDataTool = createTool( 'deleteData', 'Delete data associated with a bot', z.object({ botId: z.string().uuid(), }), async ({ botId }, context) => { try { const client = createApiClient(context.session); // Call the API to delete bot data await client.delete(`/bots/${botId}/data`); return { content: [ { type: 'text', text: `Successfully deleted data for bot ${botId}` }, ], }; } catch (error: any) { return { content: [ { type: 'text', text: `Error deleting data: ${error.message}` }, ], }; } } ); ``` ### src/tools/listBots.ts ```typescript import { z } from 'zod'; import { createApiClient } from '../api/client.js'; import { createTool } from '../utils/tool-types.js'; /** * Tool to list bots with metadata */ export const listBotsWithMetadataTool = createTool( 'listBotsWithMetadata', 'List all bots with their metadata', z.object({}), async (_, context) => { try { const client = createApiClient(context.session); // Call the API to get bots const response = await client.get('/bots'); const bots = response.data; return { content: [ { type: 'text', text: JSON.stringify(bots, null, 2) }, ], }; } catch (error: any) { return { content: [ { type: 'text', text: `Error listing bots: ${error.message}` }, ], }; } } ); ``` ### src/tools/index.ts ```typescript // Export all tools export { deleteDataTool } from './deleteData.js'; export { listBotsWithMetadataTool } from './listBots.js'; export { selectEnvironmentTool } from './environment.js'; // Add exports for other tools ``` ## Running the Server 1. Build the project: ```bash npm run build ``` 2. Start the server: ```bash npm start ``` ## Environment Variables The server supports the following environment variables: - `MEETING_BAAS_API_KEY`: API key for authenticating with the Meeting BaaS API - `MEETING_BAAS_API_URL`: Base URL for the Meeting BaaS API (default: https://api.meetingbaas.com) - `PORT`: Port to run the server on (default: 3000) - `MCP_FROM_CLAUDE`: Set to 'true' when running from Claude Desktop - `REDIS_URL`: URL for Redis connection (optional) - `MPC_SERVER_URL`: URL for the MPC server (default: http://localhost:7020) - `MEETING_BOT_NAME`: Default name for bots - `MEETING_BOT_IMAGE`: Default image URL for bots - `MEETING_BOT_ENTRY_MESSAGE`: Default entry message for bots - `MEETING_BOT_EXTRA`: JSON string with extra bot configuration ## Additional Resources - [FastMCP Documentation](https://github.com/fastmcp/fastmcp) - [Meeting BaaS SDK Documentation](https://github.com/meeting-baas/sdk) - [Zod Documentation](https://github.com/colinhacks/zod)

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/Meeting-BaaS/meeting-mcp'

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