DSAIL Quickstarts Guides Compare REST MCP tools Errors Legal Support

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.

Use when two reviewers apply the same written criteria differently, or a handbook's decided rules need repeatable application. The person has already decided the policy; DSAIL formalises decisions, it does not draft policy, resolve conflicting policies or interpret an ambiguous regulation.

Compiles 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 a check will enforce — so what will be rejected is known before anything is submitted.

Also returns review: a plain-text rendering of every question this ruleset asks and every decision it makes. It is what a person signs when they approve the hash, so it is worth showing them before an approval is recorded.

Renders nothing on its own, so iterating on it costs nothing. On failure the result carries diagnostics naming the offending line, plus the grammar below as hint. On success the hash is what the review widget, a check and an approval all address.

parent_hash (optional) links this revision to the one it edits, so the review can show a diff; source relayed from the widget's own edit control arrives with the parent it names. label (optional, inert): {domain: <level-one id>, topic: <short phrase>, confidence: high|medium|low}, the caller's reading of what domain the policy belongs to, stored as vocabulary ids only; or {declined: true}.

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; units go on literals (below) 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.

Literals: numbers 42 3.5 0.05 with a unit 3000 "mi" 25000 "USD" 0 "USD" -- a number followed by the unit as a QUOTED string. A bare -- unit word is a syntax error: 3000 mi fails to parse. booleans True False -- capitalised. Lowercase true and false are read as -- identifiers and fail as undefined variables. enum members "low" "high" -- double-quoted strings

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 UNKNOWN a claim it needs was submitted as unknown, or is unconstrained AMBIGUOUS reserved. It means an assertion's evidence contradicted itself, which needs several answers for one claim. A claim dictionary holds exactly one value per claim, so no check submitted to this service produces it. Handle the word if you switch on results; there is no input that triggers it.

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.

Rule descriptions. Directly above every assert, with no blank line between, write what the rule requires in plain words as an ordinary comment block (no @ tag). The review widget shows it as the rule's description and a person edits it there, so it must say what the assert beneath it actually checks. dsail_compile over MCP refuses an assert that has none, naming its line.

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"];

// No loan may exceed one million dollars. assert within_cap { loanAmount <= 1000000 "USD" }; // A loan over 500,000 USD needs an appraisal on file. assert large_but_documented { Or(loanAmount <= 500000 "USD", hasAppraisal) }; // A loan over 250,000 USD needs an appraisal on file. assert appraisal_when_large { IF loanAmount > 250000 "USD" THEN hasAppraisal ELSE True END }; // High-risk applicants are not permitted. assert tier_permitted { riskTier != "high" };

Arguments:

dsail_check

Check extracted values against a written policy and get the same answer every time, with the rule that decided.

Use when reviewers disagree about applying decided rules to the same facts, or repeated reviews give inconsistent answers. Takes the ruleset and claim values extracted by the caller's own model.

Validates a claim dictionary and solves it against a ruleset, in one call.

Takes ruleset_hash (a compiled ruleset's content hash) or source. claims maps every declared claim name to a value; a claim that could not be determined is the string "unknown", which the rules treat as a first-class answer rather than a guess.

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), with the assertion's name and source text, and which claims had no value. AMBIGUOUS is reserved: it needs several answers for one claim, and a claim dictionary holds one, so no check sent here returns it. A rejected dictionary names every failing field at once as {field, expected, received}.

There is no overall verdict, by design: the four per-assertion words are the whole answer, and what a FALSE costs is a decision for whoever reads it.

This is the one metered call, priced in Jaxon Verified Units: one rule checked against up to 1,000 characters is one JVU, so a call carrying more rules or more text costs more than one. An exhausted allowance answers ENTITLEMENT_EXCEEDED with a message and a personalised upgrade_url; upgrading resumes the same conversation, and everything except checking keeps working meanwhile.

Arguments:

dsail_get_prompt_pack

Get the exact questions to ask a document so the answers can be checked against a written policy.

Use when reviewers pull different facts from the same kind of document, or the caller needs a consistent set of questions before applying decided rules.

Returns the extraction contract for a compiled ruleset: one prompt per claim (question, answer format, vocabulary, unit), the claim JSON schema, and the exact validation rules a check enforces. The prompts are for the caller's own model to run; this service calls no model. The assembled claim dictionary is what a check takes.

