mcp_server¶
MCP server tools for an autonomous agent. See the MCP server guide for usage and the trust boundary.
mcp_server
¶
MCP server exposing crossverify as a toolchain for an autonomous agent.
An agent can scaffold a project, write the Python + R analysis, call
verify_analysis, read the structured pass/fail/info result, revise, and
re-verify — without parsing console text or shelling out to the CLI itself.
Trust boundary — READ THIS¶
verify_analysis executes the analysis code the project points at (it
imports the Python module and Rscript-runs the R script). An agent that can
choose project_path can therefore cause arbitrary code execution. Two
guardrails are enforced here, but they are not a sandbox:
- Path containment (enforced by the server). A project's
data/python.module/r.scriptmust resolve inside the project folder; a project that escapes it (absolute path or..) comes backverdict="invalid"with the offending path, and no analysis code runs. The server forces this regardless of the project file, so an agent that authors the project cannot disable containment by settingallow_external_paths: true. The opt-out lives with the operator, not the project: setCROSSVERIFY_MCP_ALLOW_EXTERNAL=1in the server's environment to honor the project flag again, and only for projects you trust. - Bounded subprocess. Each
verify_analysisruns in its own process group with a timeout (CROSSVERIFY_MCP_TIMEOUTseconds, default 300) and a minimal environment (an allowlist — the parent's tokens/secrets are not passed). On timeout the whole process group is killed, including theRscriptgrandchild, so a runaway or hostile analysis is genuinely time-bounded and cannot harvest the server's environment.
Still: run this server in a sandbox (container/VM, no credentials in env,
least-privilege filesystem). The read-only tools (validate_project,
scaffold_project, inspect_dataset) do not execute analysis code.
Run it::
pip install "crossverify[mcp]"
crossverify-mcp # stdio transport
_timeout()
¶
Per-verification wall-clock budget in seconds (CROSSVERIFY_MCP_TIMEOUT).
_minimal_env()
¶
Build the allowlisted environment handed to the verification subprocess.
_force_contain()
¶
Whether the server overrides the project's allow_external_paths and contains.
The opt-out flag lives in the (untrusted) project file an agent can author, so
by default the server ignores it and enforces path containment. An operator who
deliberately runs trusted projects can restore the flag's effect by setting
CROSSVERIFY_MCP_ALLOW_EXTERNAL=1 (also true/yes) on the server.
Source code in crossverify/mcp_server.py
_kill_process_tree(proc)
¶
Kill a child and its whole process group/tree, best effort.
The verification child spawns an Rscript grandchild (Phase 5); killing only
the direct child would orphan it. The child is started in its own session
(POSIX) or process group (Windows) so the group can be signalled as a unit.
Source code in crossverify/mcp_server.py
_run_bounded(cmd, env, cwd, timeout)
¶
Run cmd with a wall-clock timeout, killing the whole tree on expiry.
subprocess.run(timeout=...) signals only the direct child, so the Rscript
grandchild would survive the timeout and keep consuming resources. Starting the
child in a new process group lets the timeout path kill the group, making the
advertised time bound real.
Returns:
| Type | Description |
|---|---|
|
|
Raises:
| Type | Description |
|---|---|
TimeoutExpired
|
After killing the process tree, if the timeout elapsed before the child completed. |
Source code in crossverify/mcp_server.py
verify_analysis(project_path, phases=None, skip_r=False, seed=None)
¶
Run the six-phase verification on a project and return structured results.
Executes the project's analysis in a bounded subprocess (timeout + minimal
environment) and returns the full machine-readable result. A failed check is a
normal outcome (verdict="fail"), not an error. A project that fails
validation — including one whose paths escape the project folder — returns
verdict="invalid" with a problems list and runs no analysis code.
The result carries a scope_caveat: agreement across Python and R is strong
evidence a number is not a tool-specific artifact, but it is not proof the
analysis is correct (a shared specification error agrees perfectly). Do not
report a verified result as "correct".
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project_path
|
str
|
Path to the project YAML file. |
required |
phases
|
list[int] | None
|
Phase numbers to run (1-6); omit to run all. |
None
|
skip_r
|
bool
|
Skip Phase 5 cross-tool triangulation (Python-only). |
False
|
seed
|
int | None
|
Override the project's random seed. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
The verification result dict ( |
dict
|
the Python-vs-R |
dict
|
or |
dict
|
produced no parseable output. |
Source code in crossverify/mcp_server.py
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | |
validate_project(project_path)
¶
Check a project file and return any problems, without running an analysis.
Use this to fix a project's configuration before calling verify_analysis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
project_path
|
str
|
Path to the project YAML file. |
required |
Path containment is reported the same way verify_analysis enforces it, so a
project that validates here will not be rejected for an out-of-tree path later.
Returns:
| Type | Description |
|---|---|
dict
|
|
dict
|
list is empty. |
dict
|
malformed (no |
Source code in crossverify/mcp_server.py
scaffold_project(target_dir)
¶
Scaffold a new project (project.yaml + analysis.py + analysis.R) to fill in.
Existing files are left untouched, so this is safe to re-run.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target_dir
|
str
|
Directory to scaffold into (created if needed). |
required |
Returns:
| Type | Description |
|---|---|
dict
|
|
Source code in crossverify/mcp_server.py
inspect_dataset(csv_path)
¶
Summarize a dataset (Phase-1 intake) without running an analysis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
csv_path
|
str
|
Path to the CSV dataset. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
|
dict
|
|