Skip to main content
Glama
evidiq

EVIDIQ Rubric MCP

Official
by evidiq

EVIDIQ Rubric turns "is this deliverable acceptable?" into a deterministic, signed verdict against a spec both sides agreed on before the work started. Notary proves an output happened; Rubric decides whether it is acceptable — with a verdict anyone holding the same bytes can recompute.

Deterministic by construction: JSON Schema, normalised string and number matching, arithmetic re-checks, and a small declarative criteria language. No model in the hot path. Same input, same rule set, same spec → byte-identical verdict and digest.

What a Rubric report proves, and what it does not. Proves: this exact deliverable, under this spec version and this rule set version, produced these criterion results and this verdict; anyone with the same bytes recomputes the same digest. Does not prove: that the deliverable is correct, useful, or complete in a sense the spec did not encode. A PASS means the spec's criteria were met — no more.

Position in the family

Service

Question

Boundary against Rubric

Core

Can I trust this agent?

Counterparty risk before work. Rubric judges the work.

Notary

Can I prove this output happened?

Provenance of an output. Rubric judges its conformance.

Operator

Can an agent act in a browser?

Produces work; does not grade it.

Sentinel

Is this MCP safe?

Judges a service. Rubric judges a deliverable.

Atlas

What does this dataset say?

Analyses data. Rubric checks an answer against a spec.

Lineage

Is this dependency trustworthy?

Supply chain, not deliverables.

Vault

What exactly did the agent do?

Records history. Rubric decides acceptance; the verdict belongs in Vault.

Redact

Is this content safe to share?

Sensitive values. Rubric checks completeness and grounding.

Warden

Is this code safe to commit?

Source code quality. Rubric checks any deliverable against its spec.

Assay

What will this transaction do?

Pre-signature intent, not deliverables.

Rubric

Does this deliverable meet the contract?

The acceptance verdict, signed and 0G-anchored.

Composition worth documenting: Rubric decides, Vault records the verdict, Notary timestamps the deliverable, Core scored the counterparty.

Related MCP server: stillos-notary-mcp

Status

Field

Value

MCP slot

#11

Public endpoint

https://mcp.evidiq.dev/rubric/mcp

Host port

3011 (loopback only; the public surface is the Traefik reverse proxy)

Repository

to be created at github.com/evidiq/evidiq-rubric-mcp (not yet pushed — pending operator authorisation)

OKX.AI agent id

not yet registered (registration is a separate, explicit step the operator runs after a proven paid call)

Engine

EVIDIQ-Rubric/1.0

Spec DSL version

1.0

Rule set version

1.0.0

Tests

118 passing (npm test)

Deployment

image evidiq-rubric:latest, container evidiq-rubric, env /root/evidiq-rubric.env (0600, root-only), Traefik PathPrefix(/rubric) with a rubric-strip middleware

Endpoints

Path

Method

Purpose

/mcp

POST

The MCP endpoint (Streamable HTTP).

/mcp

HEAD

402 — answered explicitly so a reachability prober never hangs.

/mcp

empty POST

402 — never 400, never 406.

/x402

GET

Public pricing discovery listing all ten tools and the v2 challenge.

/skill.md

GET

The agent-readable Skill.

/health

GET

Liveness.

All paths are served after Traefik strips /rubric. The container never sees the prefix, so the x402 challenge advertises PUBLIC_BASE_URL/mcp, never req.url (RUNBOOK §27-B).

Tools — five paid, five free

Prices are fixed atomic USD₮0 amounts and never read from an env var.

Paid

Tool

Atomic

USDT0

Returns

check_schema

5000

0.005

Validates a structured deliverable against a JSON Schema (draft 2020-12) or an inline shape. Per-path violations with JSON Pointers, expected vs actual type (never the value), and a PASS/BLOCK verdict.

verify_grounding

10000

0.010

Every quoted span, number, date and citation in the deliverable is checked against the supplied sources. Reports unsupported spans with offsets and which source each supported span matched.

check_acceptance

15000

0.015

