OpenRTB linter: validates OpenRTB 2.x bid requests and bid responses against versioned IAB spec snapshots. Rust core, CLI, npm/WASM, MCP server.
- ✓Open-source license (Apache-2.0)
- ✓Actively maintained (<30d)
- ✓Clear description
- ✓Topics declared
- ✓Documented (README)
git clone https://github.com/aleksUIX/rtblint{
"mcpServers": {
"rtblint": {
"command": "rtblint"
}
}
}MCP Servers overview
# RTBlint
**OpenRTB linter.** Validates OpenRTB 2.x bid requests and bid responses against versioned IAB Tech Lab spec snapshots, from 2.0 through the monthly 2.6 releases (currently up to 2.6-202606).
[](https://crates.io/crates/rtblint)
[](https://github.com/aleksUIX/rtblint/actions)
[](https://smithery.ai/servers/aleksander/rtblint)
[](https://scorecard.dev/viewer/?uri=github.com/aleksUIX/rtblint)
Website and playground: [rtblint.org](https://rtblint.org)
## What it checks
- Malformed JSON and wrong top-level shape
- Required fields, including required non-empty arrays
- Unknown objects and fields per version catalog (`ext` subtrees stay open)
- Type mismatches (string, integer, float, boolean, object, and array forms)
- Documented enum values, including AdCOM lists and vendor ranges (500+)
- Deprecated, moved, removed, and not-yet-available fields across versions
- Semantic rules: site/app/dooh exclusivity, imp media type presence, skippable video dependencies, duration exclusivity, seatbid/nbr presence on responses, and more
- Response markup coherence: `bid.adm` content vs the declared `bid.mtype` (native JSON encoding, VAST/DAAST roots, double-encoded payloads)
- Request/response cross-validation: with the originating request supplied, every bid's `impid`, `mtype`, `adm` markup, `dealid`, seat, and currency are checked against what the request actually offered
- JSON dialect: spec JSON types flag fields such as `imp.secure` and `regs.coppa` as integers, while the IAB OpenRTB protobuf schema declares 28 of them `bool`. Either encoding is correct on its own transport and wrong on the other, so the caller declares which one it meant
- Exchange profiles apply published request, response and paired constraints for the selected vendor and traffic direction. Optional extensions stay optional unless the source explicitly requires them. Profiles include Google Authorized Buyers, Prebid Server, Microsoft Monetize, Magnite DV+ xAPI and separate buyer, supplier and SDK contracts. See [exchange profile evidence](docs/exchange-profiles/) for sources, fixtures and limits
- Nested specs OpenRTB carries as strings or opaque `ext`: Native Ads 1.2 markup (`imp.native.request` and native `bid.adm`, including required-asset pairing), GPP header vs `gpp_sid` and TCF 2 shape, `${AUCTION_*}` macros on billing and loss URLs, EID/SUA structure, SKAdNetwork `ext.skadn`
- Privacy signal contradictions: `regs.coppa` / `device.lmt` / `device.dnt` / `regs.gdpr` versus identifiers on the same payload, TCF Purpose 1 versus device-storage IDs, US Privacy Opt-Out Sale versus hashed EIDs, email-shaped `user.id` / `site.page`, DSA Transparency field presence. Findings describe the document. They do not state a legal conclusion.
- ARTF envelopes and mutation sets, including applying the mutations and revalidating what comes out
Every finding carries a stable rule id, a severity, a message, and a JSON path.
## ARTF
[ARTF](https://iabtechlab.com/standards/artf/), the IAB Tech Lab Agentic Real Time Framework, hands an agent an OpenRTB payload inside an `RTBRequest` envelope and takes back *mutations*: proposed changes the orchestrator may accept or reject one at a time. Nothing in the framework checks that the auction still validates once they are applied, and a mutation is only meaningful relative to the request it targets.
```bash
# The envelope, plus full OpenRTB validation of what it carries
rtblint validate --type artf-request rtb-request.json
# The mutation set against the auction it targets
rtblint validate --type artf-response --request rtb-request.json rtb-response.json
# Apply the mutations, revalidate, and report only what the mutations broke
rtblint validate --type artf-response --apply --request rtb-request.json rtb-response.json
```
Three passes:
- **Envelope.** Required members, `lifecycle` against the payloads actually carried, `tmax` plausibility for an in-auction call, `originator` and `applicable_intents` enum values, and the carried bid request and bid response validated as protobuf JSON.
- **Mutations.** The response id echoes the extension point request id (not the bid request id), each declared intent is in `applicable_intents`, the operation and payload oneof member match the intent, and every semantic path (`/imp/{id}`, `/imp/{id}/pmp/deals/{id}`, `/user/data/segment`, `/seatbid/{seat}/bid/{id}`) resolves to something the auction carries. `ADJUST_DEAL_MARGIN` is reported as having no OpenRTB field to write to, because it does not.
- **Applied.** The mutations are written in and the result revalidated, reporting the OpenRTB findings the mutations introduced with pre-existing findings filtered out. What the agent broke, not what arrived broken.
The ARTF v1.0 document and its `.proto` use different vocabularies for the same mutation (`activateSegments` and a `value: {IDsPayload: ...}` wrapper against `ACTIVATE_SEGMENTS` and top-level oneof members). Payloads written from the document are mapped and reported as `artf.mutation.legacy_spec_encoding` rather than dismissed as unknown.
## Surfaces
| Surface | Package | Status |
|---------|---------|--------|
| Rust CLI | [`rtblint`](https://crates.io/crates/rtblint) | Working |
| Rust library | [`rtblint-core`](https://crates.io/crates/rtblint-core) | Working |
| MCP server | [`rtblint-mcp`](https://crates.io/crates/rtblint-mcp) | Working |
| Node (WASM) | `rtblint-core` on npm | Working |
| GitHub Action | [`aleksUIX/rtblint`](https://github.com/aleksUIX/rtblint) | Working |
| Python | `rtblint` on PyPI | Not implemented yet |
| Go | `github.com/aleksUIX/rtblint/go` | Not implemented yet |
OpenRTB 3.0 validates through its layered envelope: the transport objects (Openrtb, Request, Item, Deal, Source, Response, Seatbid, Bid) and the AdCOM 1.0 domain objects under `item.spec` (Placement), `bid.media` (Ad), and `request.context`. A 2.x payload sent to a 3.0 validator gets a migration diagnostic rather than a bare parse error. The 2.6-202204 snapshot has no extracted catalog and reports itself as unsupported instead of passing payloads silently. See [ROADMAP.md](ROADMAP.md) for what's next and [CHANGELOG.md](CHANGELOG.md) for release history.
## CLI
```bash
cargo install rtblint
rtblint validate request.json
rtblint validate --type response response.json
rtblint validate --type response --request request.json response.json
rtblint validate --version 2.5 --format json request.json
rtblint validate --dialect proto-json grpc-bid-request.json
rtblint validate --profile google-ab google-bid-request.json
rtblint validate --profile prebid-server pbs-auction.json
rtblint validate --profile xandr xandr-bid-request.json
rtblint validate --profile magnite magnite-bid-request.json
rtblint validate --resolve --cache ./supply-cache request.json
rtblint validate --summary bids.ndjson
rtblint validate --batch --summary bids.ndjson
cat request.json | rtblint validate --stdin
```
`--request` supplies the originating bid request for paired checks, including batch validation. `--dialect proto-json` selects the protobuf JSON encoding. `--profile` selects the vendor contract and traffic direction. Xandr checks bidder-facing Microsoft Monetize traffic; Magnite checks the DV+ xAPI extension model. Their optional seller, context and inventory identity fields are checked when supplied. Account-specific and runtime acceptance remain outside the payload. `--resolve --cache <dir>` checks SupplyChain hops against sellers.json and ads.txt / app-ads.txt from a local directory:
```text
<dir>/sellers/<asi>/sellers.json
<dir>/ads/<site.domain>/ads.txt
<dir>/app-ads/<app.bundle>/app-ads.txt
```
Nothing is fetched; populate the cache yourself. `--batch` lints one JSON object per line from a file or stdin. `--summary` adds rule-frequency totals for a captured stream (`--summary bids.ndjson` for the histogram alone). See [ARTF](#artf) for `--type artf-request` and `--type artf-response`.
Exit codes: 0 valid, 1 validation errors, 2 usage or I/O error.
## GitHub Action
The Action lives in this repo. Pin a release tag so CI downloads that CLI tarball:
```yaml
- uses: aleksUIX/rtblint@v0.13.0
with:
path: fixtures/bid-request.json
spec-version: 2.6-202505
```
`version` selects the CLI release (`auto` follows the action's own `v*` tag). `spec-version` is the OpenRTB snapshot. Linux and macOS runners, x86_64 and aarch64.
## Node
```js
import { validate, validateResponse, validateResponseAgainstRequest } from "rtblint-core";
const report = validate(JSON.stringify(bidRequest), "2.6-202505");
if (!report.valid) {
for (const issue of report.issues) {
console.log(`[${issue.severity}] ${issue.path}: ${issue.message} (${issue.id})`);
}
}
// Cross-validate a response against the request it answers.
const paired = validateResponseAgainstRequest(
JSON.stringify(bidResponse),
JSON.stringify(bidRequest)
);
```
For gRPC bidstream payloads and ARTF:
```js
import {
validateDialect,
validateProfile,
validateArtfRequest,
validateArtfResponseApplied,
protoBoolDivergences,
} from "rtblint-core";
validateDialect(JSON.stringify(bidRequest), "proto-json");
validateProfile(JSON.stringify(bidRequest), "google-ab");
validateProfile(JSON.stringify(bidRequest), "prebid-server");
validateArtfRequest(JSON.stringify(rtbRequest));
// { result, application }: what the mutations broke, and the payloads they produced
const { result, application } = validateArtfResponseApplied(
JSON.stringify(rtbResponse),
JSON.stringify(rtbRequest)
);
protoBoolDivergences(); // the 28 fields the two schemas type differently
```
## MCP server
Hosted Streamable HTTP (no install): [https://rtblint.org/mcp](https://rtblinWhat people ask about rtblint
What is aleksUIX/rtblint?
+
aleksUIX/rtblint is mcp servers for the Claude AI ecosystem. OpenRTB linter: validates OpenRTB 2.x bid requests and bid responses against versioned IAB spec snapshots. Rust core, CLI, npm/WASM, MCP server. It has 2 GitHub stars and its last recorded update is dated 2026-10-10.
How do I install rtblint?
+
You can install rtblint by cloning the repository (https://github.com/aleksUIX/rtblint) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.
Is aleksUIX/rtblint safe to use?
+
Our security agent has analyzed aleksUIX/rtblint and assigned a Trust Score of 95/100 (tier: Verified). See the full breakdown of passed checks and flags on this page.
Who maintains aleksUIX/rtblint?
+
aleksUIX/rtblint is maintained by aleksUIX. The last recorded GitHub activity is dated 2026-10-10, with 0 open issues.
Are there alternatives to rtblint?
+
Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.
Deploy rtblint to your cloud
Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.
Maintain this repo? Add a badge to your README
Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.
[](https://claudewave.com/repo/aleksuix-rtblint)<a href="https://claudewave.com/repo/aleksuix-rtblint"><img src="https://claudewave.com/api/badge/aleksuix-rtblint" alt="Featured on ClaudeWave: aleksUIX/rtblint" width="320" height="64" /></a>More MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ and follow here for daily tips and tricks: https://x.com/Scrapling_dev
The fastest path to AI-powered full stack observability, even for lean teams.