Ferrok API Documentation
Scan MCP server configurations for security vulnerabilities. Map findings to the OWASP MCP Top 10. Integrate automated security into your workflow.
API v1 — Ferrok Engine 0.1.0. All endpoints return JSON. OpenAPI spec at /openapi.json. Swagger UI at /docs.
Authentication
All requests require a Bearer token in the Authorization header.
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://api.ferrok.dev/v1/health
100 scans/month. No key required for /v1/health.
Base URL
https://api.ferrok.dev
Local dev: http://localhost:8000.
Quickstart
Send your first scan in under a minute:
curl -X POST https://api.ferrok.dev/v1/scan \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"config": {
"server_name": "my-mcp-server",
"transport": "stdio",
"command": "npx",
"args": ["-y", "@my-org/mcp-server"],
"tools": [{
"name": "read_file",
"description": "Read a file from the filesystem",
"inputSchema": {
"type": "object",
"properties": { "path": { "type": "string" } },
"required": ["path"]
}
}]
}
}'
Response:
{
"scan_id": "scan_a1b2c3d4",
"summary": {
"overall_score": 62,
"grade": "C-",
"total_findings": 5,
"pass_fail": "FAIL"
},
"findings": [ /* ... */ ]
}
POST /v1/scan
Analyzes an MCP server configuration and returns findings mapped to the OWASP MCP Top 10.
Responses
| Status | Description |
|---|---|
200 | Scan completed. Returns ScanResponse. |
401 | Missing or invalid API key. |
422 | Validation error. |
429 | Rate limit exceeded. |
500 | Internal error. |
GET /v1/health
Health check. No auth required.
{ "status": "healthy", "version": "0.1.0", "engine": "Ferrok" }
ScanRequest
| Field | Type | Description | |
|---|---|---|---|
| config | MCPServerConfig | required | Server configuration to scan. |
| include_recommendations | boolean | optional | Include fix advice. Default: true. |
MCPServerConfig
| Field | Type | Description | |
|---|---|---|---|
| server_name | string | optional | Human-readable name. |
| server_url | string | optional | Server URL. |
| transport | string | optional | stdio | sse | streamable-http |
| command | string | optional | Launch command (stdio). |
| args | string[] | optional | Command arguments. |
| env | object | optional | Env vars. Scanned for secrets. |
| capabilities | object | optional | Declared capabilities. |
| tools | ToolDefinition[] | optional | Tool definitions. |
ToolDefinition
| Field | Type | Description | |
|---|---|---|---|
| name | string | required | Tool identifier. |
| description | string | optional | Scanned for injection. |
| inputSchema | object | optional | JSON Schema. Alias: input_schema. |
ScanResponse
| Field | Type | Description |
|---|---|---|
| scan_id | string | Unique ID. |
| timestamp | datetime | UTC ISO 8601. |
| server_name | string | null | Echo of request. |
| summary | ScanSummary | Aggregated scores. |
| tools_analyzed | ToolAnalysis[] | Per-tool breakdown. |
| findings | Finding[] | Security findings. |
| metadata | object | Engine info, duration. |
Finding
| Field | Type | Description |
|---|---|---|
| id | string | e.g. TP-001, PA-003 |
| title | string | Short description. |
| severity | enum | critical high medium low info |
| owasp_category | string | e.g. MCP01:ToolPoisoning |
| description | string | Detailed explanation. |
| affected_tool | string | null | Triggering tool. |
| evidence | string | null | Detection trigger. |
| recommendation | string | null | Fix guidance. |
ScanSummary
| Field | Type | Description |
|---|---|---|
| overall_score | integer (0-100) | Security score. |
| grade | string | A+ to F. |
| total_findings | integer | Total count. |
| critical_count | integer | Critical findings. |
| high_count | integer | High findings. |
| medium_count | integer | Medium findings. |
| low_count | integer | Low findings. |
| info_count | integer | Informational. |
| pass_fail | string | PASS or FAIL. |
Scoring & Grades
Start at 100. Deductions per finding:
| Severity | Deduction | CI/CD |
|---|---|---|
| critical | -25 | FAIL |
| high | -15 | FAIL |
| medium | -8 | Score only |
| low | -3 | Score only |
| info | 0 | Info only |
| Score | Grade | Score | Grade |
|---|---|---|---|
| 95-100 | A+ | 60-69 | C- |
| 90-94 | A | 50-59 | D+ |
| 85-89 | A- | 40-49 | D- |
| 80-84 | B+ | 0-39 | F |
| 70-79 | B- |
pass_fail returns FAIL if any critical or high finding exists, regardless of score.
Severity Levels
| Level | Meaning | Example |
|---|---|---|
| critical | Active exploitation risk. | Prompt injection in tool. |
| high | Serious vulnerability. | Hardcoded AWS secret. |
| medium | Moderate risk. | Unconstrained fs write. |
| low | Best-practice violation. | Missing schema description. |
| info | Informational. | Stdio transport detected. |
OWASP MCP Top 10
Scanner Modules
Tool Poisoning Scanner TP-*
Prompt injection, hidden instructions, data exfiltration, role manipulation, encoded chars, zero-width unicode.
Permission Analyzer PA-*
Filesystem, network, code execution, database, secrets access, wildcard patterns.
Schema Validator SV-*
Missing schemas, non-object roots, empty properties, unconstrained strings, description mismatches.
Transport Security TS-*
Insecure HTTP, hardcoded secrets, npx -y supply chain, shell injection, missing capabilities.
CI/CD Integration
GitHub Actions
# .github/workflows/mcp-security.yml
name: MCP Security Scan
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Scan MCP config
run: |
RESULT=$(curl -s -X POST https://api.ferrok.dev/v1/scan \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${{ secrets.FERROK_API_KEY }}" \
-d @mcp-config.json)
echo "$RESULT" | jq .
VERDICT=$(echo "$RESULT" | jq -r '.summary.pass_fail')
[ "$VERDICT" = "PASS" ] || exit 1
GitLab CI
mcp_security:
stage: test
script:
- |
RESULT=$(curl -s -X POST https://api.ferrok.dev/v1/scan \
-H "Authorization: Bearer $FERROK_API_KEY" -d @mcp-config.json)
[ "$(echo $RESULT | jq -r '.summary.pass_fail')" = "PASS" ] || exit 1
Python
import httpx, sys, json
resp = httpx.post(
"https://api.ferrok.dev/v1/scan",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"config": json.load(open("mcp-config.json"))}
)
result = resp.json()
if result["summary"]["pass_fail"] == "FAIL":
sys.exit(1)
Error Handling
| Status | Meaning | Action |
|---|---|---|
401 | Unauthorized | Check API key. |
422 | Validation | Check detail array. |
429 | Rate limited | Wait and retry. |
500 | Server error | Retry once. |
Rate Limits
| Plan | Scans/mo | Req/min |
|---|---|---|
| Free | 100 | 10 |
| Starter | 2,000 | 30 |
| Pro | 10,000 | 60 |
| Enterprise | Unlimited | Custom |
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1709683200