Skip to main content
Glama
http-transport.ts11 kB
import { createServer, IncomingMessage, ServerResponse } from 'http'; import { URL } from 'url'; import { RAGService } from '../rag/rag-service.js'; import { FileService } from '../connectors/file-service.js'; import { WebService } from '../connectors/web-service.js'; import { TaskService } from '../connectors/task-service.js'; export class HttpTransport { private httpServer: any; private port: number; private ragService: RAGService; private fileService: FileService; private webService: WebService; private taskService: TaskService; constructor(port: number = 3333) { this.port = port; this.ragService = new RAGService(); this.fileService = new FileService(); this.webService = new WebService(); this.taskService = new TaskService(); } async connect(): Promise<void> { this.httpServer = createServer(this.handleRequest.bind(this)); return new Promise((resolve, reject) => { this.httpServer.listen(this.port, () => { console.error(`🌐 HTTP MCP сервер запущен на порту ${this.port}`); resolve(); }); this.httpServer.on('error', (error: any) => { console.error('Ошибка запуска HTTP сервера:', error); reject(error); }); }); } private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> { try { const url = new URL(req.url || '/', `http://${req.headers.host}`); const path = url.pathname; // CORS заголовки res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } // Обработка различных эндпоинтов switch (path) { case '/health': await this.handleHealth(req, res); break; case '/tools': await this.handleListTools(req, res); break; case '/call': await this.handleCallTool(req, res); break; case '/status': await this.handleStatus(req, res); break; default: await this.handleNotFound(req, res); } } catch (error) { console.error('Ошибка обработки HTTP запроса:', error); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Внутренняя ошибка сервера' })); } } private async handleHealth(req: IncomingMessage, res: ServerResponse): Promise<void> { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'ok', service: 'ai-ops-hub', version: '0.1.0', timestamp: new Date().toISOString() })); } private async handleListTools(req: IncomingMessage, res: ServerResponse): Promise<void> { if (req.method !== 'GET') { res.writeHead(405, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Метод не разрешен' })); return; } try { // Возвращаем список доступных инструментов const tools = [ { name: 'rag_search', description: 'Поиск по личному корпусу документов', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Поисковый запрос' }, limit: { type: 'number', description: 'Максимальное количество результатов', default: 5 } }, required: ['query'] } }, { name: 'rag_add_document', description: 'Добавить документ в RAG корпус', inputSchema: { type: 'object', properties: { uri: { type: 'string', description: 'URI документа' }, content: { type: 'string', description: 'Содержимое документа' }, title: { type: 'string', description: 'Заголовок документа' } }, required: ['uri', 'content', 'title'] } }, { name: 'file_read', description: 'Читать содержимое файла', inputSchema: { type: 'object', properties: { path: { type: 'string', description: 'Путь к файлу' } }, required: ['path'] } }, { name: 'file_write', description: 'Записать содержимое в файл', inputSchema: { type: 'object', properties: { path: { type: 'string', description: 'Путь к файлу' }, content: { type: 'string', description: 'Содержимое для записи' } }, required: ['path', 'content'] } }, { name: 'web_fetch', description: 'Получить содержимое веб-страницы', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'URL страницы' } }, required: ['url'] } }, { name: 'task_create', description: 'Создать новую задачу', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Заголовок задачи' }, project: { type: 'string', description: 'Проект' }, due: { type: 'string', description: 'Срок выполнения (YYYY-MM-DD)' } }, required: ['title'] } }, { name: 'task_list', description: 'Список задач', inputSchema: { type: 'object', properties: { project: { type: 'string', description: 'Фильтр по проекту' }, status: { type: 'string', description: 'Статус (open/completed)', enum: ['open', 'completed'] } } } } ]; res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ jsonrpc: '2.0', id: 'list-tools', result: { tools } })); } catch (error) { console.error('Ошибка получения списка инструментов:', error); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Ошибка получения инструментов' })); } } private async handleCallTool(req: IncomingMessage, res: ServerResponse): Promise<void> { if (req.method !== 'POST') { res.writeHead(405, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Метод не разрешен' })); return; } try { // Читаем тело запроса const body = await this.readRequestBody(req); const { name, arguments: args } = JSON.parse(body); if (!name) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Имя инструмента обязательно' })); return; } // Вызываем соответствующий сервис let result; switch (name) { case 'rag_search': result = await this.ragService.search(args.query, args.limit || 5); break; case 'rag_add_document': await this.ragService.addDocument(args.uri, args.content, args.title); result = { message: 'Документ добавлен' }; break; case 'file_read': result = await this.fileService.readFile(args.path); break; case 'file_write': await this.fileService.writeFile(args.path, args.content); result = { message: 'Файл записан' }; break; case 'web_fetch': result = await this.webService.fetchPage(args.url); break; case 'task_create': result = await this.taskService.createTask(args.title, args.project, args.due); break; case 'task_list': result = await this.taskService.listTasks(args.project, args.status); break; default: res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: `Неизвестный инструмент: ${name}` })); return; } res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ jsonrpc: '2.0', id: 'call-tool', result: { content: result } })); } catch (error) { console.error('Ошибка вызова инструмента:', error); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Ошибка вызова инструмента' })); } } private async handleStatus(req: IncomingMessage, res: ServerResponse): Promise<void> { if (req.method !== 'GET') { res.writeHead(405, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Метод не разрешен' })); return; } res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ service: 'ai-ops-hub', version: '0.1.0', uptime: process.uptime(), memory: process.memoryUsage(), timestamp: new Date().toISOString() })); } private async handleNotFound(req: IncomingMessage, res: ServerResponse): Promise<void> { res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Эндпоинт не найден', available: ['/health', '/tools', '/call', '/status'] })); } private async readRequestBody(req: IncomingMessage): Promise<string> { return new Promise((resolve, reject) => { let body = ''; req.on('data', (chunk) => { body += chunk.toString(); }); req.on('end', () => { resolve(body); }); req.on('error', reject); }); } async disconnect(): Promise<void> { if (this.httpServer) { return new Promise((resolve) => { this.httpServer.close(() => { console.error('🌐 HTTP MCP сервер остановлен'); resolve(); }); }); } } }

Implementation Reference

Latest Blog Posts

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/Galiusbro/MCP'

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