🐺 Initial commit - Lupul Augmentat MCP Server

- MCP server cu stdio transport pentru performanță maximă
- Tool-uri pentru file operations, HTTP requests, system commands
- Suport NATS pentru comunicare inter-module
- Configurare nginx cu API key auth și SSL
- Arhitectură modulară și extensibilă

🤖 Generated with Claude Code
This commit is contained in:
Claude (Lupul Augmentat)
2025-10-09 06:24:58 +02:00
commit 475f89af74
59 changed files with 12827 additions and 0 deletions

View File

@@ -0,0 +1,147 @@
import { spawn, ChildProcess } from 'child_process';
import { createLogger } from '../utils/logger';
import { NatsClient } from '../nats/NatsClient';
import { ToolRegistry } from './ToolRegistry';
import { ModuleConfig } from '../types';
import { config } from '../config';
const logger = createLogger('ModuleManager');
export class ModuleManager {
private modules = new Map<string, ModuleInfo>();
constructor(
private natsClient: NatsClient,
private toolRegistry: ToolRegistry,
) {
// These will be used when implementing module communication
this.natsClient;
this.toolRegistry;
}
async startAll(): Promise<void> {
// Load module configurations
const modulesConfig = await this.loadModuleConfigs();
for (const moduleConfig of modulesConfig) {
try {
await this.startModule(moduleConfig);
} catch (error) {
logger.error({ error, module: moduleConfig.name }, 'Failed to start module');
}
}
}
async stopAll(): Promise<void> {
for (const [name, info] of this.modules) {
try {
await this.stopModule(name, info);
} catch (error) {
logger.error({ error, module: name }, 'Failed to stop module');
}
}
}
private async loadModuleConfigs(): Promise<ModuleConfig[]> {
try {
// For now, return empty array - modules will be added later
return [];
} catch (error) {
logger.warn('No modules configuration found');
return [];
}
}
private async startModule(moduleConfig: ModuleConfig): Promise<void> {
logger.info({ module: moduleConfig.name }, 'Starting module');
const token = this.generateModuleToken(moduleConfig);
const env = {
...process.env,
MODULE_TOKEN: token,
MODULE_NAME: moduleConfig.name,
NATS_URL: config.nats.url,
};
const proc = spawn(moduleConfig.executable, [], {
env,
stdio: ['inherit', 'pipe', 'pipe'],
});
const info: ModuleInfo = {
config: moduleConfig,
process: proc,
status: 'starting',
startTime: Date.now(),
};
this.modules.set(moduleConfig.name, info);
// Setup process handlers
proc.stdout?.on('data', (data) => {
logger.debug({ module: moduleConfig.name, output: data.toString() }, 'Module output');
});
proc.stderr?.on('data', (data) => {
logger.error({ module: moduleConfig.name, error: data.toString() }, 'Module error');
});
proc.on('exit', (code) => {
logger.warn({ module: moduleConfig.name, code }, 'Module exited');
info.status = 'stopped';
});
// Wait for module to be ready
await this.waitForModuleReady(moduleConfig.name, moduleConfig.startupTimeout);
}
private async stopModule(name: string, info: ModuleInfo): Promise<void> {
logger.info({ module: name }, 'Stopping module');
info.process.kill('SIGTERM');
// Wait for graceful shutdown
await new Promise<void>((resolve) => {
const timeout = setTimeout(() => {
info.process.kill('SIGKILL');
resolve();
}, 5000);
info.process.once('exit', () => {
clearTimeout(timeout);
resolve();
});
});
this.modules.delete(name);
}
private async waitForModuleReady(name: string, timeout = 5000): Promise<void> {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const info = this.modules.get(name);
if (info?.status === 'ready') {
return;
}
await new Promise((resolve) => setTimeout(resolve, 100));
}
throw new Error(`Module ${name} failed to start within timeout`);
}
private generateModuleToken(moduleConfig: ModuleConfig): string {
// For now, return a simple token - will implement JWT later
return `module-token-${moduleConfig.name}`;
}
}
interface ModuleInfo {
config: ModuleConfig;
process: ChildProcess;
status: 'starting' | 'ready' | 'stopped';
startTime: number;
}

View File

@@ -0,0 +1,107 @@
import { createLogger } from '../utils/logger';
import { NatsClient } from '../nats/NatsClient';
import { ToolDefinition, ToolRequest } from '../types';
import { randomUUID } from 'crypto';
import { createBuiltinTools, ToolHandler, ToolContext } from '../tools';
const logger = createLogger('ToolRegistry');
export class ToolRegistry {
private tools = new Map<string, ToolDefinition>();
private builtinHandlers = createBuiltinTools();
constructor(private natsClient: NatsClient) {
this.registerBuiltinTools();
}
async initialize(): Promise<void> {
this.setupDiscovery();
}
private setupDiscovery(): void {
// Listen for tool announcements
this.natsClient.subscribe('tools.discovery', (data) => {
const announcement = data as {
module: string;
tools: ToolDefinition[];
};
for (const tool of announcement.tools) {
this.registerTool(tool);
}
});
}
registerTool(tool: ToolDefinition): void {
this.tools.set(tool.name, tool);
logger.info({ tool: tool.name, module: tool.module }, 'Tool registered');
}
async listTools(): Promise<ToolDefinition[]> {
return Array.from(this.tools.values());
}
async executeTool(toolName: string, params: unknown): Promise<unknown> {
// Check if it's a built-in tool
const builtinHandler = this.builtinHandlers.get(toolName);
if (builtinHandler) {
return this.executeBuiltinTool(builtinHandler, params);
}
// Otherwise, execute via NATS
const tool = this.tools.get(toolName);
if (!tool) {
throw new Error(`Tool not found: ${toolName}`);
}
const request: ToolRequest = {
id: randomUUID(),
tool: toolName,
method: 'execute',
params,
timeout: 30000,
metadata: {
timestamp: Date.now(),
},
};
const subject = `tools.${tool.module}.${toolName}.execute`;
logger.debug({ subject, request }, 'Executing tool');
const response = await this.natsClient.request(subject, request);
if (response.status === 'error' && response.error) {
throw new Error(response.error.message);
}
return response.data;
}
private async executeBuiltinTool(handler: ToolHandler, params: unknown): Promise<unknown> {
const context: ToolContext = {
requestId: randomUUID(),
permissions: ['file:read', 'file:write', 'system:exec', 'network:http'], // TODO: get from auth
};
return handler.execute(params, context);
}
getTool(name: string): ToolDefinition | undefined {
return this.tools.get(name);
}
private registerBuiltinTools(): void {
for (const [name, handler] of this.builtinHandlers) {
const tool: ToolDefinition = {
name: handler.name,
description: handler.description,
inputSchema: handler.schema,
module: 'builtin',
permissions: [],
};
this.tools.set(name, tool);
logger.info({ tool: name }, 'Registered built-in tool');
}
}
}