Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@

### Changed

- Adopt swift-codex 0.5.1 and MCP SDK fork 0.13.2-computer-mcp.1, preserving
existing runtime behavior and refreshing the SDK-derived method inventory.
- Classify native plugin reconciliation as destructive, Bedrock discovery as
read-only, and Bedrock setup as an external write.
- Ship swift-codex's Apache-2.0 WITH Swift-exception license and notices.
- Adapter databases initialize from one complete schema. Unknown migration
histories fail explicitly and require a new state directory.

Expand All @@ -22,4 +27,6 @@

### Fixed

- Undecodable MCP tool arguments, including out-of-range integers, receive
JSON-RPC invalid-params errors correlated with the request ID.
- Valid upstream Exec session identifiers are accepted by release builds.
4 changes: 2 additions & 2 deletions Documentation/Reference/Release.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ requirements are:

| Dependency | Requirement |
| --- | --- |
| MCP Swift SDK organization fork (`computer-mcp/swift-sdk`) | `exact: "0.13.1-computer-mcp.1"` |
| swift-codex | `exact: "0.4.1"` |
| MCP Swift SDK organization fork (`computer-mcp/swift-sdk`) | `exact: "0.13.2-computer-mcp.1"` |
| swift-codex | `exact: "0.5.1"` |
| swift-subprocess | `.upToNextMinor(from: "1.0.2")` |
| swift-argument-parser | `from: "1.8.2"` |
| GRDB | `from: "7.11.1"` |
Expand Down
10 changes: 5 additions & 5 deletions Package.resolved

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