Arguments:

dsail_save_ruleset

Keep a written policy's rules as a named, immutable revision, so a later check answers exactly as this one did.

Use when a handbook changes but earlier reviews must remain tied to the rules used then, or a team needs a name for a decided policy's formal rules.

Stores 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 the workspace in use: a ruleset saved here is reachable from any later conversation through the same connection, and from no other.

created is true when these exact bytes were stored for the first time, and false when an identical source had already been stored, by a compile or an earlier save. It says nothing about the name: the first save under a new name returns false if the same source was compiled before.

label (optional, inert): {domain: <level-one id>, topic: <short phrase>, confidence: high|medium|low}, or {declined: true}. Stored as vocabulary ids only.

Arguments:

dsail_load_ruleset

Read a saved policy ruleset exactly as it was stored.

Use when someone asks which saved rules were used, or needs to inspect an existing revision rather than author a new one.

Loads 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 a compile returns, for bytes already stored.

An approval follows the stored bytes, so source re-compiled from memory is a different object the moment the two texts drift; the source this returns, with the hash it came with as parent_hash, is the starting point for an edit. Loading shows nothing on screen.

Arguments:

dsail_list_rulesets

See which written policies already have rules a program can check.

Use when a team may already have formalised the handbook or written criteria, and the caller needs to find existing rules before starting again.

The rulesets saved in the workspace in use. 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, so the rows are already in front of the user. The namespace field is a service-internal identifier, not a name a person would recognise.

Arguments:

dsail_record_approval

Bind a person's sign-off to the exact rules a program will check.

Use when a reviewer has seen the formal rules and is ready to sign off, or revised rules need a fresh human approval.

Binds a human approval to one exact ruleset hash. An approval follows the bytes, not the name: a later revision is not covered by it.

name saves 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; it is optional only for a hash already saved under a name. approver and note are recorded with the approval. The review widget's approve control composes the exact arguments for this call, because a chat client cannot call tools from inside a widget.

Arguments:

dsail_get_account_status

Know how much of this credential's allowance is used before a limit interrupts a check.

Use when an upcoming check could be interrupted by the account's allowance limit and the caller needs to see usage first.

This account's entitlement and usage position. usage is the meter that refuses the next check — used, limit and remaining — and stub: false means it is enforced. Only checks are metered; compiling, editing, saving, loading and approving rulesets are free and stay free at any limit. When the tier is gated the payload carries upgrade_url, a personalised link the user can follow at any time. Also reports the current application-domain vocabulary version, whether this account may label rulesets, how many API keys it may still issue, and terms — the terms-of-service version that governs this tier with the URLs of the terms, privacy and data-handling pages. account.project is an internal identifier that names nothing a user would recognise. account.organizations lists the owner of the workspace currently in use (a team id while working in a team), not the teams the user belongs to; the workspace listing is where those are.

Arguments:

dsail_issue_api_key

Issues a REST API key for this account.

The key authenticates calls to the DSAIL REST API, sent as the x-jaxon-credential header, and reaches the rulesets this account holds. Optional label records what the key is for. The token is returned once and cannot be recovered; the service keeps only a hash. An account has a limited number of keys.

Arguments:

dsail_unit_library

Find out whether two units convert before a rule compares them, so the check does not answer UNKNOWN forever.

Use when a document supplies a quantity in one unit while the written rule uses another, or a unit mismatch is leaving a comparison unresolved.

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 — which makes this worth knowing before rules that mix units are written. With from_unit and to_unit it answers for one pair with 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:

dsail_add_unit_converter

Give a policy check the conversion it needs between two units, with a factor a person supplied.

Use when a rule and its claim values use different units and a person has supplied the conversion factor needed to compare them.

Adds a unit converter to this project so rules mixing the two units resolve instead of answering UNKNOWN. to = from * factor + offset.

The factor is a policy decision with legal weight — an exchange rate fixed by a contract, a density fixed by a spec — and attribution records the person or document it came from, because a figure recalled from training would enter results as though a person had decided it.

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. The result says so.

Arguments:

dsail_list_workspaces

See which teams you work in and which one you are working in now.

Use when a ruleset the user expects is missing from a listing, before saving something that belongs to a particular team, and whenever the user mentions a colleague, a team or sharing.

