Skip to content

Commit 973b46b

Browse files
feat: 2025-11-25 review fixes (#201)
* fix: review fixes of the latest changes * fix: updated unit tests * chore: more conformance compliance improvements * chore: reverted protocol changes * chore: more compliance improvements * chore: more conformance compliance improvements * chore: improvements to the backcompat and updated to Tool unit tests * chore: ci update
1 parent 6112a39 commit 973b46b

35 files changed

Lines changed: 692 additions & 507 deletions

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ jobs:
2525
steps:
2626
- uses: actions/checkout@v4
2727

28+
- name: Prepare Linux apt dependencies
29+
if: matrix.os == 'ubuntu-latest'
30+
run: |
31+
sudo -n apt-get update
32+
sudo -n apt-get install -y --no-install-recommends libcurl4-openssl-dev
33+
2834
- name: Setup Swift on Linux
2935
if: matrix.os == 'ubuntu-latest'
3036
uses: vapor/swiftly-action@v0.2

Package.resolved

Lines changed: 1 addition & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,6 @@
33

44
import PackageDescription
55

6-
// Base dependencies needed on all platforms
7-
var dependencies: [Package.Dependency] = [
8-
.package(url: "https://github.com/apple/swift-system.git", from: "1.0.0"),
9-
.package(url: "https://github.com/apple/swift-log.git", from: "1.5.0"),
10-
.package(url: "https://github.com/apple/swift-async-algorithms.git", from: "1.0.0"),
11-
.package(url: "https://github.com/mattt/eventsource.git", from: "1.1.0"),
12-
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
13-
]
14-
15-
// Target dependencies needed on all platforms
16-
var targetDependencies: [Target.Dependency] = [
17-
.product(name: "SystemPackage", package: "swift-system"),
18-
.product(name: "Logging", package: "swift-log"),
19-
.product(name: "AsyncAlgorithms", package: "swift-async-algorithms"),
20-
.product(
21-
name: "EventSource", package: "eventsource",
22-
condition: .when(platforms: [.macOS, .iOS, .tvOS, .visionOS, .watchOS, .macCatalyst])),
23-
.product(name: "NIOCore", package: "swift-nio"),
24-
.product(name: "NIOPosix", package: "swift-nio"),
25-
.product(name: "NIOHTTP1", package: "swift-nio"),
26-
]
27-
286
let package = Package(
297
name: "mcp-swift-sdk",
308
platforms: [
@@ -47,27 +25,55 @@ let package = Package(
4725
name: "mcp-everything-client",
4826
targets: ["MCPConformanceClient"])
4927
],
50-
dependencies: dependencies,
28+
dependencies: [
29+
.package(url: "https://github.com/apple/swift-system.git", from: "1.0.0"),
30+
.package(url: "https://github.com/apple/swift-log.git", from: "1.5.0"),
31+
.package(url: "https://github.com/mattt/eventsource.git", from: "1.1.0"),
32+
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
33+
],
5134
targets: [
52-
// Targets are the basic building blocks of a package, defining a module or a test suite.
53-
// Targets can depend on other targets in this package and products from dependencies.
5435
.target(
5536
name: "MCP",
56-
dependencies: targetDependencies,
37+
dependencies: [
38+
.product(name: "SystemPackage", package: "swift-system"),
39+
.product(name: "Logging", package: "swift-log"),
40+
.product(
41+
name: "EventSource", package: "eventsource",
42+
condition: .when(platforms: [.macOS, .iOS, .tvOS, .visionOS, .watchOS, .macCatalyst])),
43+
],
5744
swiftSettings: [
5845
.enableUpcomingFeature("StrictConcurrency")
5946
]
6047
),
6148
.testTarget(
6249
name: "MCPTests",
63-
dependencies: ["MCP"] + targetDependencies),
50+
dependencies: [
51+
"MCP",
52+
.product(name: "SystemPackage", package: "swift-system"),
53+
.product(name: "Logging", package: "swift-log"),
54+
.product(
55+
name: "EventSource", package: "eventsource",
56+
condition: .when(platforms: [.macOS, .iOS, .tvOS, .visionOS, .watchOS, .macCatalyst])),
57+
]
58+
),
6459
.executableTarget(
6560
name: "MCPConformanceServer",
66-
dependencies: ["MCP"] + targetDependencies,
67-
path: "Sources/MCPConformance/Server"),
61+
dependencies: [
62+
"MCP",
63+
.product(name: "Logging", package: "swift-log"),
64+
.product(name: "NIOCore", package: "swift-nio"),
65+
.product(name: "NIOPosix", package: "swift-nio"),
66+
.product(name: "NIOHTTP1", package: "swift-nio"),
67+
],
68+
path: "Sources/MCPConformance/Server"
69+
),
6870
.executableTarget(
6971
name: "MCPConformanceClient",
70-
dependencies: ["MCP"] + targetDependencies,
71-
path: "Sources/MCPConformance/Client")
72+
dependencies: [
73+
"MCP",
74+
.product(name: "Logging", package: "swift-log"),
75+
],
76+
path: "Sources/MCPConformance/Client"
77+
)
7278
]
7379
)

