Skip to main content
Glama
ConnorBoetig-dev

Unrestricted Development MCP Server

network.ts25.1 kB
import { z } from 'zod'; import { exec } from 'child_process'; import { promisify } from 'util'; import * as fs from 'fs'; import * as path from 'path'; import * as https from 'https'; import * as http from 'http'; import * as net from 'net'; import { URL } from 'url'; const execAsync = promisify(exec); interface ToolResponse { content: Array<{ type: "text"; text: string; }>; isError?: boolean; } // ==================== HTTP Request Tools ==================== // Universal HTTP Request Schema export const httpRequestSchema = z.object({ url: z.string().describe('URL to send request to'), method: z.enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']).default('GET').describe('HTTP method'), headers: z.record(z.string()).optional().describe('Request headers as key-value pairs'), body: z.union([z.string(), z.record(z.any())]).optional().describe('Request body (JSON object or string)'), auth: z.object({ type: z.enum(['bearer', 'basic', 'apikey']).describe('Authentication type'), token: z.string().optional().describe('Bearer token or API key'), username: z.string().optional().describe('Username for basic auth'), password: z.string().optional().describe('Password for basic auth'), header: z.string().optional().describe('Header name for API key (e.g., "X-API-Key")') }).optional().describe('Authentication configuration'), timeout: z.number().default(30000).describe('Request timeout in milliseconds'), followRedirects: z.boolean().default(true).describe('Follow HTTP redirects'), responseType: z.enum(['json', 'text', 'binary']).default('json').describe('Expected response type'), cwd: z.string().optional().describe('Working directory') }); // HTTP Download Schema export const httpDownloadSchema = z.object({ url: z.string().describe('URL to download from'), destination: z.string().describe('Destination file path (absolute or relative)'), headers: z.record(z.string()).optional().describe('Request headers for authentication'), timeout: z.number().default(60000).describe('Download timeout in milliseconds'), cwd: z.string().optional().describe('Working directory') }); // HTTP Health Check Schema export const httpHealthCheckSchema = z.object({ url: z.string().describe('URL to check health'), expectedStatus: z.number().default(200).describe('Expected HTTP status code'), timeout: z.number().default(5000).describe('Request timeout in milliseconds'), cwd: z.string().optional().describe('Working directory') }); // ==================== Network Diagnostic Tools ==================== // Network Ping Schema export const networkPingSchema = z.object({ host: z.string().describe('Host to ping (domain or IP)'), count: z.number().default(4).describe('Number of ping requests'), timeout: z.number().default(5).describe('Timeout in seconds per ping'), cwd: z.string().optional().describe('Working directory') }); // Network Port Check Schema export const networkPortCheckSchema = z.object({ host: z.string().describe('Host to check'), port: z.number().describe('Port number to check'), timeout: z.number().default(3000).describe('Connection timeout in milliseconds'), cwd: z.string().optional().describe('Working directory') }); // Network DNS Lookup Schema export const networkDnsLookupSchema = z.object({ hostname: z.string().describe('Hostname to resolve'), recordType: z.enum(['A', 'AAAA', 'MX', 'TXT', 'NS', 'CNAME', 'SOA', 'PTR', 'ANY']).default('A').describe('DNS record type'), cwd: z.string().optional().describe('Working directory') }); // Network Curl Schema export const networkCurlSchema = z.object({ url: z.string().describe('URL for curl request'), method: z.string().optional().describe('HTTP method (default: GET)'), headers: z.array(z.string()).optional().describe('Headers in format ["Header: value"]'), data: z.string().optional().describe('Request body data'), options: z.array(z.string()).optional().describe('Additional curl options (e.g., ["-v", "--insecure"])'), cwd: z.string().optional().describe('Working directory') }); // Network Netstat Schema export const networkNetstatSchema = z.object({ listening: z.boolean().default(false).describe('Show only listening ports'), protocol: z.enum(['tcp', 'udp', 'all']).default('all').describe('Filter by protocol'), numeric: z.boolean().default(true).describe('Show numeric addresses instead of resolving'), cwd: z.string().optional().describe('Working directory') }); // Network Traceroute Schema export const networkTracerouteSchema = z.object({ destination: z.string().describe('Destination host (domain or IP)'), maxHops: z.number().default(30).describe('Maximum number of hops'), timeout: z.number().default(5).describe('Timeout per hop in seconds'), cwd: z.string().optional().describe('Working directory') }); // ==================== HTTP Request Tool Implementations ==================== export async function httpRequest(args: z.infer<typeof httpRequestSchema>): Promise<ToolResponse> { const startTime = Date.now(); try { const parsedUrl = new URL(args.url); const isHttps = parsedUrl.protocol === 'https:'; const httpModule = isHttps ? https : http; // Prepare headers const headers: Record<string, string> = { 'User-Agent': 'Claude-Code-MCP/1.0', ...args.headers }; // Handle authentication if (args.auth) { if (args.auth.type === 'bearer' && args.auth.token) { headers['Authorization'] = `Bearer ${args.auth.token}`; } else if (args.auth.type === 'basic' && args.auth.username && args.auth.password) { const credentials = Buffer.from(`${args.auth.username}:${args.auth.password}`).toString('base64'); headers['Authorization'] = `Basic ${credentials}`; } else if (args.auth.type === 'apikey' && args.auth.token && args.auth.header) { headers[args.auth.header] = args.auth.token; } } // Handle request body let bodyData: string | undefined; if (args.body) { if (typeof args.body === 'string') { bodyData = args.body; headers['Content-Type'] = headers['Content-Type'] || 'text/plain'; } else { bodyData = JSON.stringify(args.body); headers['Content-Type'] = headers['Content-Type'] || 'application/json'; } headers['Content-Length'] = Buffer.byteLength(bodyData).toString(); } // Make request return await new Promise((resolve) => { const options = { method: args.method, headers, timeout: args.timeout }; const req = httpModule.request(args.url, options, (res) => { const chunks: Buffer[] = []; res.on('data', (chunk) => chunks.push(Buffer.from(chunk))); res.on('end', () => { const responseTime = Date.now() - startTime; const rawBody = Buffer.concat(chunks); let responseBody: any; try { if (args.responseType === 'json') { responseBody = JSON.parse(rawBody.toString('utf-8')); } else if (args.responseType === 'text') { responseBody = rawBody.toString('utf-8'); } else { responseBody = rawBody.toString('base64'); } } catch { responseBody = rawBody.toString('utf-8'); } resolve({ content: [{ type: "text" as const, text: JSON.stringify({ success: true, request: { method: args.method, url: args.url, headers: args.headers }, response: { statusCode: res.statusCode, statusMessage: res.statusMessage, headers: res.headers, body: responseBody }, timing: { duration: responseTime, unit: 'ms' } }, null, 2) }] }); }); }); req.on('error', (error) => { resolve({ content: [{ type: "text" as const, text: JSON.stringify({ success: false, error: error.message, request: { method: args.method, url: args.url }, timing: { duration: Date.now() - startTime, unit: 'ms' } }, null, 2) }], isError: true }); }); req.on('timeout', () => { req.destroy(); resolve({ content: [{ type: "text" as const, text: JSON.stringify({ success: false, error: `Request timeout after ${args.timeout}ms`, request: { method: args.method, url: args.url } }, null, 2) }], isError: true }); }); if (bodyData) { req.write(bodyData); } req.end(); }); } catch (error: any) { return { content: [{ type: "text" as const, text: JSON.stringify({ success: false, error: error.message, request: { method: args.method, url: args.url } }, null, 2) }], isError: true }; } } export async function httpDownload(args: z.infer<typeof httpDownloadSchema>): Promise<ToolResponse> { try { const parsedUrl = new URL(args.url); const isHttps = parsedUrl.protocol === 'https:'; const httpModule = isHttps ? https : http; // Resolve destination path const destPath = path.isAbsolute(args.destination) ? args.destination : path.join(args.cwd || process.cwd(), args.destination); // Ensure directory exists const destDir = path.dirname(destPath); if (!fs.existsSync(destDir)) { fs.mkdirSync(destDir, { recursive: true }); } const headers = args.headers || {}; const startTime = Date.now(); return await new Promise((resolve) => { const file = fs.createWriteStream(destPath); let downloadedBytes = 0; const request = httpModule.get(args.url, { headers, timeout: args.timeout }, (response) => { const totalBytes = parseInt(response.headers['content-length'] || '0', 10); response.on('data', (chunk) => { downloadedBytes += chunk.length; }); response.pipe(file); file.on('finish', () => { file.close(); const duration = Date.now() - startTime; resolve({ content: [{ type: "text" as const, text: JSON.stringify({ success: true, url: args.url, destination: destPath, size: downloadedBytes, totalSize: totalBytes, duration, statusCode: response.statusCode }, null, 2) }] }); }); }); request.on('error', (error) => { fs.unlink(destPath, () => {}); // Clean up partial file resolve({ content: [{ type: "text" as const, text: JSON.stringify({ success: false, error: error.message, url: args.url, destination: destPath }, null, 2) }], isError: true }); }); request.on('timeout', () => { request.destroy(); fs.unlink(destPath, () => {}); resolve({ content: [{ type: "text" as const, text: JSON.stringify({ success: false, error: `Download timeout after ${args.timeout}ms`, url: args.url }, null, 2) }], isError: true }); }); }); } catch (error: any) { return { content: [{ type: "text" as const, text: JSON.stringify({ success: false, error: error.message, url: args.url, destination: args.destination }, null, 2) }], isError: true }; } } export async function httpHealthCheck(args: z.infer<typeof httpHealthCheckSchema>): Promise<ToolResponse> { const startTime = Date.now(); try { const parsedUrl = new URL(args.url); const isHttps = parsedUrl.protocol === 'https:'; const httpModule = isHttps ? https : http; return await new Promise((resolve) => { const req = httpModule.get(args.url, { timeout: args.timeout }, (res) => { const responseTime = Date.now() - startTime; const isHealthy = res.statusCode === args.expectedStatus; // Consume response data to free up memory res.resume(); resolve({ content: [{ type: "text" as const, text: JSON.stringify({ success: true, healthy: isHealthy, url: args.url, statusCode: res.statusCode, expectedStatus: args.expectedStatus, responseTime, message: isHealthy ? 'Service is healthy' : `Unexpected status code: ${res.statusCode} (expected ${args.expectedStatus})` }, null, 2) }] }); }); req.on('error', (error) => { resolve({ content: [{ type: "text" as const, text: JSON.stringify({ success: false, healthy: false, url: args.url, error: error.message, responseTime: Date.now() - startTime, message: 'Service is unreachable' }, null, 2) }], isError: true }); }); req.on('timeout', () => { req.destroy(); resolve({ content: [{ type: "text" as const, text: JSON.stringify({ success: false, healthy: false, url: args.url, error: `Timeout after ${args.timeout}ms`, message: 'Service health check timed out' }, null, 2) }], isError: true }); }); }); } catch (error: any) { return { content: [{ type: "text" as const, text: JSON.stringify({ success: false, healthy: false, url: args.url, error: error.message }, null, 2) }], isError: true }; } } // ==================== Network Diagnostic Tool Implementations ==================== async function executeNetworkCommand(command: string, cwd?: string): Promise<ToolResponse> { try { const { stdout, stderr } = await execAsync(command, { cwd: cwd || process.cwd(), shell: '/bin/bash', timeout: 60000 }); return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, command, stdout: stdout.trim(), stderr: stderr.trim(), cwd: cwd || process.cwd() }, null, 2) }] }; } catch (error: any) { return { content: [{ type: "text" as const, text: JSON.stringify({ success: false, command, stdout: error.stdout?.trim() || '', stderr: error.stderr?.trim() || error.message, exitCode: error.code || 1, cwd: cwd || process.cwd() }, null, 2) }], isError: true }; } } export async function networkPing(args: z.infer<typeof networkPingSchema>): Promise<ToolResponse> { // Use -c for count, -W for timeout (in seconds) const command = `ping -c ${args.count} -W ${args.timeout} ${args.host}`; return executeNetworkCommand(command, args.cwd); } export async function networkPortCheck(args: z.infer<typeof networkPortCheckSchema>): Promise<ToolResponse> { try { const startTime = Date.now(); const timeoutMs = args.timeout; return await new Promise((resolve) => { const socket = new net.Socket(); let isResolved = false; const cleanup = () => { if (!isResolved) { isResolved = true; socket.destroy(); } }; socket.setTimeout(timeoutMs); socket.on('connect', () => { cleanup(); const duration = Date.now() - startTime; resolve({ content: [{ type: "text" as const, text: JSON.stringify({ success: true, host: args.host, port: args.port, open: true, duration, message: `Port ${args.port} is open on ${args.host}` }, null, 2) }] }); }); socket.on('timeout', () => { cleanup(); resolve({ content: [{ type: "text" as const, text: JSON.stringify({ success: true, host: args.host, port: args.port, open: false, duration: Date.now() - startTime, message: `Port ${args.port} connection timeout on ${args.host}` }, null, 2) }] }); }); socket.on('error', (error: any) => { cleanup(); const isRefused = error.code === 'ECONNREFUSED'; resolve({ content: [{ type: "text" as const, text: JSON.stringify({ success: true, host: args.host, port: args.port, open: false, duration: Date.now() - startTime, message: isRefused ? `Port ${args.port} is closed on ${args.host}` : error.message }, null, 2) }] }); }); socket.connect(args.port, args.host); }); } catch (error: any) { return { content: [{ type: "text" as const, text: JSON.stringify({ success: false, host: args.host, port: args.port, error: error.message }, null, 2) }], isError: true }; } } export async function networkDnsLookup(args: z.infer<typeof networkDnsLookupSchema>): Promise<ToolResponse> { const command = `nslookup -type=${args.recordType} ${args.hostname}`; return executeNetworkCommand(command, args.cwd); } export async function networkCurl(args: z.infer<typeof networkCurlSchema>): Promise<ToolResponse> { let command = 'curl'; if (args.method) { command += ` -X ${args.method}`; } if (args.headers) { args.headers.forEach(header => { command += ` -H "${header}"`; }); } if (args.data) { command += ` -d '${args.data}'`; } if (args.options) { command += ` ${args.options.join(' ')}`; } command += ` "${args.url}"`; return executeNetworkCommand(command, args.cwd); } export async function networkNetstat(args: z.infer<typeof networkNetstatSchema>): Promise<ToolResponse> { let command = 'netstat'; if (args.numeric) { command += ' -n'; } if (args.listening) { command += ' -l'; } if (args.protocol === 'tcp') { command += ' -t'; } else if (args.protocol === 'udp') { command += ' -u'; } else { command += ' -tu'; } // Add common flags for better output command += ' -p'; // Show program names return executeNetworkCommand(command, args.cwd); } export async function networkTraceroute(args: z.infer<typeof networkTracerouteSchema>): Promise<ToolResponse> { const command = `traceroute -m ${args.maxHops} -w ${args.timeout} ${args.destination}`; return executeNetworkCommand(command, args.cwd); } // ==================== Tool Metadata ==================== export const networkTools = [ { name: 'http_request', description: 'Make HTTP/HTTPS requests with full control over method, headers, body, and authentication. Supports GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS. Use for API testing, data fetching, and external service integration.', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'URL to send request to' }, method: { type: 'string', enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'], default: 'GET' }, headers: { type: 'object', additionalProperties: { type: 'string' }, description: 'Request headers' }, body: { description: 'Request body (JSON object or string)' }, timeout: { type: 'number', default: 30000 }, cwd: { type: 'string', description: 'Working directory' } }, required: ['url'] } }, { name: 'http_download', description: 'Download files from URLs to local filesystem. Supports authentication headers and progress tracking. Use for downloading packages, assets, or any remote resources.', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'URL to download from' }, destination: { type: 'string', description: 'Destination file path' }, headers: { type: 'object', additionalProperties: { type: 'string' } }, timeout: { type: 'number', default: 60000 }, cwd: { type: 'string' } }, required: ['url', 'destination'] } }, { name: 'http_health_check', description: 'Check if a service/API endpoint is healthy and responding. Verifies expected status code. Use for monitoring service availability during development.', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'URL to check health' }, expectedStatus: { type: 'number', default: 200 }, timeout: { type: 'number', default: 5000 }, cwd: { type: 'string' } }, required: ['url'] } }, { name: 'network_ping', description: 'Ping a host to check network connectivity and measure latency. Use for debugging connection issues or verifying host availability.', inputSchema: { type: 'object', properties: { host: { type: 'string', description: 'Host to ping' }, count: { type: 'number', default: 4 }, timeout: { type: 'number', default: 5 }, cwd: { type: 'string' } }, required: ['host'] } }, { name: 'network_port_check', description: 'Check if a specific port is open on a host. Use for verifying services are listening on expected ports (e.g., DB on 5432, API on 3000).', inputSchema: { type: 'object', properties: { host: { type: 'string', description: 'Host to check' }, port: { type: 'number', description: 'Port number' }, timeout: { type: 'number', default: 3000 }, cwd: { type: 'string' } }, required: ['host', 'port'] } }, { name: 'network_dns_lookup', description: 'Perform DNS lookups for various record types (A, AAAA, MX, TXT, NS, CNAME, etc.). Use for debugging DNS issues or discovering service configurations.', inputSchema: { type: 'object', properties: { hostname: { type: 'string', description: 'Hostname to resolve' }, recordType: { type: 'string', enum: ['A', 'AAAA', 'MX', 'TXT', 'NS', 'CNAME', 'SOA', 'PTR', 'ANY'], default: 'A' }, cwd: { type: 'string' } }, required: ['hostname'] } }, { name: 'network_curl', description: 'Execute advanced curl commands with custom options. Use for complex HTTP operations that need specific curl features.', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'URL for curl request' }, method: { type: 'string' }, headers: { type: 'array', items: { type: 'string' } }, data: { type: 'string' }, options: { type: 'array', items: { type: 'string' } }, cwd: { type: 'string' } }, required: ['url'] } }, { name: 'network_netstat', description: 'List network connections, listening ports, and network statistics. Use for debugging port conflicts or seeing what services are running.', inputSchema: { type: 'object', properties: { listening: { type: 'boolean', default: false }, protocol: { type: 'string', enum: ['tcp', 'udp', 'all'], default: 'all' }, numeric: { type: 'boolean', default: true }, cwd: { type: 'string' } } } }, { name: 'network_traceroute', description: 'Trace the network route to a destination. Use for debugging network connectivity issues or investigating latency problems.', inputSchema: { type: 'object', properties: { destination: { type: 'string', description: 'Destination host' }, maxHops: { type: 'number', default: 30 }, timeout: { type: 'number', default: 5 }, cwd: { type: 'string' } }, required: ['destination'] } } ];

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/ConnorBoetig-dev/mcp2'

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