Apidiff
БесплатноНе проверенBreaking-change detector for OpenAPI / GraphQL across commits
Описание
Breaking-change detector for OpenAPI / GraphQL across commits
README
APIDIFF
Breaking-change detector for OpenAPI / GraphQL across commits
PyPI CI Ports License: COCL 1.0 Suite
Developer Tools — fast, single-purpose, CI- and agent-friendly.
pip install cognis-apidiff
apidiff diff openapi.old.json openapi.new.json # → classified changes in milliseconds
apidiff compares two versions of an API definition and tells you, with a clear
severity (BREAKING / WARNING / INFO), exactly what changed and whether it
will break existing consumers. Point it at the baseline and the candidate in
your CI pipeline and it fails the build before a breaking change ships. It is
dependency-free, offline, and read-only — it diffs two files; it
never calls your API and never touches the network.
🔎 Example output
Real, reproducible output from the tool — runs offline:
$ apidiff-emit --version
apidiff 0.6.0
$ apidiff-emit --help
usage: apidiff [-h] [--version] {diff,mcp} ...
Breaking-change detector for OpenAPI / GraphQL across commits.
positional arguments:
{diff,mcp}
diff Diff two API definitions
mcp Run the MCP stdio server (requires the 'mcp' extra)
options:
-h, --help show this help message and exit
--version show program's version number and exit
Blocks above are real
apidiffoutput — reproduce them from a clone.
Sample result format (illustrative values — run on your own data for real findings):
{
"findings": [
{
"id": "123456",
"title": "Suspicious Network Traffic",
"description": "Potential malicious activity detected on port 443",
"severity": "medium",
"labels": ["network", "traffic"],
"created_at": "2023-02-15T14:30:00Z"
},
{
"id": "789012",
"title": "Unusual File Access",
"description": "User accessed a file with unusual permissions",
"severity": "high",
"labels": ["file", "access"],
"created_at": "2023-02-15T14:31:00Z"
}
]
}
Usage — step by step
apidiff detects breaking changes between two OpenAPI / GraphQL definitions.
- Install (either way works today):
pip install cognis-apidiff # PyPI pip install "git+https://github.com/cognis-digital/apidiff.git" # from source - Diff a baseline against a candidate definition:
apidiff diff openapi.old.json openapi.new.json - Force the format when auto-detection is ambiguous:
apidiff diff schema.old.graphql schema.new.graphql --fmt graphql - Read the output as JSON for tooling, or SARIF for code scanning:
apidiff diff openapi.old.json openapi.new.json --format json apidiff diff openapi.old.json openapi.new.json --format sarif - Automate in CI —
--fail-oncontrols when the diff returns a non-zero exit:apidiff diff openapi.old.json openapi.new.json --fail-on breaking
Input formats. OpenAPI documents are read as JSON (OpenAPI 3.x or Swagger 2.0). If your spec is YAML, convert it first — e.g.
python -c "import sys,yaml,json; json.dump(yaml.safe_load(open(sys.argv[1])), open(sys.argv[2],'w'))" openapi.yaml openapi.json— then diff the JSON. GraphQL is read as SDL text (.graphql).
Contents
- Why apidiff? · Features · Quick start · What counts as breaking · Example · Demos · Architecture · AI stack · Polyglot ports · How it compares · Integrations · Install anywhere · Scope & safety · Related · Contributing
Why apidiff?
A renamed field, a newly-required parameter, a dropped 2xx response — small
diffs that silently break every client of your API. Code review rarely catches
them; integration tests catch them late. apidiff makes the contract itself the
gate.
apidiff is single-purpose, scriptable, and self-hostable: point it at a
baseline + candidate, get prioritized results in the format your workflow
already speaks (table · JSON · SARIF), gate CI on it, and let agents drive it
over MCP.
Features
- ✅ Detects breaking changes in OpenAPI 3.x / Swagger 2.0 (JSON) and GraphQL SDL
- ✅ Severity model: BREAKING / WARNING / INFO with stable change codes (
path.removed,param.added.required,response.property.removed,enum.value.removed, …) - ✅ Output as table · JSON · SARIF 2.1.0 (GitHub code scanning)
- ✅ CI gate via exit codes +
--fail-on breaking|warning|never - ✅ Zero runtime dependencies — Python standard library only
- ✅ Offline & read-only — diffs two files; no network, no live API access
- ✅ Runs on Linux / macOS / Windows · Docker · devcontainer
- ✅ MCP server (
apidiff mcp) so AI agents can drive it - ✅ Verified polyglot ports in JavaScript, Go, and Rust (
ports/)
Quick start
pip install cognis-apidiff
apidiff --version
apidiff diff openapi.old.json openapi.new.json # human table
apidiff diff openapi.old.json openapi.new.json --format json # machine-readable
apidiff diff openapi.old.json openapi.new.json --format sarif # code-scanning
apidiff diff openapi.old.json openapi.new.json --fail-on breaking # CI gate
apidiff diff schema.old.graphql schema.new.graphql --fmt graphql # GraphQL SDL
What counts as breaking
apidiff classifies each change by who it hurts. A change is BREAKING when it can break an existing consumer; WARNING when it is risky but tolerable; and INFO when it is purely additive.
| Change | OpenAPI | GraphQL | Severity |
|---|---|---|---|
| Endpoint / type removed | path.removed, operation.removed |
type.removed |
🔴 BREAKING |
| Field a client reads removed | response.property.removed |
field.removed |
🔴 BREAKING |
| New required input | param.added.required, requestBody.required.added |
input.field.added.required, arg.added.required |
🔴 BREAKING |
| Existing input made required | param.required.added |
arg.required.added |
🔴 BREAKING |
| Type narrowed / changed | *.property.type.changed |
field.type.changed |
🔴 BREAKING |
Success (2xx) response dropped |
response.removed |
— | 🔴 BREAKING |
| Enum value removed | — | enum.value.removed |
🔴 BREAKING |
| Optional input / parameter removed | param.removed |
arg.type.changed |
🟡 WARNING |
| Enum value added | — | enum.value.added |
🟡 WARNING |
| New optional field / endpoint / type | path.added, param.added, *.property.added |
field.added, arg.added, type.added |
🟢 INFO |
GraphQL nullability is direction-aware: loosening an output field (
String!→String) breaks readers, while tightening an input field (String→String!) breaks callers. apidiff scores each correctly.
Example
$ apidiff diff demos/01-basic/openapi.old.json demos/01-basic/openapi.new.json
format: openapi
changes: 4 breaking: 3 warning: 0 info: 1
[BREAKING] DELETE /pets/{petId} Operation 'DELETE /pets/{petId}' was removed
[BREAKING] GET /pets New required parameter 'tenant' (in: query) added
[BREAKING] GET /pets response property 'tag' was removed
[INFO ] /pets/{petId}/notes Path '/pets/{petId}/notes' was added
The same diff as machine-readable JSON:
$ apidiff diff demos/01-basic/openapi.old.json demos/01-basic/openapi.new.json --format json
{
"format": "openapi",
"summary": { "total": 4, "breaking": 3, "warning": 0, "info": 1 },
"changes": [
{ "severity": "BREAKING", "code": "operation.removed",
"location": "DELETE /pets/{petId}", "message": "Operation 'DELETE /pets/{petId}' was removed" },
{ "severity": "BREAKING", "code": "param.added.required",
"location": "GET /pets", "message": "New required parameter 'tenant' (in: query) added" },
{ "severity": "BREAKING", "code": "response.property.removed",
"location": "GET /pets", "message": "response property 'tag' was removed" },
{ "severity": "INFO", "code": "path.added",
"location": "/pets/{petId}/notes", "message": "Path '/pets/{petId}/notes' was added" }
]
}
The process exits 1 when breaking changes are found, so the CI step fails before the change ships.
Demos — real-world scenarios
Each folder under demos/ is a self-contained, runnable scenario: a
baseline + candidate definition in apidiff's real input format, plus a
SCENARIO.md describing where the data came from, what to expect, the exact
command, and how to act. Every demo is verified by the test suite.
| Demo | Format | What it shows |
|---|---|---|
| 01-basic | OpenAPI | Removed op + new required param + dropped response field |
| 02-graphql-schema | GraphQL | Field removals across a release |
| 03-safe-evolution | OpenAPI | Purely additive release → green build (exit 0) |
| 04-graphql-required-input | GraphQL | New required input field & argument |
| 05-response-shape-change | OpenAPI | Response property type change + dropped field |
| 06-status-code-dropped | OpenAPI | Removed 206 + removed query param (mixed severity) |
| 07-sarif-codescanning | OpenAPI | --format sarif for GitHub code scanning |
| 08-graphql-enum-and-args | GraphQL | Enum churn + argument made required |
| 09-request-body-tightening | OpenAPI | Request-body field becomes required |
| 10-multitenant-rollout | OpenAPI | Big mixed PR: breaking + additive across many paths |
# run any demo
python -m apidiff diff demos/02-graphql-schema/schema.old.graphql \
demos/02-graphql-schema/schema.new.graphql
Architecture
flowchart LR
OLD[old definition] --> D[detect format]
NEW[new definition] --> D
D -->|openapi| OA[OpenAPI diff]
D -->|graphql| GQ[GraphQL SDL diff]
OA --> R[classify BREAKING / WARNING / INFO]
GQ --> R
R --> T[table]
R --> J[JSON]
R --> S[SARIF 2.1.0]
The engine is one module (apidiff/core.py): a format detector, two diff
walkers (OpenAPI JSON, GraphQL SDL via a stdlib subset parser), a severity
classifier, and three renderers. No plugins, no config files, no I/O beyond
reading the two files you name.
Use it from any AI stack
apidiff is interoperable with every popular way of using AI:
- MCP server —
apidiff mcpexposes anapidiff_diff(old_path, new_path, fmt)tool to Claude Desktop, Cursor, Cognis.Studio, and the uncensored-fleet (needs themcpextra:pip install "cognis-apidiff[mcp]") - OpenAI-compatible / JSON — pipe
apidiff diff old new --format jsoninto any agent or LLM - LangChain · CrewAI · AutoGen · LlamaIndex — wrap the CLI/JSON as a tool in one line
- CI / scripts — exit codes + SARIF for non-AI pipelines
Polyglot ports — same engine, four languages
Drop apidiff into any stack or ship a single static binary. Each port in
ports/ mirrors the reference CLI (diff OLD NEW [--format table|json]), emits the same change codes and the same exit-code contract
(1 on breaking, 0 clean, 2 on error), and has its own smoke test wired
into the ports.yml CI workflow. The JavaScript
port is byte-for-byte verified against the Python reference across every OpenAPI
demo.
| Language | Run | Test |
|---|---|---|
| Python (reference) | apidiff diff old.json new.json |
python -m pytest |
| JavaScript / Node | node ports/javascript/index.js diff old.json new.json |
node --test ports/javascript/test.js |
| Go | cd ports/go && go run . diff old.json new.json |
go test ./... |
| Rust (zero crates) | cd ports/rust && cargo run -- diff old.json new.json |
cargo test |
How it compares
| Cognis apidiff | oasdiff | |
|---|---|---|
| Self-hostable, no account | ✅ | varies |
| Single command, zero config | ✅ | ⚠️ |
| JSON + SARIF for CI | ✅ | varies |
| MCP-native (AI agents) | ✅ | ❌ |
| Polyglot ports (JS/Go/Rust) | ✅ | ❌ |
| Open license | ✅ COCL | varies |
Built in the spirit of oasdiff, re-framed the Cognis way. Missing a credit? Open a PR.
Integrations
Pipes into your stack: SARIF for code-scanning, JSON for anything, an
MCP server (apidiff mcp) for AI agents, and an apidiff-emit forwarder
that maps findings onto STIX / MISP / Sigma / Splunk / Elastic / Slack / webhook
via cognis-connect. See
docs/INTEGRATIONS.md.
# forward a diff to Slack (dry-run shown; cognis-connect is an optional extra)
apidiff diff old.json new.json --format json | apidiff-emit --to slack --url "$WEBHOOK" --dry-run
Install — every way, every platform
pip install "git+https://github.com/cognis-digital/apidiff.git" # pip (works today)
pipx install "git+https://github.com/cognis-digital/apidiff.git" # isolated CLI
uv tool install "git+https://github.com/cognis-digital/apidiff.git" # uv
pip install cognis-apidiff # PyPI (when published)
docker run --rm ghcr.io/cognis-digital/apidiff:latest --help # Docker
brew install cognis-digital/tap/apidiff # Homebrew tap
curl -fsSL https://raw.githubusercontent.com/cognis-digital/apidiff/main/install.sh | sh
| Linux | macOS | Windows | Docker | Cloud |
|---|---|---|---|---|
scripts/setup-linux.sh |
scripts/setup-macos.sh |
scripts/setup-windows.ps1 |
docker run ghcr.io/cognis-digital/apidiff |
DEPLOY.md (AWS/Azure/GCP/k8s) |
Edge / air-gap
apidiff has zero runtime dependencies and makes no network calls — it
only reads the two files you hand it. That makes it ideal for air-gapped CI:
clone once (or vendor the single apidiff/ package), and it runs anywhere
Python 3.10+ is present. The Go and Rust ports compile to a single static
binary with no third-party crates/modules, so you can drop one executable into
a locked-down runner with no package manager at all.
Scope & safety
- Passive and read-only. apidiff opens two local definition files and compares them. It does not call any API, send traffic, or perform active scanning of any kind.
- No network. The core has no network code path; it runs fully offline.
(The optional
apidiff-emitforwarder can POST findings to a SIEM/Slack webhook you configure, and supports--dry-run; this is opt-in and never runs during a diff.) - Defensive use. apidiff is a developer/CI quality gate. Use it on API contracts you own or are authorized to review.
- Deterministic. Same inputs → same output. No fabricated data, no external state.
Related Cognis tools
- mcpforge — Scaffold, test, and publish MCP servers in minutes
- promptlint — Lint, version, and test prompts as code with a CI gate
- envdoctor — .env validator, secret-presence and config-drift checker
- codeglance — Repo onboarding map — architecture + hotspots for humans and agents
- flakefinder — Flaky-test detector from CI history with quarantine suggestions
- licenselens — Dependency license + SBOM gate, developer-CLI first
Explore the suite → 🗂️ all 170+ tools · ⭐ awesome-cognis · 🔗 cognis-sources · 🤖 uncensored-fleet · 🧠 engram
Contributing
PRs, new rules, and demo scenarios are welcome under the collaboration-pull model — see CONTRIBUTING.md and SECURITY.md. New language ports should add a smoke test wired into ports.yml and verify against the OpenAPI demos.
⭐ If
apidiffsaved you time, star it — it genuinely helps others find it.
Interoperability
apidiff composes with the 300+ tool Cognis suite — JSON in/out and a shared
OpenAI-compatible /v1 backbone. See INTEROP.md for the
suite map, composition patterns, and reference stacks.
License
Source-available under the Cognis Open Collaboration License (COCL) v1.0 — free for personal, internal-evaluation, research, and educational use; commercial / production use requires a license ([email protected]). See LICENSE.
Установка Apidiff
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/cognis-digital/apidiffFAQ
Apidiff MCP бесплатный?
Да, Apidiff MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Apidiff?
Нет, Apidiff работает без API-ключей и переменных окружения.
Apidiff — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить Apidiff в Claude Desktop, Claude Code или Cursor?
Открой Apidiff на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
GitHub
PRs, issues, code search, CI status
автор: GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
автор: mcpdotdirectAmap Maps Mcp Server
MCP server for using the AMap Maps API
автор: duxiaohuiSupabase
Database, auth and storage
автор: SupabaseEverything
Reference / test server with prompts, resources, and tools.
Git
Tools to read, search, and manipulate Git repositories.
Sequential Thinking
Dynamic and reflective problem-solving through thought sequences.
Time
Time and timezone conversion capabilities.
Compare Apidiff with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории development
