Skip to main content
Glama

IT Tools MCP Server

index.ts•5.52 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { Client as SSHClient } from "ssh2"; import fs from "fs"; import path from "path"; import os from "os"; function resolvePrivateKey(privateKeyArg?: string): string | undefined { // If not provided, try default keys if (!privateKeyArg) { const home = os.homedir(); const defaultKeys = [ path.join(home, '.ssh', 'id_rsa'), path.join(home, '.ssh', 'id_ed25519'), ]; for (const keyPath of defaultKeys) { if (fs.existsSync(keyPath)) { return fs.readFileSync(keyPath, 'utf8'); } } return undefined; } // If it looks like a path, try to read it if ( privateKeyArg.startsWith('/') || privateKeyArg.startsWith('~') || privateKeyArg.endsWith('.pem') || privateKeyArg.endsWith('.key') ) { let keyPath = privateKeyArg; if (keyPath.startsWith('~')) { keyPath = path.join(os.homedir(), keyPath.slice(1)); } if (fs.existsSync(keyPath)) { return fs.readFileSync(keyPath, 'utf8'); } else { throw new Error('Private key file not found: ' + keyPath); } } // Otherwise, assume it's the key content return privateKeyArg; } export function registerScp(server: McpServer) { server.registerTool("scp", { description: "Copy files to or from a remote host using SFTP (SCP-like)", inputSchema: { target: z.string().describe("Target host"), user: z.string().describe("Username"), direction: z.enum(["upload", "download"]).describe("Direction: upload (local to remote) or download (remote to local)"), localPath: z.string().describe("Local file path (source for upload, destination for download)"), remotePath: z.string().describe("Remote file path (destination for upload, source for download)"), privateKey: z.string().optional().describe("Private key for authentication (PEM format, optional, or path to key file)") }, // VS Code compliance annotations annotations: { title: "Scp", description: "Copy files to or from a remote host using SFTP (SCP-like)", readOnlyHint: false } }, async ({ target, user, direction, localPath, remotePath, privateKey }) => { try { const { Client } = await import("ssh2"); const fs = await import("fs"); let resolvedKey: string | undefined; try { resolvedKey = resolvePrivateKey(privateKey); } catch (err: any) { return { content: [{ type: "text", text: `SCP key error: ${err.message}` }] }; } return await new Promise((resolve) => { const conn = new Client(); let finished = false; const finish = (msg: string) => { if (!finished) { finished = true; try { conn.end(); } catch {} resolve({ content: [{ type: "text", text: msg }] }); } }; // Connection timeout (20s) const timeout = setTimeout(() => { finish(`SCP connection timed out after 20 seconds`); }, 20000); conn.on("ready", () => { clearTimeout(timeout); conn.sftp((err: any, sftp: any) => { if (err) { finish(`SFTP error: ${err.message}`); return; } if (direction === "upload") { let readStream, writeStream; try { readStream = fs.createReadStream(localPath); writeStream = sftp.createWriteStream(remotePath); } catch (streamErr: any) { finish(`Upload failed: ${streamErr.message}`); return; } writeStream.on("close", () => finish(`Upload complete: ${localPath} → ${user}@${target}:${remotePath}`)); writeStream.on("error", (err: any) => finish(`Upload failed: ${err.message}`)); readStream.on("error", (err: any) => finish(`Upload failed: ${err.message}`)); readStream.pipe(writeStream); } else { let readStream, writeStream; try { readStream = sftp.createReadStream(remotePath); writeStream = fs.createWriteStream(localPath); } catch (streamErr: any) { finish(`Download failed: ${streamErr.message}`); return; } writeStream.on("close", () => finish(`Download complete: ${user}@${target}:${remotePath} → ${localPath}`)); writeStream.on("error", (err: any) => finish(`Download failed: ${err.message}`)); readStream.on("error", (err: any) => finish(`Download failed: ${err.message}`)); readStream.pipe(writeStream); } }); }).on("error", (err: any) => { clearTimeout(timeout); finish(`SCP connection error: ${err.message}`); }); try { conn.connect({ host: target, username: user, ...(resolvedKey ? { privateKey: resolvedKey } : {}) }); } catch (err: any) { clearTimeout(timeout); finish(`SCP connect threw: ${err.message}`); } }); } catch (fatalErr: any) { return { content: [{ type: "text", text: `SCP fatal error: ${fatalErr.message || fatalErr}` }] }; } } ); }

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/wrenchpilot/it-tools-mcp'

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