Evaluates the deliverable against an acceptance spec. Returns PASS / REVIEW / BLOCK, per-criterion results, and the criteria that decided it.

diff_deliverable

20000

0.020

Compares a revision against the previous version and the spec: which criteria flipped, what changed by section, and whether a previously failing criterion now passes.

attest_acceptance

30000

0.030

Binds spec digest + deliverable digest + verdict, signs EIP-191, anchors the digest to 0G (best effort, never fatal). The artifact for a dispute. Refuses without a signer.

Free

Tool

Returns

rubric_capabilities

Spec DSL version, criterion catalog with severities, supported deliverable kinds, limits, full pricing, and the names of all ten tools.

validate_spec

Parse-checks the spec and the deliverable; returns detected kinds, criterion count, and counts by severity — no findings, no charge. Must refuse exactly what the paid tools refuse.

estimate_cost

One tool, or the whole table when none is named. Unknown name → says it is not a tool of this service and shows the table.

verify_rubric_report

Recomputes the digest and verifies the EIP-191 signature offline. On an unsigned report: digest verdict plus "origin cannot be attributed".

get_artifact

Report or attestation by id within its in-memory TTL. A miss is found: false, never an error.

The acceptance spec — a small declarative language

A spec is JSON. No expressions, no code, no regex the caller cannot see. It is versioned (specVersion: "1.0") and an unknown version is rejected rather than guessed at.

{
  "specVersion": "1.0",
  "deliverable": { "kind": "markdown" },
  "criteria": [
    { "id": "has-summary", "severity": "blocker",
      "type": "requiredSection", "section": "Summary", "minWords": 40 },
    { "id": "cites-sources", "severity": "blocker",
      "type": "minCitations", "count": 2 },
    { "id": "no-placeholder", "severity": "blocker",
      "type": "forbiddenPhrase", "phrases": ["TODO", "lorem ipsum", "as an AI"] },
    { "id": "length", "severity": "high",
      "type": "wordCount", "min": 300, "max": 1200 },
    { "id": "schema", "severity": "blocker",
      "type": "jsonSchema", "schemaRef": "#/schemas/report" },
    { "id": "numbers-grounded", "severity": "high",
      "type": "numbersGrounded", "tolerance": 0.001 }
  ]
}

Criterion types for v1, all deterministic:

  • requiredSection — markdown heading by name; optional minWords for the section body.

  • forbiddenPhrase — case-insensitive, smart-quote-folded; reports offsets and phrase indices, never text.

  • wordCountmin / max inclusive.

  • jsonSchema — bounded JSON Schema 2020-12 evaluation (see below).

  • minCitations — distinct citation markers (e.g. [1], [Smith 2020], [unknown]).

  • citationsResolve — each marker must resolve against a supplied source id.

  • numbersGrounded — every numeric claim matched against sources within tolerance.

  • dateSanity — dates fall within an optional windowStart/windowEnd window.

  • requiredField — JSON Pointer (RFC 6901) path present in a JSON deliverable.

  • enumValue — JSON Pointer field value is in an allowed set.

  • uniqueBy — a JSON array has distinct values at a JSON Pointer field.

  • arithmeticConsistentsumField sums to targetField across rows.

Verdict rules, fixed and documented:

  • any failing blockerBLOCK

  • no blocker, any failing highREVIEW

  • everything passes → PASS

  • a criterion that cannot be evaluated (unparseable input, missing source) is INCONCLUSIVE, reported, excluded from PASS, and forces at least REVIEW.

  • empty deliverable, empty criteria, every criterion INCONCLUSIVE → REFUSED (a verdict about nothing is the worst answer the service can give).

Grounding without a model

verify_grounding extracts quoted spans, numerals with units, percentages, dates, and citation markers from the deliverable, and looks for support in the supplied sources using, in order: exact match; whitespace- and case-normalised match; unicode-NFC and smart-quote-folded match; and for numbers, value equality within a stated tolerance after unit normalisation.

What it reports: supported with the source id and offset, unsupported with the offset in the deliverable, or inconclusive with a reason. What it never does: decide whether an unsupported sentence is false. Absence of support is not evidence of falsehood, and the report says so in those words.

