Skip to main content
Glama
DeanWard

HAL (HTTP API Layer)

HTTP PUT Request

http-put

Send an HTTP PUT request to a specified URL with customizable headers, body, and secret substitution using HAL (HTTP API Layer), enabling secure and flexible API interactions.

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

TableJSON Schema
NameRequiredDescriptionDefault
bodyNo
contentTypeNoapplication/json
headersNo
urlYes

Implementation Reference

  • src/index.ts:607-626 (registration)
    Registration of the 'http-put' MCP tool using server.registerTool, defining its title, description, input schema (using Zod), and handler function that calls the shared makeHttpRequest.
    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 });
      }
    );
  • Specific handler function for the http-put tool. Prepares the request headers by adding Content-Type and delegates the actual HTTP request to the shared makeHttpRequest helper.
    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 });
    }
  • Zod-based input schema defining the parameters for the http-put tool: url (required URL), body (optional string), headers (optional record), 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')
    }
  • Core implementation of HTTP requests shared across all HTTP tools (including http-put). Handles secret substitution in URL/body/headers/query, URL validation (whitelist/blacklist), fetch execution, JSON parsing, secret redaction in responses, and error handling.
    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
         };
      }
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It discloses behavioral traits such as supporting secret substitution and optional body/headers, but lacks details on error handling, rate limits, authentication requirements beyond secrets, or response format. This is adequate but has clear gaps for a mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded, with two sentences that efficiently convey the core functionality and key feature (secret substitution). Every sentence adds value without redundancy, making it easy to understand quickly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (HTTP mutation tool with 4 parameters, no annotations, and no output schema), the description is minimally viable. It covers basic purpose and secret substitution but misses details like error behavior, response handling, and full parameter guidance. It's adequate but incomplete for safe agent use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It adds meaning by explaining the purpose of parameters (e.g., URL for the request, body and headers as optional, and secret substitution syntax), though it doesn't detail all four parameters individually. This provides good context beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Make an HTTP PUT request') and resource ('to a specified URL'), distinguishing it from sibling tools like http-get or http-post by specifying the HTTP method. It also mentions optional body and headers, providing a complete picture of what the tool does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage context by specifying 'HTTP PUT request' and mentioning secret substitution, which suggests when to use this tool (e.g., for updating resources with authentication). However, it does not explicitly state when to choose PUT over alternatives like PATCH or POST, or provide exclusions, leaving some ambiguity compared to siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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