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
| Name | Required | Description | Default |
|---|---|---|---|
| method | Yes | HTTP method to use | |
| endpoint | Yes | Endpoint path (e.g. "/users"). Do not include full URLs - only the path. Example: "/api/users" will resolve to "https://api.example.com/api/users" | |
| body | No | Optional request body for POST/PUT requests | |
| headers | No | 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. |
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'], }, }, ], })); - src/index.ts:327-353 (schema)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'], }, }, - src/index.ts:357-511 (handler)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; } }); - src/index.ts:121-157 (helper)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; }; - src/index.ts:34-40 (helper)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; }