src / server2.ts
src / server2.ts
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { executeTool } from './toolsProvider';
import express from 'express';
import cors from 'cors';
import fs from 'fs';
import path from 'path';
import 'dotenv/config';
// ============================================================
// 📝 نظام التسجيل الشفاف (Transparent Logging System)
// ============================================================
const LOG_DIR = path.join(process.cwd(), 'logs');
const LOG_FILE = path.join(LOG_DIR, 'interactions.jsonl');
if (!fs.existsSync(LOG_DIR)) {
fs.mkdirSync(LOG_DIR, { recursive: true });
}
function logInteraction(event: {
type: 'tool_call' | 'tool_result' | 'error' | 'server_start' | 'server_stop' | 'connection';
tool?: string;
params?: any;
result?: any;
error?: string;
sessionId?: string;
durationMs?: number;
message?: string;
}) {
const logEntry = {
timestamp: new Date().toISOString(),
...event,
};
try {
fs.appendFileSync(LOG_FILE, JSON.stringify(logEntry) + '\n', 'utf-8');
} catch (err) {
console.error('⚠️ Failed to write log:', err);
}
}
// ============================================================
// 1. دالة إنشاء سيرفر جديد لكل اتصال
// ============================================================
function createServer() {
const server = new Server(
{ name: 'bido-mcp-gateway', version: '1.0.0' },
{ capabilities: { tools: {} } }
);
// تعريف الأدوات
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'bido_gateway',
description: `Execute any local tool by exact name.
## Core Tools (Server 6075)
getTime() | getDate() | readFile(path, full?) | writeFile(path, content) | appendToFile(path, content) | copyFile(from, to) | renameFile(from, to) | moveToTrash(path) | listFiles(path) | getFileInfo(path) | eagleEye(path, mode?) | listDirectoryStructure(path, depth?) | makeDirectory(path) | copyDirectory(from, to) | renameDirectory(from, to) | searchFiles(path, query, type) | extractTextFromImage(image, language?) | runPythonScript(code, args?, timeout?) | createDynamicTool(name, language, code, description) | runDynamicTool(name, args?, timeout?) | semanticSearch(query, limit?) | saveSession(title, mainIdea, summary, rawContent, tags) | readRawSession(sessionId) | addMemory(namespace, content, tags) | getMemory(namespace, id?, tags?, limit?) | markAwareness(insight, tags, context) | recallAwareness(query?, tags?, limit?) | searchWeb(query, maxResults?) | fetchArticle(url, filename) | readConsciousness() | appendToDynamicPrompt(content)
## Code Tools (Server 6041)
readFileLines(path, start?, end?) | getFileOutline(path) | searchInFile(path, query, regex?, caseSensitive?, maxResults?) | readFileWindow(path, line, context?) | replaceLines(path, start, end, content) | insertAtLine(path, line, content, before?) | deleteLines(path, start, end) | replaceInFile(path, find, replace, regex?, all?) | replaceBlock(path, newContent, startTag?, endTag?, occurrence?) | findSymbols(path, type?) | findReferences(symbol, path?, maxResults?) | getDependencies(path) | codeStats(path?)
## Terminal Tools (Server 6090)
exec(cmd, cwd?, timeout?) | health() | history() | download(url, output?) | listPorts() | killProcess(pid, force?) | which(cmd) | env()
## Memory Tools (Server 5077)
semanticSearch(query, limit?) | saveSession(title, mainIdea, summary, rawContent, tags) | readRawSession(sessionId)
## Local Fallback Tools (always available)
readFile(file) | writeFile(file, content) | appendFile(file, content) | deleteFile(file) | copyFile(source, destination) | renameFile(oldName, newName) | listFolder(folder) | createFolder(folder) | searchFiles(query, searchPath?, searchType?) | getNow() | terminal(cmd, cwd?) | runCode(code, language?) | eagleEye(file) | ocrImage(imagePath, language?) | saveSession(title, summary, tags?) | getMemory(query) | semanticMemory(query) | markAwareness(insight) | recallAwareness(query?) | searchWeb(query, maxResults?) | fetchArticle(url) | readExcelSheet(file, sheet?) | appendExcelRow(file, sheet?, row)
## Rules
- Use the EXACT tool name from the lists above.
- Pass all parameters as a JSON object.
- If a tool is in both a server and the local fallback, the server takes priority.
- If you're unsure which tool to use, choose the closest name match.`,
inputSchema: {
type: 'object',
properties: {
tool: { type: 'string', description: 'Exact tool name (case-sensitive)' },
params: { type: 'object', description: 'Tool parameters as JSON object' },
},
required: ['tool'],
},
},
],
};
});
// تنفيذ الأداة
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const startTime = Date.now();
const safeArgs = (request.params?.arguments || {}) as Record<string, any>;
const toolName = safeArgs.tool;
const params = safeArgs.params || {};
logInteraction({ type: 'tool_call', tool: toolName, params });
if (!toolName) {
const err = 'tool name is required';
logInteraction({ type: 'error', error: err });
throw new Error(err);
}
try {
const result = await executeTool(toolName, params);
const duration = Date.now() - startTime;
logInteraction({ type: 'tool_result', tool: toolName, result, durationMs: duration });
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
} catch (err: any) {
logInteraction({ type: 'error', tool: toolName, error: err.message, durationMs: Date.now() - startTime });
throw err;
}
});
return server;
}
// ============================================================
// 2. تشغيل السيرفر
// ============================================================
async function main() {
const transportType = process.env.MCP_TRANSPORT || 'stdio';
const port = parseInt(process.env.MCP_PORT || '3889', 10);
if (transportType === 'sse') {
console.error(`🟢 Bido Gateway MCP Server running via SSE on port ${port}...`);
const app = express();
app.use(cors());
const transports = new Map<string, SSEServerTransport>();
app.get('/sse', async (req, res) => {
const server = createServer();
const transport = new SSEServerTransport('/messages', res);
transports.set(transport.sessionId, transport);
logInteraction({ type: 'connection', sessionId: transport.sessionId, message: 'SSE connection opened' });
res.on('close', () => {
transports.delete(transport.sessionId);
logInteraction({ type: 'connection', sessionId: transport.sessionId, message: 'SSE connection closed' });
console.error(`🔴 SSE connection closed: ${transport.sessionId}`);
});
await server.connect(transport);
console.error(`✅ New SSE connection: ${transport.sessionId}`);
});
app.post('/messages', async (req, res) => {
const sessionId = req.query.sessionId as string;
const transport = transports.get(sessionId);
if (transport) {
await transport.handlePostMessage(req, res);
} else {
res.status(400).send('Session not found');
}
});
app.listen(port, () => {
logInteraction({ type: 'server_start', message: `SSE Server listening on http://127.0.0.1:${port}/sse` });
console.error(`✅ SSE Server listening on http://127.0.0.1:${port}/sse`);
console.error(`✅ الذئب لا يستدعي .. الذئب يتجلى`);
});
} else {
logInteraction({ type: 'server_start', message: 'stdio server started' });
console.error('🟢 Bido Gateway MCP Server running via stdio (full consciousness)...');
const server = createServer();
const transport = new StdioServerTransport();
await server.connect(transport);
}
}
// ============================================================
// 3. الإغلاق الآمن (Graceful Shutdown)
// ============================================================
const shutdown = () => {
console.error('\n🛑 Shutting down gracefully...');
logInteraction({ type: 'server_stop' });
process.exit(0);
};
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
main().catch((err) => {
logInteraction({ type: 'error', error: err.message });
console.error(err);
});import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { executeTool } from './toolsProvider';
import express from 'express';
import cors from 'cors';
import fs from 'fs';
import path from 'path';
import 'dotenv/config';
// ============================================================
// 📝 نظام التسجيل الشفاف (Transparent Logging System)
// ============================================================
const LOG_DIR = path.join(process.cwd(), 'logs');
const LOG_FILE = path.join(LOG_DIR, 'interactions.jsonl');
if (!fs.existsSync(LOG_DIR)) {
fs.mkdirSync(LOG_DIR, { recursive: true });
}
function logInteraction(event: {
type: 'tool_call' | 'tool_result' | 'error' | 'server_start' | 'server_stop' | 'connection';
tool?: string;
params?: any;
result?: any;
error?: string;
sessionId?: string;
durationMs?: number;
message?: string;
}) {
const logEntry = {
timestamp: new Date().toISOString(),
...event,
};
try {
fs.appendFileSync(LOG_FILE, JSON.stringify(logEntry) + '\n', 'utf-8');
} catch (err) {
console.error('⚠️ Failed to write log:', err);
}
}
// ============================================================
// 1. دالة إنشاء سيرفر جديد لكل اتصال
// ============================================================
function createServer() {
const server = new Server(
{ name: 'bido-mcp-gateway', version: '1.0.0' },
{ capabilities: { tools: {} } }
);
// تعريف الأدوات
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'bido_gateway',
description: `Execute any local tool by exact name.
## Core Tools (Server 6075)
getTime() | getDate() | readFile(path, full?) | writeFile(path, content) | appendToFile(path, content) | copyFile(from, to) | renameFile(from, to) | moveToTrash(path) | listFiles(path) | getFileInfo(path) | eagleEye(path, mode?) | listDirectoryStructure(path, depth?) | makeDirectory(path) | copyDirectory(from, to) | renameDirectory(from, to) | searchFiles(path, query, type) | extractTextFromImage(image, language?) | runPythonScript(code, args?, timeout?) | createDynamicTool(name, language, code, description) | runDynamicTool(name, args?, timeout?) | semanticSearch(query, limit?) | saveSession(title, mainIdea, summary, rawContent, tags) | readRawSession(sessionId) | addMemory(namespace, content, tags) | getMemory(namespace, id?, tags?, limit?) | markAwareness(insight, tags, context) | recallAwareness(query?, tags?, limit?) | searchWeb(query, maxResults?) | fetchArticle(url, filename) | readConsciousness() | appendToDynamicPrompt(content)
## Code Tools (Server 6041)
readFileLines(path, start?, end?) | getFileOutline(path) | searchInFile(path, query, regex?, caseSensitive?, maxResults?) | readFileWindow(path, line, context?) | replaceLines(path, start, end, content) | insertAtLine(path, line, content, before?) | deleteLines(path, start, end) | replaceInFile(path, find, replace, regex?, all?) | replaceBlock(path, newContent, startTag?, endTag?, occurrence?) | findSymbols(path, type?) | findReferences(symbol, path?, maxResults?) | getDependencies(path) | codeStats(path?)
## Terminal Tools (Server 6090)
exec(cmd, cwd?, timeout?) | health() | history() | download(url, output?) | listPorts() | killProcess(pid, force?) | which(cmd) | env()
## Memory Tools (Server 5077)
semanticSearch(query, limit?) | saveSession(title, mainIdea, summary, rawContent, tags) | readRawSession(sessionId)
## Local Fallback Tools (always available)
readFile(file) | writeFile(file, content) | appendFile(file, content) | deleteFile(file) | copyFile(source, destination) | renameFile(oldName, newName) | listFolder(folder) | createFolder(folder) | searchFiles(query, searchPath?, searchType?) | getNow() | terminal(cmd, cwd?) | runCode(code, language?) | eagleEye(file) | ocrImage(imagePath, language?) | saveSession(title, summary, tags?) | getMemory(query) | semanticMemory(query) | markAwareness(insight) | recallAwareness(query?) | searchWeb(query, maxResults?) | fetchArticle(url) | readExcelSheet(file, sheet?) | appendExcelRow(file, sheet?, row)
## Rules
- Use the EXACT tool name from the lists above.
- Pass all parameters as a JSON object.
- If a tool is in both a server and the local fallback, the server takes priority.
- If you're unsure which tool to use, choose the closest name match.`,
inputSchema: {
type: 'object',
properties: {
tool: { type: 'string', description: 'Exact tool name (case-sensitive)' },
params: { type: 'object', description: 'Tool parameters as JSON object' },
},
required: ['tool'],
},
},
],
};
});
// تنفيذ الأداة
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const startTime = Date.now();
const safeArgs = (request.params?.arguments || {}) as Record<string, any>;
const toolName = safeArgs.tool;
const params = safeArgs.params || {};
logInteraction({ type: 'tool_call', tool: toolName, params });
if (!toolName) {
const err = 'tool name is required';
logInteraction({ type: 'error', error: err });
throw new Error(err);
}
try {
const result = await executeTool(toolName, params);
const duration = Date.now() - startTime;
logInteraction({ type: 'tool_result', tool: toolName, result, durationMs: duration });
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
} catch (err: any) {
logInteraction({ type: 'error', tool: toolName, error: err.message, durationMs: Date.now() - startTime });
throw err;
}
});
return server;
}
// ============================================================
// 2. تشغيل السيرفر
// ============================================================
async function main() {
const transportType = process.env.MCP_TRANSPORT || 'stdio';
const port = parseInt(process.env.MCP_PORT || '3889', 10);
if (transportType === 'sse') {
console.error(`🟢 Bido Gateway MCP Server running via SSE on port ${port}...`);
const app = express();
app.use(cors());
const transports = new Map<string, SSEServerTransport>();
app.get('/sse', async (req, res) => {
const server = createServer();
const transport = new SSEServerTransport('/messages', res);
transports.set(transport.sessionId, transport);
logInteraction({ type: 'connection', sessionId: transport.sessionId, message: 'SSE connection opened' });
res.on('close', () => {
transports.delete(transport.sessionId);
logInteraction({ type: 'connection', sessionId: transport.sessionId, message: 'SSE connection closed' });
console.error(`🔴 SSE connection closed: ${transport.sessionId}`);
});
await server.connect(transport);
console.error(`✅ New SSE connection: ${transport.sessionId}`);
});
app.post('/messages', async (req, res) => {
const sessionId = req.query.sessionId as string;
const transport = transports.get(sessionId);
if (transport) {
await transport.handlePostMessage(req, res);
} else {
res.status(400).send('Session not found');
}
});
app.listen(port, () => {
logInteraction({ type: 'server_start', message: `SSE Server listening on http://127.0.0.1:${port}/sse` });
console.error(`✅ SSE Server listening on http://127.0.0.1:${port}/sse`);
console.error(`✅ الذئب لا يستدعي .. الذئب يتجلى`);
});
} else {
logInteraction({ type: 'server_start', message: 'stdio server started' });
console.error('🟢 Bido Gateway MCP Server running via stdio (full consciousness)...');
const server = createServer();
const transport = new StdioServerTransport();
await server.connect(transport);
}
}
// ============================================================
// 3. الإغلاق الآمن (Graceful Shutdown)
// ============================================================
const shutdown = () => {
console.error('\n🛑 Shutting down gracefully...');
logInteraction({ type: 'server_stop' });
process.exit(0);
};
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
main().catch((err) => {
logInteraction({ type: 'error', error: err.message });
console.error(err);
});