Findings carry offsets and kinds, never the matched text for anything that could be sensitive — a report gets pasted into tickets.

Bounded JSON Schema evaluation (§8.4)

check_schema and the jsonSchema criterion use AJV (draft 2020-12) under a strict wrapper:

  • No remote $ref. A $ref starting with http(s):// is rejected.

  • No $ref cycles. A visited set keyed by resolved ref catches cycles.

  • Depth cap (default 32) and node cap (default 10 000) prevent exponential blowup on pathological schemas.

  • A time budget (default 2 000 ms) bounds total evaluation. A timeout returns INCONCLUSIVE, never PASS.

Per-path violations report expected vs actual type, never the value, so a rejected {name: 123} against {name: {type: "string"}} reports expectedType: "string", actualType: "number" without echoing 123.

Integrity — deterministic digest + optional EIP-191

Every report carries an integrity block:

{
  "integrity": {
    "algorithm": "SHA-256",
    "digest": "<sha256 of canonical envelope>",
    "signature": "0x…",          // optional
    "signer": "0x…",             // optional
    "signed": false,             // present only when no signer is configured
    "note": "No signer configured; the digest matches but the origin cannot be attributed."
  }
}

The canonical envelope is property-order-stable JSON over engine, ruleSetVersion, deliverableDigest, specDigest, verdict, counts, criteria, limits. The same bytes produce the same digest, regardless of who signs. verify_rubric_report recomputes the digest and verifies the EIP-191 signature offline; an unsigned report gets a digest verdict plus "origin cannot be attributed" — never valid: true.

OKX Payment SDK — the official path

Rubric uses the official OKX Onchain OS Payment SDK (@okxweb3/x402-core + @okxweb3/x402-evm) against the OKX facilitator, reused from Sentinel/Atlas/Warden (RUNBOOK §23). The price reaches the SDK as an explicit USD₮0 atomic AssetAmount, so the quoted fee token always matches the token registered on the listing.

Perbehaviours, each of which has its own past rejection (RUNBOOK §0.13, §29-A):

  • HEAD /mcp402, immediately.

  • Empty unauthenticated POST /mcp402 with a challenge. Never 400.

  • The challenge resource.url comes from PUBLIC_BASE_URL, never req.url.

  • Prices reach the SDK as USD₮0 atomic amounts.

  • A facilitator success without a transaction hash is honoured with a [x402] SETTLED WITHOUT TX warning; pending/timeout without a hash fails.

  • A malformed payment envelope is refused locally (~75 ms), never forwarded.

  • Free tools answer without any payment header, always.

Hard safety rules (PLAIN §8)

  1. A credential in the input (private key, mnemonic, API key by issuer shape) → refused, and nothing is stored, attested, or anchored.

  2. Never echo deliverable content or source text in findings. Offsets, kinds, counts.

  3. No network fetching of sources in v1. Sources are supplied inline. A URL in a spec is rejected with a reason, not fetched.

  4. jsonSchema evaluation is bounded: no remote $ref, no $ref cycles, depth and node caps, and a time budget.

  5. Artifacts live in memory with a short TTL, addressed by digest. Nothing is written to disk.

Limits

Limit

Default

Env

Maximum input bytes

524288

RUBRIC_MAX_INPUT_BYTES

Maximum sources

20

RUBRIC_MAX_SOURCES

Maximum criteria

100

RUBRIC_MAX_CRITERIA

Artifact TTL (ms)

600000

RUBRIC_ARTIFACT_TTL_MS

Pricing

  • Per paid tool call: atomic USD₮0 amounts as listed above, on X Layer (eip155:196, chain 196).

  • Token: USDT0 (0x779ded0c9e1022225f8e0630b35a9b54be713736, 6 decimals).

  • EIP-712 token domain: { name: "USD₮0", version: "1" }.

  • Free tools are always HTTP 200 and never gated.

