dVeracity Semantic MCP server
Official# dVeracity Semantic MCP server
[](https://smithery.ai/servers/ajvdvoort/dveracity)
Gives any MCP-capable AI agent (Claude Code, Cursor, custom agents) metered access
to the **dVeracity Semantic API** — natural-language queries over the
verified-emissions knowledge graph (Open Footprint / PPDM / OGMP-methane) — and
**VaaS** standards validation.
## Prerequisites
1. A free **Explorer** account (OAuth) or a paid plan: https://dveracity.com/pricing
— Explorer includes API-key management and a monthly metered-call allowance.
2. An **API key** (`dvrc_…`): `POST /api/v1/api-keys` (or the dashboard)
3. API **credits** for calls beyond the free allowance: `POST /api/v1/vaas/credits/purchase`
(or ask the agent to use `billing_purchase`)
The machine-readable service contract lives at `GET /api/v1/semantic/manifest`
(public, no auth).
## Install
From this directory: `npm install`
### Claude Code
```bash
claude mcp add dveracity \
-e DVERACITY_API_KEY=dvrc_yourkey \
-- node /path/to/dVE/mcp/semantic-mcp/index.js
```
### Generic MCP JSON config (Cursor, etc.)
```json
{
"mcpServers": {
"dveracity": {
"command": "node",
"args": ["/path/to/dVE/mcp/semantic-mcp/index.js"],
"env": { "DVERACITY_API_KEY": "dvrc_yourkey" }
}
}
}
```
Optional: `DVERACITY_API_URL` overrides the API base URL (defaults to prod).
### KERI mode — verified agent identity (optional)
If the agent holds a **dVeracity Agent Authorization credential** (an ACDC issued
by its Legal Entity, chained to the Legal Entity's vLEI — see
`elm/docs/VLEI_AGENT_TOKENS_DESIGN.md`), set:
```
DVERACITY_KERI_AID=<the agent's AID (credential issuee)>
DVERACITY_KERI_PRESENTATION=/path/to/agent-credential.cesr # self-contained CESR
```
The server then authenticates the agent by verifiable presentation
(challenge → exchange → 1-hour session, refreshed transparently) and attaches
`X-Keri-Session` to every call: the API key keeps carrying **billing**, the KERI
session adds **verified identity** — every metered call is attributed to the
agent AID and Legal Entity LEI in dVeracity's audit trail. The `keri_identity`
tool (free) shows the active identity. Scope denials (a credential that doesn't
carry e.g. `semantic:query`) surface as actionable errors naming the carried
scopes. Signify-based nonce signing is a planned enhancement.
## Tools
| Tool | Cost | What it does |
|---|---|---|
| `semantic_query` | credits | Natural-language question over the verified-emissions knowledge graph |
| `semantic_templates` | free | Catalog of supported query templates |
| `credits_balance` | free | Remaining credit balance |
| `list_standards` | free | Standards VaaS can validate against |
| `validate_data` | credits | Validate a payload against a supported standard |
| `keri_identity` | free | This agent's verified vLEI identity, when KERI mode is configured |
Every tool definition carries `_meta.pricing`:
`{ "unit": "credit", "amount": <int>, "usd": <decimal> }`, with `amount` 0 for a
free tool. A plan-gated tool also carries `"plan_required": "<tier>"` — 0
credits on `compliance_policies` or `compliance_evaluate` means "included in
the VaaS plan", not "free for any account".
### Billing
| Tool | Cost | What it does |
|---|---|---|
| `billing_plans` | free | Plans, credit packs, per-credit price and the free allowance (anonymous on the hosted endpoint) |
| `billing_status` | free | This account's plan, balance and remaining free allowance |
| `billing_purchase` | free | One-time credit checkout: returns a `checkoutUrl` for a human, plus a `paymentLink` when the deployment configures one |
| `billing_subscribe` | free | Membership checkout: returns `checkoutUrl` only; recurring charges need human approval |
### Open Footprint canonical model
Design-time tools for building an application on the Open Footprint standard.
Reading the model is free; only the check at the end is metered.
| Tool | Cost | What it does |
|---|---|---|
| `ofp_models` | free | The model domains (nine as of openfootprint b60a24e), and which database dialects have published DDL |
| `ofp_search_entities` | free | Search the canonical entities (262 entries as of b60a24e) by name, description or field |
| `ofp_entity` | free | One entity in full: fields, types, keys, relationships, physical table |
| `ofp_sectors` | free | Industry sectors, each with a status |
| `ofp_sector` | free | One sector, with its reference artifacts |
| `ofp_policies` | free | A sector's Rego guardrails, or an explicit "none published" |
| `compliance_policies` | paid plan | Approved regulatory compliance policies (rules extracted from a regulation, admin-approved); never drafts |
| `compliance_policy` | paid plan | One approved policy: Rego, extracted rules, inputs, provenance |
| `compliance_policy_register` | paid plan | The published release register: every provision, its legal reference, review status and why it is or is not enforced |
| `compliance_evaluate` | paid plan | Execute one named approved policy against a record: four-valued decision, per-check results, missing inputs, remediation, coverage. Stores an audit record |
| `compliance_evaluation` | paid plan | Read back one stored evaluation — the caller's own only |
| `ofp_validate` | credits | Check a payload against the model and, optionally, sector guardrails |
| `ofp_semantics` | free | O-DEF semantic codes, for aligning another system's fields onto the model |
| `ofp_semantic_code` | free | Which canonical fields carry one code — the reverse lookup a connector needs |
| `ofp_model_provenance` | free | Which snapshot of the standard this deployment serves |
Two behaviours are deliberate and worth knowing before you build against them.
**Ambiguous entity names fail rather than resolve.** dozens of entity names (the `ofp_models` summary reports the count as `ambiguousNameCount`)
are defined in more than one domain — `Country` is in four. `ofp_entity` without
a `domain` returns an error listing the candidates instead of picking one. Pass
`domain` whenever you know it.
**Semantic codes vary in usefulness.** Most canonical fields carry an O-DEF code,
but a code shared by many fields is a generic classification, and aligning to one
asserts almost nothing. The `ofp_models` summary reports the real shape under
`precision` — `preciseFieldThreshold` (the `fieldCount` at or below which a code
is precise rather than generic), `fieldsOnPreciseCodes` and
`preciseShareOfAnnotated` — and those follow the vendored snapshot rather than
this document. Every code is returned with its `fieldCount`: check it before
aligning to one, and pass `maxFieldCount` at that threshold to `ofp_semantics` to
see only the precise ones.
**"Nothing published" is an answer, not an error.** Most sectors are named in the
taxonomy but have no reference implementation, and only seven publish policy
guardrails. `ofp_policies` on such a sector returns `published: false` with a
reason, and `ofp_validate` reports `policy.ran: false`. Both mean *no rules are
published*, never *there are no constraints* — a payload checked for structure
alone is not a compliant one, and should not be described as one.
A fourth outcome, `unevaluable`, means the sector's rules ran but every rule that came back false reads an input the payload does not carry (`policy.missingInputs`, e.g. `co2e_kg`, `direction`, `counterparty_industry`). Those are e-ledger record fields, not canonical Open Footprint field names — `ofp_policies` lists them per policy under `inputs`. Unevaluable is neither a pass nor a breach, and `valid` is `null`.
### What `ofp_validate` checks, and what it does not
The response is a contract, not a verdict. Every call reports the check
catalogue in two lists: `checked` (what ran) and `checks_not_run` (what did not,
each with a `reason` and usually a `detail`). Read both before describing a
payload as anything.
| Check | Status | Reason reported when it does not run |
|---|---|---|
| `schema` — presence, primary key, types, declared constraints | runs | `entity_has_no_fields` |
| `value_range` | **runs** where the model declares a bound: a quantity outside the declared minimum or maximum is rejected. Which fields declare a bound moves with the vendored snapshot, so it fires on some entities and not others — read `checked` and `schema.coverage.constraintsDeclared` per call | `no_range_declared`, `no_numeric_fields` |
| `unit_coherence` | not run | `not_implemented` |
| `calculation_reproducibility` | **runs** where the record carries all three of a quantity, an emission factor value and a stated CO2e result: `quantity × factor = result` is reproduced within 1%, and a record that contradicts itself is rejected | `no_calculation_inputs`, `calculation_inputs_incomplete`, `ambiguous_calculation_inputs`, `calculation_inputs_unparseable` |
| `temporal_consistency` | **runs**: a validity period whose end precedes its start is rejected | `no_validity_pair`, `validity_pair_incomplete`, `validity_values_unparseable` |
| `enum_membership` | **runs** where the vocabulary publishes its members: a well-formed key for a referent that does not exist is rejected | `no_members_published`, `no_reference_field_supplied`, `reference_ids_unparseable` |
| `referential_integrity` | not run: keys are pattern- and member-checked, never resolved against live records | `no_data_plane` |
| `factor_provenance` | not run | `not_implemented` |
| `materiality` | not run | `not_implemented` |
| `sector_policy` | runs when a sector with published guardrails covers the record type | `no_sector_supplied`, `no_policy_published`, `policy_source_missing`, `policy_package_unparseable`, `not_applicable`, `unevaluable` |
Any check can also report `schema_not_run`, which means the structural check it
builds on could not run at all.
`calculation_reproducibility` is self-referential and nothing more. It resolves
its three inputs from the model — the field carrying the emission-measure role, a
`… Factor Value` (never a `… Factor ID`, which is a reference to a factor rather
than a number to multiply by) and a `… CO2e …` result — or, for a record in the
vocabulary the guardrails read, from `quantity_value` / `emission_factor_value` /
`co2e_kg`. A slot the record fills more than once is `ambiguous_calculation_inputs`
and the check declines rather than picks. The 1% tolerance is the one
`CalculationAccuracy` in the verification engine and `calc_tolerance` in the
ISO 14064 quantification policy use, so the identity has one tolerance on every
surface. What it catches is a record whose own numbers do not multiply out. It
does **not** say the factor is the right one for this source and period
(`factor_provenance`, still not run) or that the quantity came from the meter it
claims (the data plane, still not run) — a reproduced calculation is a
self-consistent record, not a verified one.
- `schemaValid` is the structural verdict (`null` if the structural check could not run).
- `assuranceLevel` names the depth earned: `schema-only`, `schema-and-value`,
`schema-value-and-policy` or `full`. Each level needs every check beneath it,
so guardrails without a value check is still `schema-only`. The ladder is gated
on `value_range` — a bound the model itself declares — so a record that
reproduced its own arithmetic without one still reads `schema-only`;
`checked` is where that shows up, and it is the more precise of the two.
- Every violation and warning carries `severity` (`error` | `warning`). Rule ids
are stable: `required_field_missing`, `pattern_mismatch`, `format_mismatch`,
`type_mismatch`, `primary_key_missing`, `enum_violation`, `value_range` (with
`bound`: `minimum` | `maximum`), `temporal_consistency`, `enum_membership`,
`calculation_reproducibility` (with `expected`, `received`, `tolerance` and the
three `inputs` it read), …
`unknown_field` is a per-field warning and stays one.
- `unknown_field` warnings carry `didYouMean`: up to three canonical candidates,
each with a confidence and the O-DEF code that field carries. The list is empty
when nothing in the model is a plausible match — a key that belongs to another
system stays a key that belongs to another system. `schema.normalisations`
separately discloses keys that resolved through case and separator folding.
- `advisories` carries findings that are true of the payload but do not bear on
its validity. Today that is `deprecated_field`: a field the model has retired,
severity `warning`, carrying `modelDescription` verbatim and a `successor` when
the model names one. Where the model names no replacement the key is absent
rather than guessed. An advisory never changes `schemaValid`, and the same
entries also appear in `schema.warnings`.
- `valid` is **deprecated** (see `deprecations` in the response). It keeps its
0.5.0 meaning through the 0.5.x line and is removed no earlier than 0.6.0.
Read `schemaValid` instead.
## Billing behavior (for agents)
Metered calls return an **HTTP 402** when the account is out of credits. The
server surfaces this as a tool error that tells the agent to ask its **human
operator** to purchase credits or upgrade — agents should relay that message and
stop, not retry.
## Retrying metered calls safely
Metered tools advertise `_meta.idempotency` (`{ header, parameter,
maxLength }`). Send the key either as the `Idempotency-Key` header on the `/mcp`
request or as `params._meta.idempotencyKey` on the `tools/call` itself. The
hosted endpoint forwards it to the metered REST call: the same key is charged
once and replays the original result, and a different body under the same key is
refused. If both the header and the parameter are present they must match; a
mismatch is refused with a JSON-RPC `-32602` error before the tool runs. Tools
without `_meta.idempotency` are not metered, so a key has no effect on them.
## Test
`npm test` (no network; the HTTP layer is stubbed).
## Where it is listed
- [Smithery](https://smithery.ai/servers/ajvdvoort/dveracity) — one-click add for Smithery toolbox users
- [Official MCP Registry](https://registry.modelcontextprotocol.io/v0/servers?search=dveracity) — `com.dveracity/semantic-mcp`
- [npm](https://www.npmjs.com/package/@dveracity/semantic-mcp) — `@dveracity/semantic-mcp`
TDQS
Scored across 16 tools
Most tools are clearly separated by resource and action: list/get pairs (ofp_sectors/ofp_sector), search/fetch pairs (ofp_search_entities/ofp_entity), and query/template pairs (semantic_query/semantic_templates). The main confusion risk is validate_data vs ofp_validate, which both validate payloads for credits but target different rule sets, though the descriptions do clarify that boundary.
The ofp_ prefix on ten tools creates a strong family signal, and plural/singular pairs (ofp_sectors/ofp_sector, ofp_semantics/ofp_semantic_code) encode list-vs-get nicely. However, the convention isn't uniform: the entity list is ofp_search_entities rather than ofp_entities, and non-OFP tools mix verb_noun (validate_data, list_standards), adjective_noun (semantic_query), and noun_noun (credits_balance) patterns. Overall readable but stylistically mixed.
At 16 tools the server sits just above the ideal range, but the count is justified by the breadth of the Open Footprint domain it exposes: model exploration, sector/policy lookups, semantic codes, validation, and account utilities. The OFP family alone needs list/get pairs for entities, sectors, and codes, plus validation and provenance tools. No tool feels redundant, though a few are niche.
The surface covers the read-and-validate lifecycle for the Open Footprint domain: discover the model, find entities, list sectors and policies, validate payloads, and query the knowledge graph, with free list_standards and semantic_templates acting as pre-paid discovery steps. The main gaps are that entities can only be found via search (no full enumeration of the 239) and there is no validation-history or standard-detail tool. These are workaround-able rather than dead ends.