experiment-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@experiment-mcpplan a sample size for a 2% lift in redemption rate"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
experiment-mcp
An MCP server for designing and reading retail-offer experiments. It is a portfolio project: a small, offline, fully tested server that exercises the major Model Context Protocol features on synthetic offer data.
The statistics are ordinary two-sample calculations (sample size, hash assignment, lift with an optional CUPED adjustment, a chi-square sample-ratio check, and a percentile bootstrap). They are not a production experimentation platform, and the bundled rows are not real customers.
Architecture
flowchart LR
subgraph clients [Clients]
Desktop[Claude Desktop / Claude Code]
Inspector[MCP Inspector]
HTTPClient[HTTP MCP client]
end
subgraph server [experiment-mcp]
MCP[MCPServer]
Stats[stats]
Store[(SQLite experiments)]
CSV[synthetic CSV]
end
Desktop -->|stdio| MCP
Inspector -->|stdio| MCP
HTTPClient -->|Streamable HTTP + Bearer JWT| MCP
MCP --> Stats
MCP --> Store
MCP --> CSV
DevAuth[localhost dev token minter] -.->|JWKS| MCPsrc/experiment_mcp/stats/ has no MCP imports. server.py is the protocol layer: tools, resources, prompts, completions, sampling, and elicitation.
The SDK is mcp 2.x. FastMCP was renamed to MCPServer; this project uses that decorator API (@mcp.tool, @mcp.resource, @mcp.prompt, @mcp.completion).
Related MCP server: bourne mcp
Why MCP instead of a plain API?
The calculations live in ordinary Python functions. MCP is the contract a model client uses to find and call them.
One server definition is enough for Claude Desktop, Claude Code, Cursor, and MCP Inspector. Each new client is configuration. Each new tool is one function. A hand-written function-calling adapter is per client, so N clients and M operations become N times M wrappers instead of N plus M.
Clients discover tools, resources, and prompts at runtime, with schemas generated from the function signatures. close_experiment can ask the user to confirm mid-call. draft_readout asks the client model to write prose, so this process has no model API key. Bootstrap analyze_lift reports progress. Creating or closing an experiment notifies subscribers. Resources and prompts are context, not just actions. readOnlyHint and destructiveHint are client hints. On HTTP, experiments:read and experiments:write are enforced per tool.
A plain HTTP call is the better fit when no model is involved, when a single request/response is enough, or when the extra session and protocol machinery is not worth it. MCP does not replace ordinary API review. The comparison, including a schema side by side, is in docs/mcp-vs-api.md. A short click-through is in docs/demo.md. How a call is dispatched, where the process runs, and how writes are gated is in docs/design-faq.md.
Feature matrix
MCP feature | Where |
Tools with pydantic input validation and structured output |
|
Tool annotations |
|
Static resources |
|
Resource templates |
|
List-changed and resource-updated notifications |
|
Prompts |
|
Completions | prompt arguments |
Sampling |
|
Sampling fallback | same tool returns a template when the client does not |
Elicitation |
|
Elicitation fallback | if the client cannot ask, the record stays open until the call is repeated with |
Progress | bootstrap |
Logging | the same tool writes Python logs and deprecated MCP |
Cancellation | the bootstrap loop checks for a cancel signal between batches and awaits, so a cancelled call stops |
stdio |
|
Streamable HTTP |
|
OAuth 2.1 resource server | bearer JWT validation, |
Scopes |
|
Data
src/experiment_mcp/data/synthetic/ is generated by python -m experiment_mcp.synthetic with seed 20261102. metadata.json and SYNTHETIC.md label it as synthetic. The generator plants a redemption-probability gap and a spend shift so the tools have a deterministic file to read. Those planted settings are simulation inputs, not measured lift.
The tables are customers.csv (segment, region, pre-period spend) and exposures.csv (offer WEEKEND_15, arm, redeemed, post-period spend). Assignment uses the same salted hash as assign_cells.
Optional: dunnhumby The Complete Journey
This server does not download that dataset. If you already have a local copy and accept dunnhumby's terms, a rough mapping onto these columns is:
customer id and any segment fields you derive, plus a pre-period spend sum, become
customers.csvcampaign exposure, a redemption flag, and spend during the campaign window become
exposures.csv
Point the server at that directory with --data-dir only after you add a metadata.json whose origin is honest about the source. The committed path stays synthetic.
Quickstart
Requires Python 3.12 and uv.
uv sync
uv run experiment-mcp serveThat is stdio. It does not check a token. The security boundary is the process that launched it. The SQLite file defaults to .experiment_mcp/experiments.db in the working directory.
Claude Desktop
{
"mcpServers": {
"experiment-mcp": {
"command": "uv",
"args": [
"--directory",
"/absolute/path/to/experiment-mcp",
"run",
"experiment-mcp",
"serve"
]
}
}
}Claude Code
claude mcp add experiment-mcp -- uv --directory /absolute/path/to/experiment-mcp run experiment-mcp serveStreamable HTTP and auth
HTTP is an OAuth 2.1 resource server. It validates bearer tokens. It does not sign users in. A token must be an RS256 JWT whose iss, aud, and exp match the server, and whose scope string contains the scopes for the tool you call.
experiments:read covers the compute and list tools. experiments:write is required to create or close an experiment, and it also allows read tools. Protected-resource metadata advertises both. The SDK's required_scopes list is applied to every HTTP request, so this server leaves that list empty and enforces the split inside each tool. Unauthenticated calls get 401 with a resource_metadata pointer.
Local token minter (loopback only, not a production identity provider, no end-user login):
uv run experiment-mcp dev-authIt prints a dev secret and the JWKS URL. In another shell:
export EXPERIMENT_MCP_ISSUER_URL=http://127.0.0.1:9000
export EXPERIMENT_MCP_RESOURCE_URL=http://127.0.0.1:8000/mcp
export EXPERIMENT_MCP_JWKS_URL=http://127.0.0.1:9000/.well-known/jwks.json
uv run experiment-mcp serve --transport http --host 127.0.0.1 --port 8000Mint a token. The dev secret is the Authorization value on this request. It is not the access token.
curl -s -X POST http://127.0.0.1:9000/token \
-H "Authorization: Bearer $EXPERIMENT_MCP_DEV_AUTH_SECRET" \
-H "Content-Type: application/json" \
-d '{"resource":"http://127.0.0.1:8000/mcp","scope":"experiments:read experiments:write","sub":"local-dev"}'aud / resource must be exactly EXPERIMENT_MCP_RESOURCE_URL. Issuer and resource URLs must be https unless the host is loopback.
Offline alternative, no dev server: experiment-mcp mint-token writes a private key (mode 0600) and a JWKS file, prints one JWT, and you set EXPERIMENT_MCP_JWKS_PATH instead of EXPERIMENT_MCP_JWKS_URL.
EXPERIMENT_MCP_ALLOW_INSECURE_NO_AUTH=1 starts HTTP with no token check. The process logs a warning. Do not expose that process. It cannot be combined with issuer or JWKS settings.
DNS rebinding protection is on. Extra Host and Origin values go in EXPERIMENT_MCP_ALLOWED_HOSTS and EXPERIMENT_MCP_ALLOWED_ORIGINS (comma-separated). Binding 0.0.0.0 is allowed for a container; add the public host to the allow list.
MCP Inspector
uv run --with "mcp[cli]" mcp dev src/experiment_mcp/app.py:mcpOr point the Inspector at the stdio command uv run experiment-mcp serve. For HTTP, connect to http://127.0.0.1:8000/mcp and paste a bearer token from the minter.
Docker
The image listens on port 8000 and exits unless the auth environment variables above are set (or insecure mode is set explicitly). The database path inside the image is /data/experiments.db.
docker build -t experiment-mcp .
docker run --rm -p 8000:8000 \
-e EXPERIMENT_MCP_ISSUER_URL \
-e EXPERIMENT_MCP_RESOURCE_URL \
-e EXPERIMENT_MCP_JWKS_URL \
-e EXPERIMENT_MCP_ALLOWED_HOSTS \
experiment-mcpExample conversation
The numbers below are one seeded synthetic draw (seed 20261102, offer WEEKEND_15). They are rounded. They are not a real incrementality result. The generator's planted redemption probabilities are 0.08 and 0.12, plus a 0.01 nudge when pre-period spend is above the median, so an estimate near a 4 point gap is what that draw should look like.
User: How many people per arm do I need to see a 2 point redemption lift from a 10% baseline?
Assistant calls power_calc with metric=proportion, baseline=0.1, mde=0.02. The normal approximation returns 3841 per arm at alpha 0.05 and 80% power, equal allocation, two-sided. That formula does not account for peeking or extra metrics.
User: What happened on the synthetic weekend offer?
Assistant calls analyze_lift with offer_id=WEEKEND_15 and outcome=redeemed. On this file the control redemption rate is about 8.83% (n=2016) and treatment is about 13.16% (n=1984). Absolute lift is about +4.33 percentage points (95% CI about +2.39 to +6.26). Relative lift is about +49%. CUPED on pre-period spend barely moves that interval here, because the covariate is only weakly related to the binary outcome in this draw.
The same call with outcome=post_spend gives control mean spend about 21.09 and treatment about 23.45, absolute lift about +2.36 (95% CI about +2.01 to +2.71). With CUPED, theta is about 0.51 and the interval tightens to about +2.32 to +2.69.
User: Write that up and close the experiment.
Assistant calls create_experiment, reads experiment://{id}/results, then draft_readout. If the client supports sampling, the client's model writes the prose from those facts. If it does not, the tool returns a template that repeats the facts and adds none. close_experiment then asks the user to confirm. Declining leaves the record running.
Tools, briefly
power_calcsolves for per-arm sample size or absolute MDE. Proportions use an unpooled two-sample normal formula. Means use a known common sigma. Equal allocation, two-sided, no continuity correction.assign_cellsmapsSHA-256(salt + 0x1f + unit_id)into named weights. Arm order is alphabetical. The same salt and id always get the same arm.analyze_liftreads the synthetic offer or raw arrays. The interval is a normal approximation from each arm's sample variance, or a percentile bootstrap (100 to 10,000 replicates) with progress reports. CUPED uses one pooled theta.check_srmis a chi-square goodness of fit. The default alpha is 0.001. A mismatch is evidence against the intended weights, not a diagnosis.create_experiment,list_experiments,close_experimentare the SQLite record. Closing is destructive and idempotent once closed.
Evals
evals/tasks.json is a small set of natural-language tasks with the tool call they should produce. evals/recorded.json is the recorded call for each task. Offline mode does not call a model:
uv run python -m experiment_mcp.evalsIt checks that every recorded call matches the expected tool and arguments exactly, then runs those calls in-process. CI runs this. It does not call a model.
Live mode sends each task to OpenRouter and scores the tool call the model returns. The tool list is tools/list from this server, converted to the OpenAI tool format. Model calls are not executed. The default model is openai/gpt-4o-mini. --model overrides EVAL_MODEL. --repeats runs each task more than once. Results go to evals/results/<model>-<date>.json and a Markdown summary beside it. tasks.json has eight tasks, so the score is indicative, not a benchmark.
OPENROUTER_API_KEY=... uv run python -m experiment_mcp.evals --live --model openai/gpt-4o-miniIn GitHub Actions, add the repository secret OPENROUTER_API_KEY, then open Actions, choose Evals (live), and run the workflow. The workflow is workflow_dispatch only. It writes the summary on the job and uploads evals/results/ as an artifact. It does not run on push or pull request.
Development
uv sync
uv run ruff check .
uv run ruff format --check .
uv run mypy
uv run pytest
uv run python -m experiment_mcp.evalsGitHub Actions runs the same steps.
Regenerate the synthetic files with uv run python -m experiment_mcp.synthetic. Review the diff. Do not treat a regenerated file as new evidence.
Design decisions and limitations
The HTTP server validates tokens and publishes protected-resource metadata. The dev minter is a separate localhost process with a shared secret. It is not an authorization-code server and must not be deployed as one.
confirm=trueonclose_experimentis the fallback when elicitation is unavailable. A client can set it without showing a person a form. Treat that as an explicit confirmation from whoever is driving the client, not as a second factor.Sampling and MCP logging are deprecated as of the 2026-07-28 spec (SEP-2577).
draft_readoutstill uses sampling when the client offers it, and otherwise returns the template. Logs always go to the Python logger. Protocol log notifications are still sent for clients that opt in, and they may warn.Lift is a difference in means. There is no sequential testing, no multiple-comparison adjustment, and no CUPED beyond the linear pooled adjustment. Bootstrap intervals are percentiles, not BCa. Relative intervals use the delta method, or bootstrap percentiles when that method is selected.
Two arms are analyzed at a time. Assignment can name more than two arms.
SQLite is a single local file. There is no multi-replica subscription bus.
stdio does not authenticate. Do not point it at a dataset you would not hand to the desktop app.
The example figures above are from the committed synthetic generator. They will change if the seed or the generator changes. They are not a benchmark against a public study.
Available Tools
8 toolsanalyze_liftAnalyze liftARead-only
Estimate absolute and relative lift with a confidence interval.
Pass offer_id to read the bundled synthetic exposures, or pass control
and treatment arrays. Bootstrap intervals report progress and stop
between batches if the client cancels. CUPED uses a pre-period
covariate when one is available. Reported arm means stay on the
original scale; the lift uses adjusted outcomes when CUPED is on.
| Name | Required | Description | Default |
|---|---|---|---|
| seed | No | ||
| alpha | No | ||
| control | No | Control outcomes. | |
| outcome | No | redeemed or post_spend for an offer. Any label for raw arrays. | redeemed |
| offer_id | No | Offer id in the bundled dataset. | |
| ci_method | No | normal | |
| treatment | No | Treatment outcomes. | |
| use_cuped | No | Adjust both arms with a pooled CUPED theta. | |
| control_arm | No | control | |
| treatment_arm | No | treatment | |
| control_covariate | No | Control CUPED covariate. | |
| treatment_covariate | No | Treatment CUPED covariate. | |
| bootstrap_iterations | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| alpha | Yes | |
| control | Yes | |
| outcome | Yes | |
| cancelled | No | |
| ci_method | Yes | |
| treatment | Yes | |
| use_cuped | Yes | |
| cuped_theta | No | |
| absolute_lift | Yes | |
| relative_lift | No | |
| absolute_ci_low | Yes | |
| relative_ci_low | No | |
| absolute_ci_high | Yes | |
| relative_ci_high | No | |
| bootstrap_iterations | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, so safety is covered and the description adds genuine behavioral detail beyond them: bootstrap reports progress and halts between batches on client cancellation, and with CUPED the arm means stay on the original scale while lift uses adjusted outcomes. That scale-semantics note is non-obvious and prevents misreading results. It does not mention error behavior when neither offer_id nor arrays are supplied.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose, then input modes, then behavioral caveats — a sensible order with no filler sentences. It is dense but every sentence carries information; a short lead-in labeling the CUPED sentences would improve scannability slightly.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained, and the primary call pattern is covered. However, for a 13-parameter statistical tool with only 54% schema coverage, the description leaves the no-argument default path and the arm-label parameters unexplained, which an agent would need to select correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 54% across 13 parameters, and the description compensates for some: offer_id vs control/treatment arrays, the CUPED covariate, and bootstrap ci_method. It says nothing about seed, bootstrap_iterations, alpha, outcome labels, or the control_arm/treatment_arm selectors, leaving several parameters meaningful only via their schema titles.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence names a specific verb and output ('Estimate absolute and relative lift with a confidence interval'), which is unambiguous against siblings like power_calc or check_srm. It does not explicitly contrast itself with those siblings, so an agent must infer the boundary rather than being routed.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly states the two invocation modes: 'Pass offer_id to read the bundled synthetic exposures, or pass control and treatment arrays.' That is a real either/or routing instruction. It stops short of saying when to prefer this tool over power_calc or draft_readout, and gives no exclusion conditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
assign_cellsAssign experiment cellsARead-only
Assign units to arms with a salted SHA-256 bucket.
Arms are ordered by name. The same salt and unit id always map to the same arm. This does not write an experiment record.
| Name | Required | Description | Default |
|---|---|---|---|
| salt | Yes | Experiment salt. Changing it reassigns units. | |
| splits | Yes | Arm name to traffic weight. Weights must sum to 1. | |
| unit_ids | Yes | Stable unit ids, usually customer ids. |
Output Schema
| Name | Required | Description |
|---|---|---|
| salt | Yes | |
| counts | Yes | |
| arm_order | Yes | |
| assignments | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, and the description adds genuinely new behavioral facts: determinism ('the same salt and unit id always map to the same arm'), ordering ('arms are ordered by name'), and the explicit non-persistence guarantee. It stops short of covering output shape, but an output schema exists, so the remaining gap is minor.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the core action and mechanism, and no filler. The SHA-256 detail is arguably implementation-level, but it is what justifies the determinism claim, so it earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only, three-parameter computation tool with full schema coverage and an output schema, the description covers purpose, determinism, ordering, and the non-write guarantee. Nothing an agent needs in order to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline would be 3, but the description adds real meaning: it explains the stability contract between salt and unit id and clarifies that splits are matched against name-ordered arms. That ties the three parameters together in a way the schema alone does not.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Assign units to arms') plus the mechanism ('salted SHA-256 bucket'), which immediately separates it from siblings like check_srm, analyze_lift, or create_experiment. An agent can tell this is a deterministic assignment computation, not an experiment mutation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The negative guidance 'This does not write an experiment record' usefully implies this is a compute/preview step rather than a persistence step, but it never names an alternative or states the condition under which an agent should call this versus create_experiment or check_srm. Usage is only inferable, not spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_srmCheck sample ratioARead-only
Chi-square test of observed arm counts against intended weights.
A mismatch means the counts are unlikely given the weights. It does not identify the cause.
| Name | Required | Description | Default |
|---|---|---|---|
| alpha | No | Mismatch threshold. 0.001 is the usual SRM bar. | |
| counts | Yes | Observed assignments per arm. | |
| expected_weights | Yes | Intended traffic weights. Must sum to 1 and match the count keys. |
Output Schema
| Name | Required | Description |
|---|---|---|
| alpha | Yes | |
| counts | Yes | |
| p_value | Yes | |
| mismatch | Yes | |
| chi_square | Yes | |
| expected_counts | Yes | |
| expected_weights | Yes | |
| degrees_of_freedom | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, so safety is covered. The description adds genuine value with the caveat that a mismatch does not identify a cause, but says nothing about minimum sample size requirements or how borderline results near alpha should be interpreted.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with zero waste; the operation is front-loaded and the interpretation caveat follows. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema and full annotation coverage reduce the burden, but the description still lacks the when-to-use context and sample-size expectations an agent needs to decide whether to call this versus power_calc. Adequate, not complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, including the alpha default and its 'usual SRM bar' note, so the schema carries the parameter burden and baseline 3 applies. The description adds no meaning about counts or expected_weights beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: a chi-square test of observed arm counts against intended weights. An agent can tell this apart from power_calc or analyze_lift. Minor gap: the tool name's abbreviation (SRM) is never expanded, so the domain term is only implied by 'A mismatch means...'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the statistical operation but there is no explicit when-to-use, no prerequisites, and no routing to siblings such as power_calc or assign_cells. The only guidance given is interpretive (a mismatch does not identify the cause), not procedural.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
close_experimentClose experimentADestructiveIdempotent
Mark an experiment closed after the user confirms.
The confirmation form is elicited when the client supports it. If the client does not, the experiment stays open until this tool is called again with confirm=true. Declining or cancelling the form does not close it.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | Leave false to ask the user through elicitation. Set true only after a person has agreed, which is the fallback when the client cannot show a form. | |
| experiment_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| closed | Yes | |
| detail | Yes | |
| reason | Yes | |
| status | No | |
| experiment_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, idempotentHint=true, and readOnlyHint=false, so the safety profile is covered. The description adds genuinely non-obvious behavior beyond that: the elicitation form is client-dependent, and a failed/declined confirmation leaves the experiment open, meaning the call may be a no-op rather than a partial mutation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The confirmation contract is front-loaded in the first sentence and the fallback path follows logically. The passive phrasing ("The confirmation form is elicited when the client supports it") is slightly indirect, but nothing is redundant or padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need no explanation, and the description fully covers the two-phase confirmation protocol an agent must understand to avoid a silent no-op. It omits whether closing is reversible and what permissions are needed, which are minor given destructiveHint is already set.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 50%: the confirm parameter is documented in the schema, and the description reinforces its meaning (only true after a person has agreed) as the elicitation fallback. experiment_id carries no description anywhere, though its meaning is self-evident from the name, so this lands at the baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence gives a specific verb and resource ("Mark an experiment closed") and adds the gating condition ("after the user confirms"). It is trivially separable from siblings such as create_experiment, list_experiments, and draft_readout without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly states when to call it (after user confirmation) and what to do when elicitation is unavailable (call again with confirm=true), plus a negative case (declining/cancelling does not close). It stops short of naming sibling alternatives, but for a terminal-state mutation the when/when-not guidance is explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_experimentCreate experimentC
Store a running experiment. This does not assign traffic by itself.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| salt | No | ||
| notes | No | ||
| offer_id | No | ||
| hypothesis | Yes | ||
| traffic_split | No | ||
| primary_metric | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | |
| name | Yes | |
| salt | Yes | |
| notes | Yes | |
| status | Yes | |
| offer_id | Yes | |
| closed_at | Yes | |
| created_at | Yes | |
| hypothesis | Yes | |
| traffic_split | Yes | |
| primary_metric | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare this as a non-readonly, non-idempotent, non-destructive write. The description adds useful scope by clarifying that traffic assignment does not happen here, preventing an incorrect assumption, but it does not explain permissions, validation behavior, or what the traffic_split/salt fields do.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The two sentences are tight and front-loaded, with no filler. It is appropriately brief, though that brevity comes at the cost of leaving important parameter and workflow context unexplained.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although an output schema and annotations reduce the need to explain return values and safety, the description is too thin for a seven-parameter write tool with 0% schema description coverage. It gives no help on required fields, optional configuration such as traffic_split or salt, or how this tool relates to the broader experiment workflow.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% across seven parameters, and the description adds no meaning for name, hypothesis, primary_metric, salt, traffic_split, offer_id, or notes. This is the same failure mode as the low-calibration example where three parameters at 0% coverage scored a 1; here the gap is larger.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb ('Store') and resource ('experiment'), and its caveat 'does not assign traffic by itself' helps separate it from assign_cells. It is clear enough to identify the tool, though 'running experiment' is slightly vague about whether it is creating a new record or persisting an existing one.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The only usage-related information is the negative constraint that it does not assign traffic, which hints that assignment is a separate step. There is no explicit guidance on when to use this versus list_experiments, close_experiment, or assign_cells, nor any prerequisite or sequencing guidance beyond that single caveat.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
draft_readoutDraft a readoutARead-only
Ask the client model to summarize an experiment, with a template fallback.
Sampling is deprecated in the 2026-07-28 spec and many clients do not offer it. Those clients get a deterministic draft built from the same facts.
| Name | Required | Description | Default |
|---|---|---|---|
| experiment_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| text | Yes | |
| facts | Yes | |
| source | Yes | |
| experiment_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnlyHint, openWorldHint=false), and the description adds genuinely useful behavioral context that structured fields cannot convey: output may come from client-side sampling or a deterministic template fallback, so results vary in nature across clients. It still does not say whether the LLM path is validated, whether failures fall back silently, or what quality/consistency differences to expect.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the primary action and followed by the fallback caveat. No filler, though the line breaks and stray indentation in the source make it slightly less tidy than it could be.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple, one parameter, and an output schema exists so return values need not be explained. However, the nondeterministic-vs-template duality is the tool's defining behavioral trait and the description leaves it ambiguous whether the caller can detect which path was taken or how to force the deterministic one.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
A single required experiment_id with 0% schema description coverage; the description does not explain the identifier's form, where to obtain it, or whether it must reference an existing experiment. With only one, self-descriptively named parameter the gap is small, but the low coverage means the description should have compensated and does not.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'summarize an experiment' producing a readout draft. None of the siblings (power_calc, analyze_lift, check_srm, list_experiments, create_experiment, close_experiment) overlap with summarization, so the purpose is distinguishable without opening schemas, though the description never explicitly contrasts itself with the analysis siblings an agent might otherwise reach for.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage context is only implied: you call it to get a readout draft for an experiment. There is no statement of when to prefer this over analyze_lift or list_experiments, no preconditions (does the experiment need results first?), and no note on whether an experiment_id that doesn't exist is an error.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_experimentsList experimentsCRead-only
List experiment records stored in this server's SQLite file.
| Name | Required | Description | Default |
|---|---|---|---|
| status | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| experiments | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, and the mention of a local SQLite file merely restates that the data is local. The description doesn't disclose the default behavior when status is omitted, the ordering, whether results are paginated, or what an empty result means — behavioral traits the annotations don't cover.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One short, front-loaded sentence with no filler or redundancy. It is efficient, though the brevity comes at the cost of the missing parameter and usage detail noted above.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-value explanation isn't required, and the tool is simple with one optional parameter. Still, for a listing tool the description should say what the default listing contains and how the status filter behaves, which it omits.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must carry the burden for the single status parameter, but it never mentions status at all. It doesn't explain the running/closed enum values or that the null default returns all experiments, leaving the parameter's meaning undocumented in both places.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb and resource: "List experiment records." The clause about the SQLite file adds scope, though it doesn't differentiate this tool from any sibling or note that the other siblings are mutation/analysis tools rather than other listers.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use guidance at all: nothing says to call this before close_experiment or draft_readout, and nothing indicates whether an unfiltered call returns everything. The agent must infer usage entirely from the name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
power_calcSample size or MDEARead-only
Estimate equal-allocation sample size or MDE for a two-sided test.
Provide exactly one of mde or n_per_arm. The normal approximation does
not include peeking, multiple metrics, or a finite-population correction.
| Name | Required | Description | Default |
|---|---|---|---|
| mde | No | Absolute difference to detect. Omit when solving for MDE. | |
| alpha | No | Two-sided type I error. | |
| power | No | Desired power, 1 - beta. | |
| sigma | No | Outcome standard deviation. Required for mean designs. | |
| metric | Yes | ||
| baseline | No | Control rate. Required for proportion designs. | |
| n_per_arm | No | Per-arm sample size. Omit when solving for sample size. |
Output Schema
| Name | Required | Description |
|---|---|---|
| mde | No | Absolute difference (treatment minus control) the design can detect. |
| alpha | Yes | |
| power | Yes | |
| sigma | No | |
| metric | Yes | |
| formula | Yes | |
| baseline | No | |
| n_per_arm | No | |
| allocation | No | |
| solved_for | Yes | |
| alternative | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, so safety is covered. The description adds substantive behavior beyond that: equal allocation only, two-sided test, and an explicit list of what the normal approximation ignores (peeking, multiple metrics, finite-population correction). It does not describe output shape, but an output schema exists to carry that.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, nothing redundant. The core purpose is front-loaded and the constraint and caveat follow in priority order.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter statistical tool with an output schema, the description covers the solve-mode constraint and the modeling limitations that an agent must know to use results correctly. Return values need not be explained given the output schema, and the remaining parameter detail is carried by 86% schema coverage.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 86%, so the per-parameter meanings are largely handled structurally and a baseline 3 applies. The description earns an extra point by stating a cross-parameter constraint the schema cannot express — exactly one of mde or n_per_arm must be supplied — which is the single most likely invocation error for this tool.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Estimate equal-allocation sample size or MDE for a two-sided test.' That tells an agent both what is computed and the two solve modes, which no sibling tool covers. It does not name or differentiate against siblings, but the purpose itself is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Provide exactly one of mde or n_per_arm' is an explicit operational rule that selects the solve direction, and the caveat sentence tells the agent when the result is not valid (peeking, multiple metrics, finite-population correction). It stops short of naming an alternative approach for those excluded cases, so there is no full when-to-use-instead routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
8 tool updates
v0.1.0- First observed
analyze_lift - First observed
assign_cells - First observed
check_srm - First observed
close_experiment - First observed
create_experiment - First observed
draft_readout - First observed
list_experiments - First observed
power_calc
TDQS
Scored across 8 tools
Each tool targets a distinct phase of the experiment workflow: power planning, assignment, lift analysis, SRM checking, record lifecycle, and readout drafting. The descriptions explicitly clarify boundary cases, such as assign_cells not writing a record and create_experiment not assigning traffic, which further reduces misselection.
All tool names use consistent snake_case, and most follow a verb_noun pattern such as assign_cells, analyze_lift, and create_experiment. The main minor deviation is power_calc, which uses a noun abbreviation rather than a clear verb form like calculate_power.
Eight tools are well-scoped for an experiment management and analysis server. Each tool covers a meaningful part of the workflow without obvious redundancy or inflated surface area.
The surface covers experimental design, assignment, analysis, SRM checks, record creation/listing/closure, and readout drafting. Minor gaps remain, such as no get_experiment or update_experiment operation, but the core lifecycle is well represented.
Maintenance
Related MCP Connectors
Governed data discovery, exact queries, decisions, simulations, and runtime utilities over MCP.
Analytics your AI agent can actually use. Track, experiment, and optimize via MCP.
Shopify CRO platform via MCP: 100+ tools for analytics, experiments, authoring, proven lift.
Independent statistical feature evaluation for machine learning and forecasting. ML Referee tests up to 1,000 candidate features in one run against a declared baseline, helping researchers and AI agents decide which inputs to pursue, which bounded feature repairs merit further testing, and where more evidence is needed—without a separate ablation run for every feature. Use the MCP tools to discover input schemas, check access and credits, preflight a dataset, obtain a quote, submit an authorized evaluation, and retrieve structured results and reports. Results are scoped to the submitted evidence and evaluation design; they do not guarantee future model performance. Invite-only beta with manual approval. Public tool discovery requires no authentication; account and evaluation tools require OAuth through approved client connections and available evaluation credits. Connect directly to https://api.mlreferee.com/mcp. Website: https://mlreferee.com Request beta access: https://mlreferee.com/beta Agent setup and documentation: https://mlreferee.com/agents
Related MCP Servers
AlicenseNot gradedqualityDmaintenanceEnables running LLM evaluations, experiments, and custom evaluators through a standardized MCP interface.16Apache 2.0- AlicenseAqualityAmaintenanceProvides MCP access to plan and execute reproducible computational experiments with immutable execution plans, artifact provenance, verification checks, and support for direct, Slurm, and PBS backends.17Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables statistical A/B testing by computing Welch's two-sample t-test, Satterthwaite degrees of freedom, and two-tailed p-values, alongside time-series forecasting, anomaly detection, regression, and PCA through MCP tools.7MIT
- AlicenseNot gradedqualityBmaintenanceConnects MCP clients to a hosted experimentation platform so agents can start, pause, stop, and roll back live website A/B tests, inspect Bayesian results, and capture hypotheses.226 npmMIT