Security boundaries

  • The signer key is read from RUBRIC_SIGNER_PRIVATE_KEY and is the ONLY way reports get signed. There is no fallback key in the source. With no signer configured, the service runs unsigned and attest_acceptance refuses — an unsigned attestation would attest to nothing.

  • The 0G anchor key is OG_PRIVATE_KEY, used to anchor digests only, never payloads. 0G failure is best-effort: never fatal to a paid call (the caller already paid; the local content-addressed artifact remains verifiable).

  • The OKX SDK credentials (OKX_API_KEY/SECRET_KEY/PASSPHRASE) are the ONLY path to verification and settlement when present. A partial credential set throws at startup — never silently downgrades to a non-SDK path.

  • The settle key (X402_SETTLE_KEY) is the legacy self-hosted settlement path. It is bypassed when OKX SDK credentials are present.

Quickstart

# Free: capabilities and pricing discovery
curl https://mcp.evidiq.dev/rubric/x402 | python3 -m json.tool

# Free: validate a spec before paying
curl -X POST https://mcp.evidiq.dev/rubric/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"validate_spec","arguments":{"spec":{"specVersion":"1.0","deliverable":{"kind":"markdown"},"criteria":[{"id":"x","severity":"blocker","type":"wordCount","min":10}]}}}}'

# Paid: check_acceptance (402 without a PAYMENT-SIGNATURE envelope)
curl -X POST https://mcp.evidiq.dev/rubric/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"check_acceptance","arguments":{"deliverable":"# Summary\nThis deliverable meets the agreed spec.","spec":{"specVersion":"1.0","deliverable":{"kind":"markdown"},"criteria":[{"id":"has-summary","severity":"blocker","type":"requiredSection","section":"Summary","minWords":5}]}}}}'
# Expected: 402 with amount:"15000", x402Version:2, scheme:"exact"

Self-host

# 1. Install + build
npm ci && npm run build

# 2. Run with no payment env → all tools free (honest downgrade)
PORT=3100 npm start

# 3. Verify
curl -s localhost:3100/health
curl -sX POST localhost:3100/mcp -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

# 4. Add OKX SDK credentials + RUBRIC_SIGNER_PRIVATE_KEY to enable the paid path
PORT=3100 OKX_API_KEY=… OKX_SECRET_KEY=… OKX_PASSPHRASE=… \
  X402_PAY_TO=0x2a8efe3093278bb4bd3b2d9c7b5ba992ca4fc9b0 \
  PUBLIC_BASE_URL=http://localhost:3100 \
  RUBRIC_SIGNER_PRIVATE_KEY=… \
  npm start

Development

npm test            # vitest run — 118 tests
npm run build       # tsc -p tsconfig.json
npm run dev         # tsx watch start-server.ts

Configuration (/root/evidiq-rubric.env on the box; .env.example here)

See .env.example for the full list with names only (no values). Note in particular:

  • Variable names are named after THIS service. A copy-pasted variable name is what put a public signing key in production once (RUBRIC_SIGNER_PRIVATE_KEY, never VAULT_SIGNER_PRIVATE_KEY).

  • Every key is accepted with or without the 0x prefix — every deployment here stores bare hex.

  • The passphrase contains #; keep it unquoted for docker --env-file.

Proven on-chain

The pre-submit gate (PLAN §13) requires one real paid call per paid tool at least once across the suite, with the receipt mining status 0x1, the container logging [x402] SETTLED with a hash, and the report signature tracing to the project signer (0x8a3c7524…ee7D, the family OG signer = RUBRIC_SIGNER_PRIVATE_KEY).

The broadcaster for every settled tx is the OKX official facilitator relayer (not our buyer, not our settle wallet) — which is what proves the SDK is the live settlement path (RUNBOOK §27-F).

Tool

Atomic

Settle tx

Block

Receipt

Signer

Verified

check_schema

5000

0xa3a45c28e169977c12ed82a1b748e2a264886cdcad777ded4354889bdf2300d2

66395009

0x1

0x8a3c7524…ee7D

valid:true, digestMatch:true, signatureValid:true

verify_grounding

10000

0xc74c10945fbd5639d19c311430fbd4ca73fcc4ea48545df2dbc5811e90717197

