mcpmark / synth /README.md
haochengsama's picture
Add files using upload-large-folder tool
97cb846 verified
|
Raw
History Blame Contribute Delete
6.32 kB

Synthetic filesystem-task generator

Mass-produce MCPMark filesystem benchmark tasks (test environment + description.md + verify.py + meta.json) with deterministic, self-checked verifiers. An LLM (DeepSeek via .mcp_env, by default) only makes file content and names realistic; it never writes the verification logic.

Why it is reliable

Two verifier strategies, both checked by a built-in oracle at generation time:

  • Recomputable β€” verify.py re-derives the correct answer from the resulting files (e.g. recompute the 10 smallest files, regroup by content hash). No stored answer key, so the model cannot shortcut it.
  • Planted ground truth β€” for computation/semantic tasks the generator controls the data and labels it (e.g. category: personal|work, song rating/year), so the answer is exactly computable without LLM judgement.

Every task is validated before it is written: the oracle solves it and verify.py must exit 0 on the correct answer and non-zero on the untouched environment. Tasks that fail this self-check are discarded.

Usage

# from repo root, inside the mcpmark conda env
conda run -n mcpmark python -m synth.generate --n 13 --seed 1      # LLM content
conda run -n mcpmark python -m synth.generate --n 13 --no-llm      # offline
conda run -n mcpmark python -m synth.generate --n 4 --types music_report,budget_computation

Each task gets a unique category_id like synth_music_report_01. Run them with the normal pipeline (synth is a substring filter that matches them all):

conda run -n mcpmark python -m pipeline --mcp filesystem --k 1 \
  --models deepseek-v3.2-instruct --tasks synth --exp-name synth-run

Coverage (8 benchmark categories)

Generator key Benchmark category Verifier
size_classification file_property recomputable
extension_grouping file_property (variant) recomputable
smallest_merge file_context recomputable
duplicate_finder file_context recomputable
uppercase file_context planted
pattern_matching file_context recomputable
file_splitting file_context recomputable
structure_mirror folder_structure recomputable
author_folders papers recomputable
gradebased_score student_database recomputable
music_report desktop planted
budget_computation desktop_template planted
clause_lookup legal_document planted

Not covered: threestudio / votenet β€” these run on real 3D ML codebases and cannot be faithfully synthesized (only loose structural analogs would be possible).

Each subtask folder has its own README.md with task details, a worked example, and a sample trajectory β€” see e.g. generators/filesystem/duplicate_finder/README.md. Note these tasks reorganize files (move/group); they never delete or rewrite file contents, and any output folder (e.g. duplicates/) is created by the evaluated model, not the generator.

Inspect a trajectory

Every pipeline run saves the agent trajectory to results/<exp>/<model>__filesystem/run-<k>/<task>/messages.json. Render it as a readable timeline (πŸ‘€ instruction, πŸ’¬ thoughts, πŸ”§ tool calls, πŸ“€ results):

conda run -n mcpmark python -m synth.trace results/<exp> --list           # list all
conda run -n mcpmark python -m synth.trace results/<exp>/<model>__filesystem/run-1/<task>

Add a new task type

Create generators/filesystem/<your_subtask>/__init__.py with a Generator subclass, then add it to the REGISTRY tuple in generators/filesystem/__init__.py. Implement four methods:

  • build(env_dir, llm, rng) -> spec β€” write the initial files, return a spec dict.
  • description(spec) -> str β€” the instructions the model sees. Be precise: the verifier is exact, so any output-format ambiguity will fail otherwise-correct work.
  • verify_src(spec) -> str β€” a self-contained verify.py (stdlib only). Read the test dir from os.environ["FILESYSTEM_TEST_DIR"]; sys.exit(0) on pass, non-zero on fail. Use _render_verify(body, consts) to inject constants safely.
  • solve(work_dir, spec) β€” the oracle: perform the correct solution in place. This both powers the self-check and forces you to prove the task is solvable.

Files

synth/
β”œβ”€β”€ generate.py                  # CLI: build tasks + oracle self-check
β”œβ”€β”€ trace.py                     # CLI: render a saved trajectory
β”œβ”€β”€ llm.py                       # LiteLLM wrapper (DeepSeek default) + offline fallback
└── generators/                  # organized by MCP service β†’ subtask
    β”œβ”€β”€ __init__.py              #   merges per-service REGISTRYs
    └── filesystem/              #   the filesystem service
        β”œβ”€β”€ __init__.py          #     filesystem REGISTRY
        β”œβ”€β”€ base.py              #     Generator base class + shared helpers
        β”œβ”€β”€ duplicate_finder/__init__.py    # one folder per subtask
        β”œβ”€β”€ smallest_merge/__init__.py
        β”œβ”€β”€ size_classification/__init__.py
        β”œβ”€β”€ extension_grouping/__init__.py
        β”œβ”€β”€ uppercase/__init__.py
        β”œβ”€β”€ pattern_matching/__init__.py
        β”œβ”€β”€ file_splitting/__init__.py
        β”œβ”€β”€ structure_mirror/__init__.py
        β”œβ”€β”€ author_folders/__init__.py
        β”œβ”€β”€ gradebased_score/__init__.py
        β”œβ”€β”€ music_report/__init__.py
        β”œβ”€β”€ budget_computation/__init__.py
        └── clause_lookup/__init__.py

Each subtask folder holds its Generator (and is the natural place to later add per-task templates/fixtures). Generated tasks land in the project's existing tasks/ and test_environments/; trajectories land in results/.

To add a new MCP service, create generators/<service>/ with its own base.py and subtask folders, then merge its REGISTRY in generators/__init__.py.