Skip to main content
Glama
orneryd

M.I.M.I.R - Multi-agent Intelligent Memory & Insight Repository

by orneryd
mcp-tools-api.ts9.49 kB
/** * @module api/mcp-tools-api * @description REST API wrapper for MCP tools * * Provides HTTP endpoints that expose all 13 MCP tools as REST APIs. * Each tool can be called via POST with JSON parameters. * * **Available Tools:** * - Memory operations (6): memory_node, memory_edge, memory_batch, memory_lock, memory_clear, get_task_context * - File indexing (3): index_folder, remove_folder, list_folders * - Vector search (2): vector_search_nodes, get_embedding_stats * - TODO management (2): todo, todo_list * * **Endpoints:** * - `GET /api/mcp/tools` - List all available tools * - `POST /api/mcp/tools/:toolName` - Execute a specific tool * * @example * ```typescript * // Call memory_node tool * fetch('/api/mcp/tools/memory_node', { * method: 'POST', * headers: { 'Content-Type': 'application/json' }, * body: JSON.stringify({ * operation: 'add', * type: 'memory', * properties: { title: 'Test', content: 'Content' } * }) * }); * ``` */ import { Router } from 'express'; import type { IGraphManager } from '../types/index.js'; import { handleIndexFolder, handleRemoveFolder, handleListWatchedFolders } from '../tools/fileIndexing.tools.js'; export function createMCPToolsRouter(graphManager: IGraphManager): Router { const router = Router(); /** * POST /mcp/index-folder - Start indexing and watching a folder * * Initiates file indexing with automatic watching for changes. * Supports embeddings generation, recursive scanning, and pattern filtering. * * Request Body: * - path: Folder path to index (required) * - recursive: Scan subdirectories (default: true) * - generate_embeddings: Generate vector embeddings (default: from env) * - file_patterns: File patterns to include (optional) * - ignore_patterns: Patterns to ignore (optional) * * @returns JSON with indexing result and watch status * * @example * fetch('/mcp/index-folder', { * method: 'POST', * headers: { 'Content-Type': 'application/json' }, * body: JSON.stringify({ * path: '/workspace/src', * recursive: true, * generate_embeddings: true * }) * }).then(r => r.json()); */ router.post('/mcp/index-folder', async (req, res) => { try { // Default generate_embeddings based on MIMIR_EMBEDDINGS_ENABLED env var const embeddingsEnabledByDefault = process.env.MIMIR_EMBEDDINGS_ENABLED !== 'false'; const { path, recursive = true, generate_embeddings = embeddingsEnabledByDefault, file_patterns, ignore_patterns } = req.body; if (!path) { return res.status(400).json({ error: 'Missing required parameter: path' }); } console.log(`📁 API: Indexing folder ${path} (embeddings: ${generate_embeddings})`); // Get FileWatchManager from the global state // Note: This assumes FileWatchManager is passed through or accessible const watchManager = (globalThis as any).fileWatchManager; if (!watchManager) { return res.status(500).json({ error: 'FileWatchManager not initialized' }); } const result = await handleIndexFolder( { path, recursive, generate_embeddings, file_patterns, ignore_patterns }, graphManager.getDriver(), watchManager ); res.json(result); } catch (error: any) { console.error('❌ Index folder error:', error); res.status(500).json({ error: 'Failed to index folder', details: error.message }); } }); /** * POST /mcp/remove-folder - Stop watching and remove indexed files * * Stops file watching and removes all indexed files from database. * * Request Body: * - path: Folder path to remove (required) * * @returns JSON with removal result * * @example * fetch('/mcp/remove-folder', { * method: 'POST', * headers: { 'Content-Type': 'application/json' }, * body: JSON.stringify({ path: '/workspace/src' }) * }).then(r => r.json()); */ router.post('/mcp/remove-folder', async (req, res) => { try { const { path } = req.body; if (!path) { return res.status(400).json({ error: 'Missing required parameter: path' }); } console.log(`🗑️ API: Removing folder ${path}`); const watchManager = (globalThis as any).fileWatchManager; if (!watchManager) { return res.status(500).json({ error: 'FileWatchManager not initialized' }); } const result = await handleRemoveFolder( { path }, graphManager.getDriver(), watchManager ); res.json(result); } catch (error: any) { console.error('❌ Remove folder error:', error); res.status(500).json({ error: 'Failed to remove folder', details: error.message }); } }); /** * POST /mcp/save-conversation - Save chat conversation to memory * * Stores conversation messages as a memory node for future reference. * * Request Body: * - messages: Array of message objects with role, content, timestamp * * @returns JSON with memory ID * * @example * fetch('/mcp/save-conversation', { * method: 'POST', * headers: { 'Content-Type': 'application/json' }, * body: JSON.stringify({ * messages: [ * { role: 'user', content: 'Hello', timestamp: Date.now() }, * { role: 'assistant', content: 'Hi!', timestamp: Date.now() } * ] * }) * }).then(r => r.json()); */ router.post('/mcp/save-conversation', async (req, res) => { try { const { messages } = req.body; if (!messages || !Array.isArray(messages) || messages.length === 0) { return res.status(400).json({ error: 'Missing or invalid messages array' }); } console.log(`💭 API: Saving conversation with ${messages.length} messages`); // Format the conversation as markdown const conversationText = messages .map((msg: any) => { const role = msg.role === 'user' ? '**User**' : '**Assistant**'; const timestamp = new Date(msg.timestamp).toLocaleString(); return `${role} (${timestamp}):\n${msg.content}\n`; }) .join('\n---\n\n'); // Create a title from the first user message const firstUserMessage = messages.find((m: any) => m.role === 'user'); const title = firstUserMessage ? `Chat: ${firstUserMessage.content.substring(0, 50)}${firstUserMessage.content.length > 50 ? '...' : ''}` : 'Chat Conversation'; // Save as memory node const session = graphManager.getDriver().session(); try { const result = await session.run( ` CREATE (m:Node:Memory { id: randomUUID(), type: 'memory', title: $title, content: $content, category: 'conversation', messageCount: $messageCount, createdAt: datetime(), tags: ['chat', 'conversation'] }) RETURN m.id AS memoryId `, { title, content: conversationText, messageCount: messages.length } ); const memoryId = result.records[0]?.get('memoryId'); console.log(`✅ Conversation saved as memory: ${memoryId}`); res.json({ success: true, memoryId, message: `Conversation saved with ${messages.length} messages` }); } finally { await session.close(); } } catch (error: any) { console.error('❌ Save conversation error:', error); res.status(500).json({ error: 'Failed to save conversation', details: error.message }); } }); /** * GET /mcp/list-folders - List all indexed folders * * Returns all folders currently being watched with their status. * * @returns JSON with folders array * * @example * fetch('/mcp/list-folders') * .then(r => r.json()) * .then(data => { * data.folders.forEach(f => { * console.log(f.path, f.status, f.filesIndexed); * }); * }); */ router.get('/mcp/list-folders', async (req, res) => { try { console.log('📋 API: Listing indexed folders'); const watchManager = (globalThis as any).fileWatchManager; if (!watchManager) { return res.status(500).json({ error: 'FileWatchManager not initialized' }); } const result = await handleListWatchedFolders(graphManager.getDriver()); // Transform the response to match frontend expectations const folders = result.watches?.map((watch: any) => ({ path: watch.containerPath || watch.folder, // Use containerPath (/workspace/...) instead of internal folder path recursive: watch.recursive, filePatterns: watch.file_patterns, status: watch.active ? 'active' : 'inactive', filesIndexed: typeof watch.files_indexed === 'object' ? watch.files_indexed.low : watch.files_indexed })) || []; res.json({ folders }); } catch (error: any) { console.error('❌ List folders error:', error); res.status(500).json({ error: 'Failed to list folders', details: error.message }); } }); return router; }

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/orneryd/Mimir'

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