Hardware-in-the-loop development platform for the Commodore 64.
Markdown
at main

Ultimate Tool MCP adapter #

Distribution copy. This is the sanitized public copy shipped in the docs/ directory of the release archives. It is derived from the repository document docs/MCP.md, which is withheld from every archive in its original form. Transformations applied for distribution: this provenance notice; the physical-gate sentence for the source-linked capture was reduced to the same limits stated inline, with the pinned device identity removed, because the acceptance record it linked to is withheld. No tool, envelope, or error content was changed, and every relative link below resolves inside both archives.

Adapter schema: 12

ultimate-tool-mcp is a local stdio MCP server over the versioned Ultimate Tool HTTP service. It does not implement builds, targets, jobs, hardware authorization, or evidence access independently.

Start the project service first:

cargo run -q -p ultimate-tool -- serve

Then configure an MCP client to launch the adapter:

{
  "mcpServers": {
    "ultimate-tool": {
      "command": "/ABSOLUTE/PATH/TO/ultimate-tool-mcp",
      "args": [
        "--service-url",
        "http://127.0.0.1:6464"
      ]
    }
  }
}

Build the binary with:

cargo build --release -p ultimate-tool-mcp

The adapter accepts only an http://127.0.0.1 origin without credentials, paths, queries, or fragments. MCP protocol bytes are the only stdout output; startup failures go to stderr.

Tools #

  • get_project
  • list_targets
  • list_runs
  • get_run
  • resolve_address
  • get_run_timeline
  • get_run_diagnostic_report
  • get_run_diagnostic_items
  • get_crash_report
  • compare_runs
  • compare_run_checkpoints
  • list_checkpoint_comparisons
  • get_checkpoint_comparison
  • read_evidence
  • get_job
  • get_active_job
  • probe_ultimate
  • prepare_debug_stream_capture
  • cancel_debug_stream_capture_preparation
  • start_debug_stream_capture
  • prepare_source_linked_capture
  • start_source_linked_capture
  • start_test_job
  • create_native_preparation
  • create_native_intent
  • get_native_intent
  • revoke_native_intent
  • start_native_run
  • start_native_health_check
  • start_riscv_smoke_job (legacy bounded smoke)

Every tool returns a structured envelope containing adapter schema_version, operation, and either data or a stable error with code, layer, message, and optional HTTP status. The same JSON is also returned as text for older MCP clients.

Jobs are asynchronous. start_test_job returns a process-local job_id; poll get_job until its state is finished, then inspect its immutable run_ids.

get_run returns the complete versioned run record. Schema-9 records may include crash_state metadata for capture-entry registers, an optional exact site, and the registered raw context, bounded stack, zero-page, and selected-global evidence files. They also expose exact supporting-artifact metadata and the explicit screen_match result used by uninstrumented screen-observation tests.

resolve_address accepts a run UUID, decimal/$ABCD/0xABCD address, and optional exact context. It returns the same schema-2 resolver result as the HTTP service. resolved, ambiguous, unmapped, and unavailable are successful tool results rather than MCP errors. The adapter envelope is schema 12; its HTTP data contains API schema 13 and resolver_schema_version = 2.

get_run_timeline pages one immutable run's checksummed 6510 debug-stream capture with optional cursor (DATAGRAM_ORDINAL:ENTRY_INDEX), limit (1–4096, default 256), inclusive address range, access (read/write), and exact context. It forwards to the HTTP timeline endpoint without local decoding and returns the service's bounded page: bus_only or source_linked mode, gap and uncertainty rows, and per-row source resolution where the run carries its own analysis. Instruction, call, and return labels are intentionally never produced. Invalid cursors, limits, or tampered evidence arrive as structured timeline_failed errors.

get_run_diagnostic_report accepts a run UUID and returns the HTTP service's schema-1 diagnostic report: status (finalized or provisional), expectations, expected-versus-observed results, artifact, evidence integrity, nullable budget/comparison/checkpoints summaries, and per-section row counts. get_run_diagnostic_items returns one bounded page of a section (checkpoints, budget, comparison_sections, comparison_objects, comparison_symbols, or assertions) with an optional limit (1–100, default 50) and opaque cursor. Both forward to the HTTP diagnostic routes without local decoding. The section, limit, and cursor shape are validated locally before any request, so malformed input never reaches the service; an unknown run is a structured 404 not-found and valid-input integrity or section failures arrive as structured diagnostic_report_failed errors. The data labels the inner contract with diagnostic_report_schema_version = 1.

get_crash_report accepts a crashed run UUID and optional exact context. It returns the HTTP service's schema-2 report. resolved, ambiguous, unmapped, and unavailable are successful tool results; malformed or checksum-invalid registered inputs are structured crash_analysis_failed errors. The data labels that inner contract with crash_analysis_schema_version = 2.

compare_runs accepts baseline and candidate run UUIDs plus an optional exact context. It returns the HTTP service's schema-1 comparison. compared, unavailable, and incompatible are successful tool results. An optional baseline_run_id on start_test_job compares the candidate build once and preserves the same normalized comparison in every resulting immutable run.

