| title | MCP BSON Benchmark — Correctness Oracle + Efficiency Measurement |
|---|---|
| status | draft |
| date | 2026-05-03 |
Status: Draft
Date: 2026-05-03
Author: AI-assisted design
Use the Studio Pro MCP server as a dual-purpose tool:
-
BSON correctness oracle — Studio Pro is the authoritative BSON serializer. Executing the same logical operations via MCP and via mxcli, then diffing the resulting mxunit files, reveals structural bugs in mxcli's BSON output that
mx checkdoes not catch. -
Empirical efficiency benchmark — Running the doctype-test scripts through both paths measures the token and time cost claims in
MXCLI_STRATEGIC_POSITIONING.mdrather than arguing them analytically. The MDL path externalises all execution to a subprocess; the MCP path routes every schema, document read, and incremental update through Claude's context window. The difference should be quantifiable and significant.
mx check validates semantic correctness ("does this microflow make sense?") but not structural completeness. mxcli could:
- Omit optional fields that Studio Pro always writes
- Use wrong
$typestorage names that happen to parse but behave differently at runtime - Write wrong default values that affect UI or runtime behaviour
- Produce subtly wrong association pointer structure (the ParentPointer/ChildPointer inversion is a documented example)
None of these are caught by mx check. They become visible only when Studio Pro opens the file and the developer sees unexpected behaviour — or when a comparison against Studio Pro's own output is performed.
The strategic positioning document (docs/01-project/MXCLI_STRATEGIC_POSITIONING.md) argues that MDL has a structural token advantage over MCP-style tool-call-by-tool-call editing. The argument is sound analytically, but "10–100× fewer tokens" is more persuasive as a measured result than as a back-of-envelope calculation.
The doctype-test scripts are an ideal corpus: they cover the full feature surface, are already written, and span domain model, microflows, pages, security, and workflows. Running each through both paths yields a reproducible dataset.
doctype-test script (e.g. 01-domain-model-examples.mdl)
│
├──────────────────────────────────────────────────┐
│ Path A: MDL │ Path B: MCP
▼ ▼
mxcli exec script.mdl -p ProjectA.mpr Claude reads MDL statement by statement
│ → ped_get_schema (per type)
│ → ped_find_document (idempotency check)
│ → ped_create_document / ped_update_document
│ → ped_check_errors
▼ ▼
mprcontents/**/*.mxunit (new files) mprcontents/**/*.mxunit (new files in SP project)
│ │
└─────────────────────┬────────────────────────────┘
▼
BSON diff (normalised)
+ metrics report
Both paths start from a copy of the same blank template project (mx-template-projects/Template1024App-main). New documents appear as new mxunit files in mprcontents/. The comparison reads these files, matches them by document name and type, and diffs their BSON content field-by-field.
Each document in an MPR v2 project is a separate BSON file at:
mprcontents/<uuid[0:2]>/<uuid[2:4]>/<uuid>.mxunit
After executing each path, the new mxunit files are identified by comparing the file set against the baseline template. These are the documents created by that path.
Documents are matched by qualified name + document type, not by UUID. The document name is embedded in the BSON content (e.g., name field for microflows, entity name for domain model entries).
For domain models, the comparison is at the subdocument level (entity by entity, attribute by attribute), since the entire domain model lives in one mxunit file.
| Field class | Treatment |
|---|---|
All UUID-typed fields ($id, element IDs, pointer references) |
Replace with stable ordinal derived from structural path (e.g., entity[0]/attribute[2]) |
Canvas position fields (relativeMiddlePoint, x, y, width, height) |
Strip — legitimately differ |
| Array element ordering | Sort canonically by name field where present |
| Auto-generated captions / default display strings | Include — discrepancies here are real bugs |
Fields that remain after normalisation must match exactly. Any mismatch is a correctness finding.
{
"script": "01-domain-model-examples.mdl",
"document": "DmTest/DomainModel",
"status": "DIFF",
"findings": [
{
"path": "/entities/0/attributes/2/type",
"mxcli": "String",
"studio_pro": "String",
"note": "match"
},
{
"path": "/entities/0/defaultEntityAccessRights",
"mxcli": null,
"studio_pro": "ReadWrite",
"note": "mxcli omits field Studio Pro always writes"
}
]
}For each script / path combination, record:
| Metric | MDL path | MCP path |
|---|---|---|
| Wall time (s) | Time for mxcli exec to complete |
Time from first tool call to ped_check_errors returning clean |
| Claude tool calls | 1 (mxcli exec bash call) |
N (sum of all ped_* calls) |
| Tokens in (est.) | Script size + 1 tool invocation | Sum of all tool call inputs (schemas + document reads + payloads) |
| Tokens out (est.) | mxcli stdout (pass/fail summary) | Sum of all tool call responses |
| Errors encountered | 0 or exit code | ped_check_errors findings requiring fix iterations |
Token counts for the MCP path can be estimated from the raw JSON payload sizes of each tool call, or measured precisely if the benchmark is run via the Claude API with usage reporting enabled.
The MDL path token cost intentionally excludes script authoring time — the doctype-test scripts are pre-authored. This isolates execution cost and represents the steady-state case (an agent that has already written the script or is replaying it).
Script: 01-domain-model-examples.mdl
Covers: CREATE MODULE, CREATE ENUMERATION, CREATE ENTITY (all attribute types, constraints, indexes, generalization, documentation), CREATE ASSOCIATION.
MCP translation: mechanical. Each MDL statement maps to 1–2 MCP calls. No expression encoding or widget hierarchy required.
Expected findings: missing default field values (access rights, documentation defaults), possible $type field differences for enumeration subtypes.
Script: 02-microflow-examples.mdl
Covers: parameters, return types, activities (CreateObject, ChangeObject, Retrieve, Commit, microflow calls, show message, decisions, loops, etc.).
MCP translation: harder. Each activity requires schema fetch + correct ID references between sequenceflows and activities. Expression strings must be correctly encoded. Ordering within the flow matters.
Expected findings: sequenceflow connection points, expression field encoding differences, missing default activity properties.
Script: 03-page-examples.mdl
Covers: layouts, widget hierarchies, pluggable widgets.
MCP translation: most complex. Widget trees are deeply nested and highly order-sensitive.
Expected findings: widget default property differences, pluggable widget type/object field completeness.
The MCP path does not use an automated translator. Claude reads the MDL script and executes the equivalent operations using MCP tools, following the same conventions it uses for any Studio Pro editing task. This is intentional:
- It tests the realistic MCP workflow (how an agent would actually use PED to achieve the same result)
- It captures the real token cost of interactive MCP editing, including schema fetching and error recovery
- It does not require building a separate translator that could short-circuit the measurement
The translation is logged as part of the benchmark output — the full sequence of MCP calls made to achieve the task is captured alongside the metrics.
If the BSON correctness diff is clean for a given script, it confirms mxcli produces Studio Pro-equivalent output for that feature area — a strong quality signal.
If the efficiency measurement shows, e.g., that 01-domain-model-examples.mdl (creating ~20 entities + enumerations) takes 1 mxcli call vs. 60+ MCP tool calls with 40k+ tokens of schema and document payload, that is a concrete, citable data point for the strategic positioning argument — not an analytic estimate.
The benchmark is primarily a workflow, not a new mxcli command. The infrastructure needed:
| Component | Description | Location |
|---|---|---|
| Blank project copy script | Copy template, reset module state | scripts/benchmark-setup.sh |
| mxunit diff tool | Read two mxunit file sets, normalise UUIDs/positions, output JSON diff | cmd/mxcli/cmd_bson_diff.go (or standalone script) |
| Benchmark runner | Orchestrates both paths, collects metrics, writes report | docs/14-eval/benchmark/ |
| Results store | JSON per script per run, for trend tracking | benchmark-results/ (gitignored) |
The mxunit diff tool (mxcli bson-diff) is the only new mxcli command required. It takes two project directories and a document list, and outputs the normalised JSON diff. It reuses the existing BSON parser in sdk/mpr/parser.go.
docs/11-proposals/
└── PROPOSAL_mcp_bson_benchmark.md (this file)
docs/14-eval/benchmark/
├── README.md (how to run)
├── run-benchmark.sh (orchestration script)
└── results/ (gitignored)
└── 2026-05-03/
├── 01-domain-model/
│ ├── mdl-metrics.json
│ ├── mcp-metrics.json
│ └── bson-diff.json
└── summary.md
cmd/mxcli/
└── cmd_bson_diff.go (new: mxcli bson-diff command)
- Studio Pro project path — the mx-demo-1 MCP server project is at
./mx-test-projects/test6-app/, which is accessible on the local filesystem. It is v2 format with 373 mxunit files. Both sides of the comparison can be done as direct BSON file reads — noped_read_documentJSON proxy needed.
-
Project reset between runs — the MCP path modifies the live Studio Pro project (
test6-app). Between benchmark runs, do we reset by reverting the mprcontents state withgit checkout -- mx-test-projects/test6-app/mprcontents, or does Studio Pro need to close and reopen a clean copy? -
Token measurement precision — for Phase 1, estimating from payload JSON sizes is sufficient. For Phase 2+, running via the Claude API with
usagereporting gives exact counts. Worth deciding whether Phase 1 uses the estimation approach or sets up the API harness from the start.