Every workspace this user may work in — their own, plus one per team — and which one is currently in use. The current workspace decides what a listing shows and where a save writes. It does NOT decide who pays: every check is billed to the user's own account, in any workspace.

signed_in says whether a person is behind this connection. When it is false the caller holds an API key, which names a workspace and not a person, so creating a team, inviting, removing and switching workspace are all refused; remedy is the command that signs the person in, and their key keeps working for everything else.

Arguments:

dsail_use_workspace

Work in a different team, or in your own workspace.

Use when the user wants to work on a different team's rulesets, or to go back to their own.

Chooses which workspace this user's saving and listing happen in. workspace names a team by its name or its id, or is personal for their own workspace. The choice persists across sessions. If two of their teams share a name the call is refused and names the ids, rather than guessing.

Arguments:

dsail_create_team

Start a team so colleagues share one set of policy rules.

Use when the user says a colleague cannot see their rulesets, or wants work shared with named people rather than kept to themselves.

Creates a team with a ruleset library of its own and makes this user its administrator. The team starts EMPTY: their existing rulesets stay in their own workspace and are not moved or copied; copying a ruleset is how work gets into it. A team is not a billing account: it has no allowance or subscription, and each member's checks in it are billed to that member's own account. name is a label shown to members, not an identifier, and does not have to be unique.

Arguments:

dsail_invite_to_team

Invite a colleague into a team by email address.

Use when an administrator wants a specific colleague to reach the team's rulesets.

Issues a single-use invitation to one email address. This service sends no mail, so what comes back is a link the administrator passes on themselves. Only the invited address can accept it, by signing in as that address, and only once, so a forwarded link is useless to anyone else. Restricted to the team's administrators.

Arguments:

dsail_team_members

See who is in a team and who administers it.

Use before removing somebody, changing who administers a team, or when the user asks who can see the team's rulesets.

Who is in this team and what each one may do. Every member reads and writes the whole of the team's library; an administrator can additionally invite, remove and promote. Only a member of the team may ask.

Arguments:

dsail_set_team_role

Let another member administer a team, or stop them administering it.

Use when the user wants somebody else able to invite and remove, or no longer able to.

Makes a member an administrator, or returns an administrator to an ordinary member. role is admin or member. It does not change what they can read or write — every member already sees the whole library — only whether they can change who is in the team. member is their email address as the team's member list reports it, or the subject listed beside it. Demoting the last administrator is refused. Restricted to the team's administrators.

Arguments:

dsail_remove_from_team

Take somebody out of a team and cut off the keys they issued for it.

Use when somebody has left the team or should no longer reach its rulesets.

Removes a member. Their access ends on their next call, including through any API key they issued for this team, which is revoked. What they saved stays with the team — removing a person does not remove their work. A team is not left without an administrator: removing the last one is refused until somebody else has been promoted. member is their email address as the team's member list reports it, or the subject listed beside it. Restricted to the team's administrators.

Arguments:

dsail_copy_ruleset

Put a copy of one of your saved policy rulesets into a team.

Use when a ruleset the user already has should also be available to a team — the usual way a new team gets its first library.

Copies a saved ruleset from one of this user's workspaces into another. It is a copy: the original stays exactly where it was. destination and the optional source name a team or personal; source defaults to the workspace in use. The revisions it cites travel with it so a diff still works, but recorded approvals stay behind — an approval is one workspace's sign-off, so the copy arrives unapproved and must be approved there if the team needs that. A name already in use in the destination is refused rather than overwritten; new_name copies it under another.

Arguments:

dsail_review

Show a person the rules a program will check, so they can approve exactly those bytes.

Use when the policy owner needs to inspect how decided criteria were formalised before signing off, or a later revision needs its own review.

Presents a compiled ruleset to the user in the inline review widget: each rule with what it says, the claims it reads and a Yes / No / Unknown answer for each, a check that shows every rule's own result, the source (editable), the approve control, and the project's unit-conversion library when the ruleset uses units.

Takes the ruleset_hash of a ruleset that compiled clean, or the name a stored ruleset is saved under; a hash that failed to compile has nothing to show. One rendering per revision is what the user sees, and a later revision gets its own.

Returns the same payload a compile returns for that hash, plus the name it is saved under (null when nothing names it yet) and every approval bound to it.

Arguments: