# api-mcp

[![npm version](https://img.shields.io/npm/v/@hassanjamal/api-mcp.svg)](https://www.npmjs.com/package/@hassanjamal/api-mcp)
[![npm downloads](https://img.shields.io/npm/dw/@hassanjamal/api-mcp.svg)](https://www.npmjs.com/package/@hassanjamal/api-mcp)
[![node](https://img.shields.io/node/v/@hassanjamal/api-mcp.svg)](https://nodejs.org)
[![license](https://img.shields.io/npm/l/@hassanjamal/api-mcp.svg)](./LICENSE)

> 📦 **Published on npm:** [`@hassanjamal/api-mcp`](https://www.npmjs.com/package/@hassanjamal/api-mcp)
> — install with `npm i -g @hassanjamal/api-mcp` (no cloning or building needed).

An **MCP (Model Context Protocol) server** you plug into any codebase — React Native, web,
Flutter, or a backend — that:

1. **Discovers** every API endpoint in the code (client calls *and* server routes)
2. **Reads or generates** an OpenAPI / Swagger spec (if none exists, it builds one)
3. **Generates** test cases + edge cases for each endpoint, based on its HTTP method and params
4. **Auto-logs in per role** (from just credentials — finds the login endpoint, extracts the token)
5. **Harvests real IDs** from responses + JWTs and injects the *right* resource id into each path param
6. **Executes** them against your running server — like Postman — scoped to each role's real permissions
7. **Reports** everything as **PDF + XLSX**, a **Postman `.txt`**, and an **importable Postman collection** (`postman_collection.json`) so you can spot-check any endpoint's live response yourself

The **QA one-call** (`qa_audit`) does all of it from a project path + role logins. All artifacts are
written to a `.api-mcp/` folder inside the project you point it at.

---

## What it detects

| Platform | Frameworks / libraries detected |
| -------- | ------------------------------- |
| **Web** | `fetch`, `axios` (`.get/.post/…`, `axios({url,method})`, `.request()`, custom instances), Angular `HttpClient`, **RTK Query**, **SWR**, **jQuery** (`$.get/$.post/$.ajax`) |
| **React Native** | `fetch`, `axios`, `apisauce` (JS libraries, same detectors as web) |
| **Flutter / Dart** | `http` (`Uri.parse`/`Uri.https`), `Dio`, **Retrofit-dart** (`@GET`), **Chopper** (`@Get(path:)`) |
| **Mobile native** | **Retrofit** (Android/Kotlin `@GET`), **Alamofire** (iOS/Swift `AF.request`) |
| **Backend routes** | Express / Fastify / Koa `router`, NestJS decorators, FastAPI, Flask, Spring (`@GetMapping`) |

Detection is text/pattern based, so it works on any language without running the code, and
handles both `:param` / `{param}` / `<param>` styles and JS `${...}` / Dart `$var` interpolation.
Every framework above is covered by the regression suite (`npm test`) so accuracy can't silently
regress.

---

## Install

Install once, globally — this gives a fast, reliable `api-mcp` command:

```bash
npm i -g @hassanjamal/api-mcp
```

> Requires **Node.js 18+**. Global install is recommended over `npx` because `npx` re-checks
> the registry on every launch (~6s) and can trip a host's connection health-check; the global
> binary starts in ~1s.

---

## Connect it to a host

An MCP server does nothing on its own — a **host** (the app you chat with an AI in) connects to
it and exposes its tools. Pick your host:

### Claude Code

Register it once for **all** your projects (user scope):

```bash
claude mcp add -s user api-mcp -- api-mcp
```

Verify: `claude mcp list` → `api-mcp - ✔ Connected`. (Mind the spacing: `-- api-mcp`.)

### Cursor

Settings → **MCP** → *Add new server*, or edit `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "api-mcp": { "command": "api-mcp" }
  }
}
```

### Claude Desktop

Edit `claude_desktop_config.json` (Windows: `%APPDATA%\Claude\claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "api-mcp": { "command": "api-mcp" }
  }
}
```

Restart the host after editing config. The tools (and the `/qa-audit`, `/scan` slash commands)
then appear to the AI.

**Updating:** `npm i -g @hassanjamal/api-mcp@latest`

---

## Slash commands (Claude Code)

Two guided entry points show up as slash commands:

| Command | Does |
| ------- | ---- |
| `/qa-audit` | Prompts for base URL + role logins, then runs the full read-only per-role audit |
| `/scan` | Scans the current project and summarizes the endpoints found |

You can also just ask in natural language (see below) — the slash commands are a convenience.

---

## Tools exposed

| Tool | What it does |
| ---- | ------------ |
| `scan_endpoints` | Scan a project and list every endpoint found. Writes `discovery-report.md` + `endpoints.json`. |
| `detect_openapi` | Report any existing Swagger/OpenAPI spec in the project. |
| `generate_openapi` | Build an OpenAPI 3.0 spec from the code (JSON or YAML). Reuses an existing spec unless told otherwise. |
| `generate_tests` | Produce happy-path / edge / negative / security test cases. Writes `tests.json`. |
| `run_tests` | Execute the tests against your live server and save responses + a report. |
| `full_audit` | One call that runs the entire pipeline end-to-end. |
| `export_report` | Regenerate the **HTML** report + **colorful XLSX** from already-saved JSON. |
| `detect_base_url` | Auto-detect the API base URL from `.env` / axios / Dart config (web + mobile). |
| `login` | Auto-detect the login endpoint, post credentials, and extract the token. |
| `qa_audit` | **QA one-call:** role credentials → auto-login → role-scoped, precise-ID testing → **PDF + XLSX** reports. |

Once connected, just ask the AI in natural language, e.g.:

> "Run a full API audit on this project against http://localhost:3000 using my API_TOKEN."

The AI will call `full_audit` with the right arguments.

---

## QA workflow (the easy path)

A QA engineer provides only **credentials per role** — the tool figures out the rest
(base URL, login endpoint, token extraction, per-role runs):

> "Run a qa_audit on this project with admin `a@x.com / pw`, teacher `t@x.com / pw`,
> student `s@x.com / pw`."

`qa_audit` then:
1. **Detects the API base URL** from the code (`.env`, axios/Dart `baseURL`) — or you pass `baseUrl`
2. **Logs in as each role**, extracting the token from the response **or the JWT** (`token`, `accessToken`, `data.token`, `jwt`…)
3. **Harvests real resource IDs** from every response + each JWT and injects the *correct* id into each
   path param (`{courseId}` ← a real course id; a bare `{id}` ← that endpoint's own resource), preferring
   ids the current role can access — turning wrong-id `404`s into real `200`s
4. **Scopes each role** to endpoints its route-guard permits (reads `authorizeAdmin()`, `@Roles('teacher')`…),
   so you get more `200`s and far less `403`/`404` noise
5. **Skips session-breaking endpoints** (logout/refresh/delete-account/password/register) so a test can't
   revoke its own token, and **paces around rate limits** (marks throttled requests *not tested*, not failed)
6. **Writes PDF + XLSX only** — overall `qa-audit.pdf`, per-role `qa-report-{role}.pdf`/`.xlsx`, and
   `endpoints.pdf`/`.xlsx`

**Read-only by default.** Useful arguments:

| Argument | Default | Effect |
| -------- | ------- | ------ |
| `roles` | — | Per role, either `{role, email, password}` (auto-login), `{role, token}` (a pre-obtained bearer/JWT — Clerk/Auth0/SSO), or `{role, token, headers}` (see below) |
| `baseUrl` | auto-detect | API base URL |
| `includeWrites` | `false` | also run POST/PUT/PATCH/DELETE (throwaway/staging only) |
| `realisticBodies` | `false` | fill write bodies with seeded, reproducible Faker values (names/emails/dates) instead of fixed placeholders; needs the optional `@faker-js/faker` dep, only affects `includeWrites` runs |
| `roleScoped` | `true` | test each role only on endpoints its guard permits (`false` = full RBAC coverage) |
| `includeDestructive` | `false` | also test logout/refresh/delete-account/password/register |
| `maxRateLimitWaitMs` | `8000` | max pacing wait per request; raise (e.g. `900000`) for exhaustive coverage |

#### Token & header auth (Clerk / SSO / staging dev-bypass)

For apps whose auth isn't a simple email/password login, give each role a **pre-obtained
token** instead of credentials — the tool skips login and sends it as a bearer:

```json
{ "role": "admin", "token": "eyJhbGci…" }
```

Some staging setups use a **dev-bypass**: one shared bearer token for everyone, plus a
header that selects the acting user. Add per-role `headers` — they're merged into every
request made as that role, before the bearer:

```json
[
  { "role": "user",  "token": "<shared>" },
  { "role": "coach", "token": "<shared>", "headers": { "x-dev-user-id": "<coach-id>" } },
  { "role": "admin", "token": "<shared>", "headers": { "x-dev-user-id": "<admin-id>" } }
]
```

This drives the full RBAC matrix from a single bypass token. (To grab a fresh Clerk token
with no code: log into the web app → F12 → Console → `await window.Clerk.session.getToken()`.)

### Web vs mobile — what to provide

The tool always tests the **HTTP API**, so the inputs are the same for both. The only
difference is which source files get scanned for endpoints.

| | Web app | Mobile app (React Native / Flutter / native) |
| --- | ------- | -------------------------------------------- |
| Provide | API base URL* + role credentials | API base URL* + role credentials — **same** |
| Base URL source | frontend `.env` (`VITE_API_URL`…) | the app's config/constants (the backend it calls) |
| What's scanned | JS/TS (`fetch`, `axios`) | Dart (`http`, `Dio`), + native client libs |
| Execution | identical | identical |

### Understanding the results (why not all 200s?)

A **healthy** API returns a mix of status codes under automated testing — that's it correctly
guarding itself, not a sign it's broken. Each request passes gates in order; the status tells you
which gate stopped it:

| Status | Meaning | A problem? |
| ------ | ------- | ---------- |
| **200** | Worked — returned data | ✅ success |
| **400** | A *genuine* bad request — real missing query param or business rule | ❌ No — validation *working* |
| **401** | Wrong identity for that endpoint | ❌ No — auth *working* |
| **403** | Role guard **or** ownership guard blocked it (caller doesn't own that resource) | ❌ No — RBAC *working* (the inverse curve across roles proves it) |
| **404** | That resource doesn't exist for this user | ❌ No — correct "not found" |
| **500** | Server **crashed** (unhandled exception) | 🔴 **Yes — the real bug to fix** |
| **SKIP** | Not run — no real id/token could be harvested for a path param | ⚪ Honest gap, not a defect |

**Role-exact real ids, never placeholders.** Each path param is filled from an id **that
same role harvested from its own live responses** (its own reads + its own login identity) —
so the id provably belongs to a resource the role can actually access, never one borrowed
from another role. Ids still chain within a role (its enrollments → its `courseId` → its
assignments → its `assignmentId`). When the role never obtained a real id for an
`{id}`/`{token}` param, the endpoint is **skipped** rather than probed with a fake or foreign
id — so a `4xx` always means a genuine bad request or a real permission block, never a
placeholder/wrong-owner artifact.

**Ownership-aware 403s.** The scanner detects resource-ownership guards (`restrictToOwner`,
`restrictToCourseAccess`, `ensureOwnership`, …). When a role sends a *real* id but doesn't own
that specific resource, the resulting 403 is labelled **ownership-enforced (expected)** in the
report — so a wall of 403s reads as *proof access control works*, not a list of problems.

You can't (and shouldn't) get all-`200`s from automated testing — that would mean the API accepts
anything. **Only the `500`s indicate actual defects.**

*The base URL is auto-detected when possible; a mobile app has no "app URL" of its own —
you give the **backend API URL** it talks to (which lives in the app's config).

## Configuration

Live execution needs to know your server URL and auth. Provide it either **per tool call**
(`baseUrl`, `bearerToken`, `headers` arguments) or via a config file in the target project root.

Copy `api-mcp.config.example.json` to `api-mcp.config.json`:

```json
{
  "baseUrl": "http://localhost:3000",
  "bearerToken": "${API_TOKEN}",
  "headers": { "X-Api-Key": "${API_KEY}" },
  "timeoutMs": 15000,
  "sampleValues": { "id": 1, "body": { "name": "example" } }
}
```

`${ENV_VAR}` references are expanded from the environment, so secrets stay out of the file.
Secret-looking headers are redacted in saved reports.

---

## Output artifacts (`.api-mcp/`)

Everything is written to a `.api-mcp/` folder inside the project. **Reports are PDF + XLSX** (no html/md clutter), plus a Postman `.txt`, an importable **Postman collection**, and — for the generator/spec tools — a couple of machine-readable JSONs.

| File | Written by | Contents |
| ---- | ---------- | -------- |
| `qa-audit.pdf` | `qa_audit` | **Overall report — opens with the STABILITY VERDICT** (STABLE / NEEDS ATTENTION / UNSTABLE) + plain-language insights + role matrix |
| `qa-report-{role}.pdf` / `.xlsx` | `qa_audit` | Per-role results (every request: real URL, status, timing) |
| `endpoints.pdf` / `.xlsx` | `qa_audit`, `scan_endpoints`, `full_audit` | Discovered-endpoint inventory |
| `endpoints-full-postman.txt` | same | Full untruncated URLs (with real ids) — copy/paste |
| **`postman_collection.json`** | `qa_audit`, `scan_endpoints`, `full_audit` | **Importable Postman collection** — folders by resource, bearer `{{token}}`, `{{baseUrl}}`, pre-filled bodies, real ids |
| `api-test-cases.pdf` / `.xlsx` | `generate_tests`, `run_tests`, `full_audit` | **Every endpoint's test cases** (happy/edge/negative/security) with expected status — grouped module-wise; actual result too when executed |
| `openapi.json` / `.yaml` | `generate_openapi`, `full_audit` | Generated OpenAPI 3.0 spec (open in https://editor.swagger.io) |
| `tests.json` | `generate_tests` | The raw generated test plan |
| `test-report.pdf` / `.xlsx` | `run_tests`, `full_audit` | Pass/fail summary + status codes per test |

---

## Postman workflow — import all APIs at once & validate

The MCP generates a **ready-to-import Postman collection** so a QA can spot-check any endpoint's live response by hand — the anti-guesswork check on the automated report.

### 1. Generate the collection
- **Static (no token/server needed):** ask the host *"Run scan_endpoints on this project."* → writes `postman_collection.json` with **all endpoints as folders, bearer `{{token}}` auth, `{{baseUrl}}`, and request bodies pre-filled from the code's DTOs**. Path ids appear as `:param` placeholders.
- **With real ids baked in:** run `qa_audit` (needs a token) → the same collection, but harvested **real ids are filled into the paths** (no `:param` editing).
- **Bake in a base URL without a token:** `full_audit` with `baseUrl` set and `execute: false` → collection points at that URL, static.

### 2. Import into Postman
Postman → **Import** → drop `.api-mcp/postman_collection.json` → you get every endpoint, organized into folders.

### 3. Set two variables (once)
Collection → **Variables** → set **`baseUrl`** (if not already baked in) and **`token`** (your bearer/JWT) → **Save**. Auth is collection-level, so **every request inherits the token** — no per-request editing.

### 4. Send & validate
Open any request → **Send**. It uses `{{baseUrl}}` + `{{token}}` + the pre-filled body. Compare its status to what `qa-audit.pdf` reported — **same base URL, token, and ids ⇒ the statuses match ⇒ the MCP report is confirmed.**

> **Re-import to refresh:** an already-imported collection doesn't auto-update. After a new `qa_audit`, re-import the file and choose **Replace** to pull in the latest real ids.

### The HTML & XLSX outputs

- **`.html` reports** are self-contained (no internet needed), color-coded by result and status
  code, and adapt to light/dark themes. Just double-click to open in any browser.
- **`test-report.xlsx`** is a styled workbook with three sheets:
  - **Summary** — totals, pass/fail counts, and a status-code distribution
  - **Endpoints** — every endpoint with method color badges
  - **Test Results** — one row per test with **complete data** (request/response bodies, headers,
    status, duration, errors), color-coded green/red/amber, with auto-filters enabled.

  Open it in Excel, Google Sheets, or LibreOffice. Secret-looking header values are redacted.

Already have `results.json` from a previous run and just want the pretty output? Ask the AI to
run `export_report` — it rebuilds the HTML + XLSX from the saved JSON without re-running anything.

---

## Test categories generated

- **happy-path** — valid request with sample params/body; expects success
- **edge-case** — non-existent resources, malformed params, empty bodies
- **negative** — malformed JSON, wrong HTTP method (expects 4xx/405)
- **security** — same request with credentials stripped (expects 401/403)

---

## Develop from source (contributors)

```bash
git clone https://github.com/Hassan-Jamal/Automated_API_MCP.git
cd Automated_API_MCP
npm install
npm run build     # compile to dist/
npm test          # 108 tests: detectors, scanner, auth, schema, faker, harvesting, query/DTO, ownership, RBAC, module-wise reports, PDF, etc.
```

The `examples/sample-app/` folder has JS, Dart, and Express files demonstrating the detectors.

---

## Do I need to run my app first?

Depends on the step:

| Step | App/server running? |
| ---- | ------------------- |
| Scan, generate Swagger, generate tests | **No** — reads source code statically |
| **Execute** tests (`run_tests` / `full_audit`) | **Yes** — sends real HTTP requests |

For a **React Native or web app**, what must be running is the **backend API server** the app
talks to (e.g. `http://localhost:3000`) — **not** the emulator or the web frontend. This tool
calls the API directly, the same API your app calls. Point `baseUrl` at that server.

If your project **is** the backend (Express/NestJS/FastAPI/Flask), start it, then run the tests.

> Tip: use `dryRun: true` on `run_tests` to preview the requests without sending any — handy
> before pointing it at a real server.

## Onboarding a new QA (2 commands)

No cloning, no building — the package is on npm. On each machine (needs Node.js 18+ and a host):

```bash
npm i -g @hassanjamal/api-mcp          # install once
claude mcp add -s user api-mcp -- api-mcp   # register for all projects
```

Verify with `claude mcp list` → `✔ Connected`. Then open an app folder and either use the
`/qa-audit` slash command or ask in natural language. To update later:
`npm i -g @hassanjamal/api-mcp@latest`.

## Notes & limits

- Detection is heuristic (regex-based). It finds string-literal paths; fully dynamic URLs built
  from variables may be partially normalized (`${expr}` → `{param}`).
- Generated request bodies are placeholders — refine them via `sampleValues` in config for
  endpoints with strict validation.
- The executor sends **real requests**. Point it at a dev/staging server, not production.
