Documentation

A/BLint lints A/B test specs against the rules your team writes, over two surfaces that do the same work. The MCP server puts the linter inside your assistant, and the REST API hands the same calls to a script or an agent that sends HTTP.

The MCP server exposes four tools: one that returns this documentation as markdown, one for projects, one for the rules inside them, and one that lints a spec against those rules.

Install#

The server is a remote MCP endpoint at https://explint.com/mcp. Claude, ChatGPT, Grok Bot, Cursor, and Gemini all connect to it.

claude mcp add --transport http ab-lint https://explint.com/mcp

Sign in#

A client that handles OAuth needs nothing beyond the URL. The first call opens a browser window where you sign in and choose the organization the connection works in, and every call after that acts inside the organization you picked.

An assistant that cannot run that flow sends an API key instead, as the header Authorization: Bearer <api key>. You create keys on the MCP page of the A/BLint workspace, and revoking one there stops it working on the next call.

Tools#

Two of the four tools name what to do with an operation string, one of create, update, remove, or list, and carry that operation's arguments in the field named after it, so a call with operation: "create" fills create and leaves the rest out. ablint__lint takes its arguments directly, and ablint__docs takes no arguments and answers with the full documentation as markdown. No call names an organization, because the credential decides which one every call works in.

Projects are named by name on ablint__project and by projectSlug on the other two, matched inside the organization ignoring case, so any casing of the name or the slug reaches the same project.

ablint__docs#

Return the full documentation of how to use the MCP server as one markdown document, covering how to connect and sign in, every tool with its arguments and answer shapes, how listings page, and what each refusal means. The tool takes no arguments, reads and writes nothing in the organization, and never spends a lint request, so an assistant can call it at the start of a session and again whenever the shape of another tool needs checking.

ablint__docs

{}

ablint__project#

Create, rename, remove, and list the organization's projects.

ArgumentWhat it carries
operationRequired. One of create, update, remove, or list.
createname for the new project.
updatename for the project to rename and newName for what to rename it to, which regenerates its slug with it.
removename for the project to remove.
listslug to keep only projects whose slug holds that text, limit for how many rows one answer carries, and cursor to continue a listing.
ablint__project

{
  "operation": "create",
  "create": { "name": "Checkout cross-sell" }
}

ablint__rule#

Create, change, remove, and list the rules inside a project. A rule carries a key such as hypothesis/names-metric, a title, an instruction written in plain English that the linter judges the spec against, a severity of error or warning, and a switch for whether it is on. The category before the slash is how the workspace groups a project's rules, and a rule is found by its key matched ignoring case.

ArgumentWhat it carries
operationRequired. One of create, update, remove, or list.
createprojectSlug, ruleKey, ruleTitle, and ruleInstruction, plus optional severity (error or warning, error by default) and enabled (on by default).
updateprojectSlug and ruleKey to name the rule, plus any of ruleTitle, newRuleKey, ruleInstruction, severity, and enabled to change.
removeprojectSlug and ruleKey to name the rule to remove.
listprojectSlug, plus optional ruleKey to keep only rules whose key holds that text, limit, and cursor.
ablint__rule

{
  "operation": "create",
  "create": {
    "projectSlug": "checkout-cross-sell",
    "ruleKey": "hypothesis/names-metric",
    "ruleTitle": "Names the metric",
    "ruleInstruction":
      "The hypothesis names the metric it expects to move."
  }
}

ablint__lint#

Lint a spec against every rule the project has switched on.

ArgumentWhat it carries
projectSlugRequired. The project to lint against, by name or slug matched ignoring case.
contentThe spec itself as markdown, HTML, or plain text, judged against the project's rules. An image embedded in it as base64 is dropped before it is judged, and content beyond the acceptable size comes back as an error.

A lint, end to end#

One lint through every surface: the ablint__lint tool call beside the same request over REST, written for curl, JavaScript, and Python.

{
  "projectSlug": "checkout-cross-sell",
  "content":
    "Hypothesis: a full-width add-to-cart button on phones increases checkout completion."
}
The answer

