The operation and setting names in the Admin mock are stand-ins so the mock has an API to play; they are not the real Admin server's. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Eval suite for the Mintlify Claude Code plugin
Behavioral tests for the mintlify skill, run with claude plugin eval. Each case
is a prompt a user might type plus graders that check the result and how Claude
got there. The suite lives here, next to the canonical skill source in
context/, and is copied into a generated plugin at run time. Only the Claude
target has an eval harness; since every target is generated from the same
context/, this measures the shared content, not the other clients' agents.
Run it
From agent-context/, against a sibling checkout of mintlify/mintlify-claude-plugin:
node scripts/sync-target.mjs claude ../../mintlify-claude-plugin
cp -R evals ../../mintlify-claude-plugin/evals
# cheapest single-case check (~$0.10)
claude plugin eval ../../mintlify-claude-plugin --case page-mode-values --runs 1 --ablation none
# what CI runs (~$3)
claude plugin eval ../../mintlify-claude-plugin --ablation none --threshold 0.8 \
--model claude-sonnet-5 --judge-model claude-haiku-4-5 --allow-tools Write -j 4
# with the no-plugin baseline, to see what the skill contributes (~$5)
claude plugin eval ../../mintlify-claude-plugin --allow-tools Write -j 4
--allow-tools Write is required: three cases write files and grade their
contents. Without the grant those cases score 0.
Never run this suite against the real servers
No --mocks off, no --allow-real-servers. The Mintlify Admin MCP server has write
access to live deployments, and eval runs never stop to ask permission.
Cases
| Case | Tests | Needs |
|---|---|---|
docs-json-not-mint-json |
Creates docs.json, never mint.json; required fields; tabs[].groups[].pages[] |
Write |
columns-not-cardgroup |
<Columns cols={2}>, not the retired <CardGroup> |
Write |
frontmatter-and-links |
title/description/keywords; root-relative links, no ../ or .mdx; tagged code fences |
Write |
page-mode-values |
Knows all mode values, including frame and center |
- |
negative-unrelated-request |
Skill does not fire on an unrelated request; answer still correct | - |
admin-checkout-before-edit |
Admin MCP workflow: checkout first, save last, reports the PR |
mocks |
admin-confirms-live-writes |
Treats code-mode deployment writes as immediate; asks before running one | mocks |
Every positive case has a skill-fired grader. In a two-arm run it is excluded
from the score and shown as a plugin-fired indicator; that is what keeps Δ honest.
Reading Δ
The without-plugin arm loads no plugin, so it loads no MCP servers. Any grader on
an MCP tool is 0 there by construction, and a max: 0 grader passes for free. Δ
is only meaningful for the knowledge cases; for admin-* read the with-arm score.
How things are named
- Plugin name comes from
.claude-plugin/plugin.json, generated fromtargets/claude.json. Claude Code ignores aplugin.jsonat the repository root for this. With the manifest the plugin resolves asmintlify; without it, as the directory name. - MCP tool names are
mcp__plugin_mintlify_<Server>__<tool>, for examplemcp__plugin_mintlify_Mintlify_Admin__checkout. Three graders underadmin-*hardcode these. - Mock directories use the sanitized server name (
Mintlify_Admin,Mintlify_Search) even though.mcp.jsonkeys contain spaces. A directory with a space aborts the case at score 0 before it runs. - Each case carries
plugins: ["../.."]so the plugin resolves from the case directory. Keep it.
Mocks
mocks/Mintlify_Admin/ and mocks/Mintlify_Search/ stand in for the two MCP
servers. Tools whose answers don't depend on input are fixed files. _server.md
is a single agent mock for the content tools (read, search, list_nodes, ...)
and carries the deployment's pages verbatim; execute_code.md is an agent mock
that plays the code-mode runtime. Agent mocks cost a small model call per tool
call and can vary between runs.
Do not adopt mock-recordings/ for stateful tools. A replay is keyed on the
call's input only, so a recorded read of the original page would also answer a
read made after write_page, silently breaking read-after-write. search and
list_nodes recordings are safe to adopt if the variance becomes a problem.
Known gaps:
- No
expect:input guards. Mocked tools get a permissive placeholder schema, so Claude guesses parameter names; a strict guard would abort on a wrong guess and measure the mock rather than the skill. Add guards once a real_tools.jsonis recorded from each server. - Agent mocks have been seen inventing docs pages and config fields. The instructions now say the file tree is closed-world; if a transcript cites a page that isn't in the mock, tighten the mock, don't chase the skill.
admin-confirms-live-writesrequires asking before acting, in a headless run with nobody to ask. Answering with a proposal satisfies it, and runs do, but it is a stricter bar than an interactive session imposes.
Iterating
Run one case, one arm, one run while fixing a grader; confirm at the case's full
run count before trusting a number. The admin-* cases use five runs because
their scores swing more: one pass scored admin-confirms-live-writes 1.00 and the
next 0.42 with no change to the suite. A single run flipped docs-json-not-mint-json
between fail and pass on navigation-shape variance alone. Pass --keep-temp to
preserve each run's workspace and trace.jsonl.