HTTP PUT Request
http-putSend HTTP PUT requests to specified URLs with custom body and headers, supporting secret substitution using environment variables.
Instructions
Make an HTTP PUT request to a specified URL with optional body and headers. Supports secret substitution using {secrets.key} syntax in URL, headers, and body where 'key' corresponds to HAL_SECRET_KEY environment variables.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | ||
| body | No | ||
| headers | No | ||
| contentType | No | application/json |
Implementation Reference
- src/index.ts:607-626 (registration)Registration of the 'http-put' tool with server.registerTool(). Includes the tool name, input schema (url required, plus optional body, headers, contentType), and the async handler that delegates to makeHttpRequest('PUT', ...).
server.registerTool( "http-put", { title: "HTTP PUT Request", description: "Make an HTTP PUT request to a specified URL with optional body and headers. Supports secret substitution using {secrets.key} syntax in URL, headers, and body where 'key' corresponds to HAL_SECRET_KEY environment variables.", inputSchema: { url: z.string().url(), body: z.string().optional(), headers: z.record(z.string()).optional(), contentType: z.string().default('application/json') } }, async ({ url, body, headers = {}, contentType }: { url: string; body?: string; headers?: Record<string, string>; contentType: string }) => { const requestHeaders = { 'Content-Type': contentType, ...headers }; return makeHttpRequest('PUT', url, { headers: requestHeaders, body }); } ); - src/index.ts:607-626 (handler)The handler function for http-put. It receives url, body, headers, contentType, sets Content-Type default header, and calls makeHttpRequest('PUT', url, { headers: requestHeaders, body }).
server.registerTool( "http-put", { title: "HTTP PUT Request", description: "Make an HTTP PUT request to a specified URL with optional body and headers. Supports secret substitution using {secrets.key} syntax in URL, headers, and body where 'key' corresponds to HAL_SECRET_KEY environment variables.", inputSchema: { url: z.string().url(), body: z.string().optional(), headers: z.record(z.string()).optional(), contentType: z.string().default('application/json') } }, async ({ url, body, headers = {}, contentType }: { url: string; body?: string; headers?: Record<string, string>; contentType: string }) => { const requestHeaders = { 'Content-Type': contentType, ...headers }; return makeHttpRequest('PUT', url, { headers: requestHeaders, body }); } ); - src/index.ts:612-617 (schema)Input schema for http-put: url (z.string().url() required), body (optional string), headers (optional record of strings), contentType (default 'application/json').
inputSchema: { url: z.string().url(), body: z.string().optional(), headers: z.record(z.string()).optional(), contentType: z.string().default('application/json') } - src/index.ts:343-447 (helper)The makeHttpRequest helper function that executes the actual HTTP request. Handles secret substitution, URL validation, query params, fetch execution, response parsing, and redaction of secrets from output.
async function makeHttpRequest( method: string, url: string, options: { headers?: Record<string, string>; body?: string; queryParams?: Record<string, any>; } = {} ) { try { const { headers = {}, body, queryParams = {} } = options; // First, substitute secrets in URL to get the final URL for validation // We need to do this in two passes to handle URL restrictions properly const processedUrl = substituteSecrets(url, url); // Now substitute secrets in headers, body, and query parameters using the processed URL const processedHeaders = substituteSecretsInObject(headers, processedUrl); const processedBody = body ? substituteSecrets(body, processedUrl) : body; const processedQueryParams = substituteSecretsInObject(queryParams, processedUrl); // Build URL with query parameters const urlObj = new URL(processedUrl); Object.entries(processedQueryParams).forEach(([key, value]) => { if (value !== undefined && value !== null) { urlObj.searchParams.set(key, String(value)); } }); const finalUrl = urlObj.toString(); // Check global URL whitelist/blacklist const urlCheck = isUrlAllowedGlobal(finalUrl); if (!urlCheck.allowed) { throw new Error(urlCheck.reason || 'URL is not allowed'); } const defaultHeaders = { 'User-Agent': 'HAL-MCP/1.0.0', ...processedHeaders }; // Add Content-Type for methods that typically send data if (['POST', 'PUT', 'PATCH'].includes(method.toUpperCase()) && processedBody && !('Content-Type' in processedHeaders)) { (defaultHeaders as any)['Content-Type'] = 'application/json'; } const response = await fetch(finalUrl, { method: method.toUpperCase(), headers: defaultHeaders, body: processedBody }); const contentType = response.headers.get('content-type') || 'text/plain'; let content: string; // HEAD requests don't have a body by design if (method.toUpperCase() === 'HEAD') { content = '(No body - HEAD request)'; } else { try { if (contentType.includes('application/json')) { const text = await response.text(); if (text.trim()) { content = JSON.stringify(JSON.parse(text), null, 2); } else { content = '(Empty response)'; } } else { content = await response.text(); } } catch (parseError) { // If JSON parsing fails, try to get text try { content = await response.text(); } catch (textError) { content = '(Unable to parse response)'; } } } // Redact secrets from response headers and content before returning const redactedHeaders = Array.from(response.headers.entries()) .map(([key, value]) => `${key}: ${redactSecretsFromText(value)}`) .join('\n'); const redactedContent = redactSecretsFromText(content); return { content: [{ type: "text" as const, text: `Status: ${response.status} ${response.statusText}\n\nHeaders:\n${redactedHeaders}\n\nBody:\n${redactedContent}` }] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; const redactedErrorMessage = redactSecretsFromText(errorMessage); return { content: [{ type: "text" as const, text: `Error making ${method.toUpperCase()} request: ${redactedErrorMessage}` }], isError: true }; } }