specvet-mcp
Provides deterministic security review of OpenAPI and Swagger documents, scanning for OWASP API Top 10 risks and reporting findings with severity, JSON Pointer, and line numbers. Includes tools for rule listing, explanations, auth coverage, SARIF output, and demo spec generation.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
specvet
Deterministic security review for OpenAPI and Swagger documents.
36 rules, each mapped to a category of the OWASP API Security Top 10 2023. Zero dependencies. SARIF for pull-request gates. MCP server for agent workflows.
Every finding carries three coordinates — a severity, a JSON Pointer that survives a reflow of the file, and a line number you can jump to.
$ specvet demo --scan
demo/openapi.yaml OpenAPI 3.0.3 · 11 operations · 2 files
79 findings (10 critical, 24 high, 36 medium, 9 low) · risk 100/100 · auth coverage 18.2%
OWASP API Top 10 coverage
API1:2023 5 finding(s), worst critical
API2:2023 19 finding(s), worst critical
API3:2023 5 finding(s), worst high
API4:2023 16 finding(s), worst medium
API5:2023 9 finding(s), worst critical
API6:2023 2 finding(s), worst high
API7:2023 2 finding(s), worst high
API8:2023 18 finding(s), worst high
API9:2023 2 finding(s), worst low
API10:2023 1 finding(s), worst medium
CRITICAL SPV002 Object path readable without authentication [API1:2023]
/paths/~1orders~1{orderId}/get
line 49
GET /orders/{orderId} takes ['orderId'] and has no security
| get:Three steps
# 1. run it against a spec you already have
uvx --from specvet specvet scan openapi.yaml
# 2. or see what it does, with nothing to install and no file to find
uvx --from specvet specvet demo --scan
# 3. put it in CI and fail the build on high or worse (the default)
specvet scan . --format sarif -o specvet.sarifExit codes: 0 clean, 1 a finding at or above --fail-on, 2 usage error.
Related MCP server: swagger-api-mcp-server
Why another API linter
Spectral, Redocly and vacuum are excellent at style. They answer "is this document well-formed and consistent?". They are also 3k★ JavaScript and Go projects with plugin systems.
specvet answers a different question: what can an attacker do with this contract? Three consequences follow from taking that seriously:
1. Rules are organised by threat, not by document shape.
Every rule is tagged API1:2023 … API10:2023, so the output answers "where
are we exposed to broken object-level authorization?" rather than "you have 47
lint warnings". specvet rules --owasp API5:2023 lists just those.
2. The second pass reads relationships, not endpoints. Reading one operation at a time cannot see:
SPV021— a path that authenticatesGETbut notDELETE. Each endpoint looks defensible alone; together they say the authorization is backwards.SPV005— asecuritySchemesentry nosecurityrequirement references. The document looks authenticated and authenticates nothing.
3. Absent is not the same as empty.
security: missing is an omission. security: [] is a decision. The loader
keeps them apart, so SPV023 (an explicitly public write) is reported as a
decision while SPV004 (no security anywhere) is reported as a hole.
Rules
Rule | Severity | OWASP | What it catches |
SPV001 | high | API1 | Object write with no 403/404 — "not yours" and "not there" are indistinguishable |
SPV002 | critical | API1 | Anonymous read on an identifier path: enumeration is the vulnerability |
SPV003 | medium | API1 | No 404 declared for an identifier-addressed read |
SPV004 | critical | API2 | Operation with no security requirement anywhere |
SPV005 | high | API2 | Scheme defined but never required |
SPV006 | high | API2 | API key in the query string (logs, history, |
SPV007 | high | API2 | HTTP Basic over a plaintext server |
SPV008 | high | API2 | OAuth2 implicit flow |
SPV009 | medium | API2 | OAuth2 flow with no scopes |
SPV010 | low | API2 | Bearer scheme with no |
SPV011 | high | API2 | No top-level |
SPV012 | high | API3 | Sensitive-looking property in a 2xx schema |
SPV013 | medium | API3 | Request schema accepts unknown properties (mass assignment) |
SPV014 | medium | API3 | One schema used for both request and 2xx response |
SPV015 | medium | API4 | Array property with no |
SPV016 | low | API4 | String property with no |
SPV017 | medium | API4 | Numeric property with no bounds |
SPV018 | medium | API4 | No |
SPV019 | low | API4 | Pagination parameter with no |
SPV020 | critical | API5 | Administrative path with no authentication |
SPV021 | high | API5 | Authorization inconsistent within one path |
SPV022 | high | API5 | Anonymous write operation |
SPV023 | medium | API5 | Write operation explicitly public |
SPV024 | high | API6 | Sensitive flow with neither auth nor |
SPV025 | medium | API6 | Money-moving POST with no idempotency key |
SPV026 | high | API7 | Caller-supplied URL parameter with no format constraint |
SPV027 | medium | API7 | URL-accepting endpoint with no authentication |
SPV028 | high | API8 |
|
SPV029 | medium | API8 | No error response declared |
SPV030 | low | API8 | 2xx response with no content |
SPV031 | medium | API8 | Swagger 2.0 — a retired format |
SPV032 | high | API8 | No |
SPV033 | low | API8 | Wildcard host or unresolved server template |
SPV034 | low | API9 | Deprecated operation still published |
SPV035 | low | API9 | No |
SPV036 | medium | API10 |
|
specvet explain SPV021 prints the reasoning and the fix for any of them.
Usage
specvet scan openapi.yaml # text report
specvet scan ./specs --format json # walk a directory
specvet scan openapi.yaml --fail-on critical # only block on the worst
specvet scan openapi.yaml --ignore SPV018 # accept a risk, explicitly
specvet rules --owasp API4:2023
specvet coverage openapi.yaml # what share of ops are authenticated
specvet demo > openapi.yaml # a spec with every mistake in itOutput is deterministic: the same document produces byte-identical findings. There is no LLM in the loop and no confidence score, because a gate that answers differently on two runs of the same commit is not a gate.
Suppressions
/health:
get: # specvet: ignore[SPV004] - public by design
responses: ...# specvet: ignore[SPV004] drops that rule on that line; a bare
# specvet: ignore drops everything on it. Prefer --ignore in CI so the
accepted risk is visible in the command, not buried in a comment.
CI
- name: Audit the API contract
run: |
pip install specvet
specvet scan . --fail-on high --format sarif -o specvet.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: specvet.sarifMCP
uvx --from "specvet[mcp]" specvet-mcpSix tools: specvet_scan, specvet_scan_text (audit a spec an agent just
generated, no filesystem round trip), specvet_rules, specvet_explain,
specvet_coverage, specvet_demo.
Install
uvx --from specvet specvet --version
pip install specvet
git clone https://github.com/xuange-hu/specvet && cd specvet && pip install -e .Python 3.10+. No runtime dependencies — the YAML and JSON readers are part of
the package, which is why pip install specvet adds nothing to your
environment.
PyPI status. The
Publishjob is red for one known reason: PyPI Trusted Publishing has to be registered once in the PyPI UI before the first upload can succeed, and that step is still pending. The tool itself is unaffected — thegit cloneline above works today.
Design notes
Zero dependencies is a feature, not an accident. A contract gate runs in slim containers and on air-gapped runners. Both readers (
yamlscan,jsonloc) keep line numbers, which is the only reason they exist instead of PyYAML andjson.Evidence over opinion. A finding without a pointer and a line is a suggestion.
test_samples.pygives every rule a spec that triggers it, so a rule cannot silently become unreachable.Deterministic or useless. No network, no model, no sampling.
Contributing
git clone https://github.com/xuange-hu/specvet && cd specvet
pytest -q # 307 tests
ruff check .Adding a rule means: an entry in RULES with an OWASP tag, a check function,
a sample in tests/test_samples.py, and a row in the table above. The sample
is not optional — it is how the rule proves it still works.
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Detect breaking changes, generate changelogs, diff, and validate OpenAPI specs.
Scores how well AI agents will use your API, 0-100, from its OpenAPI spec. Free and read-only.
Generate SBOMs, scan vulnerabilities, and analyze dependencies from local projects or Git repos.
Schema-aware API monitoring: diff OpenAPI specs for breaking changes, manage endpoints.
Related MCP Servers
- AlicenseAqualityCmaintenanceDiscovers, inspects, and executes requests against OpenAPI or Swagger APIs directly from documentation URLs or specification files. It enables users to trace parameter usage across an API and perform authenticated HTTP requests through a structured toolset.820 npm5MIT
- AlicenseAqualityDmaintenanceParses Swagger 2.0 and OpenAPI 3.x specifications, exposing API endpoints, schemas, and authentication through MCP tools with local caching to reduce token usage.1110 npm1MIT
- FlicenseAqualityBmaintenanceEnables LLMs to dereference and query OpenAPI/Swagger specifications, search endpoints and schemas, validate payloads, extract security schemes, and generate production-ready integration code in multiple languages.8-
- AlicenseAqualityCmaintenanceEnables local analysis of OpenAPI 3 and Swagger 2 JSON/YAML spec files on disk, with no remote fetching of specs or credentials. It groups endpoints by tag, flags operations that appear to lack security, lists deprecated operations, and produces short markdown cheat sheets for agents.4MIT