Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,11 @@ Add to `C:\Users\<YourUsername>\AppData\Roaming\Code\User\globalStorage\saoudriz
// SSL Verification (enabled by default)
"REST_ENABLE_SSL_VERIFY": "false", // Set to false to disable SSL verification for self-signed certificates
// Response Size Limit (optional, defaults to 10000 bytes)
"REST_RESPONSE_SIZE_LIMIT": "10000" // Maximum response size in bytes
"REST_RESPONSE_SIZE_LIMIT": "10000", // Maximum response size in bytes
// Custom Headers (optional)
"HEADER_X-API-Version": "2.0",
"HEADER_Custom-Client": "my-client",
"HEADER_Accept": "application/json"
}
}
}
Expand Down Expand Up @@ -129,7 +133,11 @@ Add to `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude
"AUTH_APIKEY_HEADER_NAME": "X-API-Key",
"AUTH_APIKEY_VALUE": "your-api-key",
// SSL Verification (enabled by default)
"REST_ENABLE_SSL_VERIFY": "false" // Set to false to disable SSL verification for self-signed certificates
"REST_ENABLE_SSL_VERIFY": "false", // Set to false to disable SSL verification for self-signed certificates
// Custom Headers (optional)
"HEADER_X-API-Version": "2.0",
"HEADER_Custom-Client": "my-client",
"HEADER_Accept": "application/json"
}
}
}
Expand All @@ -146,7 +154,11 @@ Note: Replace the environment variables with your actual values. Only configure
- Test REST API endpoints with different HTTP methods
- Support for GET, POST, PUT, and DELETE requests
- Detailed response information including status, headers, and body
- Custom header support
- Custom Headers:
- Global headers via HEADER_* environment variables
- Case-insensitive prefix (HEADER_, header_, HeAdEr_)
- Case preservation for header names
- Priority-based application (per-request > auth > custom)
- Request body handling for POST/PUT methods
- Response Size Management:
- Automatic response size limiting (default: 10KB/10000 bytes)
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

164 changes: 123 additions & 41 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,54 @@ interface ValidationResult {
};
}

// Function to sanitize headers by removing sensitive values and non-approved headers
const sanitizeHeaders = (
headers: Record<string, any>,
isFromOptionalParams: boolean = false
): Record<string, any> => {
const sanitized: Record<string, any> = {};

for (const [key, value] of Object.entries(headers)) {
const lowerKey = key.toLowerCase();

// Always include headers from optional parameters
if (isFromOptionalParams) {
sanitized[key] = value;
continue;
}

// Handle authentication headers
if (
lowerKey === 'authorization' ||
(AUTH_APIKEY_HEADER_NAME && lowerKey === AUTH_APIKEY_HEADER_NAME.toLowerCase())
) {
sanitized[key] = '[REDACTED]';
continue;
}

// For headers from config/env
const customHeaders = getCustomHeaders();
if (key in customHeaders) {
// Show value only for headers that are in the approved list
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 lowerKey = key.toLowerCase();
sanitized[key] = safeHeaders.has(lowerKey) ? value : '[REDACTED]';
}
}

return sanitized;
};

