@hassanjamal/api-mcp
v0.1.26
Published
AI-powered API testing MCP server — automated API test automation that discovers endpoints from source code (no OpenAPI spec needed), tests every endpoint per role (RBAC) with real harvested IDs, and reports a stability verdict + an importable Postman col
Maintainers
Readme
api-mcp
📦 Published on npm:
@hassanjamal/api-mcp— install withnpm 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:
- Discovers every API endpoint in the code (client calls and server routes)
- Reads or generates an OpenAPI / Swagger spec (if none exists, it builds one)
- Generates test cases + edge cases for each endpoint, based on its HTTP method and params
- Auto-logs in per role (from just credentials — finds the login endpoint, extracts the token)
- Harvests real IDs from responses + JWTs and injects the right resource id into each path param
- Executes them against your running server — like Postman — scoped to each role's real permissions
- 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:
npm i -g @hassanjamal/api-mcpRequires Node.js 18+. Global install is recommended over
npxbecausenpxre-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):
claude mcp add -s user api-mcp -- api-mcpVerify: claude mcp list → api-mcp - ✔ Connected. (Mind the spacing: -- api-mcp.)
Cursor
Settings → MCP → Add new server, or edit ~/.cursor/mcp.json:
{
"mcpServers": {
"api-mcp": { "command": "api-mcp" }
}
}Claude Desktop
Edit claude_desktop_config.json (Windows: %APPDATA%\Claude\claude_desktop_config.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
[email protected] / pw, teacher[email protected] / pw, student[email protected] / pw."
qa_audit then:
- Detects the API base URL from the code (
.env, axios/DartbaseURL) — or you passbaseUrl - Logs in as each role, extracting the token from the response or the JWT (
token,accessToken,data.token,jwt…) - 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-id404s into real200s - Scopes each role to endpoints its route-guard permits (reads
authorizeAdmin(),@Roles('teacher')…), so you get more200s and far less403/404noise - 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)
- Writes PDF + XLSX only — overall
qa-audit.pdf, per-roleqa-report-{role}.pdf/.xlsx, andendpoints.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:
{ "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:
[
{ "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-200s from automated testing — that would mean the API accepts
anything. Only the 500s 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:
{
"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.jsonwith all endpoints as folders, bearer{{token}}auth,{{baseUrl}}, and request bodies pre-filled from the code's DTOs. Path ids appear as:paramplaceholders. - With real ids baked in: run
qa_audit(needs a token) → the same collection, but harvested real ids are filled into the paths (no:paramediting). - Bake in a base URL without a token:
full_auditwithbaseUrlset andexecute: 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
.htmlreports 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.xlsxis 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)
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: trueonrun_teststo 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):
npm i -g @hassanjamal/api-mcp # install once
claude mcp add -s user api-mcp -- api-mcp # register for all projectsVerify 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
sampleValuesin config for endpoints with strict validation. - The executor sends real requests. Point it at a dev/staging server, not production.
