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
18 changes: 17 additions & 1 deletion Sources/MCPServerKit/MCPServerKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@ public enum MCPServerKitVersion {

public enum MCPProtocolVersion: String, CaseIterable, Codable, Sendable {
case v2024_11_05 = "2024-11-05"
case v2025_03_26 = "2025-03-26"
case v2025_06_18 = "2025-06-18"
case v2025_11_25 = "2025-11-25"

public static let fallback = v2024_11_05
/// Latest version this package implements. During initialization the server must answer with
/// its latest supported version when the client proposes an unsupported value.
public static let latest = v2025_11_25
public static let fallback = latest

/// Streamable HTTP compatibility version used when a subsequent request omits the version
/// header and the server does not retain negotiated session state.
public static let missingHTTPHeaderFallback = v2025_03_26

public static func negotiated(requested: String?) -> String {
let trimmed = requested?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
Expand Down Expand Up @@ -169,6 +177,7 @@ public struct MCPRequestParams: Codable, Equatable, Sendable {
public let name: String?
public let arguments: [String: MCPJSONValue]?
public let uri: String?
public let cursor: String?
public let protocolVersion: String?

/// A compatibility view of scalar arguments used by pre-2025 handlers.
Expand All @@ -183,18 +192,21 @@ public struct MCPRequestParams: Codable, Equatable, Sendable {
case name
case arguments
case uri
case cursor
case protocolVersion
}

public init(
name: String?,
arguments: [String: MCPJSONValue]?,
uri: String? = nil,
cursor: String? = nil,
protocolVersion: String? = nil
) {
self.name = name
self.arguments = arguments
self.uri = uri
self.cursor = cursor
self.protocolVersion = protocolVersion
}

Expand All @@ -203,12 +215,14 @@ public struct MCPRequestParams: Codable, Equatable, Sendable {
name: String?,
arguments: [String: String],
uri: String? = nil,
cursor: String? = nil,
protocolVersion: String? = nil
) {
self.init(
name: name,
arguments: arguments.mapValues(MCPJSONValue.string),
uri: uri,
cursor: cursor,
protocolVersion: protocolVersion
)
}
Expand All @@ -217,6 +231,7 @@ public struct MCPRequestParams: Codable, Equatable, Sendable {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decodeIfPresent(String.self, forKey: .name)
uri = try container.decodeIfPresent(String.self, forKey: .uri)
cursor = try container.decodeIfPresent(String.self, forKey: .cursor)
protocolVersion = try container.decodeIfPresent(String.self, forKey: .protocolVersion)
arguments = try container.decodeIfPresent([String: MCPJSONValue].self, forKey: .arguments)
}
Expand All @@ -226,6 +241,7 @@ public struct MCPRequestParams: Codable, Equatable, Sendable {
try container.encodeIfPresent(name, forKey: .name)
try container.encodeIfPresent(arguments, forKey: .arguments)
try container.encodeIfPresent(uri, forKey: .uri)
try container.encodeIfPresent(cursor, forKey: .cursor)
try container.encodeIfPresent(protocolVersion, forKey: .protocolVersion)
}

Expand Down
13 changes: 11 additions & 2 deletions Tests/MCPServerKitTests/StygianMCPTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ import Testing
#expect(decoded == request)
}

@Test func preservesListCursorThroughRoundTrip() throws {
let payload = Data(#"{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{"cursor":"page-2"}}"#.utf8)
let request = try JSONDecoder().decode(MCPRequest.self, from: payload)
#expect(request.params?.cursor == "page-2")
#expect(try JSONDecoder().decode(MCPRequest.self, from: JSONEncoder().encode(request)) == request)
}

@Test func encodesInitializeResultWithCapabilities() throws {
let result = MCPInitializeResult(
protocolVersion: MCPProtocolVersion.negotiated(requested: "2024-11-05"),
Expand Down Expand Up @@ -198,10 +205,12 @@ import Testing
}

@Test func negotiatesLatestProtocolAndFallsBackForUnknownVersions() {
#expect(MCPProtocolVersion.negotiated(requested: "2025-03-26") == "2025-03-26")
#expect(MCPProtocolVersion.negotiated(requested: "2025-11-25") == "2025-11-25")
#expect(MCPProtocolVersion.negotiated(requested: " 2025-11-25 ") == "2025-11-25")
#expect(MCPProtocolVersion.negotiated(requested: "2099-01-01") == MCPProtocolVersion.fallback.rawValue)
#expect(MCPProtocolVersion.negotiated(requested: nil) == MCPProtocolVersion.fallback.rawValue)
#expect(MCPProtocolVersion.negotiated(requested: "2099-01-01") == MCPProtocolVersion.latest.rawValue)
#expect(MCPProtocolVersion.negotiated(requested: nil) == MCPProtocolVersion.latest.rawValue)
#expect(MCPProtocolVersion.missingHTTPHeaderFallback.rawValue == "2025-03-26")
}

@Test func encodesToolListCursorAndErrorData() throws {
Expand Down
Loading