#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListResourcesRequestSchema,
ListToolsRequestSchema,
ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import dotenv from 'dotenv';
import { ChangerawrClient } from './client/changerawr-client.js';
import { toolRegistry } from './tools/index.js';
import { resourceRegistry } from './resources/index.js';
// Load environment variables
dotenv.config();
class ChangerawrMCPServer {
private server: Server;
private changerawrClient: ChangerawrClient;
constructor() {
this.server = new Server(
{
name: 'changerawr-mcp-server',
version: '1.0.0',
}
);
// Initialize Changerawr client
this.changerawrClient = new ChangerawrClient({
baseUrl: process.env.CHANGERAWR_BASE_URL || 'http://localhost:3000',
apiKey: process.env.CHANGERAWR_API_KEY || '',
});
this.setupHandlers();
}
private setupHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: toolRegistry.getToolDefinitions(),
};
});
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
const tool = toolRegistry.getTool(name);
if (!tool) {
throw new Error(`Unknown tool: ${name}`);
}
const result = await tool.execute(args, this.changerawrClient);
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: 'text',
text: `Error: ${errorMessage}`,
},
],
isError: true,
};
}
});
// List available resources
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: resourceRegistry.getResourceDefinitions(),
};
});
// Handle resource reads
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
try {
const resource = resourceRegistry.getResourceByUri(uri);
if (!resource) {
throw new Error(`Unknown resource: ${uri}`);
}
const content = await resource.read(uri, this.changerawrClient);
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(content, null, 2),
},
],
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
throw new Error(`Failed to read resource ${uri}: ${errorMessage}`);
}
});
}
async run() {
// Validate configuration
if (!process.env.CHANGERAWR_API_KEY) {
console.error('Error: CHANGERAWR_API_KEY environment variable is required');
process.exit(1);
}
if (!process.env.CHANGERAWR_BASE_URL) {
console.warn('Warning: CHANGERAWR_BASE_URL not set, using default: http://localhost:3000');
}
// Test connection to Changerawr
try {
await this.changerawrClient.testConnection();
console.error('✅ Connected to Changerawr successfully');
} catch (error) {
console.error('❌ Failed to connect to Changerawr:', error);
process.exit(1);
}
// Start the server
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('🚀 Changerawr MCP Server running');
}
}
// Start the server if this file is run directly
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Check if this is the main module
const isMainModule = process.argv[1] === __filename || process.argv[1] === __filename.replace(/\.js$/, '.ts');
if (isMainModule) {
const server = new ChangerawrMCPServer();
server.run().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});
}
export { ChangerawrMCPServer };