Skip to content
Open
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
17 changes: 17 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,20 @@ build/
src/version.ts
.DS_Store
*.log
pnpm-lock.yaml

# Test generated files
test/uploads/
test/fixtures/

# Secrets and tokens
.npmrc
.env
.env.local
*.token

# AI
.serena
CLAUDE.md
.mcp.json
.claude
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
## [0.5.0](https://github.com/qwang07/mcp-rest-api/compare/v0.4.0...v0.5.0) (2025-10-23)

### Features

* **file-upload**: add comprehensive file upload support with multipart/form-data ([2ae2535](https://github.com/qwang07/mcp-rest-api/commit/2ae2535))
- Upload single or multiple files via local file paths
- Support file + form fields + JSON body mixing
- Configurable file size limits via FILE_UPLOAD_SIZE_LIMIT (default: 10MB)
- Path traversal attack protection
- Modular file-utils.ts with validation and FormData creation
- 23 unit tests with TDD approach
- Complete documentation and examples

### Documentation

* Updated README.md with file upload features and examples
* Added FILE_UPLOAD_SIZE_LIMIT configuration to config.md
* Added comprehensive file upload examples to examples.md

### Tests

* Added 23 unit tests for file upload functionality
* Test server for integration testing
* End-to-end test suite

## [0.4.0](https://github.com/dkmaker/mcp-rest-api/compare/v0.3.0...v0.4.0) (2025-01-08)


Expand Down
76 changes: 56 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,30 @@
[![MseeP.ai Security Assessment Badge](https://mseep.net/pr/dkmaker-mcp-rest-api-badge.png)](https://mseep.ai/app/dkmaker-mcp-rest-api)

# MCP REST API Tester
# MCP REST API Tester (Enhanced)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![NPM Package](https://img.shields.io/npm/v/dkmaker-mcp-rest-api.svg)](https://www.npmjs.com/package/dkmaker-mcp-rest-api)
[![smithery badge](https://smithery.ai/badge/dkmaker-mcp-rest-api)](https://smithery.ai/server/dkmaker-mcp-rest-api)
[![NPM Package](https://img.shields.io/npm/v/@qwang007/mcp-rest-api.svg)](https://www.npmjs.com/package/@qwang007/mcp-rest-api)

A TypeScript-based MCP server that enables testing of REST APIs through Cline. This tool allows you to test and interact with any REST API endpoints directly from your development environment.
> **📦 Enhanced Fork**: This is an enhanced version of [dkmaker-mcp-rest-api](https://github.com/dkmaker/mcp-rest-api) with added **file upload support**. Original project by [@zenturacp](https://github.com/zenturacp).
>
> **New in v0.5.0**: Full multipart/form-data file upload support! 🎉

<a href="https://glama.ai/mcp/servers/izr2sp4rqo">
<img width="380" height="200" src="https://glama.ai/mcp/servers/izr2sp4rqo/badge?refresh=1234" />
</a>
A TypeScript-based MCP server that enables testing of REST APIs through Cline and Claude Desktop. This tool allows you to test and interact with any REST API endpoints directly from your development environment, including **uploading files**.

## Installation

### Installing via Smithery

To install REST API Tester for Claude Desktop automatically via [Smithery](https://smithery.ai/server/dkmaker-mcp-rest-api):
### Install via npm

```bash
npx -y @smithery/cli install dkmaker-mcp-rest-api --client claude
npm install -g @qwang007/mcp-rest-api
```

### Installing Manually
1. Install the package globally:
Or use with npx (no installation required):

```bash
npm install -g dkmaker-mcp-rest-api
npx @qwang007/mcp-rest-api
```

2. Configure Cline Custom Instructions:
### Manual Setup

1. Configure Cline Custom Instructions:

To ensure Cline understands how to effectively use this tool, add the following to your Cline custom instructions (Settings > Custom Instructions):

Expand Down Expand Up @@ -73,7 +70,7 @@ Access these resources to understand usage, response formats, and configuration
- Restart server after configuration changes
```

3. Add the server to your MCP configuration:
2. Add the server to your MCP configuration:

While these instructions are for Cline, the server should work with any MCP implementation. Configure based on your operating system:

Expand All @@ -87,7 +84,7 @@ Add to `C:\Users\<YourUsername>\AppData\Roaming\Code\User\globalStorage\saoudriz
"rest-api": {
"command": "node",
"args": [
"C:/Users/<YourUsername>/AppData/Roaming/npm/node_modules/dkmaker-mcp-rest-api/build/index.js"
"C:/Users/<YourUsername>/AppData/Roaming/npm/node_modules/@qwang007/mcp-rest-api/build/index.js"
],
"env": {
"REST_BASE_URL": "https://api.example.com",
Expand All @@ -103,6 +100,8 @@ Add to `C:\Users\<YourUsername>\AppData\Roaming\Code\User\globalStorage\saoudriz
"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
// File Upload Size Limit (optional, defaults to 10485760 bytes = 10MB)
"FILE_UPLOAD_SIZE_LIMIT": "52428800", // Maximum file upload size in bytes (50MB)
// Custom Headers (optional)
"HEADER_X-API-Version": "2.0",
"HEADER_Custom-Client": "my-client",
Expand All @@ -122,7 +121,7 @@ Add to `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude
"command": "npx",
"args": [
"-y",
"dkmaker-mcp-rest-api"
"@qwang007/mcp-rest-api"
],
"env": {
"REST_BASE_URL": "https://api.example.com",
Expand All @@ -136,6 +135,8 @@ Add to `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude
"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
// File Upload Size Limit (optional, defaults to 10485760 bytes = 10MB)
"FILE_UPLOAD_SIZE_LIMIT": "52428800", // Maximum file upload size in bytes (50MB)
// Custom Headers (optional)
"HEADER_X-API-Version": "2.0",
"HEADER_Custom-Client": "my-client",
Expand All @@ -155,6 +156,13 @@ Note: Replace the environment variables with your actual values. Only configure

- Test REST API endpoints with different HTTP methods
- Support for GET, POST, PUT, DELETE, and PATCH requests
- **File Upload Support**:
- Upload single or multiple files in one request
- Multipart/form-data encoding
- Customizable field names, filenames, and content types
- Combined file + form field uploads
- Configurable file size limits (default: 10MB per file)
- Path traversal protection for security
- Detailed response information including status, headers, and body
- Custom Headers:
- Global headers via HEADER_* environment variables
Expand Down Expand Up @@ -207,6 +215,34 @@ use_mcp_tool('rest-api', 'test_request', {
"X-Custom-Header": "custom-value"
}
});

// Upload a file
use_mcp_tool('rest-api', 'test_request', {
"method": "POST",
"endpoint": "/upload",
"files": [
{
"fieldName": "avatar",
"filePath": "./profile-pic.jpg"
}
]
});

// Upload multiple files with form fields
use_mcp_tool('rest-api', 'test_request', {
"method": "POST",
"endpoint": "/posts",
"files": [
{
"fieldName": "thumbnail",
"filePath": "./image.png"
}
],
"formFields": {
"title": "My Post",
"description": "Post description"
}
});
```

## Development
Expand Down
37 changes: 25 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "dkmaker-mcp-rest-api",
"version": "0.4.0",
"description": "A generic REST API tester for testing HTTP endpoints",
"name": "@qwang007/mcp-rest-api",
"version": "0.5.0",
"description": "A generic REST API tester for testing HTTP endpoints with file upload support. Enhanced fork of dkmaker-mcp-rest-api.",
"license": "MIT",
"type": "module",
"bin": {
"dkmaker-mcp-rest-api": "./build/index.js"
"mcp-rest-api": "./build/index.js"
},
"files": [
"build",
Expand All @@ -15,18 +15,26 @@
"scripts": {
"prebuild": "node scripts/build.js",
"build": "tsc",
"prepare": "npm run build",
"prepare": "pnpm run build",
"watch": "tsc --watch",
"inspector": "npx @modelcontextprotocol/inspector build/index.js"
"inspector": "npx @modelcontextprotocol/inspector build/index.js",
"test": "node --test test/**/*.test.js",
"test:watch": "node --test --watch test/**/*.test.js",
"test:coverage": "node --test --experimental-test-coverage test/**/*.test.js",
"test:server": "node test/test-server.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.4",
"axios": "^1.7.9"
"axios": "^1.7.9",
"form-data": "^4.0.1"
},
"devDependencies": {
"@commitlint/cli": "^19.6.1",
"@commitlint/config-conventional": "^19.6.0",
"@types/form-data": "^2.5.0",
"@types/node": "^20.11.24",
"express": "^5.1.0",
"multer": "^2.0.2",
"typescript": "^5.3.3"
},
"keywords": [
Expand All @@ -37,17 +45,22 @@
"testing",
"cline",
"development",
"typescript"
"typescript",
"file-upload",
"multipart"
],
"author": "qwang007",
"contributors": [
"zenturacp (original author)"
],
"author": "zenturacp",
"repository": {
"type": "git",
"url": "git+https://github.com/dkmaker/mcp-rest-api.git"
"url": "git+https://github.com/qwang07/mcp-rest-api.git"
},
"bugs": {
"url": "https://github.com/dkmaker/mcp-rest-api/issues"
"url": "https://github.com/qwang07/mcp-rest-api/issues"
},
"homepage": "https://github.com/dkmaker/mcp-rest-api#readme",
"homepage": "https://github.com/qwang07/mcp-rest-api#readme",
"engines": {
"node": ">=18.0.0"
}
Expand Down
82 changes: 82 additions & 0 deletions src/file-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as fs from 'fs/promises';
import * as fsSync from 'fs';
import * as path from 'path';
import FormData from 'form-data';

export interface FileUpload {
fieldName: string;
filePath: string;
fileName?: string;
contentType?: string;
}

// (filePath: string) => void
export const validateFilePath = (filePath: string): void => {
if (filePath.includes('..')) {
throw new Error('Path traversal attack detected');
}
};

// (filePath: string) => Promise<void>
export const checkFileExists = async (filePath: string): Promise<void> => {
try {
await fs.access(filePath, fs.constants.R_OK);
} catch (error) {
throw new Error(`File does not exist or is not readable: ${filePath}`);
}
};

// (filePath: string, sizeLimit: number) => Promise<void>
export const checkFileSize = async (filePath: string, sizeLimit: number): Promise<void> => {
const stats = await fs.stat(filePath);
if (stats.size > sizeLimit) {
throw new Error(`File exceeds size limit: ${stats.size} bytes > ${sizeLimit} bytes`);
}
};

// (files: FileUpload[], sizeLimit: number) => Promise<void>
export const validateFiles = async (files: FileUpload[], sizeLimit: number): Promise<void> => {
for (const file of files) {
validateFilePath(file.filePath);
await checkFileExists(file.filePath);
await checkFileSize(file.filePath, sizeLimit);
}
};

// (files: FileUpload[], formFields?: Record<string, string>, body?: any) => Promise<FormData>
export const createFormData = async (
files: FileUpload[],
formFields?: Record<string, string>,
body?: any
): Promise<FormData> => {
const formData = new FormData();

// Add files
for (const file of files) {
const fileStream = fsSync.createReadStream(file.filePath);
const fileName = file.fileName || path.basename(file.filePath);
const options: any = { filename: fileName };

if (file.contentType) {
options.contentType = file.contentType;
}

formData.append(file.fieldName, fileStream, options);
}

// Add form fields
if (formFields) {
for (const [key, value] of Object.entries(formFields)) {
formData.append(key, value);
}
}

// Add body fields (convert to JSON string)
if (body) {
for (const [key, value] of Object.entries(body)) {
formData.append(key, typeof value === 'string' ? value : JSON.stringify(value));
}
}

return formData;
};
Loading