4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/groue/GRDB.swift.git", from: "7.11.1"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.8.2"),
.package(url: "https://github.com/computer-mcp/swift-sdk.git", exact: "0.13.1-computer-mcp.1"),
.package(url: "https://github.com/swift-library/swift-codex.git", exact: "0.4.1"),
.package(url: "https://github.com/computer-mcp/swift-sdk.git", exact: "0.13.2-computer-mcp.1"),
.package(url: "https://github.com/swift-library/swift-codex.git", exact: "0.5.1"),
.package(
url: "https://github.com/swiftlang/swift-subprocess.git", .upToNextMinor(from: "1.0.2")),
],
Expand Down
8 changes: 5 additions & 3 deletions Sources/CodexAdapter/CodexAppServerMethodCatalog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ enum CodexAppServerMethodCatalog {

static func risk(for method: String) -> CodexOperationRisk? {
switch method {
case "account/rateLimits/read", "account/read", "account/usage/read",
case "account/bedrock/discover", "account/rateLimits/read", "account/read",
"account/usage/read",
"account/workspaceMessages/read", "app/installed", "app/list", "app/read", "config/read",
"configRequirements/read", "experimentalFeature/list", "externalAgentConfig/detect",
"externalAgentConfig/import/readHistories", "fs/getMetadata", "fs/readDirectory",
Expand All @@ -146,7 +147,7 @@ enum CodexAppServerMethodCatalog {
"thread/increment_elicitation", "thread/memoryMode/set",
"thread/queue/delete", "thread/queue/reorder", "command/exec/resize", "process/resizePty":
return .workspaceWrite
case "account/login/cancel", "account/login/start", "account/logout",
case "account/bedrock/setup", "account/login/cancel", "account/login/start", "account/logout",
"account/rateLimitResetCredit/consume", "account/sendAddCreditsNudgeEmail",
"config/batchWrite", "config/value/write", "experimentalFeature/enablement/set",
"externalAgentConfig/import", "externalAgentConfig/import/recordHistory", "feedback/upload",
Expand All @@ -158,7 +159,8 @@ enum CodexAppServerMethodCatalog {
"remoteControl/enable", "remoteControl/pairing/start", "mcpServer/event/stream/start",
"mcpServer/event/stream/stop":
return .externalWrite
case "fs/remove", "marketplace/remove", "plugin/share/delete", "plugin/uninstall",
case "fs/remove", "marketplace/remove", "plugin/reconcile", "plugin/share/delete",
"plugin/uninstall",
"thread/revert", "threadSection/delete", "memory/reset", "project/delete",
"remoteControl/client/revoke", "thread/backgroundTerminals/clean",
"thread/backgroundTerminals/terminate", "command/exec/terminate", "process/kill":
Expand Down
83 changes: 65 additions & 18 deletions Sources/CodexAdapter/Resources/Protocol/adoption.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"plugin/installed",
"plugin/list",
"plugin/read",
"plugin/reconcile",
"plugin/share/checkout",
"plugin/share/delete",
"plugin/share/list",
Expand Down Expand Up @@ -101,6 +102,8 @@
"windowsSandbox/setupStart"
],
"experimental": [
"account/bedrock/discover",
"account/bedrock/setup",
"collaborationMode/list",
"environment/add",
"environment/info",
Expand Down Expand Up @@ -155,22 +158,66 @@
]
},
"excluded": [
{ "method": "FuzzyFileSearch", "reason": "legacy method is not adopted" },
{ "method": "GetAuthStatus", "reason": "legacy method is not adopted" },
{ "method": "GetConversationSummary", "reason": "legacy method is not adopted" },
{ "method": "GitDiffToRemote", "reason": "legacy method is not adopted" },
{ "method": "account/bedrock/discover", "reason": "Bedrock account onboarding is not adopted" },
{ "method": "account/bedrock/setup", "reason": "Bedrock account onboarding is not adopted" },
{ "method": "fuzzyFileSearch", "reason": "deprecated upstream method is not adopted" },
{ "method": "fuzzyFileSearch/sessionStart", "reason": "experimental fuzzy session is not adopted" },
{ "method": "fuzzyFileSearch/sessionStop", "reason": "experimental fuzzy session is not adopted" },
{ "method": "fuzzyFileSearch/sessionUpdate", "reason": "experimental fuzzy session is not adopted" },
{ "method": "initialize", "reason": "connection lifecycle owns the handshake" },
{ "method": "initialized", "reason": "connection lifecycle owns the handshake" },
{ "method": "plugin/reconcile", "reason": "plugin reconciliation is not adopted" },
{ "method": "userVerification/delete", "reason": "user verification administration is not adopted" },
{ "method": "userVerification/enroll", "reason": "user verification administration is not adopted" },
{ "method": "userVerification/status", "reason": "user verification administration is not adopted" },
{ "method": "userVerification/verify", "reason": "user verification administration is not adopted" }
]
{
"method": "FuzzyFileSearch",
"reason": "legacy method is not adopted"
},
{
"method": "GetAuthStatus",
"reason": "legacy method is not adopted"
},
{
"method": "GetConversationSummary",
"reason": "legacy method is not adopted"
},
{
"method": "GitDiffToRemote",
"reason": "legacy method is not adopted"
},
{
"method": "fuzzyFileSearch",
"reason": "deprecated upstream method is not adopted"
},
{
"method": "fuzzyFileSearch/sessionStart",
"reason": "experimental fuzzy session is not adopted"
},
{
"method": "fuzzyFileSearch/sessionStop",
"reason": "experimental fuzzy session is not adopted"
},
{
"method": "fuzzyFileSearch/sessionUpdate",
"reason": "experimental fuzzy session is not adopted"
},
{
"method": "initialize",
"reason": "connection lifecycle owns the handshake"
},
{
"method": "initialized",
"reason": "connection lifecycle owns the handshake"
},
{
"method": "userVerification/delete",
"reason": "user verification administration is not adopted"
},
{
"method": "userVerification/enroll",
"reason": "user verification administration is not adopted"
},
{
"method": "userVerification/status",
"reason": "user verification administration is not adopted"
},
{
"method": "userVerification/verify",
"reason": "user verification administration is not adopted"
}
],
"adoptedServerRequests": {
"experimental": [
"currentTime/read"
]
}
}
6 changes: 3 additions & 3 deletions Sources/CodexAdapter/Resources/Protocol/receipt.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"codexVersion": "0.154.0",
"sdk": {
"version": "0.4.1",
"revision": "35e4a3dc07e60f084efb8a06424a8315c2ab6f64"
"version": "0.5.1",
"revision": "827e750a265d46dc062eb99015e532100bb09975"
},
"upstream": {
"tag": "rust-v0.154.0",
"commit": "6b9826e3aa83b1a5947db50f4332cb9c65f1b340"
},
"adoptionSHA256": "a4b439ce69cb5993b2907c480fe7caf338a6e15dab2892746c0ad320cff794c3",
"adoptionSHA256": "0a60742f6b7dfe11881f4f2d26233be55414798d3af2c240c31b2f6a56de0bd1",
"files": {
"stable/ClientRequest.json": {
"sha256": "da767fedd73502ceb644f5d7974927e1a6b7f1917b092aaa029e81949abf0125",
Expand Down
8 changes: 6 additions & 2 deletions THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
The adapter's original and extracted integration code is Computer MCP code
licensed under this repository's [LICENSE](LICENSE), FSL-1.1-ALv2.

The MCP Swift SDK transport fork and swift-codex are MIT-licensed. Swift Subprocess
and other Swift dependencies retain their own upstream licenses. The packaging
| Component | Selected release | License |
| --- | --- | --- |
| swift-codex | 0.5.1 | Apache-2.0 WITH Swift-exception |
| MCP Swift SDK transport fork (`computer-mcp/swift-sdk`) | 0.13.2-computer-mcp.1 | Apache-2.0 and retained MIT contributions, with CC-BY-4.0 documentation, under the upstream licensing transition |

Swift Subprocess and other Swift dependencies retain their own upstream licenses. The packaging
script copies license and notice files from each pinned SwiftPM checkout into
`ThirdPartyNotices/`, together with the unchanged `Package.resolved`. This is a
resolved-dependency inventory, not a claim that every resolved product is linked.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ struct CodexAppServerProviderTests {
@Test
func experimentalCallsRequireExplicitAdmissionAndRemainDistinctFromStableTools() async throws {
let provider = makeProvider()
#expect(provider.tools.filter { $0.name.hasPrefix("codex.app.native.") }.count == 96)
#expect(provider.tools.filter { $0.name.hasPrefix("codex.app.native.") }.count == 97)
#expect(!provider.tools.contains { $0.name == "codex.app.native.mock.experimentalMethod" })
await #expect(throws: CodexToolError.self) {
try await provider.call(
Expand Down
14 changes: 14 additions & 0 deletions Tests/CodexAdapterTests/CodexAppServerRuntimeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,17 @@ final class CodexAppServerRuntimeTests {
request.payload.objectValue?["params"]?.objectValue?["message"]
== .string("Use token=[REDACTED] to continue")
)
let params = try #require(request.payload.objectValue?["params"])
let native = try JSONDecoder().decode(
CodexAppServerProtocol.Stable.McpServerElicitationRequestParams.self,
from: JSONEncoder().encode(params))
guard case .mcpserverelicitationrequestparamsoption4(let scoped) = native else {
Issue.record("Expected a URL elicitation request.")
return
}
#expect(scoped.serverName == "fixture-mcp")
#expect(scoped.threadId == "thread_fixture")
#expect(scoped.turnId == "turn-fixture")
_ = try await runtime.respond(
requestID: request.id,
response: .object(["action": .string("decline")])
Expand Down Expand Up @@ -1774,6 +1785,9 @@ struct AppServerProcessFixture {
"elicitationId": .string("elicitation-fixture"),
"message": .string(message),
"mode": .string("url"),
"serverName": .string("fixture-mcp"),
"threadId": .string("thread_fixture"),
"turnId": .string("turn-fixture"),
"url": .string("https://example.invalid/authorize"),
]),
])
Expand Down
42 changes: 35 additions & 7 deletions Tests/CodexAdapterTests/CodexNativeProjectionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,35 @@ struct CodexNativeProjectionTests {
#expect(Set(actual.map(\.method)) == Set(inventory.adoption.adopted[channel.rawValue] ?? []))
#expect(actual.allSatisfy { $0.risk == CodexAppServerMethodCatalog.risk(for: $0.method) })
}
#expect(methods.filter { $0.channel == .stable }.count == 96)
#expect(methods.filter { $0.channel == .experimental }.count == 51)
#expect(methods.filter { $0.channel == .stable }.count == 97)
#expect(methods.filter { $0.channel == .experimental }.count == 53)
#expect(Set(methods.map(\.toolName)).count == methods.count)
for exclusion in inventory.adoption.excluded {
#expect(!methods.contains { $0.method == exclusion.method })
}
}

@Test(arguments: [
(
"account/bedrock/discover", CodexOperationRisk.readOnly,
ProtocolInventory.Channel.experimental
),
("account/bedrock/setup", .externalWrite, .experimental),
("plugin/reconcile", .destructive, .stable),
])
func adoptedMethodEffectsDefineToolAnnotations(
method: String, risk: CodexOperationRisk, channel: ProtocolInventory.Channel
) throws {
let descriptor = try #require(CodexAppServerMethodCatalog.method(named: method))
#expect(descriptor.channel == channel)
#expect(descriptor.risk == risk)
#expect(descriptor.tool._meta?["io.github.computer-mcp/risk"] == .string(risk.rawValue))
#expect(descriptor.tool.annotations.readOnlyHint == (risk == .readOnly))
#expect(descriptor.tool.annotations.destructiveHint == (risk == .destructive))
#expect(descriptor.tool.annotations.idempotentHint == (risk == .readOnly))
#expect(descriptor.tool.annotations.openWorldHint == true)
}

@Test func schemasPreserveNativeFieldsAndReferences() throws {
let method = try #require(CodexAppServerMethodCatalog.method(named: "turn/start"))
let schema = try #require(method.tool.inputSchema.objectValue)
Expand Down Expand Up @@ -91,15 +112,20 @@ struct CodexNativeProjectionTests {
try write.validate(params: .object(["path": .string("/tmp/file"), "dataBase64": .string("")]))
}

@Test func sdkAdoptionCannotAddAnUnclassifiedMethod() throws {
@Test(arguments: ProtocolInventory.Channel.allCases)
func sdkAdoptionCannotAddAnUnclassifiedMethod(channel: ProtocolInventory.Channel) throws {
let directory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: directory) }
let request = Data(
#"{"oneOf":[{"properties":{"method":{"enum":["future/new"]}},"required":["method"]}]}"#.utf8)
let adoption = Data(
#"{"schema":"swift-codex.codex-app-server-method-adoption.v1","upstreamTag":"rust-v1.2.3","adopted":{"stable":["future/new"],"experimental":[]},"excluded":[]}"#
.utf8)
let adoption = try JSONSerialization.data(withJSONObject: [
"schema": "swift-codex.codex-app-server-method-adoption.v1", "upstreamTag": "rust-v1.2.3",
"adopted": Dictionary(
uniqueKeysWithValues: ProtocolInventory.Channel.allCases.map {
($0.rawValue, $0 == channel ? ["future/new"] : [])
}), "excluded": [],
])
var files: [String: Any] = [:]
for channel in ProtocolInventory.Channel.allCases {
try FileManager.default.createDirectory(
Expand All @@ -115,7 +141,9 @@ struct CodexNativeProjectionTests {
"codexVersion": "1.2.3", "adoptionSHA256": AppServerSchema.digest(adoption), "files": files,
]).write(to: directory.appendingPathComponent("receipt.json"))
let inventory = try ProtocolInventory(directory: directory)
#expect(throws: SchemaError.self) {
#expect(CodexAppServerMethodCatalog.risk(for: "future/new") == nil)
#expect(throws: SchemaError.invalid("Adopted SDK method lacks projection policy: future/new."))
{
try CodexAppServerMethodCatalog.derive(inventory: inventory)
}
}
Expand Down
Loading
Loading