Skip to content

FW-76..96: MCP control plane starting point#31

Merged
mrmidi merged 24 commits into
DICEfrom
feat/MCP
Jun 19, 2026
Merged

FW-76..96: MCP control plane starting point#31
mrmidi merged 24 commits into
DICEfrom
feat/MCP

Conversation

@mrmidi

@mrmidi mrmidi commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Summary

This PR lands the first ASFW MCP control-plane slice on top of DICE.

It is intentionally a starting point rather than the finished agent-facing product. The branch establishes the architecture, core schemas, policy gates, mock/test harness, Swift SDK bridge, hosted HTTP/SSE endpoint, live driver adapter, and basic SwiftUI lifecycle controls.

Included

  • MCP architecture doc and tool taxonomy for ASFW FireWire control-plane work.
  • Telemetry/resources and dynamic tool catalog surfaces.
  • Async read/write/CAS schemas with typed node, generation, address, and payload inputs.
  • Policy engine for read-only, mock, developer-write, raw-developer, dry-run, stale-generation, and unsupported-surface decisions.
  • Mock transport and Swift test gate before real hardware access.
  • Register, IRM/CAS, AV/C/FCP, CMP, DICE/TCAT, and SBP-2 MCP tool surfaces.
  • Swift SDK bridge and app-hosted Streamable HTTP/SSE MCP endpoint.
  • SwiftUI MCP Control Plane settings/lifecycle panel.
  • Live driver adapter for topology, telemetry, and async transaction plumbing.

Current State

  • MCP is disabled by default and controlled from the ASFW app UI.
  • The v1 host is app-owned HTTP/SSE on loopback; no helper target and no Unix socket transport.
  • Write-capable surfaces are still guarded by policy/test gates and are not intended to be exposed casually.
  • This does not finish agent installer/onboarding, hardware smoke workflows, or production hardening.

Validation

Rebased onto origin/DICE at dcf3b71 and reran the MCP Swift test slice:

xcodebuild -project ASFW.xcodeproj -scheme ASFW -destination 'platform=macOS' \
  -only-testing:ASFWTests/MCPAvcFcpToolsTests \
  -only-testing:ASFWTests/MCPCmpToolsTests \
  -only-testing:ASFWTests/MCPControlViewModelTests \
  -only-testing:ASFWTests/MCPDiceTcatToolsTests \
  -only-testing:ASFWTests/MCPHostTests \
  -only-testing:ASFWTests/MCPHostedEndToEndTests \
  -only-testing:ASFWTests/MCPIrmCasToolsTests \
  -only-testing:ASFWTests/MCPLiveDriverControlTests \
  -only-testing:ASFWTests/MCPMockHarnessTests \
  -only-testing:ASFWTests/MCPRegisterToolsTests \
  -only-testing:ASFWTests/MCPSbp2ToolsTests \
  -only-testing:ASFWTests/MCPSDKBridgeTests \
  -only-testing:ASFWTests/MCPTestGateTests \
  -only-testing:ASFWTests/MCPToolDispatchTests \
  -only-testing:ASFWTests/MCPTransactionSchemaTests \
  -only-testing:ASFWTests/MCPWritePolicyTests test

Result: passed.

mrmidi and others added 23 commits June 19, 2026 13:35
Document the app-hosted local HTTP/SSE direction, disabled-by-default runtime model, SwiftUI-owned enablement, and MCP core/driver-control layering for the ASFW MCP Control Plane.
Document the always-visible MCP tools, dynamic protocol groups, runtime-mode visibility matrix, capability predicates, naming rules, and write-gated discovery behavior for the ASFW MCP Control Plane.
Document the MCP telemetry URI scheme, common resource envelope, compact JSON shapes, raw-data opt-in policy, and before/after snapshot comparison model for ASFW agent diagnostics.
Add pure Swift MCP model/core scaffolding, an in-process mock transport, deterministic mock driver fixtures, a safe default hardware smoke plan, Swift Testing coverage, and harness documentation.
Add the fail-closed MCP test gate, protocol-hint discovery checks, SBP-2 mock fixture coverage, Swift Testing gate tests, and documentation for mandatory gate behavior before real agent hardware access.
Add the guard rail for MCP write-capable tools. The engine classifies a write
request by operation type, address space, protocol surface, runtime/developer
mode, and node generation, returning one structured decision:

  allowed | denied | dryRunOnly | requiresDeveloperMode |
  unsupportedAddressSpace | staleGeneration | unsupportedProtocol

Each decision carries a human-readable reason and a machine-readable error code
plus required mode/capability (decision vocabulary per MCP_TOOL_TAXONOMY.md §11).

Safety invariant ASFWMCPPolicyDecision.reachesDriverWritePath: only `allowed`
may reach the driver/user-client write path — every refusal and every dry run is
false, so denied and dry-run writes cannot touch hardware. Mock mode resolves any
otherwise-authorized write to a dry run; classification still applies so refusals
surface in mock too.

