Skip to main content
Glama

Opik MCP Server

by comet-ml
index.ts5.7 kB
import fs from 'fs'; // Import other modules import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { makeApiRequest } from './utils/api.js'; // Import custom transports import { SSEServerTransport } from './transports/sse-transport.js'; // Import environment variables loader - no console output import './utils/env.js'; import { logToFile, logFile } from './utils/logging.js'; // Import tool loaders import { loadTraceTools } from './tools/trace.js'; import { loadPromptTools } from './tools/prompt.js'; import { loadProjectTools } from './tools/project.js'; import { loadMetricTools } from './tools/metrics.js'; import { loadIntegrationTools } from './tools/integration.js'; // Import configuration import { loadConfig } from './config.js'; const config = loadConfig(); // Only initialize log file if debug mode is enabled if (config.debugMode) { try { fs.writeFileSync(logFile, `Opik MCP Server Started: ${new Date().toISOString()}\n`); // Log process info logToFile(`Process ID: ${process.pid}, Node Version: ${process.version}`); logToFile(`Arguments: ${process.argv.join(' ')}`); logToFile( `Loaded configuration: API=${config.apiBaseUrl}, Workspace=${config.workspaceName || 'None'}` ); // Register error handlers process.on('uncaughtException', err => { logToFile(`UNCAUGHT EXCEPTION: ${err.message}`); logToFile(err.stack || 'No stack trace'); }); process.on('unhandledRejection', reason => { logToFile(`UNHANDLED REJECTION: ${reason}`); }); process.on('exit', code => { logToFile(`Process exiting with code ${code}`); }); } catch (error) { // Silently fail if we can't write to the log file } } // Rest of imports import { ProjectResponse } from './types.js'; // Create and configure server - no console output here export let server = new McpServer( { name: config.mcpName, version: config.mcpVersion, }, { capabilities: { resources: {}, // Enable resources capability tools: {}, // Enable tools capability }, } ); // Load tools based on enabled toolsets logToFile(`Loading toolsets: ${config.enabledToolsets.join(', ')}`); if (config.enabledToolsets.includes('integration')) { server = loadIntegrationTools(server); logToFile('Loaded integration toolset'); } if (config.enabledToolsets.includes('prompts')) { server = loadPromptTools(server); logToFile('Loaded prompts toolset'); } if (config.enabledToolsets.includes('projects')) { server = loadProjectTools(server); logToFile('Loaded projects toolset'); } if (config.enabledToolsets.includes('traces')) { server = loadTraceTools(server); logToFile('Loaded traces toolset'); } if (config.enabledToolsets.includes('metrics')) { server = loadMetricTools(server); logToFile('Loaded metrics toolset'); } // Add resources to the MCP server if (config.workspaceName) { // Define a workspace info resource server.resource('workspace-info', 'opik://workspace-info', async () => ({ contents: [ { uri: 'opik://workspace-info', text: JSON.stringify( { name: config.workspaceName, apiUrl: config.apiBaseUrl, selfHosted: config.isSelfHosted, }, null, 2 ), }, ], })); // Define a projects resource that provides the list of projects in the workspace server.resource('projects-list', 'opik://projects-list', async () => { try { const response = await makeApiRequest<ProjectResponse>('/v1/private/projects'); if (!response.data) { return { contents: [ { uri: 'opik://projects-list', text: `Error: ${response.error || 'Unknown error fetching projects'}`, }, ], }; } return { contents: [ { uri: 'opik://projects-list', text: JSON.stringify(response.data, null, 2), }, ], }; } catch (error) { logToFile(`Error fetching projects resource: ${error}`); return { contents: [ { uri: 'opik://projects-list', text: `Error: Failed to fetch projects data`, }, ], }; } }); } // ----------- SERVER CONFIGURATION TOOLS ----------- // Main function to start the server export async function main() { logToFile('Starting main function'); // Create the appropriate transport based on configuration let transport; if (config.transport === 'sse') { logToFile(`Creating SSEServerTransport on port ${config.ssePort}`); transport = new SSEServerTransport({ port: config.ssePort || 3001, }); // Explicitly start the SSE transport logToFile('Starting SSE transport'); await transport.start(); } else { logToFile('Creating StdioServerTransport'); transport = new StdioServerTransport(); } // Connect the server to the transport logToFile('Connecting server to transport'); server.connect(transport); logToFile('Transport connection established'); // Log server status if (config.transport === 'sse') { logToFile(`Opik MCP Server running on SSE (port ${config.ssePort})`); } else { logToFile('Opik MCP Server running on stdio'); } logToFile('Main function completed successfully'); // Start heartbeat for keeping the process alive setInterval(() => { logToFile('Heartbeat ping'); }, 5000); } // Start the server main().catch(error => { logToFile(`Error starting server: ${error}`); process.exit(1); });

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/comet-ml/opik-mcp'

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