interface ResponseObject {
request: {
url: string;
Expand All @@ -67,11 +115,13 @@ interface ResponseObject {
validation: ValidationResult;
}

const normalizeBaseUrl = (url: string): string => url.replace(/\/+$/, '');

const isValidEndpointArgs = (args: any): args is EndpointArgs => {
if (typeof args !== 'object' || args === null) return false;
if (!['GET', 'POST', 'PUT', 'DELETE'].includes(args.method)) return false;
if (typeof args.endpoint !== 'string') return false;
if (args.headers !== undefined && typeof args.headers !== 'object') 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;
Expand All @@ -91,6 +141,22 @@ const hasBasicAuth = () => AUTH_BASIC_USERNAME && AUTH_BASIC_PASSWORD;
const hasBearerAuth = () => !!AUTH_BEARER;
const hasApiKeyAuth = () => AUTH_APIKEY_HEADER_NAME && AUTH_APIKEY_VALUE;

// Collect custom headers from environment variables
const getCustomHeaders = (): Record<string, string> => {
const headers: Record<string, string> = {};
const headerPrefix = /^header_/i; // Case-insensitive match for 'header_'

for (const [key, value] of Object.entries(process.env)) {
if (headerPrefix.test(key) && value !== undefined) {
// Extract header name after the prefix, preserving case
const headerName = key.replace(headerPrefix, '');
headers[headerName] = value;
}
}

return headers;
};

class RestTester {
private server!: Server;
private axiosInstance!: AxiosInstance;
Expand All @@ -115,7 +181,7 @@ class RestTester {

const https = await import('https');
this.axiosInstance = axios.create({
baseURL: process.env.REST_BASE_URL,
baseURL: normalizeBaseUrl(process.env.REST_BASE_URL!),
validateStatus: () => true, // Allow any status code
httpsAgent: REST_ENABLE_SSL_VERIFY ? undefined : new https.Agent({ // Disable SSL verification only when explicitly set to false
rejectUnauthorized: false
Expand Down Expand Up @@ -202,42 +268,42 @@ class RestTester {
tools: [
{
name: 'test_request',
description: `Test a REST API endpoint and get detailed response information.

Base URL: ${process.env.REST_BASE_URL}

SSL Verification: ${REST_ENABLE_SSL_VERIFY ? 'Enabled' : 'Disabled'} (see config resource for SSL settings)

Authentication: ${
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'
}

The tool automatically:
- Normalizes endpoints (adds leading slash, removes trailing slashes)
- Handles authentication header injection
- 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.
} | ${(() => {
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',
Expand All @@ -249,19 +315,19 @@ See the config resource for all configuration options.
},
endpoint: {
type: 'string',
description: `Endpoint path (e.g. "/users"). Do not include full URLs - only the path. Example: "/api/users" will resolve to "${process.env.REST_BASE_URL}/api/users"`,
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',
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',
},
},
type: 'string'
}
}
},
required: ['method', 'endpoint'],
},
Expand Down Expand Up @@ -291,15 +357,25 @@ See the config resource for all configuration options.
const config: AxiosRequestConfig = {
method: request.params.arguments.method as Method,
url: normalizedEndpoint,
headers: request.params.arguments.headers || {},
headers: {},
};

// Add request body for POST/PUT
if (['POST', 'PUT'].includes(request.params.arguments.method) && request.params.arguments.body) {
config.data = request.params.arguments.body;
}

// Handle authentication based on environment variables
// 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 = {
Expand Down Expand Up @@ -335,15 +411,18 @@ See the config resource for all configuration options.
request: {
url: fullUrl,
method: config.method || 'GET',
headers: config.headers as Record<string, string | undefined>,
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: response.headers as Record<string, any>,
headers: sanitizeHeaders(response.headers as Record<string, any>, false),
body: response.data,
},
validation: {
Expand Down Expand Up @@ -395,7 +474,10 @@ See the config resource for all configuration options.
request: {
url: `${process.env.REST_BASE_URL}${normalizedEndpoint}`,
method: config.method,
headers: config.headers,
headers: {
...sanitizeHeaders(config.headers as Record<string, string | undefined>, false),
...sanitizeHeaders(request.params.arguments.headers || {}, true)
},
body: config.data
}
}
Expand Down
22 changes: 22 additions & 0 deletions src/resources/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ This document describes all available configuration options for the REST API tes
- Values: Set to `false` to disable SSL verification for self-signed certificates
- Usage: Disable when testing APIs with self-signed certificates in development environments

## Custom Headers Configuration

### Custom Headers (Optional)
- Description: Add custom headers to all requests using environment variables
- Pattern: `HEADER_<HeaderName>=<Value>` (prefix is case-insensitive)
- Examples:
```bash
HEADER_X-API-Version=2.0
header_Custom-Client=my-client
HeAdEr_Accept=application/json
```
- Usage: Headers are added to all requests. The header name after `HEADER_` preserves its exact case
- Priority: Per-request headers > Authentication headers > Custom global headers

## Authentication Configuration

The tool supports three authentication methods. Configure one based on your API's requirements.
Expand Down Expand Up @@ -73,6 +87,14 @@ REST_APIKEY_HEADER_NAME=X-API-Key
REST_APIKEY_VALUE=your-api-key
```

### API with Custom Headers
```bash
REST_BASE_URL=https://api.example.com
HEADER_X-API-Version=2.0
HEADER_Custom-Client=my-client
HEADER_Accept=application/json
```

## Changing Configuration

Configuration can be updated by:
Expand Down