66395729

0x1

0x8a3c7524…ee7D

valid:true, digestMatch:true, signatureValid:true

check_acceptance

15000

0xc6a5c5c2219dbd7f8333d62603bbc997ad9d892e412f78df10d688c206984439

66395081

0x1

0x8a3c7524…ee7D

valid:true, digestMatch:true, signatureValid:true

diff_deliverable

20000

0x84b3e129d88493a76731e1920e251ddc6a8550b8ba09ec99b794634f87401947

66395738

0x1

0x8a3c7524…ee7D

valid:true, digestMatch:true, signatureValid:true

attest_acceptance

30000

0xe39d63c3181f429e880612bcfb110ea1c037e4331fcbf58f7065f9065d062d86

66395093

0x1

0x8a3c7524…ee7D

valid:true, digestMatch:true, signatureValid:true

Container logs show [x402] SETTLED for every paid tool name with its tx hash. The free verify_rubric_report tool was called immediately after each paid call on the report the service just produced; all five returned valid:true, digestMatch:true, signatureValid:true (§13 item 10).

Free tool valid-argument sweep (§13 item 3)

Every free tool called with genuinely valid arguments against the live endpoint returns a substantive, non-error result. Per-tool:

Free tool

Valid args

Result (vs description)

rubric_capabilities

{}

engine:EVIDIQ-Rubric/1.0, specDslVersion:1.0, 10 tools (5 paid + 5 free), 12 criterion types, signing.available:true ✓ matches description

validate_spec

valid spec + deliverable

ok:true, criterionCount:2, bySeverity:{blocker:2}, detectedDeliverableKind:markdown ✓ no findings, as advertised

estimate_cost

{tool:"check_acceptance"}

{atomic:"15000", usd:"0.015"} ✓ correct ladder price

verify_rubric_report

unsigned report shape

valid:false, signed:false, error:"…origin cannot be attributed" ✓ honest on unsigned (§0.1)

get_artifact

{artifactId:"art_testdoesnotexist123"}

found:false, reason:"No such artifact…", not isError ✓ miss is an answer (§0.5)

The bare-{} sweep (§13 item 2) and wrong-but-plausible sweep (§13 item 4) are covered by the test suite (118 passing). The capabilities diff (§13 item 6) is green: every name in tools/list appears in rubric_capabilities.

Hospitality for the OKX listing validator

onchainos payment quote https://mcp.evidiq.dev/rubric/mcp --method POST returns ok:true (§13 item 11). Quoted per-tool: check_schema 5000 (amountHuman:"0.005", tokenSymbol:"USD₮0", recipient: family 0x2a8efe30…). The SDK-path liveness check (§13 item 8) is also green: a malformed envelope is refused locally in ~85 ms; a well-formed envelope with a bad signature reaches the OKX facilitator and comes back rejected as invalid_signature in ~253 ms (RUNBOOK §29-A). Reproduce the live proofs with node paid-call-proof.mjs (default: check_schema; override with TOOL=verify_grounding node paid-call-proof.mjs, etc.). Full-suite spend across all five paid tools: about 0.08 USDT0 (the budget PLAN §13 set).

Architecture

HTTP (node:http) ── /health, /x402, /skill.md, /mcp (HEAD → 402)
                       │
                       ▼
                 withX402Gate ──── decodePaymentHeader ── verifyPaymentLocal (local preflight)
                       │                    │
                       │                    └── OkxSdkVerifier.verify + settle (official SDK)
                       │                           │
                       │                           └── poll getSettleStatus on pending/timeout
                       ▼
              rawHandler (JSON-RPC dispatch)
                       │
                       ▼
              createServer() — 5 paid tools + 5 free tools
                       │
                       ▼
       lib/rubric/ — spec, criteria (one fn per type), verdict, grounding,
                     schema (bounded AJV), report (canonical digest + EIP-191),
                     attest (EIP-191 + 0G anchor), artifacts (TTL cache),
                     secrets (credential refusal).

License

MIT © 2026 EVIDIQ. See LICENSE.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/evidiq/evidiq-rubric-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server