Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d5f6a74
FW-76: define MCP control plane architecture
mrmidi Jun 18, 2026
b05de61
FW-77: define MCP tool taxonomy
mrmidi Jun 18, 2026
fa1cff9
FW-86: define MCP telemetry resources
mrmidi Jun 18, 2026
fa30f61
FW-90: add MCP mock harness
mrmidi Jun 18, 2026
141ca8e
FW-89: add MCP Swift test gate
mrmidi Jun 18, 2026
2bad228
FW-79: implement MCP write policy engine and refusal reasons
mrmidi Jun 18, 2026
b99bfb0
FW-78: implement address-space read/write/CAS core schemas
mrmidi Jun 18, 2026
218d04c
refactor(mcp): split tool/resource catalog into per-surface slices
mrmidi Jun 18, 2026
cd30667
FW-80: expose register access tools with policy-aware writes
mrmidi Jun 18, 2026
b279ec5
FW-81: expose IRM and CAS MCP tools
mrmidi Jun 18, 2026
fa6cac6
FW-82: expose AV/C and FCP MCP tools
mrmidi Jun 18, 2026
b0691d1
FW-83: expose CMP MCP tools
mrmidi Jun 18, 2026
8db7d00
FW-84: expose SBP-2 inspection and developer control tools
mrmidi Jun 18, 2026
0abf59c
FW-85: expose DICE and TCAT low-level register tools
mrmidi Jun 18, 2026
534fc7b
FW-87: add MCP tool-use examples and return schema documentation
mrmidi Jun 18, 2026
f34669a
FW-88: design programmatic MCP workflows for agent orchestration
mrmidi Jun 18, 2026
488b637
FW-91: wire MCP callTool dispatch
mrmidi Jun 18, 2026
39cdfab
FW-95: bind MCP core to Swift SDK
mrmidi Jun 18, 2026
23d2137
FW-94: implement live MCP driver adapter
mrmidi Jun 18, 2026
8884f66
FW-92: add app-hosted MCP HTTP transport
mrmidi Jun 18, 2026
5e845aa
FW-96: verify hosted MCP end to end
mrmidi Jun 18, 2026
91c91f3
FW-93: add MCP settings lifecycle UI
mrmidi Jun 18, 2026
a92b6ec
FW-92: fix MCP loopback listener binding
mrmidi Jun 19, 2026
a5084b5
Merge branch 'DICE' into feat/MCP
mrmidi Jun 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions ASFW.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
3A1694002E8087BD000BD368 /* PCIDriverKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A1693FF2E8087BD000BD368 /* PCIDriverKit.framework */; };
3A27C5302ECDE045009CA664 /* bump.sh in Resources */ = {isa = PBXBuildFile; fileRef = 3A27C52E2ECDE045009CA664 /* bump.sh */; };
3A27C5322ECDE045009CA664 /* bump.sh in Resources */ = {isa = PBXBuildFile; fileRef = 3A27C52E2ECDE045009CA664 /* bump.sh */; };
3A9501012F13000000C0DE95 /* MCP in Frameworks */ = {isa = PBXBuildFile; productRef = 3A9501002F13000000C0DE95 /* MCP */; };
3A9501032F13000000C0DE95 /* MCP in Frameworks */ = {isa = PBXBuildFile; productRef = 3A9501022F13000000C0DE95 /* MCP */; };
3ABA31132EF8564A0046405D /* AudioDriverKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3ABA31122EF8564A0046405D /* AudioDriverKit.framework */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -95,6 +97,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
3A9501012F13000000C0DE95 /* MCP in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -112,6 +115,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
3A9501032F13000000C0DE95 /* MCP in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -185,6 +189,9 @@
3A1693DA2E808727000BD368 /* ASFW */,
);
name = ASFW;
packageProductDependencies = (
3A9501002F13000000C0DE95 /* MCP */,
);
productName = ASFW;
productReference = 3A1693D82E808727000BD368 /* ASFW.app */;
productType = "com.apple.product-type.application";
Expand Down Expand Up @@ -229,6 +236,7 @@
);
name = ASFWTests;
packageProductDependencies = (
3A9501022F13000000C0DE95 /* MCP */,
);
productName = ASFWTests;
productReference = 3AB4713F2EE31CF0003A4E2A /* ASFWTests.xctest */;
Expand Down Expand Up @@ -266,6 +274,9 @@
);
mainGroup = 3A1693CF2E808727000BD368;
minimizedProjectReferenceProxies = 1;
packageReferences = (
3A9501042F13000000C0DE95 /* XCRemoteSwiftPackageReference "swift-sdk" */,
);
preferredProjectObjectVersion = 77;
productRefGroup = 3A1693D92E808727000BD368 /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -730,6 +741,30 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
3A9501042F13000000C0DE95 /* XCRemoteSwiftPackageReference "swift-sdk" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/modelcontextprotocol/swift-sdk.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.11.0;
};
};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
3A9501002F13000000C0DE95 /* MCP */ = {
isa = XCSwiftPackageProductDependency;
package = 3A9501042F13000000C0DE95 /* XCRemoteSwiftPackageReference "swift-sdk" */;
productName = MCP;
};
3A9501022F13000000C0DE95 /* MCP */ = {
isa = XCSwiftPackageProductDependency;
package = 3A9501042F13000000C0DE95 /* XCRemoteSwiftPackageReference "swift-sdk" */;
productName = MCP;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 3A1693D02E808727000BD368 /* Project object */;
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 74 additions & 0 deletions ASFW/MCP/ASFWMCPAvcFcpTools.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import Foundation

// FW-82: AV/C and FCP tools (MCP_TOOL_TAXONOMY.md §5.6).
//
// AV/C unit/subunit inspection and inquiry/status FCP commands are read-only.
// Control/notify/vendor-dependent FCP commands mutate device state and are
// policy-gated developer-write. All AV/C tools require the "avc" protocol hint,
// so they only appear for AV/C-capable nodes. AV/C frames are big-endian byte
// payloads written to the target's FCP command register (units space).

extension ASFWMCPToolCatalog {
static let avcFcpTools: [ASFWMCPToolDefinition] = [
ASFWMCPToolDefinition(name: "asfw_avc_list_units", group: "avc_fcp", visibility: .readOnly, readOnly: true, idempotent: true, summary: "List AV/C units, subunits, and plugs.", requiredProtocolHints: ["avc"]),
ASFWMCPToolDefinition(name: "asfw_avc_get_subunit_capabilities", group: "avc_fcp", visibility: .readOnly, readOnly: true, idempotent: true, summary: "Return decoded AV/C subunit capabilities.", requiredProtocolHints: ["avc"]),
ASFWMCPToolDefinition(name: "asfw_avc_get_subunit_descriptor", group: "avc_fcp", visibility: .readOnly, readOnly: true, idempotent: true, summary: "Return bounded AV/C subunit descriptor bytes and parsed summary.", requiredProtocolHints: ["avc"]),
ASFWMCPToolDefinition(name: "asfw_fcp_send_command", group: "avc_fcp", visibility: .readOnly, readOnly: true, idempotent: false, summary: "Send an inquiry/status-only FCP/AV/C command.", requiredProtocolHints: ["avc"]),
ASFWMCPToolDefinition(name: "asfw_fcp_get_recent_responses", group: "avc_fcp", visibility: .readOnly, readOnly: true, idempotent: true, summary: "Inspect recent FCP command/response records.", requiredProtocolHints: ["avc"]),
ASFWMCPToolDefinition(name: "asfw_fcp_send_command_dev", group: "avc_fcp", visibility: .developerWrite, readOnly: false, idempotent: false, summary: "Developer-tier raw FCP command that may mutate device state.", requiredProtocolHints: ["avc"])
]
}

/// AV/C command intent (`ctype`). Only inquiry/status are non-mutating.
enum ASFWMCPAvcCommandIntent: String, Equatable, CaseIterable {
case inquiry
case status
case control
case notify
case vendorDependent

var isMutating: Bool {
switch self {
case .inquiry, .status:
return false
case .control, .notify, .vendorDependent:
return true
}
}
}

/// A raw FCP/AV/C command directed at a node's FCP command register.
struct ASFWMCPFcpCommandRequest: Equatable {
/// Target node's FCP command register address.
let address: ASFWMCPAddress
let intent: ASFWMCPAvcCommandIntent
/// AV/C frame bytes in bus (big-endian) order.
let payload: [UInt8]

/// FCP frames are bounded to 512 bytes.
static let maxPayload = 512

var validationError: ASFWMCPErrorCode? {
if payload.count > Self.maxPayload { return .payloadTooLarge }
if payload.isEmpty { return .malformedRequest }
return nil
}

/// Policy request for mutating intents, or nil for inquiry/status reads,
/// which are not gated by write policy.
func policyRequest(
currentGeneration: UInt32,
protocolSupported: Bool = true,
dryRun: Bool = false
) -> ASFWMCPPolicyRequest? {
guard intent.isMutating else { return nil }
return .forTransaction(
kind: .writeBlock,
address: address,
currentGeneration: currentGeneration,
protocolHint: "avc",
protocolSupported: protocolSupported,
dryRun: dryRun
)
}
}
73 changes: 73 additions & 0 deletions ASFW/MCP/ASFWMCPCmpTools.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import Foundation

// FW-83: Connection Management Procedures (CMP) tools (MCP_TOOL_TAXONOMY.md §5.7).
//
// Plug listing and PCR reads are read-only inspection. PCR writes and
// connection establish/break are mutations: they are compare-swaps against the
// node's plug control registers (so they share the FW-78 CAS schema and FW-79
// policy), and use the "cmp" protocol hint for discovery. CMP writes prefer CAS
// so a stale plug value surfaces as compareFailed at execution.

extension ASFWMCPToolCatalog {
static let cmpTools: [ASFWMCPToolDefinition] = [
ASFWMCPToolDefinition(name: "asfw_cmp_list_plugs", group: "cmp", visibility: .readOnly, readOnly: true, idempotent: true, summary: "List known iPCR/oPCR plug state.", requiredProtocolHints: ["cmp"]),
ASFWMCPToolDefinition(name: "asfw_cmp_read_pcr", group: "cmp", visibility: .readOnly, readOnly: true, idempotent: false, summary: "Read and decode a CMP plug control register.", requiredProtocolHints: ["cmp"]),
ASFWMCPToolDefinition(name: "asfw_cmp_write_pcr", group: "cmp", visibility: .developerWrite, readOnly: false, idempotent: false, summary: "Policy-gated CMP PCR write (compare-swap).", requiredProtocolHints: ["cmp"]),
ASFWMCPToolDefinition(name: "asfw_cmp_establish_connection", group: "cmp", visibility: .developerWrite, readOnly: false, idempotent: false, summary: "Policy-gated connection establishment.", requiredProtocolHints: ["cmp"]),
ASFWMCPToolDefinition(name: "asfw_cmp_break_connection", group: "cmp", visibility: .developerWrite, readOnly: false, idempotent: false, summary: "Policy-gated connection break.", requiredProtocolHints: ["cmp"])
]
}

/// IEC 61883-1 plug control registers: PCR_0..PCR_30 per direction.
enum ASFWMCPCmpLimits {
static let maxPlug: UInt32 = 30
}

struct ASFWMCPCmpPcrWriteRequest: Equatable {
/// Address of the target plug control register.
let address: ASFWMCPAddress
/// Plug index (0...30).
let plug: UInt32
/// Expected current PCR value (host order).
let expected: UInt32
/// New PCR value (host order).
let swap: UInt32

var validationError: ASFWMCPErrorCode? {
plug <= ASFWMCPCmpLimits.maxPlug ? nil : .malformedRequest
}

func policyRequest(currentGeneration: UInt32, protocolSupported: Bool = true, dryRun: Bool = false) -> ASFWMCPPolicyRequest {
.forTransaction(
kind: .compareSwap,
address: address,
currentGeneration: currentGeneration,
protocolHint: "cmp",
protocolSupported: protocolSupported,
dryRun: dryRun
)
}
}

struct ASFWMCPCmpConnectionRequest: Equatable {
/// Address of the plug control register backing the connection.
let address: ASFWMCPAddress
let plug: UInt32
/// True to establish, false to break.
let establish: Bool

var validationError: ASFWMCPErrorCode? {
plug <= ASFWMCPCmpLimits.maxPlug ? nil : .malformedRequest
}

func policyRequest(currentGeneration: UInt32, protocolSupported: Bool = true, dryRun: Bool = false) -> ASFWMCPPolicyRequest {
.forTransaction(
kind: .compareSwap,
address: address,
currentGeneration: currentGeneration,
protocolHint: "cmp",
protocolSupported: protocolSupported,
dryRun: dryRun
)
}
}
Loading
Loading