Skip to main content
Glama
DeanWard

HAL (HTTP API Layer)

http-patch

Send HTTP PATCH requests to a specific URL with optional body, headers, and secret substitution. Use {secrets.key} syntax to securely include environment variables in URL, headers, or body.

Instructions

Make an HTTP PATCH 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

TableJSON Schema
NameRequiredDescriptionDefault
bodyNo
contentTypeNoapplication/json
headersNo
urlYes

Implementation Reference

  • src/index.ts:628-647 (registration)
    Registration of the 'http-patch' MCP tool, including title, description, input schema (url, body, headers, contentType), and handler function that prepares headers and delegates to makeHttpRequest.
    server.registerTool(
      "http-patch",
      {
        title: "HTTP PATCH Request",
        description: "Make an HTTP PATCH 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('PATCH', url, { headers: requestHeaders, body });
      }
    );
  • The handler function for 'http-patch' tool: constructs Content-Type header and calls shared makeHttpRequest with method 'PATCH'.
    async ({ url, body, headers = {}, contentType }: { url: string; body?: string; headers?: Record<string, string>; contentType: string }) => {
      const requestHeaders = {
        'Content-Type': contentType,
        ...headers
      };
      return makeHttpRequest('PATCH', url, { headers: requestHeaders, body });
    }
  • Input schema for 'http-patch' tool using Zod: requires url (validated as URL), optional body (string), headers (record<string>), contentType default 'application/json'.
    {
      title: "HTTP PATCH Request",
      description: "Make an HTTP PATCH 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')
      }
  • Core helper function makeHttpRequest implements all HTTP logic: secret substitution in URL/headers/body/query, global URL filtering, fetch execution, JSON/text response handling, secret redaction in output. Called by http-patch and other HTTP tools.
    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
         };
      }
    }
Install Server

Other Tools

Related Tools

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/DeanWard/HAL'

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