bottlenecks-mcp-server
Version:
Model Context Protocol server for Bottlenecks database - enables AI agents like Claude to interact with bottleneck data
149 lines • 5.24 kB
JavaScript
/**
* MCP Server Implementation for Bottlenecks
* Integrates with existing Vercel-deployed Next.js APIs
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
import { createMCPTools, getToolDefinitions, TOOL_CATEGORIES, } from './tools/index.js';
/**
* Bottlenecks MCP Server
*/
export class BottlenecksMCPServer {
server;
tools;
config;
constructor(config) {
this.config = config;
// Initialize the MCP server
this.server = new Server({
name: 'bottlenecks-mcp',
version: '1.0.0',
}, {
capabilities: {
tools: {},
resources: {},
},
});
// Create tools (without API key initially)
this.tools = createMCPTools(config);
// Set up request handlers
this.setupHandlers();
}
/**
* Set up MCP request handlers
*/
setupHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
const toolDefinitions = getToolDefinitions();
return {
tools: toolDefinitions.map((tool) => ({
name: tool.name,
description: tool.description,
inputSchema: tool.inputSchema,
})),
};
});
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
// Find the requested tool
const tool = this.tools.find((t) => t.name === name);
if (!tool) {
throw new Error(`Unknown tool: ${name}`);
}
try {
// Execute the tool
const result = await tool.handler(args || {});
return {
content: result.content,
isError: result.isError || false,
};
}
catch (error) {
// Handle tool execution errors
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
return {
content: [
{
type: 'text',
text: `Error executing tool '${name}': ${errorMessage}`,
},
],
isError: true,
};
}
});
}
/**
* Update the API key for authenticated requests
*/
setApiKey(apiKey) {
// Recreate tools with the API key
this.tools = createMCPTools(this.config, apiKey);
}
/**
* Start the MCP server
*/
async start() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('Bottlenecks MCP Server started successfully');
console.error(`Connected to API: ${this.config.apiBaseUrl}`);
console.error(`Available tools: ${this.tools.length}`);
// Log tool categories
Object.entries(TOOL_CATEGORIES).forEach(([category, tools]) => {
console.error(` ${category}: ${tools.length} tools`);
});
}
/**
* Stop the MCP server
*/
async stop() {
// The server will stop when the transport is closed
console.error('Bottlenecks MCP Server stopping...');
}
/**
* Get server status
*/
getStatus() {
return {
name: 'bottlenecks-mcp',
version: '1.0.0',
apiBaseUrl: this.config.apiBaseUrl,
toolCount: this.tools.length,
categories: Object.fromEntries(Object.entries(TOOL_CATEGORIES).map(([category, tools]) => [
category,
tools.length,
])),
};
}
}
/**
* Create and configure the MCP server
*/
export function createBottlenecksMCPServer(options = {}) {
const config = {
apiBaseUrl: options.apiBaseUrl ||
(process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : null) ||
process.env.NEXT_PUBLIC_SITE_URL ||
'https://www.bottlenecksinstitute.com',
supabaseUrl: options.supabaseUrl || process.env.NEXT_PUBLIC_SUPABASE_URL || '',
supabaseKey: options.supabaseKey || process.env.SUPABASE_SERVICE_ROLE_KEY || '',
defaultTimeout: options.defaultTimeout || 30000,
};
// Validate required configuration only if we're actually going to use the database
if (!config.supabaseUrl) {
process.stderr.write('Warning: NEXT_PUBLIC_SUPABASE_URL not set. Database operations will fail.\n');
}
if (!config.supabaseKey) {
process.stderr.write('Warning: SUPABASE_SERVICE_ROLE_KEY not set. Database operations will fail.\n');
}
return new BottlenecksMCPServer(config);
}
/**
* Default export for easy importing
*/
export default BottlenecksMCPServer;
//# sourceMappingURL=server.js.map