#!/usr/bin/env node
/**
* LearnMCP Server - Standalone MCP server for learning content extraction and summarization
* Enhances Forest with learning capabilities while maintaining separation of concerns
*/
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 { createLearnLogger } from './modules/utils/custom-logger.js';
import { LearnService } from './modules/learn-service.js';
import { mcpHandlers } from './modules/mcp-handlers.js';
class LearnMCPServer {
constructor() {
this.server = new Server(
{
name: 'learn-mcp-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// Initialize logger with Forest-compatible patterns
this.logger = createLearnLogger('LearnMCPServer');
// Get data directory from environment (shared with Forest)
this.dataDir = process.env.FOREST_DATA_DIR || './.forest-data';
// Initialize core service
this.learnService = new LearnService(this.dataDir, this.logger);
this.setupHandlers();
this.logger.info('LearnMCP Server initialized', {
dataDir: this.dataDir,
version: '1.0.0',
});
}
setupHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: mcpHandlers.getToolDefinitions(),
};
});
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async request => {
const { name, arguments: args } = request.params;
this.logger.debug('Tool call received', {
tool: name,
argsKeys: Object.keys(args || {}),
});
try {
const result = await mcpHandlers.handleToolCall(name, args, this.learnService);
this.logger.debug('Tool call completed', {
tool: name,
success: true,
contentItems: result.content?.length || 0,
});
return result;
} catch (error) {
this.logger.error('Tool call failed', {
tool: name,
error: error.message,
stack: error.stack,
});
return {
content: [
{
type: 'text',
text: `Error in ${name}: ${error.message}`,
},
],
isError: true,
};
}
});
}
async start() {
try {
// Initialize the learn service
await this.learnService.initialize();
// Start the MCP server
const transport = new StdioServerTransport();
await this.server.connect(transport);
this.logger.info('LearnMCP Server started successfully', {
transport: 'stdio',
dataDir: this.dataDir,
});
} catch (error) {
this.logger.error('Failed to start LearnMCP Server', {
error: error.message,
stack: error.stack,
});
process.exit(1);
}
}
async shutdown() {
try {
await this.learnService.shutdown();
this.logger.info('LearnMCP Server shutdown complete');
} catch (error) {
this.logger.error('Error during shutdown', {
error: error.message,
});
}
}
}
// Handle graceful shutdown
const server = new LearnMCPServer();
process.on('SIGINT', async () => {
console.log('\nReceived SIGINT, shutting down gracefully...');
await server.shutdown();
process.exit(0);
});
process.on('SIGTERM', async () => {
console.log('\nReceived SIGTERM, shutting down gracefully...');
await server.shutdown();
process.exit(0);
});
// Start the server
server.start().catch(error => {
console.error('Fatal error starting LearnMCP Server:', error);
process.exit(1);
});