# DSAIL (pypi · dsail)

Turn a written policy into rules a program can check, and get the same answer every time.

- Trust score: 60/100 (medium)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-21

## Components

- remote · `agents.jaxon.ai`: 38/100, [markdown](https://verifymcp.io/servers/ai-jaxon-dsail/agents.md), [page](https://verifymcp.io/servers/ai-jaxon-dsail/agents)
- pypi · `dsail`: 60/100 (this document), [markdown](https://verifymcp.io/servers/ai-jaxon-dsail/dsail.md), [page](https://verifymcp.io/servers/ai-jaxon-dsail/dsail)

## Channel facts

- Registry: `pypi`
- Package: `dsail`
- Version: `1.0.2`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-09-21.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - Runs setuptools.build_meta at install time, a recognised native-build step with no shell scripting around it.
  - 1 of 29 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 35/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - License check failed: no license is declared.
  - Actively maintained (last published 1 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 47/100
  - AI-judged instruction clarity (good).
  - Context-footprint check failed: tool/resource definitions use about 8801 tokens (~440/item across 20 items; 20 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 0/100
  - Stability not yet verified: not enough scan history yet (needs a 30-day window).
- **Tool Coverage**: 70/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 11% of tool parameters carry a description.
- **Tool Safety**: 75/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - 0 of 1 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "dsail_remove_from_team" implies "remove" and declares no destructiveHint at all, which the MCP spec reads as destructive by default.
  - An AI judge read all 21 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

**Unverified: 1 category.** A category scored 0 because we could not verify it: a data source with nothing on this package, evidence we could not reach, or a check we could not run. We only credit what we can confirm.

## Install

### How do I install the DSAIL MCP server?

DSAIL runs locally as a PyPI package, launched with uvx dsail. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add ai-jaxon-dsail -- uvx dsail
```

### Cursor

```json
{
  "mcpServers": {
    "ai-jaxon-dsail": {
      "command": "uvx",
      "args": [
        "dsail"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "ai-jaxon-dsail": {
      "command": "uvx",
      "args": [
        "dsail"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add ai-jaxon-dsail -- uvx dsail
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "ai-jaxon-dsail": {
      "type": "local",
      "command": [
        "uvx",
        "dsail"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add ai-jaxon-dsail --command uvx --arg dsail
```

### Hermes

```yaml
mcp_servers:
  ai-jaxon-dsail:
    command: "uvx"
    args: ["dsail"]
```

### Netclaw

```json
{
  "McpServers": {
    "ai-jaxon-dsail": {
      "Transport": "stdio",
      "Command": "uvx",
      "Arguments": [
        "dsail"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add ai-jaxon-dsail -t stdio -c uvx -a dsail
```

### Other

```json
{
  "mcpServers": {
    "ai-jaxon-dsail": {
      "command": "uvx",
      "args": [
        "dsail"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-20 (score 60)

First indexed and scored.

## MCP tools (20)

### `dsail_compile` (~1329 tokens)

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 must already
have decided the policy; DSAIL does not draft policy, resolve conflicting
policies or interpret an ambiguous regulation.

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 enu…

Input parameters:

- `label`
- `parent_hash`
- `source` (string, required)

### `dsail_check` (~463 tokens)

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.

Use when reviewers disagree about applying decided rules to the same facts, or
repeated reviews give inconsistent answers. Supply the ruleset and claim
values extracted by the caller's own model. Submit a claim that could not be
determined as "unknown"; assertions that need it answer UNKNOWN rather than
guessing.

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.

This is the one metered call, and it is metered 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. If it answers ENTITLEMENT_EXCEEDED, the
account's units are spent — nothing else is: compiling, editing, saving
and approving still work. Relay that error's message to the user as an
invitation, together with its `upgrade_url`, and say that paying resumes this
same conversation with nothing to reconnect. Do not retry; the answer does not
change until they upgrade.

Input parameters:

- `claims` (object, required)
- `label`
- `ruleset_hash`
- `source`

### `dsail_get_prompt_pack` (~125 tokens)

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.

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.

Input parameters:

- `ruleset_hash`
- `source`

### `dsail_save_ruleset` (~201 tokens)

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.

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.

Input parameters:

- `label`
- `name` (string, required)
- `parent_hash`
- `source` (string, required)

### `dsail_load_ruleset` (~205 tokens)

Show 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.

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.

Input parameters:

- `name` (string, required)

### `dsail_list_rulesets` (~159 tokens)

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 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.

### `dsail_record_approval` (~247 tokens)

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.

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.

Input parameters:

- `approver` (string, required)
- `label`
- `name`
- `note`
- `ruleset_hash` (string, required)

### `dsail_get_account_status` (~231 tokens)

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, so you can warn the user before a limit surprises them. `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 you may give the user at any time. Also reports the current application-domain vocabulary version, whether this account may label rulesets, and `terms` — the terms-of-service version that governs this tier with the URLs of the terms, privacy and data-handling pages — so you can tell a user which terms apply without them going to look. Do not read the `account.project` identifier out to a user; it names nothing they would recognise.

### `dsail_issue_api_key` (~179 tokens)

Move a policy check from a chat into production code without rewriting the rules a person already approved.

Use when the user wants code outside a chat to run checks against the rulesets this account already holds.

Issue a production REST API key for this account, for code that will call DSAIL outside a chat. The key reaches exactly the rulesets this account already holds, so a check from a service answers what a check from here answers. Optional `label` records what it is for. The token comes back ONCE: show it to the user verbatim, tell them it is not recoverable, and do not repeat it later in the conversation or store it anywhere. Call this only when the user asks for one — an account has a limited number and `dsail_get_account_status` reports how many.

Input parameters:

- `label`

### `dsail_unit_library` (~217 tokens)

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. 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.

Input parameters:

- `from_unit`
- `to_unit`

### `dsail_add_unit_converter` (~241 tokens)

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.

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.

Input parameters:

- `attribution`
- `factor` (number, required)
- `from_unit` (string, required)
- `offset` (number)
- `to_unit` (string, required)

### `dsail_list_workspaces` (~242 tokens)

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 FIRST 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 `dsail_list_rulesets` shows and where `dsail_save_ruleset` writes. It does NOT decide who pays: a check against a team's ruleset is billed to that team wherever the user happens to be working.

\**Read `signed_in` before you plan anything involving a team.** When it is false this caller is holding an API key, which names a workspace and not a person, so creating a team, inviting, removing and switching workspace will all be refused. Tell the user to run the command in `remedy` FIRST, rather than letting them discover it partway through — they have done nothing wrong, they are using the credential the on-ramp gave them, and their key keeps working for everything else.

### `dsail_use_workspace` (~109 tokens)

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.

Choose which workspace this user's saving and listing happen in. Name a team by its name or its id, or pass `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.

Input parameters:

- `workspace` (string, required)

### `dsail_create_team` (~160 tokens)

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.

Create a team with a ruleset library of its own and make this user its administrator. The team starts EMPTY: their existing rulesets stay in their own workspace and are not moved or copied. Use `dsail_copy_ruleset` to put any of them into the team. The team is also its own billing account — its allowance and any subscription belong to the team, separately from whatever the user has personally. `name` is a label shown to members, not an identifier, and does not have to be unique.

Input parameters:

- `name` (string, required)

### `dsail_invite_to_team` (~134 tokens)

Invite a colleague into a team by email address.

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

Issue 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 — give it to them verbatim and tell them who it is for. 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. Only an administrator of the team may call this.

Input parameters:

- `email` (string, required)
- `team_id` (string, required)

### `dsail_team_members` (~98 tokens)

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. You must be in the team to ask.

Input parameters:

- `team_id` (string, required)

### `dsail_set_team_role` (~136 tokens)

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.

Make a member an administrator, or return 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. The last administrator cannot be demoted. Only an administrator may call this.

Input parameters:

- `member` (string, required)
- `role` (string, required)
- `team_id` (string, required)

### `dsail_remove_from_team` (~129 tokens)

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.

Remove 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 can never be left without an administrator, so promote somebody else before removing the last one. Only an administrator may call this.

Input parameters:

- `member` (string, required)
- `team_id` (string, required)

### `dsail_copy_ruleset` (~203 tokens)

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.

Copy 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 do NOT — 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; pass `new_name` to copy it under another.

Input parameters:

- `destination` (string, required)
- `name` (string, required)
- `new_name`
- `source`

### `dsail_open_review` (~303 tokens)

Show a person the rules a program will check, so they can approve exactly those bytes — here, by starting the local DSAIL review UI and opening it in their browser.

Call this when a ruleset is ready for human review instead of telling the user to run `dsail serve` themselves. It starts a loopback server on this machine (outside any shell sandbox), opens the link in the default browser, and returns the link. Pass ONE of: `source` (the DSAIL text, byte-for-byte as compiled), `source_path` (a .dsail file in the repository), or `name` (a stored ruleset); nothing opens the ruleset library. The page shows the same review component the chat clients render; Approve there is recorded on the service against the exact hash. Always repeat the returned link to the user in your reply — the browser may not have opened on a remote or headless machine — and do not claim a review panel is open unless this tool returned a link.

Input parameters:

- `name` (string): A stored ruleset name to load and show.
- `open_browser` (boolean): Open the link in the default browser (default true).
- `source` (string): DSAIL source to compile and show.
- `source_path` (string): Path to a .dsail file to compile and show.
- `title` (string): Page title; defaults to the file or ruleset name.

## Diagnostics

Captured diagnostic sections: Provenance, Install scripts, Dependencies. The full working is on the page: https://verifymcp.io/servers/ai-jaxon-dsail/dsail#diagnostics

## Score history

- 2026-09-21: 60
- 2026-09-20: 60

## Common questions

### What is the DSAIL MCP server?

DSAIL is an MCP server listed in the public MCP registry as ai.jaxon/dsail. Turn a written policy into rules a program can check, and get the same answer every time. This page covers its PyPI package (dsail).

### Is the DSAIL MCP server safe to use?

DSAIL scores 60 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 21 September 2026. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the DSAIL MCP server expose?

DSAIL exposes 20 tools: dsail_compile, dsail_check, dsail_get_prompt_pack, dsail_save_ruleset, dsail_load_ruleset, and 15 more. Their descriptions and schemas cost roughly 5,111 tokens of context every time the server is loaded.

### Is the DSAIL MCP server still maintained?

DSAIL is still listed as active in the MCP registry. We last reached this channel on 21 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

## Links

- PyPI project: https://pypi.org/project/dsail/
- Socket report: https://socket.dev/pypi/package/dsail
- Repository: https://github.com/JaxonAI/dsail
- Website: https://docs.agents.jaxon.ai/
- Changelog RSS feed: https://verifymcp.io/servers/ai-jaxon-dsail/dsail.xml
- Changelog JSON feed: https://verifymcp.io/servers/ai-jaxon-dsail/dsail.json
- HTML version of this page: https://verifymcp.io/servers/ai-jaxon-dsail/dsail
