# ROBINS-I V2 MCP (pypi · robins-i-mcp)

ROBINS-I V2 (not V1): risk of bias in one result of a non-randomized cohort study

- Trust score: 68/100 (medium)
- Change this week: +4
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-08-19

## Components

- pypi · `robins-i-mcp`: 68/100 (this document), [markdown](https://verifymcp.io/servers/com-blackswancausallabs-robins-i-mcp/robins-i-mcp.md), [page](https://verifymcp.io/servers/com-blackswancausallabs-robins-i-mcp/robins-i-mcp)

## Channel facts

- Registry: `pypi`
- Package: `robins-i-mcp`
- Version: `0.1.0`
- 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-08-19.

- **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 42 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 32/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 14 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 59/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 3621 tokens (~402/item across 9 items; 9 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 50/100
  - Stability observed for 15 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 71/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 0% of tool parameters carry a description.
  - Structured output schemas are declared (100% of tools); any adoption earns full credit.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

## Install

### Claude

```bash
claude mcp add com-blackswancausallabs-robins-i-mcp -- uvx robins-i-mcp
```

### Codex

```bash
codex mcp add com-blackswancausallabs-robins-i-mcp -- uvx robins-i-mcp
```

### opencode

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

### OpenClaw

```bash
openclaw mcp add com-blackswancausallabs-robins-i-mcp --command uvx --arg robins-i-mcp
```

### Hermes

```yaml
mcp_servers:
  com-blackswancausallabs-robins-i-mcp:
    command: "uvx"
    args: ["robins-i-mcp"]
```

### Other

```json
{
  "mcpServers": {
    "com-blackswancausallabs-robins-i-mcp": {
      "command": "uvx",
      "args": [
        "robins-i-mcp"
      ]
    }
  }
}
```

## 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-08-19 (score 68, +1)

No change was recorded against any check on this day. Stability & Change Management went from 47 to 50. That category is still filling its 30-day observation window: 14 days of observed history at the previous scan, 15 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-08-17 (score 67, +1)

No change was recorded against any check on this day. Stability & Change Management went from 40 to 43. That category is still filling its 30-day observation window: 12 days of observed history at the previous scan, 13 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-08-15 (score 66, +1)

No change was recorded against any check on this day. Stability & Change Management went from 33 to 37. That category is still filling its 30-day observation window: 10 days of observed history at the previous scan, 11 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-08-13 (score 65, +1)

No change was recorded against any check on this day. Stability & Change Management went from 27 to 30. That category is still filling its 30-day observation window: 8 days of observed history at the previous scan, 9 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-08-12 (score 64, +4)

- [functional improvement] Stability: unverified → 0.27

### 2026-08-11 (score 60, 0)

- [functional] We updated how we score, so this day's move reflects our rubric, not a change to the server

### 2026-08-08 (score 60, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-08-05 (score 45, +9)

- [security regression] Malware scan: pass → unverified
- [security] Stability: Stability not yet verified: not enough scan history yet (needs a 30-day window).
- [functional regression] Security disclosure: unverified → fail
- [functional improvement] Tool coverage: unverified → 100
- [functional improvement] MCP protocol: unverified → pass
- [functional] First check of Tool coverage: 0
- [functional] First check of Schema quality: fail
- [functional] First check of Schema quality: excellent
- [functional] First check of Schema quality: fail
- [functional] First check of Tool coverage: 100

## MCP tools (9)

### `get_spec` (~178 tokens)

Return the encoded ROBINS-I V2 specification: 13 preliminaries, 40
signalling questions across 6 domains (domain 1 in two variants), the
response vocabularies, the evidence policy, and the algorithm fingerprint.

OPTIONAL in the assessment flow — introspection only. assess_result already
carries the rubric for the domain in hand, so calling both duplicates the
payload. detail='compact' (default) gives question ids, own-words labels,
response options and evidence modes; detail='full' adds every intent and
assessor note.

The descriptions are this implementation's own wording, NOT the published
signalling-question text, which is not redistributable. The published
question IDs are what make the output interoperable.

Input parameters:

- `detail` (string)
- `spec_version` (string)

Output parameters:

- `algorithm_fingerprint` (string)
- `assessment_unit` (string)
- `assessment_unit_note` (string)
- `attribution` (object)
- `domains` (array)
- `evidence_policy` (string)
- `guideline_scope` (string)
- `human_ratification` (object)
- `judgement_source` (string)
- `judgement_source_note` (string)
- `judgements` (array)
- `override_policy` (string)
- `preliminaries` (array)
- `response_semantics` (object)
- `response_vocabularies` (object)
- `source_status` (string)
- `source_status_note` (string)
- `spec_version` (string)
- `tool_version` (string)
- `variant_scope` (string)
- `variant_scope_note` (string)

### `parse_document` (~363 tokens)

PRIMARY entry point: parse a study report into a bundle with
character-offset, source-tagged section spans. Every quote you later cite is
resolved against THIS text, and every claim of absence is searched in it.

PASS `citation=`: the full bibliographic reference in APA style. It appears
on the rendered assessment so the study being judged is unambiguous.

\`document` is EITHER the raw text OR a file path — but the path must be
readable on the SERVER host. If your files live on your own filesystem, paste
the TEXT instead, or use parse_pmcid for an open-access PMCID. A path-looking
string the server cannot find raises rather than being ingested as its own
text.

PASS THE SUPPLEMENT WHEN YOU HAVE IT. `supplements` is a list of
server-readable paths merged as supplementary material. This matters more for
ROBINS-I than for a reporting audit: the target-trial specification that
settles C1-C4, and the analysis detail domains 1 and 4 turn on, routinely live
only in the appendix. Without it those questions read NI when the answer was
merely in a file nobody passed. supplement_status defaults to 'user_provided'
when supplements are given; pass 'none_exists' to assert the article has none.

Returns the section map, the text hash that keys later calls, and a cue survey
— hit counts for the 15 evidence patterns, so you can see before reading where
each domain's evidence lives and which absences already look real.

Input parameters:

- `citation` (string)
- `document` (string, required)
- `manuscript_id` (string)
- `supplement_status` (string)
- `supplements`

Output parameters:

- `citation` (string)
- `cue_survey` (array)
- `documents` (array)
- `extractor_version` (string)
- `full_text` (string)
- `manuscript_id` (string)
- `n_pages`
- `next_step` (string)
- `sections` (array)
- `source` (string)
- `supplement_status` (string)
- `text_sha256` (string)
- `warnings` (array)

### `parse_pmcid` (~186 tokens)

CONVENIENCE entry point: retrieve an open-access article from Europe PMC by
PMCID and parse it, main text plus PMC-hosted supplements, merged into one
source-tagged bundle. Use it when you have no file in hand, or to auto-fetch
an open-access paper's supplement; for a manuscript you were given, use
parse_document.

supplement_status is 'retrieved' when a supplement was obtained, else
'not_retrieved' — a supplement may still exist on the publisher site, so
absence of retrieval is never proof of absence. Raises if no open-access full
text is available. An APA-style citation is built from the article's JATS
metadata; pass citation= to override it.

Input parameters:

- `citation` (string)
- `include_supplements` (boolean)
- `pmcid` (string, required)

Output parameters:

- `citation` (string)
- `cue_survey` (array)
- `documents` (array)
- `extractor_version` (string)
- `full_text` (string)
- `manuscript_id` (string)
- `n_pages`
- `next_step` (string)
- `sections` (array)
- `source` (string)
- `supplement_status` (string)
- `text_sha256` (string)
- `warnings` (array)

### `set_prespecified_confounders` (~307 tokens)

Supply P1 — the confounding factors this REVIEW judges important for the
intervention-outcome relationship, listed before any study is assessed.

THIS IS BLOCKING. Domain 1 will not be scored without it. Question 1.1 asks
whether all IMPORTANT confounding factors were controlled for, and 'important'
is defined by this list, not by the paper's covariate table. Substituting the
paper's own list would let the study grade its own confounding control, which
is exactly the judgement ROBINS-I asks the reviewer to make independently.

'Important' means adjustment would be expected to change the estimate
meaningfully; factors with only very weak associations are excluded.

You MAY propose a list from domain knowledge or a DAG — that is useful and is
what this parameter is for — but a proposed list is not a ratified one. Leave
\`ratified_by` empty unless a human in this conversation has actually reviewed
and accepted the list; the assessment then carries P1 in its ratification
queue and is explicitly not final until they do. Do not sign it off on their
behalf.

Scoped by `review_id` because P1 belongs to the review, not to one study: the
same list applies to every result assessed under it.

Input parameters:

- `confounders` (array, required)
- `ratified_by` (string)
- `rationale` (string)
- `review_id` (string)

Output parameters:

- `n_factors` (integer)
- `note` (string)
- `prespecified_confounders` (array)
- `ratified` (boolean)
- `ratified_by` (string)
- `rationale` (string)
- `review_id` (string)

### `specify_result` (~624 tokens)

Name the ONE numerical result being assessed, and settle C4. Required
before any domain can be scored.

ROBINS-I assesses a single effect estimate, not a paper. A study reporting
three outcomes across two analyses yields six assessments; give each its own
\`result_id` and run them separately.

\`accounts_for_deviations` IS C4, and it is the highest-leverage input here:
it swaps domain 1's entire question set and algorithm, so it cannot be
deferred until domain 1 is reached.
  'no_itt'  — the analysis does NOT account for switches between the compared
              strategies or other protocol deviations. It targets the effect
              of assignment. Domain 1 variant A (baseline confounding only).
  'yes_pp'  — the analysis DOES account for them, by censoring, follow-up
              partitioning, or a g-method. It targets the effect of sustained
              receipt. Domain 1 variant B (baseline AND time-varying
              confounding).
Judge this on what the ANALYSIS DOES, not on the label the authors give their
estimand. A paper whose protocol table says 'observational analogue of the
per-protocol effect' but which never censors at deviation is 'no_itt'. Getting
this wrong means answering five questions that do not apply.

\`result_assessed` is A1: the estimate with its precision. `outcome` is A3.
\`result_location` (A2) is where it appears and why it was chosen.
\`target_trial` is C1-C3 as a dict of labelled strings — eligible participants,
intervention strategy, comparator strategy, and any note on the estimand.
\`information_sources` is D1: what you actually read. An NI answer is only
defensible relative to what was searched.

\`b1`/`b2`/`b3` are the section B screening answers (Y/PY/PN/N). b1: was any
attempt made to control confounding in this result? b2 (asked only if b1 is
PN/N): is the potential for confounding great enough that an unadjusted result
should not be considered further? b3: is the outcome measurement method
unsuitable for the outcome it is…

Input parameters:

- `accounts_for_deviations` (string, required)
- `b1` (string)
- `b2` (string)
- `b3` (string)
- `citation` (string)
- `document` (string)
- `information_sources`
- `model` (string)
- `outcome` (string, required)
- `result_assessed` (string, required)
- `result_id` (string, required)
- `result_location` (string)
- `review_id` (string)
- `target_trial`
- `text_sha256` (string, required)

Output parameters:

- `citation` (string)
- `domain1_questions` (array)
- `domain1_variant` (string)
- `estimand` (string)
- `information_sources` (array)
- `next_step` (string)
- `outcome` (string)
- `per_protocol` (boolean)
- `prespecified_confounders` (array)
- `result_assessed` (string)
- `result_id` (string)
- `result_location` (string)
- `review_id` (string)
- `screening` (object)
- `screening_terminated` (boolean)
- `target_trial` (object)
- `text_sha256` (string)
- `warnings` (array)

### `assess_result` (~253 tokens)

Return the assessment scaffold for ONE domain: the questions actually in
play, their own-words intent, the response vocabulary each accepts, what
evidence each answer must carry, and the cue searches already run against
this bundle with their hits.

THE SCAFFOLD IS PER DOMAIN BY DESIGN. Most signalling questions are
unreachable on any given path — 24 of 41 were never reached on the reference
assessment — and which of domain 1's two question sets exists at all is
decided by C4. There is no flat 41-question rubric to fetch, and asking for
one would mean answering questions the algorithm discards.

domain=0 (default) returns the overview: the preliminaries, what is settled,
which domains are done, and which are outstanding. domain=1..6 returns that
domain's scaffold. Domain 1 is refused until P1 is set.

Work a domain at a time: read the cues to find where the evidence is, answer
only the questions the algorithm reaches, then submit_answers. You do not
state a judgement — the server computes it from your answers.

Input parameters:

- `domain` (integer)
- `result_id` (string, required)

Output parameters:

- `algorithm` (string)
- `answer_schema` (object)
- `applies_if` (string)
- `assessment_unit` (string)
- `assessment_unit_note` (string)
- `citation` (string)
- `confounding_note` (string)
- `cues` (array)
- `depends_on` (array)
- `domain` (integer)
- `domain_label` (string)
- `domains_complete` (array)
- `domains_outstanding` (array)
- `evidence_modes` (object)
- `instructions` (string)
- `low_label` (string)
- `low_label_note` (string)
- `max_judgement` (string)
- `max_judgement_note` (string)
- `preliminaries` (array)
- `prespecified_confounders` (array)
- `questions` (array)
- `result_id` (string)
- `spec_version` (string)
- `structure` (string)
- `supplement_status` (string)
- `text_sha256` (string)
- `variant` (string)
- `variant_label` (string)

### `submit_answers` (~518 tokens)

Submit one domain's signalling-question answers and get back the COMPUTED
domain judgement, or (domain=0) finalize the assessment.

Each element of `answers` is one question: `question`, `response`,
\`evidence_mode`, `rationale`, plus the evidence that mode requires —
\`quotes` for manuscript_positive, `search_cue` or `search_terms` for
manuscript_absent, `prior_ref` for reviewer_prior. Answer only the questions
the algorithm reaches; omit the rest rather than answering NA. `support` is
your narrative for the domain as a whole and appears in the report.

Three things are enforced here, and all three are the point of the tool:
every quote is resolved to character offsets in the ingested bundle and an
unresolvable one is REJECTED with the nearest text found; every claim of
absence is backed by a search THE SERVER runs, so it is reproducible; and the
judgement is computed by the published algorithm from your answers, never
asserted by you. If the traversal reaches a question you did not answer, the
call returns status='incomplete' naming it — supply it and call again.

\`override_judgement` sets the domain judgement against the algorithm and
REQUIRES `override_justification`. Overrides cannot hide: the report shows
both values and the override enters the ratification queue.

domain=0 finalizes: it computes the overall judgement (worst of the six
domains by default), assembles the stamped assessment and returns the
rendered report inline. `overall_escalate` applies the tool's permitted
escalation — several moderates to serious, or several seriouses to critical —
and requires a justification. The returned `report` is THE ARTIFACT OF
RECORD: present that, not a table of your own, and repeat its ratification
queue if it is non-empty, because an assessment with unratified items is not
final.

Input parameters:

- `answers`
- `direction_of_bias` (string)
- `domain` (integer, required)
- `overall_direction_of_bias` (string)
- `overall_escalate` (boolean)
- `overall_override` (string)
- `overall_override_justification` (string)
- `override_judgement` (string)
- `override_justification` (string)
- `render` (boolean)
- `result_id` (string, required)
- `support` (string)

Output parameters:

- `accepted` (array)
- `algorithm_judgement` (string)
- `attribution`
- `domain` (integer)
- `domains` (array)
- `domains_complete` (array)
- `domains_outstanding` (array)
- `evidence`
- `evidence_warnings` (array)
- `judgement` (string)
- `judgement_label` (string)
- `needs_answer` (string)
- `needs_answer_at_node` (string)
- `next_step` (string)
- `not_reached` (array)
- `notes` (array)
- `overall` (string)
- `overall_algorithm`
- `overall_label` (string)
- `overridden` (boolean)
- `path` (array)
- `provenance`
- `ratification_queue` (array)
- `record`
- `report`
- `result_id` (string)
- `status` (string)

### `render_report` (~159 tokens)

Re-render a finalized assessment as a self-contained HTML page — the
meta panel, the six-domain summary strip, the per-domain judgement with its
algorithm trail and the evidence behind every answer, styled to the Black Swan
Causal Labs identity and carrying the provenance stamp.

Returns the HTML in `html` (display it inline or publish it as an artifact)
and the same bytes base64-encoded in `content_base64` (decode and save as
.html). This is a pure re-render of the artifact submit_answers already
stamped — nothing is re-scored. The assessment must have been finalized this
session; the cache does not survive a server restart.

Input parameters:

- `result_id` (string, required)
- `title` (string)

Output parameters:

- `attribution` (object)
- `citation` (string)
- `content_base64` (string)
- `content_type` (string)
- `domains` (array)
- `filename` (string)
- `html` (string)
- `overall` (string)
- `overall_label` (string)
- `provenance` (object)
- `ratification_queue` (array)
- `result_id` (string)

### `export_robvis` (~522 tokens)

Combine assessment RECORDS from any number of runs into a CSV for
\**robvis** (McGuinness & Higgins), the standard tool for Cochrane-style
risk-of-bias figures.

A review of 200 studies is 200 separate runs — each assessment costs a
session, and nothing in this server survives between them. So pass
\`records`: the `record` object each submit_answers(domain=0) returns. They
are small, flat and JSON-native, so a whole review's worth fits in one
context, and they carry their own provenance so every row stays traceable
to a document and an algorithm fingerprint. Omit `records` to use only what
was assessed in THIS session (convenient, but session-scoped).

READ THE RETURNED `losses` BEFORE PUBLISHING THE FIGURE. It reports records
that are not yet ratified, mixed C4 variants, equal weighting, and records
built under differing algorithm transcriptions — each of which would make
the figure claim more than the assessments support.

This is not a column dump, because robvis's ROBINS-I template is ROBINS-I
\**V1** and V2 is not drop-in compatible:

  \* V1 has SEVEN domains and orders selection of participants BEFORE
    classification of interventions. V2 has six and swaps that pair. Writing
    V2's columns out in order loses no data and raises no error — it just
    prints your classification judgement under the heading "Bias due to
    selection of participants". layout='robins_i' (the default) places each
    V2 judgement into its correct V1 SLOT and marks the dropped deviations
    domain NA. Upload it with tool='ROBINS-I'.
  \* layout='generic' writes six columns headed with V2's own domain names,
    for tool='Generic'. The headings are then right, but robvis relabels the
    judgements into ROB1's vocabulary — Moderate becomes "Some concerns",
    Serious becomes "High". Prefer 'robins_i'.

Neither layout can carry 'Low, except for concerns about uncontrolled
confounding': robvis reduces every cell to its first initial over a
five-fill palette, so it collapses to Low wh…

Input parameters:

- `labels`
- `layout` (string)
- `records`
- `result_ids`
- `review_id` (string)
- `weights`

Output parameters:

- `content_base64` (string)
- `content_type` (string)
- `csv` (string)
- `filename` (string)
- `header` (array)
- `layout` (string)
- `losses` (array)
- `n_results` (integer)
- `review_id` (string)
- `robvis_tool` (string)
- `rows` (array)
- `slot_mapping`
- `summary` (object)
- `usage` (string)

## Diagnostics

Captured diagnostic sections: Provenance, Install scripts, Dependencies. The full working is on the page: https://verifymcp.io/servers/com-blackswancausallabs-robins-i-mcp/robins-i-mcp#diagnostics

## Score history

- 2026-08-19: 68
- 2026-08-18: 67
- 2026-08-17: 67
- 2026-08-16: 66
- 2026-08-15: 66
- 2026-08-14: 65
- 2026-08-13: 65
- 2026-08-12: 64
- 2026-08-11: 60
- 2026-08-10: 60
- 2026-08-09: 60
- 2026-08-08: 60
- 2026-08-07: 45
- 2026-08-06: 45
- 2026-08-05: 45
- 2026-08-04: 36

## Links

- PyPI project: https://pypi.org/project/robins-i-mcp/
- Socket report: https://socket.dev/pypi/package/robins-i-mcp
- Repository: https://github.com/Black-Swan-Causal-Labs/robins-i-mcp
- Changelog RSS feed: https://verifymcp.io/servers/com-blackswancausallabs-robins-i-mcp/robins-i-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/com-blackswancausallabs-robins-i-mcp/robins-i-mcp.json
- HTML version of this page: https://verifymcp.io/servers/com-blackswancausallabs-robins-i-mcp/robins-i-mcp