Sources/MCP/Base/Error.swift

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ extension MCPError: Codable {
171171
public func encode(to encoder: Encoder) throws {
172172
var container = encoder.container(keyedBy: CodingKeys.self)
173173
try container.encode(code, forKey: .code)
174-
try container.encode(errorDescription ?? "Unknown error", forKey: .message)
175174

176175
// Encode additional data if available
177176
switch self {
@@ -180,13 +179,17 @@ extension MCPError: Codable {
180179
.methodNotFound(let detail),
181180
.invalidParams(let detail),
182181
.internalError(let detail):
182+
try container.encode(errorDescription ?? "Unknown error", forKey: .message)
183183
if let detail = detail {
184184
try container.encode(["detail": detail], forKey: .data)
185185
}
186186
case .serverError(_, _):
187187
// No additional data for server errors
188+
try container.encode(errorDescription ?? "Unknown error", forKey: .message)
188189
break
189-
case .urlElicitationRequired(_, let elicitations):
190+
case .urlElicitationRequired(let message, let elicitations):
191+
// Encode the raw message so decode can round-trip without prefix doubling
192+
try container.encode(message, forKey: .message)
190193
// Encode elicitations array as structured data
191194
let elicitationsData = elicitations.map { info -> [String: Value] in
192195
return [
@@ -201,8 +204,9 @@ extension MCPError: Codable {
201204
forKey: .data
202205
)
203206
case .connectionClosed:
204-
break
207+
try container.encode(errorDescription ?? "Unknown error", forKey: .message)
205208
case .transportError(let error):
209+
try container.encode(errorDescription ?? "Unknown error", forKey: .message)
206210
try container.encode(
207211
["error": error.localizedDescription],
208212
forKey: .data
@@ -279,7 +283,21 @@ extension MCPError: Codable {
279283

280284
extension MCPError: Equatable {
281285
public static func == (lhs: MCPError, rhs: MCPError) -> Bool {
282-
lhs.code == rhs.code
286+
switch (lhs, rhs) {
287+
case (.parseError(let a), .parseError(let b)): return a == b
288+
case (.invalidRequest(let a), .invalidRequest(let b)): return a == b
289+
case (.methodNotFound(let a), .methodNotFound(let b)): return a == b
290+
case (.invalidParams(let a), .invalidParams(let b)): return a == b
291+
case (.internalError(let a), .internalError(let b)): return a == b
292+
case (.serverError(let c1, let m1), .serverError(let c2, let m2)):
293+
return c1 == c2 && m1 == m2
294+
case (.urlElicitationRequired(let m1, let e1), .urlElicitationRequired(let m2, let e2)):
295+
return m1 == m2 && e1 == e2
296+
case (.connectionClosed, .connectionClosed): return true
297+
case (.transportError(let a), .transportError(let b)):
298+
return a.localizedDescription == b.localizedDescription
299+
default: return false
300+
}
283301
}
284302
}
285303

Sources/MCP/Base/Messages.swift

Lines changed: 18 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,9 @@ struct AnyMethod: Method, Sendable {
3838

3939
extension Method where Parameters == Empty {
4040
public static func request(
41-
id: ID = .random,
42-
_meta: Metadata? = nil
41+
id: ID = .random
4342
) -> Request<Self> {
44-
Request(id: id, method: name, params: Empty(), _meta: _meta)
43+
Request(id: id, method: name, params: Empty())
4544
}
4645
}
4746

@@ -55,28 +54,25 @@ extension Method {
5554
/// Create a request with the given parameters.
5655
public static func request(
5756
id: ID = .random,
58-
_ parameters: Self.Parameters,
59-
_meta: Metadata? = nil
57+
_ parameters: Self.Parameters
6058
) -> Request<Self> {
61-
Request(id: id, method: name, params: parameters, _meta: _meta)
59+
Request(id: id, method: name, params: parameters)
6260
}
6361

6462
/// Create a response with the given result.
6563
public static func response(
6664
id: ID,
67-
result: Self.Result,
68-
_meta: Metadata? = nil
65+
result: Self.Result
6966
) -> Response<Self> {
70-
Response(id: id, result: result, _meta: _meta)
67+
Response(id: id, result: result)
7168
}
7269

7370
/// Create a response with the given error.
7471
public static func response(
7572
id: ID,
76-
error: MCPError,
77-
_meta: Metadata? = nil
73+
error: MCPError
7874
) -> Response<Self> {
79-
Response(id: id, error: error, _meta: _meta)
75+
Response(id: id, error: error)
8076
}
8177
}
8278

@@ -90,23 +86,19 @@ public struct Request<M: Method>: Hashable, Identifiable, Codable, Sendable {
9086
public let method: String
9187
/// The request parameters.
9288
public let params: M.Parameters
93-
/// Metadata for this request (see spec for _meta usage, includes progressToken)
94-
public let _meta: Metadata?
9589

9690
init(
9791
id: ID = .random,
9892
method: String,
99-
params: M.Parameters,
100-
_meta: Metadata? = nil
93+
params: M.Parameters
10194
) {
10295
self.id = id
10396
self.method = method
10497
self.params = params
105-
self._meta = _meta
10698
}
10799

108100
private enum CodingKeys: String, CodingKey, CaseIterable {
109-
case jsonrpc, id, method, params, _meta
101+
case jsonrpc, id, method, params
110102
}
111103

112104
public func encode(to encoder: Encoder) throws {
@@ -115,7 +107,6 @@ public struct Request<M: Method>: Hashable, Identifiable, Codable, Sendable {
115107
try container.encode(id, forKey: .id)
116108
try container.encode(method, forKey: .method)
117109
try container.encode(params, forKey: .params)
118-
try container.encodeIfPresent(_meta, forKey: ._meta)
119110
}
120111
}
121112

@@ -129,7 +120,6 @@ extension Request {
129120
}
130121
id = try container.decode(ID.self, forKey: .id)
131122
method = try container.decode(String.self, forKey: .method)
132-
_meta = try container.decodeIfPresent(Metadata.self, forKey: ._meta)
133123

134124
if M.Parameters.self is NotRequired.Type {
135125
// For NotRequired parameters, use decodeIfPresent or init()
@@ -221,44 +211,37 @@ public struct Response<M: Method>: Hashable, Identifiable, Codable, Sendable {
221211
public let id: ID
222212
/// The response result.
223213
public let result: Swift.Result<M.Result, MCPError>
224-
/// Metadata for this response (see spec for _meta usage)
225-
public let _meta: Metadata?
226214

227215
public init(
228216
id: ID,
229-
result: Swift.Result<M.Result, MCPError>,
230-
_meta: Metadata? = nil
217+
result: Swift.Result<M.Result, MCPError>
231218
) {
232219
self.id = id
233220
self.result = result
234-
self._meta = _meta
235221
}
236222

237223
public init(
238224
id: ID,
239-
result: M.Result,
240-
_meta: Metadata? = nil
225+
result: M.Result
241226
) {
242-
self.init(id: id, result: .success(result), _meta: _meta)
227+
self.init(id: id, result: .success(result))
243228
}
244229

245230
public init(
246231
id: ID,
247-
error: MCPError,
248-
_meta: Metadata? = nil
232+
error: MCPError
249233
) {
250-
self.init(id: id, result: .failure(error), _meta: _meta)
234+
self.init(id: id, result: .failure(error))
251235
}
252236

253237
private enum CodingKeys: String, CodingKey, CaseIterable {
254-
case jsonrpc, id, result, error, _meta
238+
case jsonrpc, id, result, error
255239
}
256240

257241
public func encode(to encoder: Encoder) throws {
258242
var container = encoder.container(keyedBy: CodingKeys.self)
259243
try container.encode(jsonrpc, forKey: .jsonrpc)
260244
try container.encode(id, forKey: .id)
261-
try container.encodeIfPresent(_meta, forKey: ._meta)
262245

263246
switch result {
264247
case .success(let result):
@@ -286,7 +269,6 @@ public struct Response<M: Method>: Hashable, Identifiable, Codable, Sendable {
286269
codingPath: container.codingPath,
287270
debugDescription: "Invalid response"))
288271
}
289-
_meta = try container.decodeIfPresent(Metadata.self, forKey: ._meta)
290272
}
291273
}
292274

@@ -301,14 +283,12 @@ extension AnyResponse {
301283
let resultValue = try JSONDecoder().decode(Value.self, from: data)
302284
self = Response<AnyMethod>(
303285
id: response.id,
304-
result: .success(resultValue),
305-
_meta: response._meta
286+
result: .success(resultValue)
306287
)
307288
case .failure(let error):
308289
self = Response<AnyMethod>(
309290
id: response.id,
310-
result: .failure(error),
311-
_meta: response._meta
291+
result: .failure(error)
312292
)
313293
}
314294
}

0 commit comments

Comments
 (0)