MCP tools reference
Every tool the DSAIL MCP server publishes — name, description and arguments — as a connected model sees them.
Server name: dsail. Endpoint: https://agents.jaxon.ai/mcp (streamable HTTP, OAuth; the door claude.ai and ChatGPT dial). A coding agent uses the stdio proxy dsail mcp from the dsail package instead, which forwards to the REST door and publishes these same definitions.
Every description opens with the problem the tool solves, in the same words this site uses; the full text of each is what a connected model reads. dsail_review exists only on the chat-client door, where there is a widget to render; the other ten are on every transport.
The server instructions a model receives on connection are published separately.
dsail_compile
Turn a written policy into rules a program can check.
Compile DSAIL ruleset source. Returns the content hash that addresses this exact ruleset, the claim manifest (every claim with its question, type and vocabulary), the claim JSON schema, and the validation contract check will enforce — so you know what will be rejected before you submit anything.
It also returns review: a plain-text rendering of every question this ruleset asks and every decision it makes. Show it to the user verbatim before calling dsail_record_approval. An approval binds a person's sign-off to an exact hash, and it is worth something only if they could see what they were signing.
This tool renders nothing. Iterate on it freely — on failure the result carries diagnostics and the grammar; fix the source and call again — and when it succeeds, call dsail_review(ruleset_hash) ONCE to present the ruleset to the user in the review widget.
When a message composed by the review widget asks you to commit edited source, pass that source byte-for-byte with the parent_hash it names — it is the user's own edit — then save it under the name given (dsail_save_ruleset) and review the new hash.
label (optional, inert): {domain: <level-one id>, topic: <short phrase>, confidence: high|medium|low} — your own reading of what domain this policy belongs to, stored as vocabulary ids only. Or {declined: true}. See the server instructions.
DSAIL ruleset grammar (v1.3), the subset this service compiles to SMT:
version 1.3; -- optional; 1.2 and 1.3 are accepted
declare <name> as boolean; -- a yes/no claim declare <name> as numeric; -- a number; may carry a unit at the use site declare <name> as enum ["a","b"]; -- ORDERED vocabulary (comparable with < >) declare <name> as enum {"a","b"}; -- UNORDERED vocabulary (== and != only) declare local <name> as boolean; -- rule-local; NOT a claim, never extracted
assert <name> { <expr> }; -- the rule. Holds => compliant. assert <name> [pessimistic] { ... }; -- unknown-resolution policy: -- optimistic | pessimistic | neutral
Enum members are double-quoted strings. Every statement ends with a semicolon. You do not write let bindings for claim values — this service injects them from the claim dictionary you submit to check.
Expressions: comparison == != < <= > >= (thresholds are STRICT: > means strictly greater, NOT at-or-above. Write >= if you mean at-or-above. Equality is ==, not =.) arithmetic + - * / % LOGIC IS FUNCTION-STYLE, NOT INFIX. There is no and/or/not keyword: And(a, b, ...) Or(a, b, ...) Not(a) Xor(a, b) Implies(a, b) If(cond, a, b) conditional IF <cond> THEN <expr> [ELSE <expr>] END CASE <subject> OF "x": <expr>, "y": <expr>, DEFAULT: <expr> END quantifiers ForAll(x in s, p) Exists(x in s, p) AtLeast(n, x in s, p) AtMost(n, x in s, p) ExactlyOne(x in s, p) CountWhere(x in s, p) sets IsMember(v, s) IsSubset(a, b) Union(a, b) Intersect(a, b) Difference(a, b)
What a check answers. Every assertion gets its own result, and that is the whole answer:
TRUE the assertion holds FALSE the assertion is violated (the solver returns a counterexample) UNKNOWN a claim it needs was submitted as unknown, or is unconstrained AMBIGUOUS its evidence was contradicted
There is no combined verdict, no severity and no ranking. What a violated assertion should cost — a refusal, a human review, a note in a file — is a decision for the system reading the result, and this service does not make it.
Host annotations. These are ordinary DSAIL comments, invisible to the compiler, read by this service. Every one of them describes a CLAIM — what to ask for it and what a valid answer looks like. None of them decides an outcome:
// @ask <claim> What is ...? -- the question shown to an extractor // @context <claim> <text> -- extra context for the extractor // @range <claim> 0..100 -- numeric bounds, enforced at check time // @unit <claim> USD -- expected unit for a numeric claim
@effect, @effect-default and @consistency are NOT recognised. A ruleset carrying one does not compile.
Worked example:
version 1.3; // @ask loanAmount What is the loan principal, in USD? // @unit loanAmount USD // @range loanAmount 0..100000000 declare loanAmount as numeric; // @ask hasAppraisal Does the file contain a completed appraisal? declare hasAppraisal as boolean; declare riskTier as enum ["low","medium","high"];
assert within_cap { loanAmount <= 1000000 }; assert large_but_documented { Or(loanAmount <= 500000, hasAppraisal) }; assert tier_permitted { riskTier != "high" };
Arguments:
label: anyparent_hash: anysource: string (required)
dsail_check
Check extracted values against a written policy and get the same answer every time, with the rule that decided, with a counterexample when one is violated.
Validate a claim dictionary and solve it against a ruleset, in one call.
Pass ruleset_hash (from dsail_compile) or source. claims maps every declared claim name to a value. A claim you could not determine is submitted as the string "unknown" — never a guess, never omitted.
Returns every rule with the assertions it states, each carrying the engine's own result — TRUE (it holds), FALSE (it is violated), UNKNOWN (a claim it needs was not determined) or AMBIGUOUS (its evidence was contradicted) — plus the solver's counterexample on a FALSE, and which claims had no value. If the dictionary is rejected, every failing field is named at once as {field, expected, received}.
There is no overall verdict, by design. Report what the rules concluded in those four words and attribute them to the rules; deciding what a FALSE costs is the caller's, not this service's and not yours.
Arguments:
claims: object (required)label: anyruleset_hash: anysource: any
dsail_get_prompt_pack
Get the exact questions to ask a document so the answers can be checked against a written policy.
Get the extraction contract for a ruleset: one prompt per claim (question, answer format, vocabulary, unit), the claim JSON schema, and the exact validation rules. Run these prompts on your own model, then submit the assembled claim dictionary to dsail_check.
Arguments:
ruleset_hash: anysource: any
dsail_save_ruleset
Keep a written policy's rules as a named, immutable revision, so a later check answers exactly as this one did.
Store a named, immutable revision of a ruleset. Saving over an existing name links the previous hash as this revision's parent — nothing is ever edited in place. Scoped to your connector's namespace: a ruleset saved here is reachable from any later conversation through this same connection, and from no other.
label (optional, inert): {domain: <level-one id>, topic: <short phrase>, confidence: high|medium|low}, or {declined: true} — see the server instructions. Stored as vocabulary ids only.
Arguments:
label: anyname: string (required)parent_hash: anysource: string (required)
dsail_load_ruleset
Show a saved policy ruleset exactly as it was stored.
Load a named ruleset: its source, its hash, its claim manifest and claim schema, its diagnostics, its revision chain and any approvals bound to it — the same contract dsail_compile returns, for bytes already stored.
THIS IS HOW A STORED RULESET IS SHOWN. Never re-compile source you are holding in order to display something already saved: your copy and the stored text are different objects the moment either drifts, and an approval follows the stored bytes. When a compile really is needed — the user asked for an edit — pass the source this returned BYTE-FOR-BYTE, with parent_hash set to the hash it came with. On a client that renders MCP Apps this opens the ruleset in the review widget.
Arguments:
name: string (required)
dsail_list_rulesets
See which written policies already have rules a program can check.
The rulesets saved for this connection. On a client that renders MCP Apps this opens the ruleset library: one row per ruleset with its name, what it decides, how many questions it asks, how many rules it applies, whether anyone has approved it and when it last changed. A row opens that ruleset in the review widget.
Do not restate the rows as prose afterwards — the user is looking at them. Never show the namespace value to a person: it is a service-internal identifier, not a name.
Arguments:
- object
dsail_record_approval
Bind a person's sign-off to the exact rules a program will check.
Bind a human approval to one exact ruleset hash. An approval follows the bytes, not the name: a later revision is not covered by it.
Pass name to save the ruleset under that name as part of approving it. The name is bound BEFORE the approval is recorded, so what a person approves is in their library afterwards instead of being reachable only by a hash they would have to have kept. Omit name only for a hash already saved under one.
When a message composed by the review widget asks for this call, make it with exactly the ruleset_hash, approver, note and name it gives — that message is the user's own approval, relayed because their chat client cannot call tools from inside the widget — then call dsail_review on the same hash so they see it recorded.
Arguments:
approver: string (required)label: anyname: anynote: anyruleset_hash: string (required)
dsail_get_account_status
Know how much of this credential's allowance is used before a limit interrupts a check.
This credential's entitlement and usage position, so you can warn the user before a limit surprises them. Permissive stub in this release: the shape is final, the numbers arrive with metering enforcement. Also reports the current application-domain vocabulary version and whether this account may label rulesets.
Arguments:
- object
dsail_unit_library
Find out whether two units convert before a rule compares them, so the check does not answer UNKNOWN forever.
What this project can convert between. DSAIL numerics are dimensional: a rule comparing a value in one unit against a literal in another resolves only if a converter bridges them, and if none does, that rule answers UNKNOWN for every claim dictionary, forever. Call this before writing rules that mix units. Pass from_unit and to_unit to ask about one pair and get one of: same, convertible (with the factor), ambiguous (an interval-valued conversion such as months to days, which makes the comparison honestly UNKNOWN), unknown_unit (in no loaded library, so probably a typo), or no_path (both known, nothing bridges them). Currencies ship as alias groups with NO exchange rates: a rate is not a constant.
Arguments:
from_unit: anyto_unit: any
dsail_add_unit_converter
Give a policy check the conversion it needs between two units, with a factor a person supplied.
Add a unit converter to this project so rules mixing the two units resolve instead of answering UNKNOWN. to = from * factor + offset.
YOU MUST NOT INVENT THE FACTOR. A conversion rate is a policy decision with legal weight - an exchange rate fixed by a contract, a density fixed by a spec - and a number recalled from training would enter results as though a person had decided it. Ask the user for the figure, pass what they give you, and name the source in attribution.
This changes results. A check is reproducible from the PAIR (ruleset_hash, unit_library_hash); this edit changes the second half, so an approval recorded against the previous library no longer describes what the ruleset does. Say so when you report the result.
Arguments:
attribution: anyfactor: number (required)from_unit: string (required)offset: numberto_unit: string (required)
dsail_review
Show a person the rules a program will check, so they can approve exactly those bytes.
Present a compiled ruleset to the user in the inline review widget: source (editable), claim manifest, diff against the parent revision, an exercise panel to test claim values including an explicit "unknown", the approve control, and the project's unit-conversion library.
Call it ONCE, with the ruleset_hash a successful dsail_compile returned, after the source compiles clean — never for a failed compile, and not on every iteration. Call it again only for a later revision you want the user to see.
Returns the same payload as dsail_compile for that hash, plus the name it is saved under (null when nothing names it yet) and every approval bound to it.
Arguments:
ruleset_hash: string (required) — the hash a successfuldsail_compilereturned.