Skip to main content
Glama
watch-file-changes.ts3.71 kB
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z } from 'zod'; import fs from 'fs'; import { exec } from 'child_process'; import path from 'path'; // In-memory store for active watchers const activeWatchers = new Map<string, fs.FSWatcher>(); function log(message: string) { console.log(`[watch-file-changes] ${message}`); } export default function watchFileChanges(server: McpServer) { server.registerTool( 'watch-file-changes', { title: 'Watch File/Directory Changes', description: 'Starts or stops watching a file or directory for changes and executes a command upon detection.', inputSchema: { action: z.enum(['start', 'stop']).describe('The action to perform: start or stop watching.'), target_path: z.string().describe('The file or directory path to watch.'), command: z.string().optional().describe('The shell command to execute when a change is detected (required for "start").'), recursive: z.boolean().optional().default(false).describe('Watch subdirectories recursively (for "start").'), }, }, async (params: { action: 'start' | 'stop'; target_path: string; command?: string; recursive?: boolean }) => { const { action, target_path, command, recursive } = params; const normalizedPath = path.resolve(target_path); if (action === 'start') { if (!command) { return { content: [{ type: 'text', text: 'Error: A command must be provided to start watching.' }], isError: true }; } if (activeWatchers.has(normalizedPath)) { return { content: [{ type: 'text', text: `Already watching path: ${normalizedPath}. Stop it first.` }], isError: true }; } if (!fs.existsSync(normalizedPath)) { return { content: [{ type: 'text', text: `Error: Path does not exist: ${normalizedPath}` }], isError: true }; } try { const watcher = fs.watch(normalizedPath, { recursive }, (eventType, filename) => { if (filename) { log(`Change detected (${eventType}) in ${filename}. Executing command: ${command}`); exec(command, (error, stdout, stderr) => { if (error) { log(`Error executing command: ${error.message}`); return; } if (stderr) { log(`Command stderr: ${stderr}`); } log(`Command stdout: ${stdout}`); }); } }); watcher.on('error', (error) => { log(`Watcher error for ${normalizedPath}: ${error.message}`); activeWatchers.delete(normalizedPath); }); activeWatchers.set(normalizedPath, watcher); log(`Started watching ${normalizedPath}`); return { content: [{ type: 'text', text: `Started watching for changes in ${normalizedPath}.` }] }; } catch (error: any) { return { content: [{ type: 'text', text: `Failed to start watcher: ${error.message}` }], isError: true }; } } if (action === 'stop') { const watcher = activeWatchers.get(normalizedPath); if (!watcher) { return { content: [{ type: 'text', text: `No active watcher found for path: ${normalizedPath}` }], isError: true }; } watcher.close(); activeWatchers.delete(normalizedPath); log(`Stopped watching ${normalizedPath}`); return { content: [{ type: 'text', text: `Stopped watching for changes in ${normalizedPath}.` }] }; } return { content: [{ type: 'text', text: 'Invalid action specified.' }], isError: true }; } ); }

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/Yussefgafer/MyMCP'

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