Skip to main content
Glama
dkmaker

mcp-rest-api

test_request

Test any REST API endpoint and receive detailed response data including status, headers, body, timing, and validation messages. Handles authentication and custom headers via configuration.

Instructions

Test a REST API endpoint and get detailed response information. Base URL: https://api.example.com | SSL Verification enabled (see config resource for SSL settings) | Authentication: No authentication configured | No custom headers defined (see config resource for headers) | The tool automatically: - Normalizes endpoints (adds leading slash, removes trailing slashes) - Handles authentication header injection - Applies custom headers from HEADER_* environment variables - Accepts any HTTP status code as valid - Limits response size to 10000 bytes (see config resource for size limit settings) - Returns detailed response information including: * Full URL called * Status code and text * Response headers * Response body * Request details (method, headers, body) * Response timing * Validation messages | Error Handling: - Network errors are caught and returned with descriptive messages - Invalid status codes are still returned with full response details - Authentication errors include the attempted auth method | See the config resource for all configuration options, including header configuration.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
methodYesHTTP method to use
endpointYesEndpoint path (e.g. "/users"). Do not include full URLs - only the path. Example: "/api/users" will resolve to "https://api.example.com/api/users"
bodyNoOptional request body for POST/PUT requests
headersNoOptional request headers for one-time use. IMPORTANT: Do not use for sensitive data like API keys - those should be configured via environment variables. This parameter is intended for dynamic, non-sensitive headers that may be needed for specific requests.