Extends the FW-89 test gate with the write-path-gated check called for in
MCP_TEST_GATE.md §6, and wires ASFWMCPCore.evaluateWritePolicy. The engine
operates on an already-resolved ASFWMCPPolicyRequest, so it has no dependency on
the FW-78 transaction schemas.

19 Swift Testing cases cover every decision category and the
reachesDriverWritePath invariant across the mode/space/protocol/tier matrix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the foundational MCP schemas for FireWire async transactions:

- ASFWMCPAddress: typed node/generation + 48-bit high/low address.
- Request shapes for quadlet read, block read, quadlet write, block write, and
  quadlet compare-swap, with schema validation (4-byte alignment, S400 byte
  ceiling) returning the MCP error vocabulary (malformedRequest/payloadTooLarge).
- ASFWMCPTransactionResult: stable result shape (ok/status/rCode/generation/
  durationUsec/correlationId/payload/decoded/policy) per MCP_TOOL_TAXONOMY.md
  §5.3/§5.5, with policyRefusal/malformed factories.

Includes the bridge to the FW-79 policy surface (address->space classification,
transaction-kind->operation-type, and the forTransaction request builder), which
lives with the schemas because it depends on the concrete transaction/address
types; the policy engine itself classifies an already-resolved request. The
result's `policy` field references the FW-79 decision, so this lands on top of
FW-79.

14 Swift Testing cases cover offset composition, mutation classification, block
validation bounds, result factories, and the policy bridge. Full Swift suite
green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract the flat ASFWMCPCore.toolCatalog / resourceCatalog literals into
ASFWMCPToolCatalog and ASFWMCPResourceCatalog aggregators, with one named static
slice per protocol surface (core, bus/topology, config ROM, async transactions,
register, IRM/CAS, AV/C, CMP, SBP-2, DICE/TCAT). `all` is the single place that
lists every group and is stable across the fixed taxonomy.

Pure refactor — the aggregated set is unchanged and the full Swift suite stays
green. This is the enabler for the FW-80..85 fan-out: a surface ticket can own
(and later extract to its own file) just its slice instead of all editing one
shared array, removing the file-level collision between otherwise-independent
surfaces.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Establish the per-surface file pattern on the register group: ASFWMCPToolCatalog
hands ownership of `registerTools` to ASFWMCPRegisterTools.swift, which owns the
catalog slice, the request schemas, and the bridge to the FW-79 write policy.

Tools (MCP_TOOL_TAXONOMY.md §5.4):
- reads: asfw_read_device_register, asfw_read_device_register_block,
  asfw_read_ohci_register, asfw_snapshot_ohci_registers
- device writes (developer-write, policy-gated): asfw_write_device_register,
  asfw_write_device_register_block
- asfw_write_ohci_register_dev (raw developer tier)

Device registers are node addresses reached over FW-78 async transactions; their
writes are policy-gated via forTransaction. OHCI/controller registers are
host-side (`.ohciController`), read-only for diagnostics with bounded snapshots,
and writable only through the raw developer tier (generation-neutral). Schema
validation reuses the FW-78 block bounds; OHCI snapshots/offsets are bounded and
quadlet-aligned.

14 Swift Testing cases cover the catalog surface, mode-based discovery
(reads visible, device writes gated, OHCI write raw-tier only), schema
validation, and policy gating (requiresDeveloperMode / staleGeneration / denied
without raw tier / allowed). Full Swift suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Own the irm_cas slice in ASFWMCPIrmCasTools.swift (extracted from the aggregator):

- reads: asfw_irm_get_state, asfw_irm_get_bandwidth, asfw_irm_get_channels,
  asfw_irm_list_allocations (bus-global, listed without a protocol hint)
- mutations (developer-write, policy-gated): asfw_cas_quadlet,
  asfw_irm_allocate_channel/free_channel, asfw_irm_allocate_bandwidth/free_bandwidth

CAS reuses the FW-78 ASFWMCPCompareSwapRequest and adds a policyRequest bridge
through forTransaction, so CAS tools share the core transaction schema. IRM
channel/bandwidth allocations are compare-swaps against the IRM node's CSR-core
resource registers and classify as .csrCore/.compareSwap, with range/ceiling
validation (channel <= 63, bandwidth <= 0x1333).

