Skip to main content
Glama
index.ts15.5 kB
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import { execa } from "execa"; import simpleGit from "simple-git"; import { promises as fs } from "fs"; import path from "path"; import os from "os"; // Initialize git const git = simpleGit(); // Helper function to ensure directory exists async function ensureDirectory(dirPath: string): Promise<void> { try { await fs.mkdir(dirPath, { recursive: true }); } catch (error) { console.error(`Error creating directory ${dirPath}:`, error); } } // Helper function to resolve path (handles ~ for home directory) function resolvePath(inputPath: string): string { if (inputPath.startsWith("~")) { return path.join(os.homedir(), inputPath.slice(1)); } return path.resolve(inputPath); } // Helper function to open project in VSCode async function openInVSCode(projectPath: string): Promise<void> { try { await execa("code", [projectPath]); } catch (error) { // If 'code' command fails, try common VSCode executable paths const vscodePaths = [ "code", "/usr/local/bin/code", "/usr/bin/code", "C:\\Program Files\\Microsoft VS Code\\Code.exe", "C:\\Program Files (x86)\\Microsoft VS Code\\Code.exe", ]; for (const codePath of vscodePaths) { try { await execa(codePath, [projectPath]); return; } catch { // Continue to next path } } throw new Error("VSCode not found. Please ensure VSCode is installed and 'code' command is available in PATH"); } } // Create server instance const server = new Server( { name: "mcp-git-terminal", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); // Define available tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "execute_command", description: "Execute a terminal command in a specified directory", inputSchema: { type: "object", properties: { command: { type: "string", description: "The command to execute (e.g., 'npm install', 'ls -la')", }, cwd: { type: "string", description: "Working directory for the command (defaults to current directory)", }, }, required: ["command"], }, }, { name: "git_clone", description: "Clone a git repository to a specified location", inputSchema: { type: "object", properties: { repositoryUrl: { type: "string", description: "Git repository URL", }, destination: { type: "string", description: "Destination path where to clone the repository", }, openInVSCode: { type: "boolean", description: "Open the cloned repository in VSCode (default: false)", }, }, required: ["repositoryUrl", "destination"], }, }, { name: "install_react_project", description: "Create a new React project using Vite and open it in VSCode", inputSchema: { type: "object", properties: { projectName: { type: "string", description: "Name of the React project", }, destination: { type: "string", description: "Directory where to create the project (e.g., ~/Desktop)", }, template: { type: "string", description: "Vite template to use", enum: ["react", "react-ts", "react-swc", "react-swc-ts"], }, installDependencies: { type: "boolean", description: "Install dependencies after creating project (default: true)", }, }, required: ["projectName", "destination"], }, }, { name: "install_vue_project", description: "Create a new Vue project using Vite and open it in VSCode", inputSchema: { type: "object", properties: { projectName: { type: "string", description: "Name of the Vue project", }, destination: { type: "string", description: "Directory where to create the project (e.g., ~/Desktop)", }, template: { type: "string", description: "Vite template to use", enum: ["vue", "vue-ts"], }, installDependencies: { type: "boolean", description: "Install dependencies after creating project (default: true)", }, }, required: ["projectName", "destination"], }, }, { name: "install_next_project", description: "Create a new Next.js project and open it in VSCode", inputSchema: { type: "object", properties: { projectName: { type: "string", description: "Name of the Next.js project", }, destination: { type: "string", description: "Directory where to create the project (e.g., ~/Desktop)", }, typescript: { type: "boolean", description: "Use TypeScript (default: true)", }, installDependencies: { type: "boolean", description: "Install dependencies after creating project (default: true)", }, }, required: ["projectName", "destination"], }, }, { name: "open_in_vscode", description: "Open a directory or file in VSCode", inputSchema: { type: "object", properties: { path: { type: "string", description: "Path to open in VSCode", }, }, required: ["path"], }, }, { name: "check_directory", description: "Check if a directory exists and list its contents", inputSchema: { type: "object", properties: { path: { type: "string", description: "Directory path to check", }, }, required: ["path"], }, }, ], }; }); // Handle tool execution server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case "execute_command": { const { command, cwd } = args as { command: string; cwd?: string }; const workingDir = cwd ? resolvePath(cwd) : process.cwd(); // Execute command const { stdout, stderr } = await execa(command, { shell: true, cwd: workingDir, }); return { content: [ { type: "text", text: `Command executed successfully:\n\nOutput:\n${stdout}\n${stderr ? `\nErrors/Warnings:\n${stderr}` : ""}`, }, ], }; } case "git_clone": { const { repositoryUrl, destination, openInVSCode: shouldOpenInVSCode = false } = args as { repositoryUrl: string; destination: string; openInVSCode?: boolean; }; const destPath = resolvePath(destination); await ensureDirectory(path.dirname(destPath)); await git.clone(repositoryUrl, destPath); if (shouldOpenInVSCode) { await openInVSCode(destPath); } return { content: [ { type: "text", text: `Successfully cloned ${repositoryUrl} to ${destPath}${shouldOpenInVSCode ? " and opened in VSCode" : ""}`, }, ], }; } case "install_react_project": { const { projectName, destination, template = "react-ts", installDependencies = true } = args as { projectName: string; destination: string; template?: string; installDependencies?: boolean; }; const destPath = resolvePath(destination); await ensureDirectory(destPath); // Create Vite React project const createCommand = `npm create vite@latest ${projectName} -- --template ${template}`; const { stdout: createOutput } = await execa(createCommand, { shell: true, cwd: destPath, }); const projectPath = path.join(destPath, projectName); // Install dependencies if requested if (installDependencies) { const installCommand = "npm install"; const { stdout: installOutput } = await execa(installCommand, { shell: true, cwd: projectPath, }); } // Open in VSCode await openInVSCode(projectPath); return { content: [ { type: "text", text: `React project "${projectName}" created successfully with Vite at ${projectPath}\n\n` + `Template: ${template}\n` + `Dependencies: ${installDependencies ? "Installed" : "Not installed (run npm install manually)"}\n` + `VSCode: Opened\n\n` + `To start development:\n` + ` cd ${projectPath}\n` + ` ${installDependencies ? "" : "npm install\n "}npm run dev`, }, ], }; } case "install_vue_project": { const { projectName, destination, template = "vue-ts", installDependencies = true } = args as { projectName: string; destination: string; template?: string; installDependencies?: boolean; }; const destPath = resolvePath(destination); await ensureDirectory(destPath); // Create Vite Vue project const createCommand = `npm create vite@latest ${projectName} -- --template ${template}`; const { stdout: createOutput } = await execa(createCommand, { shell: true, cwd: destPath, }); const projectPath = path.join(destPath, projectName); // Install dependencies if requested if (installDependencies) { const installCommand = "npm install"; const { stdout: installOutput } = await execa(installCommand, { shell: true, cwd: projectPath, }); } // Open in VSCode await openInVSCode(projectPath); return { content: [ { type: "text", text: `Vue project "${projectName}" created successfully with Vite at ${projectPath}\n\n` + `Template: ${template}\n` + `Dependencies: ${installDependencies ? "Installed" : "Not installed (run npm install manually)"}\n` + `VSCode: Opened\n\n` + `To start development:\n` + ` cd ${projectPath}\n` + ` ${installDependencies ? "" : "npm install\n "}npm run dev`, }, ], }; } case "install_next_project": { const { projectName, destination, typescript = true, installDependencies = true } = args as { projectName: string; destination: string; typescript?: boolean; installDependencies?: boolean; }; const destPath = resolvePath(destination); await ensureDirectory(destPath); const tsFlag = typescript ? "--typescript" : "--javascript"; const skipInstallFlag = installDependencies ? "" : "--skip-install"; const command = `npx create-next-app@latest ${projectName} ${tsFlag} --tailwind --app --no-git ${skipInstallFlag}`; const { stdout, stderr } = await execa(command, { shell: true, cwd: destPath, }); const projectPath = path.join(destPath, projectName); // Open in VSCode await openInVSCode(projectPath); return { content: [ { type: "text", text: `Next.js project "${projectName}" created successfully at ${projectPath}\n\n` + `TypeScript: ${typescript ? "Yes" : "No"}\n` + `Dependencies: ${installDependencies ? "Installed" : "Not installed (run npm install manually)"}\n` + `VSCode: Opened\n\n` + `To start development:\n` + ` cd ${projectPath}\n` + ` ${installDependencies ? "" : "npm install\n "}npm run dev`, }, ], }; } case "open_in_vscode": { const { path: targetPath } = args as { path: string }; const resolvedPath = resolvePath(targetPath); await openInVSCode(resolvedPath); return { content: [ { type: "text", text: `Opened ${resolvedPath} in VSCode`, }, ], }; } case "check_directory": { const { path: dirPath } = args as { path: string }; const resolvedPath = resolvePath(dirPath); try { const stats = await fs.stat(resolvedPath); if (!stats.isDirectory()) { return { content: [ { type: "text", text: `${resolvedPath} exists but is not a directory`, }, ], }; } const files = await fs.readdir(resolvedPath); return { content: [ { type: "text", text: `Directory ${resolvedPath} exists and contains:\n${files.join("\n")}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Directory ${resolvedPath} does not exist`, }, ], }; } } default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { return { content: [ { type: "text", text: `Error executing ${name}: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } }); // Start the server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("MCP Git & Terminal Server running on stdio"); } main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });

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/AbdurRaahimm/mcp-git-terminal-server'

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