Implementation Reference

  • src/index.ts:286-355 (registration)
    Registration of the 'test_request' tool in the ListToolsRequestSchema handler, listing it among available tools.
        this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
          tools: [
            {
              name: 'test_request',
              description: `Test a REST API endpoint and get detailed response information. Base URL: ${normalizeBaseUrl(process.env.REST_BASE_URL!)} | SSL Verification ${REST_ENABLE_SSL_VERIFY ? 'enabled' : 'disabled'} (see config resource for SSL settings) | Authentication: ${
      hasBasicAuth() ? 
        `Basic Auth with username: ${AUTH_BASIC_USERNAME}` :
      hasBearerAuth() ? 
        'Bearer token authentication configured' :
      hasApiKeyAuth() ? 
        `API Key using header: ${AUTH_APIKEY_HEADER_NAME}` :
        'No authentication configured'
    } | ${(() => {
      const customHeaders = getCustomHeaders();
      if (Object.keys(customHeaders).length === 0) {
        return 'No custom headers defined (see config resource for headers)';
      }
      
      // List of common headers that are safe to show values for
      const safeHeaders = new Set([
        'accept',
        'accept-language',
        'content-type',
        'user-agent',
        'cache-control',
        'if-match',
        'if-none-match',
        'if-modified-since',
        'if-unmodified-since'
      ]);
      
      const headerList = Object.entries(customHeaders).map(([name, value]) => {
        const lowerName = name.toLowerCase();
        return safeHeaders.has(lowerName) ? 
          `${name}(${value})` : 
          name;
      }).join(', ');
      
      return `Custom headers defined: ${headerList} (see config resource for headers)`;
    })()} | The tool automatically: - Normalizes endpoints (adds leading slash, removes trailing slashes) - Handles authentication header injection - Applies custom headers from HEADER_* environment variables - Accepts any HTTP status code as valid - Limits response size to ${RESPONSE_SIZE_LIMIT} bytes (see config resource for size limit settings) - Returns detailed response information including: * Full URL called * Status code and text * Response headers * Response body * Request details (method, headers, body) * Response timing * Validation messages | Error Handling: - Network errors are caught and returned with descriptive messages - Invalid status codes are still returned with full response details - Authentication errors include the attempted auth method | See the config resource for all configuration options, including header configuration.
    `,
              inputSchema: {
                type: 'object',
                properties: {
                  method: {
                    type: 'string',
                    enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
                    description: 'HTTP method to use',
                  },
                  endpoint: {
                    type: 'string',
                    description: `Endpoint path (e.g. "/users"). Do not include full URLs - only the path. Example: "/api/users" will resolve to "${normalizeBaseUrl(process.env.REST_BASE_URL!)}/api/users"`,
                  },
                  body: {
                    type: 'object',
                    description: 'Optional request body for POST/PUT requests',
                  },
                  headers: {
                    type: 'object',
                    description: 'Optional request headers for one-time use. IMPORTANT: Do not use for sensitive data like API keys - those should be configured via environment variables. This parameter is intended for dynamic, non-sensitive headers that may be needed for specific requests.',
                    additionalProperties: {
                      type: 'string'
                    }
                  }
                },
                required: ['method', 'endpoint'],
              },
            },
          ],
        }));
  • Input schema for the test_request tool: object with required method (enum) and endpoint (string), optional body (object) and headers (object).
      inputSchema: {
        type: 'object',
        properties: {
          method: {
            type: 'string',
            enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
            description: 'HTTP method to use',
          },
          endpoint: {
            type: 'string',
            description: `Endpoint path (e.g. "/users"). Do not include full URLs - only the path. Example: "/api/users" will resolve to "${normalizeBaseUrl(process.env.REST_BASE_URL!)}/api/users"`,
          },
          body: {
            type: 'object',
            description: 'Optional request body for POST/PUT requests',
          },
          headers: {
            type: 'object',
            description: 'Optional request headers for one-time use. IMPORTANT: Do not use for sensitive data like API keys - those should be configured via environment variables. This parameter is intended for dynamic, non-sensitive headers that may be needed for specific requests.',
            additionalProperties: {
              type: 'string'
            }
          }
        },
        required: ['method', 'endpoint'],
      },
    },
  • Handler for the test_request tool. Validates args (via isValidEndpointArgs), normalizes the endpoint, builds the full URL, configures authentication and custom headers, executes the HTTP request using Axios, and returns a structured response with request details, response data, timing, and validation messages.
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name !== 'test_request') {
        throw new McpError(
          ErrorCode.MethodNotFound,
          `Unknown tool: ${request.params.name}`
        );
      }
    
      if (!isValidEndpointArgs(request.params.arguments)) {
        throw new McpError(
          ErrorCode.InvalidParams,
          'Invalid test endpoint arguments'
        );
      }
    
      // Ensure endpoint starts with / and remove any trailing slashes
      const normalizedEndpoint = `/${request.params.arguments.endpoint.replace(/^\/+|\/+$/g, '')}`;
      
      const fullUrl = `${request.params.arguments.host || process.env.REST_BASE_URL}${normalizedEndpoint}`;
      // Initialize request config
      const config: AxiosRequestConfig = {
          method: request.params.arguments.method as Method,
          url: fullUrl,
          headers: {},
        };
    
      // Add request body for POST/PUT/PATCH
      if (['POST', 'PUT', 'PATCH'].includes(request.params.arguments.method) && request.params.arguments.body) {
        config.data = request.params.arguments.body;
      }
    
      // Apply headers in order of priority (lowest to highest)
      
      // 1. Custom global headers (lowest priority)
      const customHeaders = getCustomHeaders();
      config.headers = {
        ...customHeaders,
        ...config.headers,
        ...(request.params.arguments.headers || {}) // Request-specific headers (middle priority)
      };
    
      // 3. Authentication headers (highest priority)
      if (hasBasicAuth()) {
        const base64Credentials = Buffer.from(`${AUTH_BASIC_USERNAME}:${AUTH_BASIC_PASSWORD}`).toString('base64');
        config.headers = {
          ...config.headers,
          'Authorization': `Basic ${base64Credentials}`
        };
      } else if (hasBearerAuth()) {
        config.headers = {
          ...config.headers,
          'Authorization': `Bearer ${AUTH_BEARER}`
        };
      } else if (hasApiKeyAuth()) {
        config.headers = {
          ...config.headers,
          [AUTH_APIKEY_HEADER_NAME as string]: AUTH_APIKEY_VALUE
        };
      }
    
      try {
        const startTime = Date.now();
        const response = await this.axiosInstance.request(config);
        const endTime = Date.now();
    
        // Determine auth method used
        let authMethod = 'none';
        if (hasBasicAuth()) authMethod = 'basic';
        else if (hasBearerAuth()) authMethod = 'bearer';
        else if (hasApiKeyAuth()) authMethod = 'apikey';
    
        // Prepare response object
        const responseObj: ResponseObject = {
          request: {
            url: fullUrl,
            method: config.method || 'GET',
            headers: {
              ...sanitizeHeaders(config.headers as Record<string, string | undefined>, false),
              ...sanitizeHeaders(request.params.arguments.headers || {}, true)
            },
            body: config.data,
            authMethod
          },
          response: {
            statusCode: response.status,
            statusText: response.statusText,
            timing: `${endTime - startTime}ms`,
            headers: sanitizeHeaders(response.headers as Record<string, any>, false),
            body: response.data,
          },
          validation: {
            isError: response.status >= 400,
            messages: response.status >= 400 ? 
              [`Request failed with status ${response.status}`] : 
              ['Request completed successfully']
          }
        };
    
        // Check response body size independently
        const bodyStr = typeof response.data === 'string' 
          ? response.data 
          : JSON.stringify(response.data);
        const bodySize = Buffer.from(bodyStr).length;
    
        if (bodySize > RESPONSE_SIZE_LIMIT) {
          // Simply truncate to the size limit
          responseObj.response.body = bodyStr.slice(0, RESPONSE_SIZE_LIMIT);
          responseObj.validation.messages.push(
            `Response truncated: ${RESPONSE_SIZE_LIMIT} of ${bodySize} bytes returned due to size limit (${RESPONSE_SIZE_LIMIT} bytes)`
          );
          responseObj.validation.truncated = {
            originalSize: bodySize,
            returnedSize: RESPONSE_SIZE_LIMIT,
            truncationPoint: RESPONSE_SIZE_LIMIT,
            sizeLimit: RESPONSE_SIZE_LIMIT
          };
        }
    
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify(responseObj, null, 2),
            },
          ],
        };
      } catch (error) {
        if (axios.isAxiosError(error)) {
          return {
            content: [
              {
                type: 'text',
              text: JSON.stringify({
                error: {
                  message: error.message,
                  code: error.code,
                  request: {
                    url: `${process.env.REST_BASE_URL}${normalizedEndpoint}`,
                    method: config.method,
                    headers: {
                      ...sanitizeHeaders(config.headers as Record<string, string | undefined>, false),
                      ...sanitizeHeaders(request.params.arguments.headers || {}, true)
                    },
                    body: config.data
                  }
                }
              }, null, 2),
              },
            ],
            isError: true,
          };
        }
        throw error;
      }
    });
  • Validation helper (isValidEndpointArgs) used by the handler to check arguments conform to the EndpointArgs interface (method, endpoint, optional body/headers/host).
    const isValidEndpointArgs = (args: any): args is EndpointArgs => {
      if (typeof args !== 'object' || args === null) return false;
      if (!['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(args.method)) return false;
      if (typeof args.endpoint !== 'string') return false;
      if (args.headers !== undefined && (typeof args.headers !== 'object' || args.headers === null)) return false;
      
      // Check if endpoint contains a full URL
      const urlPattern = /^(https?:\/\/|www\.)/i;
      if (urlPattern.test(args.endpoint)) {
        throw new McpError(
          ErrorCode.InvalidParams,
          `Invalid endpoint format. Do not include full URLs. Instead of "${args.endpoint}", use just the path (e.g. "/api/users"). ` +
          `Your path will be resolved to: ${process.env.REST_BASE_URL}${args.endpoint.replace(/^\/+|\/+$/g, '')}. ` +
          `To test a different base URL, update the REST_BASE_URL environment variable.`
        );
      }
      // Validate .host if present
      if (args.host !== undefined) {
        try {
          const url = new URL(args.host);
          if (!/^https?:$/.test(url.protocol)) {
            throw new Error();
          }
          // Remove trailing slash if present
          if (url.pathname.endsWith('/') && url.pathname !== '/') {
            url.pathname = url.pathname.replace(/\/+$/, '');
            args.host = url.origin + url.pathname;
          } else {
            args.host = url.origin + url.pathname;
          }
        } catch (e) {
          throw new McpError(ErrorCode.InvalidParams, `Invalid host format. The 'host' argument must be a valid URL starting with http:// or https://, e.g. "https://example.com" or "http://localhost:3001/api/v1". Received: "${args.host}"`);
        }
      }
      
      return true;
    };
  • TypeScript interface defining the shape of arguments accepted by the test_request tool.
    interface EndpointArgs {
      method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
      endpoint: string;
      body?: any;
      headers?: Record<string, string>;
      host?: string;
    }
Behavior5/5

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

With no annotations, the description carries the full transparency burden. It fully discloses automatic endpoint normalization, auth injection, custom headers from env vars, response size limit, acceptance of any HTTP status, and detailed response fields. Error handling and authentication details are also covered.

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

Conciseness3/5

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

The description is comprehensive but somewhat verbose, with repeated references to 'see config resource.' It front-loads the purpose effectively but could be condensed without losing meaning.

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

Completeness5/5

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

Given the tool's complexity (4 parameters, no output schema), the description covers all essential aspects: base URL, SSL, auth, headers, automatic behaviors, response details, error handling, and configuration options. It comprehensively supports agent decision-making.

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 coverage is 100%, so baseline is 3. The description adds valuable semantics: for 'headers', it warns against using sensitive data; for 'endpoint', it explains resolution against base URL; for 'body', it clarifies usage with POST/PUT. This goes beyond the schema, earning a 4.

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 'Test a REST API endpoint and get detailed response information,' which is a specific verb and resource. It also provides the base URL and key behavioral details, making the purpose unmistakable.

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

Usage Guidelines5/5

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

The description provides extensive guidance, including when to use the tool, automated behaviors (normalization, auth injection, header handling), and error handling. It also references a config resource for additional settings, helping the agent decide when and how to use the tool.

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

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/dkmaker/mcp-rest-api'

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