@cocaxcode/api-testing-mcp 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,38 +1,67 @@
1
- # @cocaxcode/api-testing-mcp
1
+ <p align="center">
2
+ <h1 align="center">@cocaxcode/api-testing-mcp</h1>
3
+ <p align="center">
4
+ A complete API testing toolkit built for AI coding agents.<br/>
5
+ Test, validate, mock, and load-test your APIs — all from natural language.
6
+ </p>
7
+ </p>
2
8
 
3
- [![npm version](https://img.shields.io/npm/v/@cocaxcode/api-testing-mcp.svg)](https://www.npmjs.com/package/@cocaxcode/api-testing-mcp)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+ <p align="center">
10
+ <a href="https://www.npmjs.com/package/@cocaxcode/api-testing-mcp"><img src="https://img.shields.io/npm/v/@cocaxcode/api-testing-mcp.svg?style=flat-square&color=cb3837" alt="npm version" /></a>
11
+ <a href="https://github.com/cocaxcode/api-testing-mcp/actions"><img src="https://img.shields.io/github/actions/workflow/status/cocaxcode/api-testing-mcp/ci.yml?style=flat-square&label=CI" alt="CI" /></a>
12
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square" alt="License" /></a>
13
+ <img src="https://img.shields.io/badge/tests-70%20passing-brightgreen?style=flat-square" alt="Tests" />
14
+ <img src="https://img.shields.io/badge/tools-20-blueviolet?style=flat-square" alt="Tools" />
15
+ <img src="https://img.shields.io/badge/node-%3E%3D20-339933?style=flat-square&logo=node.js&logoColor=white" alt="Node" />
16
+ </p>
5
17
 
6
- MCP server for API testing. Lightweight, local, zero cloud dependencies.
18
+ <p align="center">
19
+ <a href="#installation">Installation</a> •
20
+ <a href="#just-talk-to-it">Just Talk to It</a> •
21
+ <a href="#features">Features</a> •
22
+ <a href="#tool-reference">Tool Reference</a> •
23
+ <a href="#storage">Storage</a> •
24
+ <a href="#contributing">Contributing</a>
25
+ </p>
7
26
 
8
- Test your APIs directly from Claude Code, Claude Desktop, Cursor, or any MCP client — without leaving your workflow.
27
+ ---
9
28
 
10
- ## Features
29
+ ## What is this?
11
30
 
12
- - **HTTP requests** GET, POST, PUT, PATCH, DELETE with headers, body, query params
13
- - **Authentication** — Bearer token, API Key, Basic Auth built-in
14
- - **Collections** — Save, organize, and reuse requests locally
15
- - **Environments** — Manage variables per environment (dev/staging/prod)
16
- - **Variable interpolation** — Use `{{VARIABLE}}` in URLs, headers, and body
17
- - **Response metrics** — Timing (ms) and response size for every request
18
- - **Zero cloud dependencies** — Everything stored locally as JSON files
31
+ An [MCP server](https://modelcontextprotocol.io) that gives your AI assistant the ability to interact with any API. It works with Claude Code, Claude Desktop, Cursor, and any MCP-compatible client.
19
32
 
20
- ## Installation
33
+ You describe what you need. The AI figures out the rest.
21
34
 
22
- ### Claude Desktop
35
+ No cloud accounts. No subscriptions. Everything runs locally and stores data as plain JSON files you can commit to git.
23
36
 
24
- Add to your `claude_desktop_config.json`:
37
+ ---
25
38
 
26
- ```json
27
- {
28
- "mcpServers": {
29
- "api-testing": {
30
- "command": "npx",
31
- "args": ["-y", "@cocaxcode/api-testing-mcp"]
32
- }
33
- }
34
- }
35
- ```
39
+ ## Just Talk to It
40
+
41
+ This tool is designed to be used through natural language. You don't need to memorize tool names, parameters, or JSON structures — just tell the AI what you want, and it translates your intent into the right API calls.
42
+
43
+ **Here's what a real conversation looks like:**
44
+
45
+ | You say | What happens behind the scenes |
46
+ |---------|-------------------------------|
47
+ | *"Set up an environment for my local API on port 3000"* | Creates environment with `BASE_URL=http://localhost:3000` |
48
+ | *"Import my API spec from /api-docs-json"* | Downloads the OpenAPI spec, stores all endpoints and schemas |
49
+ | *"Show me all user endpoints"* | Filters and lists endpoints tagged `users` |
50
+ | *"Get all users"* | `GET /api/users` → shows the response |
51
+ | *"Create a user with random data"* | Reads the spec, generates valid mock data, sends `POST /api/users` |
52
+ | *"Verify that deleting user 5 returns 204"* | Runs the request + assertion in one step |
53
+ | *"Login as admin and then fetch the dashboard stats"* | Chains 2 requests: login → extract token → use token for next call |
54
+ | *"How fast is the health endpoint under load?"* | Fires 50 concurrent requests, reports p50/p95/p99 latencies |
55
+ | *"Run all my saved smoke tests"* | Executes every request tagged `smoke`, reports pass/fail |
56
+ | *"Export the create-user request as curl"* | Builds a ready-to-paste cURL command with resolved variables |
57
+ | *"Compare the users endpoint between dev and prod"* | Hits both URLs, diffs status codes, body, and timing |
58
+ | *"Switch to the production environment"* | Changes active env — all subsequent requests use prod URLs and tokens |
59
+
60
+ **The AI already knows your API** if you've imported the spec. It knows which fields are required, what types they expect, valid enum values, and what the response looks like. When you say *"create a blog post"*, it doesn't guess — it reads the schema and builds the request correctly.
61
+
62
+ ---
63
+
64
+ ## Installation
36
65
 
37
66
  ### Claude Code
38
67
 
@@ -40,217 +69,265 @@ Add to your `claude_desktop_config.json`:
40
69
  claude mcp add api-testing -- npx -y @cocaxcode/api-testing-mcp
41
70
  ```
42
71
 
43
- ### Custom storage directory
72
+ ### Claude Desktop / Cursor / Any MCP Client
73
+
74
+ Add to your MCP configuration file:
44
75
 
45
76
  ```json
46
77
  {
47
78
  "mcpServers": {
48
79
  "api-testing": {
49
80
  "command": "npx",
50
- "args": ["-y", "@cocaxcode/api-testing-mcp"],
51
- "env": {
52
- "API_TESTING_DIR": "/path/to/your/.api-testing"
53
- }
81
+ "args": ["-y", "@cocaxcode/api-testing-mcp"]
54
82
  }
55
83
  }
56
84
  }
57
85
  ```
58
86
 
59
- ## Tools
87
+ ### First steps
88
+
89
+ Set up an environment so the tool knows where your API lives:
60
90
 
61
- ### `request`
91
+ ```
92
+ "Create an environment called dev with BASE_URL http://localhost:3000"
93
+ ```
62
94
 
63
- Execute an HTTP request with optional authentication and variable interpolation.
95
+ From here, relative paths work automatically. `/api/users` becomes `http://localhost:3000/api/users`.
64
96
 
65
- Relative URLs (starting with `/`) automatically use `BASE_URL` from the active environment — no need to write `{{BASE_URL}}` every time.
97
+ If your API has Swagger/OpenAPI, import the spec:
66
98
 
67
99
  ```
68
- // Relative URL auto-prepends BASE_URL from active environment
69
- request({ method: "GET", url: "/api/users" })
100
+ "Import my API spec from http://localhost:3000/api-docs-json"
101
+ ```
102
+
103
+ Now the AI knows every endpoint, parameter, and schema in your API. You're ready to go.
104
+
105
+ ---
106
+
107
+ ## Features
108
+
109
+ ### HTTP Requests
70
110
 
71
- // Equivalent to:
72
- request({ method: "GET", url: "{{BASE_URL}}/api/users" })
111
+ Execute any HTTP method with headers, body, query params, and built-in auth. Relative URLs resolve from the active environment. Variables like `{{TOKEN}}` are replaced from environment values.
73
112
 
74
- // Full example with all options
113
+ ```
75
114
  request({
76
- method: "GET",
115
+ method: "POST",
77
116
  url: "/api/users",
78
- headers: { "Authorization": "Bearer {{TOKEN}}" },
79
- query: { "page": "1" },
80
- timeout: 5000
117
+ body: { name: "Jane", email: "jane@company.com" },
118
+ auth: { type: "bearer", token: "{{TOKEN}}" }
81
119
  })
82
120
  ```
83
121
 
84
- **Auth examples:**
122
+ **Supports:** GET · POST · PUT · PATCH · DELETE · HEAD · OPTIONS — Headers · Query params · JSON body · Bearer / API Key / Basic auth · Timeout · `{{variable}}` interpolation
85
123
 
86
- ```
87
- // Bearer token
88
- request({ method: "GET", url: "...", auth: { type: "bearer", token: "abc123" } })
124
+ ### Assertions
89
125
 
90
- // API Key
91
- request({ method: "GET", url: "...", auth: { type: "api-key", key: "mykey", header: "X-API-Key" } })
126
+ Run a request and validate the response against a set of rules in one step. Get structured pass/fail results.
92
127
 
93
- // Basic Auth
94
- request({ method: "GET", url: "...", auth: { type: "basic", username: "user", password: "pass" } })
128
+ ```
129
+ assert({
130
+ method: "GET",
131
+ url: "/api/health",
132
+ assertions: [
133
+ { path: "status", operator: "eq", expected: 200 },
134
+ { path: "body.status", operator: "eq", expected: "ok" },
135
+ { path: "timing.total_ms", operator: "lt", expected: 500 }
136
+ ]
137
+ })
95
138
  ```
96
139
 
97
- **Response format:**
140
+ ```
141
+ ✅ PASS — 3/3 assertions passed
142
+ GET /api/health → 200 OK (42ms)
98
143
 
99
- ```json
100
- {
101
- "status": 200,
102
- "statusText": "OK",
103
- "headers": { "content-type": "application/json" },
104
- "body": { "users": [] },
105
- "timing": { "total_ms": 142.35 },
106
- "size_bytes": 1024
107
- }
144
+ ✅ status === 200
145
+ ✅ body.status === "ok"
146
+ timing.total_ms < 500
108
147
  ```
109
148
 
110
- ### `collection_save`
149
+ **Operators:** `eq` · `neq` · `gt` · `gte` · `lt` · `lte` · `contains` · `not_contains` · `exists` · `type`
111
150
 
112
- Save a request to your local collection for reuse.
151
+ ### Request Flows
152
+
153
+ Chain multiple requests together. Extract values from one response and inject them into the next step using `{{variables}}`. Perfect for auth flows, CRUD sequences, and multi-step testing.
113
154
 
114
155
  ```
115
- collection_save({
116
- name: "get-users",
117
- request: { method: "GET", url: "https://api.example.com/users" },
118
- tags: ["users", "read"]
156
+ flow_run({
157
+ steps: [
158
+ {
159
+ name: "login",
160
+ method: "POST",
161
+ url: "/auth/login",
162
+ body: { email: "admin@test.com", password: "123456" },
163
+ extract: { "TOKEN": "body.access_token" }
164
+ },
165
+ {
166
+ name: "get-users",
167
+ method: "GET",
168
+ url: "/api/users",
169
+ headers: { "Authorization": "Bearer {{TOKEN}}" }
170
+ }
171
+ ]
119
172
  })
120
173
  ```
121
174
 
122
- ### `collection_list`
175
+ ### OpenAPI Import
123
176
 
124
- List all saved requests. Optionally filter by tag.
177
+ Import your Swagger/OpenAPI spec from a URL or local file. Once imported, the AI understands every endpoint, parameter, and schema — no guessing, no memorizing.
125
178
 
126
179
  ```
127
- collection_list({ tag: "users" })
180
+ api_import({ name: "my-backend", source: "http://localhost:3000/api-docs-json" })
181
+ api_endpoints({ name: "my-backend", tag: "users" })
182
+ api_endpoint_detail({ name: "my-backend", method: "POST", path: "/users" })
128
183
  ```
129
184
 
130
- ### `collection_get`
185
+ ### Mock Data Generation
131
186
 
132
- Get the full details of a saved request.
187
+ Generate realistic fake data from your OpenAPI spec. Respects types, formats (`email`, `uuid`, `date-time`), enums, and required fields. Use `count` for arrays.
133
188
 
134
189
  ```
135
- collection_get({ name: "get-users" })
190
+ mock({ name: "my-backend", method: "POST", path: "/users", target: "request" })
136
191
  ```
137
192
 
138
- ### `collection_delete`
139
-
140
- Delete a saved request from the collection.
141
-
142
- ```
143
- collection_delete({ name: "get-users" })
193
+ ```json
194
+ {
195
+ "email": "user42@example.com",
196
+ "name": "Test User 73",
197
+ "password": "TestPass123!",
198
+ "role": "admin"
199
+ }
144
200
  ```
145
201
 
146
- ### `env_create`
202
+ ### Load Testing
147
203
 
148
- Create a new environment with optional initial variables.
204
+ Fire N concurrent requests and get performance metrics: min, avg, percentiles (p50/p95/p99), max, and requests per second.
149
205
 
150
206
  ```
151
- env_create({
152
- name: "dev",
153
- variables: { "BASE_URL": "http://localhost:3000", "TOKEN": "dev-token" }
154
- })
207
+ load_test({ method: "GET", url: "/api/health", concurrent: 50 })
155
208
  ```
156
209
 
157
- ### `env_list`
210
+ ```
211
+ 📊 LOAD TEST — GET /api/health
158
212
 
159
- List all environments and which one is active.
213
+ Requests: 50 concurrent
214
+ Successful: 50 | Failed: 0
215
+ Requests/sec: 23.31
160
216
 
161
- ### `env_set`
217
+ ⏱️ Response times:
218
+ Min: 45ms | Avg: 187ms
219
+ p50: 156ms | p95: 412ms
220
+ p99: 523ms | Max: 567ms
221
+ ```
162
222
 
163
- Set a variable in an environment (defaults to active environment).
223
+ ### Response Diffing
224
+
225
+ Execute two requests and compare their responses field by field. Detect regressions or compare environments (dev vs prod).
164
226
 
165
227
  ```
166
- env_set({ key: "TOKEN", value: "new-token-value" })
228
+ diff_responses({
229
+ request_a: { label: "dev", method: "GET", url: "http://dev.api.com/users" },
230
+ request_b: { label: "prod", method: "GET", url: "http://prod.api.com/users" }
231
+ })
167
232
  ```
168
233
 
169
- ### `env_get`
234
+ ### Bulk Testing
170
235
 
171
- Get a specific variable or all variables from an environment.
236
+ Run every saved request in your collection (or filter by tag) and get a pass/fail summary.
172
237
 
173
238
  ```
174
- env_get({ key: "BASE_URL" })
175
- env_get({}) // returns all variables
239
+ bulk_test({ tag: "smoke" })
176
240
  ```
177
241
 
178
- ### `env_switch`
179
-
180
- Switch the active environment. Active environment variables are used for `{{interpolation}}`.
181
-
182
242
  ```
183
- env_switch({ name: "prod" })
243
+ BULK TEST — 8/8 passed | 1.2s total
244
+
245
+ ✅ health — GET /health → 200 (45ms)
246
+ ✅ list-users — GET /users → 200 (123ms)
247
+ ✅ create-post — POST /blog → 201 (89ms)
248
+ ✅ login — POST /auth/login → 200 (156ms)
184
249
  ```
185
250
 
186
- ### `api_import`
251
+ ### cURL Export
187
252
 
188
- Import an OpenAPI/Swagger spec from a URL or local file. Endpoints and schemas are stored locally for browsing.
253
+ Convert any saved request into a cURL command with resolved variables.
189
254
 
255
+ ```bash
256
+ curl -X POST \
257
+ 'https://api.example.com/users' \
258
+ -H 'Authorization: Bearer eyJhbGci...' \
259
+ -H 'Content-Type: application/json' \
260
+ -d '{"name":"Jane","email":"jane@company.com"}'
190
261
  ```
191
- api_import({
192
- name: "my-backend",
193
- source: "http://localhost:3001/api-docs-json"
194
- })
195
262
 
196
- // Or from a local file:
197
- api_import({ name: "my-backend", source: "./openapi.json" })
198
- ```
263
+ ### Collections & Environments
199
264
 
200
- ### `api_endpoints`
265
+ Save requests for reuse (with tags), manage variables across environments (dev/staging/prod), and switch contexts instantly.
201
266
 
202
- List endpoints from an imported API. Filter by tag, method, or path.
267
+ ---
203
268
 
204
- ```
205
- api_endpoints({ name: "my-backend" })
206
- api_endpoints({ name: "my-backend", tag: "users" })
207
- api_endpoints({ name: "my-backend", method: "POST" })
208
- api_endpoints({ name: "my-backend", path: "/blog" })
209
- ```
269
+ ## Tool Reference
210
270
 
211
- ### `api_endpoint_detail`
271
+ 20 tools organized in 8 categories:
212
272
 
213
- Get full details of an endpoint: parameters, request body schema, and responses. Useful to know what data to send.
273
+ | Category | Tools | Count |
274
+ |----------|-------|-------|
275
+ | **Requests** | `request` | 1 |
276
+ | **Testing** | `assert` | 1 |
277
+ | **Flows** | `flow_run` | 1 |
278
+ | **Collections** | `collection_save` · `collection_list` · `collection_get` · `collection_delete` | 4 |
279
+ | **Environments** | `env_create` · `env_list` · `env_set` · `env_get` · `env_switch` | 5 |
280
+ | **API Specs** | `api_import` · `api_endpoints` · `api_endpoint_detail` | 3 |
281
+ | **Mock** | `mock` | 1 |
282
+ | **Utilities** | `load_test` · `export_curl` · `diff_responses` · `bulk_test` | 4 |
214
283
 
215
- ```
216
- api_endpoint_detail({ name: "my-backend", method: "POST", path: "/blog" })
217
- ```
284
+ ---
218
285
 
219
286
  ## Storage
220
287
 
221
- All data is stored locally as JSON files in `.api-testing/` (in your current working directory by default):
288
+ All data lives in `.api-testing/` as plain JSON no database, no cloud sync:
222
289
 
223
290
  ```
224
291
  .api-testing/
225
- ├── active-env # Name of the active environment
226
- ├── collections/
227
- ├── get-users.json
228
- └── create-post.json
229
- ├── environments/
230
- │ ├── dev.json
231
- │ └── prod.json
232
- └── specs/
233
- └── my-backend.json # Imported OpenAPI specs
292
+ ├── active-env # Active environment name
293
+ ├── collections/ # Saved requests
294
+ ├── environments/ # Environment variables (dev, prod, ...)
295
+ └── specs/ # Imported OpenAPI specs
234
296
  ```
235
297
 
236
- You can version these files in git if you want to share collections and environments with your team.
298
+ Override the default directory:
237
299
 
238
- ## Development
300
+ ```json
301
+ {
302
+ "env": { "API_TESTING_DIR": "/path/to/shared/.api-testing" }
303
+ }
304
+ ```
305
+
306
+ Commit these files to git to share across your team.
307
+
308
+ ---
309
+
310
+ ## Contributing
239
311
 
240
312
  ```bash
241
313
  git clone https://github.com/cocaxcode/api-testing-mcp.git
242
314
  cd api-testing-mcp
243
315
  npm install
244
- npm test
245
- npm run build
316
+ npm test # 70 tests across 10 suites
317
+ npm run build # ESM bundle via tsup
318
+ npm run typecheck # Strict TypeScript
246
319
  ```
247
320
 
248
- ### Test with MCP Inspector
321
+ **Test with MCP Inspector:**
249
322
 
250
323
  ```bash
251
324
  npx @modelcontextprotocol/inspector node dist/index.js
252
325
  ```
253
326
 
327
+ **Stack:** TypeScript · MCP SDK 1.27 · Zod · Vitest · tsup
328
+
329
+ ---
330
+
254
331
  ## License
255
332
 
256
- MIT
333
+ [MIT](./LICENSE) — built by [cocaxcode](https://github.com/cocaxcode)