Resources (asfw://irm/*) deferred until readResource serves them with live data.
9 tool entries; tests cover catalog surface, mode-based discovery, request
validation, and policy gating (requiresDeveloperMode / allowed / staleGeneration).
Full Swift suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Own the avc_fcp slice in ASFWMCPAvcFcpTools.swift. All tools require the "avc"
protocol hint, so they surface only for AV/C-capable nodes:

- reads: asfw_avc_list_units, asfw_avc_get_subunit_capabilities,
  asfw_avc_get_subunit_descriptor, asfw_fcp_send_command (inquiry/status only),
  asfw_fcp_get_recent_responses
- asfw_fcp_send_command_dev (developer-write) for control/notify/vendor commands

ASFWMCPAvcCommandIntent marks inquiry/status as non-mutating and
control/notify/vendorDependent as mutating. ASFWMCPFcpCommandRequest carries
big-endian AV/C frame bytes (bounded to 512) and returns a policy request only
for mutating intents — routed through forTransaction with the "avc" protocol
hint so an unsupported protocol surfaces as unsupportedProtocol.

8 test cases cover hint-gated discovery, intent classification, payload
validation, and policy gating (read passthrough, requiresDeveloperMode/allowed,
unsupportedProtocol). Full Swift suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Own the cmp slice in ASFWMCPCmpTools.swift. All tools require the "cmp" hint:

- reads: asfw_cmp_list_plugs, asfw_cmp_read_pcr
- mutations (developer-write, policy-gated): asfw_cmp_write_pcr,
  asfw_cmp_establish_connection, asfw_cmp_break_connection

PCR writes and connection establish/break are compare-swaps against the node's
plug control registers, so they reuse the FW-78 CAS path and FW-79 policy via
forTransaction with the "cmp" hint (a stale plug value then surfaces as
compareFailed at execution). Plug indices are validated against PCR_0..PCR_30.

7 test cases cover hint-gated discovery, malformed plug IDs, policy-denied writes
in read-only mode, allowed compare-swap writes when the gate is open, and
unsupportedProtocol. Full Swift suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Own the sbp2 slice in ASFWMCPSbp2Tools.swift. All tools use the "sbp2" hint:

- reads: asfw_sbp2_list_units, asfw_sbp2_inspect_unit, asfw_sbp2_get_session_status
- mutations (raw developer tier): asfw_sbp2_login_dev, asfw_sbp2_submit_orb_dev

Per the §7 visibility matrix, SBP-2 mutations are raw developer-tier escape
hatches: login/ORB requests set requiresRawDeveloperTier, so they stay hidden
(and policy-denied) unless both the write gate and the raw tier are enabled.
ASFWMCPSbp2SessionState distinguishes absent / unsupported / inactive / active /
protocolError so inspection results are unambiguous. ORB payloads are validated
(non-empty, quadlet-aligned, bounded).

8 test cases cover hint-gated discovery, raw-tier-gated mutation visibility,
session-state coverage, ORB validation, and policy (denied without raw tier,
allowed with it). Full Swift suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Own the dice_tcat slice in ASFWMCPDiceTcatTools.swift — low-level protocol
register/application-space access, not audio-control UX (no phantom power,
routing, mixer, or device-control semantics). All tools use the "dice_tcat" hint:

- reads: asfw_dice_read_register, asfw_dice_read_block, asfw_dice_decode_status,
  asfw_tcat_read_application_block
- mutations (developer-write, policy-gated): asfw_dice_write_register,
  asfw_tcat_write_application_block

Reads/writes are node-address transactions sharing the FW-78 schema and FW-79
policy via forTransaction with the "dice_tcat" hint. Writes support readback
verification through ASFWMCPRegisterWriteVerification (requested vs readback).
Block reads/writes reuse the FW-78 bounds.

7 test cases cover capability-absence hiding, read-surface discovery, block
validation, policy gating (requiresDeveloperMode / allowed / unsupportedProtocol),
and write verification. Full Swift suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Document concrete request parameters and stable result shapes for the
implemented MCP schema layer (FW-78..85): parameter conventions (node id,
generation, address high/low, payload encoding, endian, timeout units), the
canonical ASFWMCPTransactionResult/ASFWMCPPolicyDecision shapes for
success/timeout/protocol-error/policy-denial/dry-run/compare-failed/malformed,
and worked examples for read quadlet/block, write with readback verification,
CAS, Config ROM read, AV/C inquiry, FCP control, CMP PCR read/write, IRM
allocation, and DICE/TCAT register access.

Notes that live callTool dispatch is not yet wired, so the examples document the
contract the dispatch will serialize. Avoids audio-layer controls per phase scope.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Document candidate agent workflows over the implemented MCP tool surface: bus
enumerate-and-summarize, AV/C unit probe, bounded register snapshot, before/after
bus-reset diff, and recent-transaction-failure aggregation, plus the mutating
channel-claim / PCR-connection / register-poke sequences.

Each workflow lists its expected tool sequence, marks every step read-only vs
mutating, identifies which steps can run in parallel (no data dependency), and
names a compact bounded return so agents avoid large intermediate dumps. Notes
the generation-recheck rule between discovery and mutation and that
trigger_config_rom_read is non-idempotent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mrmidi mrmidi marked this pull request as ready for review June 19, 2026 11:41
@mrmidi mrmidi merged commit 40a796c into DICE Jun 19, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant