Open-source client libraries for Kaval — the Node & Python SDKs and the MCP server (thin clients for the hosted API).
claude mcp add kaval-clients -- npx -y @usekaval/mcp{
"mcpServers": {
"kaval-clients": {
"command": "npx",
"args": ["-y", "@usekaval/mcp"],
"env": {
"KAVAL_API_KEY": "<kaval_api_key>"
}
}
}
}KAVAL_API_KEYResumen de MCP Servers
# Kaval clients
Open-source client libraries for [Kaval](https://usekaval.com). **Before an agent acts, send Kaval
the action. Kaval identifies the facts that action depends on, checks them against the sources it
watches, and answers `ALLOW`, `REVIEW`, or `BLOCK` with a signed receipt.**
**Policy engines decide whether an action is permitted under the rules; Kaval verifies whether the
facts those rules depend on are still true.**
These are **thin HTTP clients** for the hosted Kaval API (`https://api.usekaval.com`). Create an API
key at [usekaval.com](https://usekaval.com).
| Package | Language | Install | Source |
| ------------------------------- | ----------------- | ----------------------- | ---------------------------- |
| [`@usekaval/kaval`](sdks/node) | Node / TypeScript | `npm i @usekaval/kaval` | [sdks/node](sdks/node) |
| [`kaval`](sdks/python) | Python | `pip install kaval` | [sdks/python](sdks/python) |
| [`@usekaval/mcp`](packages/mcp) | MCP server | `npx -y @usekaval/mcp` | [packages/mcp](packages/mcp) |
> **0.6 is a breaking release.** Nine MCP tools collapsed to seven, and the whole verification
> surface collapsed to one call. Every removed endpoint now answers a structured
> `410 {"error":"tool_retired","replacement":"/v1/check"}`, and the clients translate that into an
> error that names `check` by name. See [Migrating from 0.5](#migrating-from-05).
## One call
```ts
import { Kaval } from "@usekaval/kaval";
const kaval = new Kaval({ apiKey: process.env.KAVAL_API_KEY });
const result = await kaval.check({
action: "Approve this prior-authorization request at the in-network rate",
context: "payer: Aetna; CPT 12345; plan HMO",
materiality: "critical",
});
if (result.decision !== "ALLOW") {
// REVIEW is never permission to act.
holdForHuman(result.facts.filter((fact) => fact.status !== "holds"));
}
```
```py
import os
from kaval import KavalClient
kaval = KavalClient(api_key=os.environ["KAVAL_API_KEY"])
result = kaval.check(action="Approve this prior-authorization request at the in-network rate")
if result["decision"] != "ALLOW":
hold_for_human(result["facts"])
```
What comes back:
| field | meaning |
| -------------- | -------------------------------------------------------------------------------------------- |
| `decision` | `ALLOW` (every material fact holds on fresh evidence) · `REVIEW` · `BLOCK` |
| `reason_codes` | why, from a closed eight-code taxonomy |
| `facts[]` | one row per fact: `status` (`holds`/`changed`/`unknown`), `materiality`, and the sources it rests on |
| `receipt` | `{ id, signature, signed_at }` — fetch the full signed document with `getReceipt(id)` |
| `latency_ms` | `{ compile, lookup, live, total }` |
A check on facts a watched source already covers is a database read and returns in tens of
milliseconds. A **cold** check does live research before it answers — search, fetch, adjudicate —
and the server lets that run for up to 100s by default, so give the call room. `mode: "fast"`
(equivalently `max_wait_ms: 0`) skips research entirely and reports anything it could not settle as
`unknown`, which is `REVIEW`.
The decision table is published, so the receipt's fact list re-derives the verdict offline, and the
Ed25519 public keys are served unauthenticated at `GET /v1/proof-verification-keys/:kid` — checking a
receipt needs no Kaval account and no API key.
The verifier that does it for you ships **inside the SDK**: `@usekaval/kaval/verify` is a
dependency-free subpath export of `@usekaval/kaval`, and the same package ships a
`kaval-receipt-verify` CLI. Neither needs a Kaval account, an API key, or Kaval's database; the only
request either can make is for the public keyset, and that one is optional. Hand it a receipt and a
keyset — archived beside the receipt, or fetched from the key endpoint — and it answers three
questions **separately**:
1. **Cryptographic validity** — does the Ed25519 signature cover the exact canonical unsigned bytes?
2. **Key trust** — is that `key_id` active or benignly retired, rather than revoked or compromised?
3. **Freshness** — `fresh`, `recheck_due`, `expired`, `not_yet_issued`, or `unknown`.
A valid signature proves who sealed those exact bytes. It does not prove the claim is still true, or
that the key is still trusted, which is why the three answers never collapse into one boolean.
```ts
import { extractReceipt, parseJsonStrict, verifyReceipt } from "@usekaval/kaval/verify";
const receipt = extractReceipt(parseJsonStrict(receiptText));
const result = verifyReceipt(receipt, parseJsonStrict(keysetText));
result.cryptographic.valid; // the signature covers these exact canonical bytes
result.key.trusted; // the signing key is not revoked or compromised
result.freshness.status; // separate fact — a check receipt carries no expiry, so `unknown`
```
```bash
# Reproducible audit: archive the keyset beside the receipt and stay entirely offline.
npx -p @usekaval/kaval kaval-receipt-verify verify receipt.json --keyset keys.json
# Or resolve the key over HTTPS from the unauthenticated endpoint.
npx -p @usekaval/kaval kaval-receipt-verify verify receipt.json \
--key-url https://api.usekaval.com/v1/proof-verification-keys
```
Exit `0` means the signature is valid and the key is trusted; a stale receipt still exits `0`,
because freshness is a separate fact — pass `--require-fresh` to make anything but `fresh` non-zero.
Exit `1` is a completed but unaccepted verification, `2` an input, I/O, or discovery failure. Parse
untrusted receipt text with `parseJsonStrict`, not `JSON.parse`: duplicate members and lossy numbers
are evidence, and `JSON.parse` throws that evidence away before any verifier can see it.
## Keep it warm: watch the sources
A check is a database read when the facts it needs are already backed by a watched source, and a
bounded research run when they are not. Registering the *name* of an authority is usually enough:
```ts
await kaval.addSource({
kind: "entity",
name: "Aetna",
intent: "payer policy bulletins",
});
```
Kaval resolves that to the pages that publish it, polls them adaptively (slower when nothing
changes, faster when it does), and re-evaluates the dependent facts when they move. `kind: "url"`
watches one page; `kind: "push"` is a document your own system sends in with `sendEvent()`. You do
not have to register first — a source a check cites is auto-watched — but registering ahead of time
is what makes the *first* check on a fact fast.
Naming an entity is what enqueues the discovery that works out *how* to acquire the pages behind it.
A `kind: "url"` source registered directly does not get one, so `recompileSource(id)` is how you ask
for one — and it is also the only way back once a source's acquisition plan breaks. It answers `202
{ source_id, job_id, created }`; `created: false` means an open job already covered it.
## Close the loop: subscribe to deltas
Watching is only half of it. Subscribe to `fact_state.delta` and Kaval pushes you what changed and
what it flipped, instead of you discovering it on the next check:
```ts
const { webhook_verification } = await kaval.subscribeFactStateDeltas({
callback_url: "https://your-app.example.com/hooks/kaval",
external_scope_ids: ["plan:HMO"], // optional filter
});
// Store webhook_verification.secret — it is shown exactly once, and it is how you
// authenticate every inbound delivery.
```
Each delivery names the source, the old and new content hashes, a diff summary, and the facts whose
state changed — `{fingerprint, text, old_state → new_state, basis}` — plus a pointer to the receipt
covering the re-evaluation. Manage subscriptions with `listWebhooks()`, `setWebhookEnabled()`,
`deleteWebhook()`, and re-drive a dead letter with `replayWebhookDelivery()`.
## Push your own documents
For documents Kaval cannot fetch — a contract, an internal policy, a customer upload — push the new
version and let the background loop do the rest:
```ts
const { changed, facts_pending_review } = await kaval.sendEvent({
namespace: "contracts",
document_id: "msa-2026-07",
content: extractedText,
scope_keys: ["contract:msa-2026-07"],
});
```
Kaval diffs it against the previous version, marks the dependent facts stale, re-evaluates them, and
emits the delta webhook. Checks that land mid-re-evaluation honestly return `REVIEW`.
## MCP
```bash
KAVAL_API_KEY=kv_live_… npx -y @usekaval/mcp
```
Seven tools over stdio: **`check`** (the one that does the work), `get_receipt` (the full signed
document behind a verdict), `add_source` / `list_sources` / `remove_source`, `report_outcome`, and
the deprecated `verify` pilot alias. See [packages/mcp](packages/mcp).
MCP clients cancel a tool call well before 100s, so the server narrows `check`'s research budget to
fit inside that envelope instead of inheriting the full default. A cold check over MCP therefore
comes back `REVIEW` more often than the same call through an SDK — register the sources it depends
on and the warm path removes the difference.
## Migrating from 0.5
Everything below folded into `check`. The old routes answer `410 tool_retired`; the clients raise
`KavalRetiredError` (Node) / `KavalRetiredError` (Python) naming the replacement, and the MCP server
returns `{"error":"tool_retired"}` with a message telling the agent to call `check`.
### MCP tools
| 0.5 tool | 0.6 |
| ------------------------------- | ------------------------------------------------------------------------------------- |
| `currentness_check` Lo que la gente pregunta sobre kaval-clients
¿Qué es LufeMC/kaval-clients?
+
LufeMC/kaval-clients es mcp servers para el ecosistema de Claude AI. Open-source client libraries for Kaval — the Node & Python SDKs and the MCP server (thin clients for the hosted API). Tiene 0 estrellas en GitHub y se actualizó por última vez today.
¿Cómo se instala kaval-clients?
+
Puedes instalar kaval-clients clonando el repositorio (https://github.com/LufeMC/kaval-clients) o siguiendo las instrucciones del README en GitHub. ClaudeWave también te ofrece bloques de instalación rápida en esta misma página.
¿Es seguro usar LufeMC/kaval-clients?
+
LufeMC/kaval-clients aún no ha sido auditado por nuestro agente de seguridad. Revisa el repositorio original en GitHub antes de usarlo en producción.
¿Quién mantiene LufeMC/kaval-clients?
+
LufeMC/kaval-clients es mantenido por LufeMC. La última actividad registrada en GitHub es de today, con 0 issues abiertos.
¿Hay alternativas a kaval-clients?
+
Sí. En ClaudeWave puedes explorar mcp servers similares en /categories/mcp, ordenados por popularidad o actividad reciente.
Despliega kaval-clients en tu cloud
Lleva este repo a producción en minutos. Cada plataforma genera su propio entorno con variables de entorno editables.
¿Mantienes este repo? Añade un badge a tu README
Pega el badge en tu README de GitHub para mostrar que está auditado por ClaudeWave. Cada badge enlaza de vuelta a esta página y muestra el Trust Score actual.
[](https://claudewave.com/repo/lufemc-kaval-clients)<a href="https://claudewave.com/repo/lufemc-kaval-clients"><img src="https://claudewave.com/api/badge/lufemc-kaval-clients" alt="Featured on ClaudeWave: LufeMC/kaval-clients" width="320" height="64" /></a>Más 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.
The fastest path to AI-powered full stack observability, even for lean teams.
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!