{
  "quality_score": 0.5,
  "errors": [
    {
      "title": "If, then, and because are all present",
      "key": "hypothesis/structure",
      "description": "The hypothesis states what changes, what it expects, and why.",
      "probability": 0.31,
      "bad_example": "We believe the new onboarding will improve the experience.",
      "good_example": "Showing the setup checklist during signup will increase activation because new users can see what a finished workspace looks like."
    }
  ],
  "warnings": [
    {
      "title": "Success metric says how it is measured",
      "key": "metric/measurement-type",
      "description": "The success metric names how it is measured, as a rate, a count, or a median.",
      "probability": 0.62,
      "bad_example": "We will measure success by engagement.",
      "good_example": "Success is the median time from signup to first project created, measured across new accounts."
    }
  ],
  "passed": [
    {
      "title": "Names the metric",
      "key": "hypothesis/names-metric",
      "description": "The hypothesis names the metric it expects to move.",
      "probability": 0.93,
      "bad_example": "The new pricing page will perform better.",
      "good_example": "The new pricing page will increase checkout completion among new visitors."
    },
    {
      "title": "One change per test",
      "key": "hypothesis/one-change",
      "description": "The spec names one change so the result can be read against it.",
      "probability": 0.86,
      "bad_example": "The variant changes the headline, the price, and the form together.",
      "good_example": "The variant changes only the header call to action, with the rest of the page unchanged."
    }
  ]
}

The same shape comes back over both surfaces.

Every rule lands in exactly one of errors, warnings, or passed, by the severity its rule carries, and each list comes back sorted by key. An entry carries the rule's title, key, description, and probability, the chance the model gave the content of satisfying the rule, together with the rule's bad_example and good_example where the rule carries them, each a short excerpt showing what a break looks like and what passing looks like, so an assistant can act on a break without a second call. A rule passes at a probability of 0.8 or more. quality_score is the share of the rules that passed, rounded to three decimals.

Lint a hypothesis in the browser

These four rules run locally as you type. A lint on the server judges your project's own rules and answers a probability for each.

50%quality score50%quality score
  • If, then, and because are all presenthypothesis/structureStates what changes, what it expects, and why.
    error
  • Names the metrichypothesis/names-metricNames the metric it expects to move.
    passed
  • Success metric says how it is measuredmetric/measurement-typeNames how it is measured, as a rate, a count, or a median.
    warning
  • One change per testhypothesis/one-changeOne change so the result can be read against it.
    passed

REST API#

Every tool above is also a REST call, so a script or an agent that sends HTTP can do the same work. The API lives at https://explint.com/api and takes an API key as the header Authorization: Bearer <api key>, created on the MCP page of the A/BLint workspace. The key decides which organization every call works in, so no call names one.

Nine endpoints cover the work the three tools do:

Each row opens a sample request and the answer it gets back.

Replace <api key> with a key from the MCP page. Paths match organization ignoring case.

A project is named in the path by name or slug, matched inside the organization ignoring case the way the tools match it. A rule is named by its key, and a key may hold slashes, so percent-encode it on the way in: hypothesis%2Fnames-metric and hypothesis/names-metric both reach the same rule.

Listings page through their rows the way the tools do: limit carries from 1 to 100 rows and 25 when it is left out, slug and ruleKey narrow a listing to the rows holding that text, and the nextCursor an answer returns is what the next call passes as cursor until it comes back null.

A lint takes the spec as content and answers the same shape ablint__lint returns. An image embedded in the content as base64 is dropped before it is judged, content beyond the acceptable size comes back as an error, and a lint no model answered comes back as the linter being unable to answer.

A refusal answers {"error": "<message>"} under a status that says what kind it is:

StatusWhat it answers
400A request to fix.
401A missing or invalid API key.
402A balance spent on linting.
403The Default project refusing removal.
404No such endpoint, project, or rule.
405A path called with the wrong method.
409A project name or rule key already taken.
502A lint the linter could not answer.

The whole surface is published as an OpenAPI document at https://explint.com/openapi.json, which is how an agent learns every path, argument, and answer in one fetch.

Also served as markdown and llms.txt.