compare_run_checkpoints accepts baseline and candidate run UUIDs and returns the HTTP service's provisional, read-only schema-1 memory-checkpoint comparison. matched, diverged, incompatible, and unavailable are successful tool results; it stores nothing. list_checkpoint_comparisons lists stored pair records newest first with an optional limit (1–200, default 50, rejected locally outside that range); get_checkpoint_comparison reads one stored pair by comparison_id, including the full comparison. Both forward to the HTTP routes without local decoding, so an unknown or corrupt pair arrives as a structured checkpoint_comparison_failed error. The data labels the inner contracts with checkpoint_comparison_schema_version = 1 for the provisional comparison and checkpoint_comparison_record_schema_version = 1 for a stored record. start_test_job also accepts compare_checkpoints (default false); when true it runs the specialized VICE-then-Ultimate checkpoint matrix, which requires exactly the two targets vice then ultimate with repeat 1 and a test whose get_project entry has memory_checkpoint_capture = true. The service validates that order and repeat before consuming the probe handle, and the resulting job snapshot carries its stored checkpoint_comparison_id.

start_test_job and probe_ultimate also accept an optional strict_gate boolean (default false). It is an opt-in fail-stop restriction, never an added permission: it leaves the probe expiry, reset count, mutation token, origin, and lease rules unchanged, and it grants no extra hardware authorization. When true the run fails closed on the first transport, status, body, or parse error instead of retrying or falling back, and it requires a probe that was itself requested with strict_gate; an ordinary probe is rejected with 422 probe_strictness_mismatch before any mutation. The bounded supported shapes, the mutation budget, and the one-resume-per-pause-episode rule are documented in CHECKPOINT_COMPARISON.md.

read_evidence remains confined to files registered in the immutable run. Text is returned up to 128 KiB, PNG evidence up to 4 MiB, and other binaries return size and SHA-256 without inserting raw bytes into model context.

Debug capture uses a two-step local-receiver contract. Call probe_ultimate, pass its single-use ID and the receiver Ethernet IPv4 address to prepare_debug_stream_capture, review the returned identity/configuration, then call start_debug_stream_capture with both exact counts set to one. A preparation may instead be cancelled without hardware access. The MCP adapter does not receive or decode UDP independently; it wraps the same service/core operation as the CLI and dashboard.

The source-linked flow adds a clean candidate build, artifact snapshot and analysis validation, and a third confirmation. Call prepare_source_linked_capture with the probe, receiver interface, and test (currently timeline-probe), review the pinned identity, stream configuration, and prerequisites, then call start_source_linked_capture with confirmed_reset_count, confirmed_stream_start_count, and confirmed_stream_stop_count all set to one. Count mismatches are rejected before any preparation is consumed; ambiguous failures are never retried. The combined operation has passed a physical gate on one reviewed, identity-pinned device through the verified wired Ethernet/IGMPv2 path, retaining 11,704 datagrams with explicit capture-bound truncation and verified post-stop pass/QUT1 evidence. That acceptance is limited to the reviewed artifact, device and network configuration, and the acceptance record itself is withheld from the distribution.

Physical-device safety #

probe_ultimate obtains the service's five-minute, single-use probe handle but does not reset or deploy to the device. Before start_test_job includes an Ultimate target:

  1. Call probe_ultimate.
  2. Present the returned identity, firmware, core, and capabilities for review.
  3. Obtain explicit confirmation of the exact reset count.
  4. If get_project returns a non-null ultimate_reu_image, separately obtain confirmation of the exact REU image write count.
  5. Pass the returned probe_id, confirmed_reset_count, and confirmed_reu_write_count.

The service consumes the probe authorization once, requires the confirmed reset count to equal repeat, and requires the REU count to equal repeat only for a declared image (zero otherwise). It probes the identity again before mutation and serializes jobs through the existing target lease. C64U_PASSWORD stays only in the service process and never crosses MCP.

Trusted-native runs use a distinct authorization flow. First call create_native_preparation without hardware access. Then call probe_ultimate, review the full target snapshot, and create a one-shot or standing intent for the preparation. Standing mode requires confirmed_standing_authorization = true and may include an expiry or maximum run count. start_native_run uses only an active intent plus the device host; the core re-probes and requires the exact intent-bound identity, firmware, FPGA/core, API version, and UE2 hash on every invocation. Each invocation gets a new visible ID and UTR2 token. Intent reuse is therefore standing permission for the same program on the same target, not blanket native-code permission. Authorization is durably latched before a potentially ambiguous FTP creation or final Run Update write; uncertain acceptance leaves the intent non-active.

start_native_health_check accepts the standing intent UUID, an eligible native run UUID, and device host. It completes only a pending post-recovery C64 health gate and cannot perform FTP or another Run Update. Parent-run eligibility and exact identity are rechecked by the core; the intent remains suspended unless the linked health-check job passes.