gRPC for the bid path.
MCP is for agents. These services are for anything that already speaks HTTP/2 and has a latency budget: an SSP, a DSP, a stitcher, a sidecar. Same Rust engine as the CLI. You run the server. There is no hosted endpoint.
VASTlint and RTBlint MCP servers are hosted. The gRPC servers are not. Reflection and grpc.health.v1 are on, so grpcurl works without a local copy of the proto. Pixellint has no gRPC service yet.
Which interface
MCP
Hosted HTTP for two of the three. Structured tool results a model can branch on.
gRPC
Unary call, deadlines, health for a load balancer. Fits inside a bid window.
CLI
Same verdict. Fewer moving parts.
The two packages
Hand-written proto, not generated from Rust types. A minor engine release must not force a client regenerate. buf breaking runs against main on every pull request.
VASTlint
RTBlint
ARTF. The two ValidateArtf* RPCs exist because the IAB Tech Lab Agentic Real Time Framework requires gRPC at the extension point. An orchestrator checks an agent on the same transport the agent was called over, before forwarding anything.
Run and call
Binaries are not on crates.io. Clone the tool repo, then from that checkout:
cargo run --release -p vastlint-grpc # localhost:50051
cargo run --release -p rtblint-grpc # localhost:50061grpcurl -plaintext localhost:50051 list
grpcurl -plaintext localhost:50051 describe openadtech.vastlint.v1.VastlintService
grpcurl -plaintext -d '{"document":"<VAST version=\"4.1\"></VAST>"}' \
localhost:50051 openadtech.vastlint.v1.VastlintService/Validate
grpcurl -plaintext -d '{}' localhost:50051 grpc.health.v1.Health/Checkgrpcurl -plaintext -d '{"document":"{}","kind":"PAYLOAD_KIND_BID_REQUEST"}' \
localhost:50061 openadtech.rtblint.v1.RtblintService/Validate
grpcurl -plaintext -d '{}' localhost:50061 grpc.health.v1.Health/CheckWhat v1 promises
- Fields are added, never renumbered or reused. Removed numbers go into
reserved. - A breaking change is
openadtech.*.v2served beside v1 through a deprecation window. - Rule ids are strings, not proto enums. The catalog moves faster than the contract. An id, once published, is permanent. Unknown ids in overrides are rejected, not ignored.
- Every verdict carries
Provenance: catalog version, catalog digest, engine version. A result without the ruleset behind it is not reproducible. - OpenRTB version is a string, not an enum. A dated 2.6 revision must not require a new wire contract. An unknown version is
INVALID_ARGUMENT, not a silent fallback.