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']
}
}
];