* 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
445 lines
16 KiB
Swift
445 lines
16 KiB
Swift
import Foundation
|
|
|
|
/// The Model Context Protocol (MCP) provides a standardized way
|
|
/// for servers to expose resources to clients.
|
|
/// Resources allow servers to share data that provides context to language models,
|
|
/// such as files, database schemas, or application-specific information.
|
|
/// Each resource is uniquely identified by a URI.
|
|
///
|
|
/// - SeeAlso: https://modelcontextprotocol.io/specification/2025-11-25/server/resources/
|
|
public struct Resource: Hashable, Codable, Sendable {
|
|
/// The resource name
|
|
public var name: String
|
|
/// A human-readable resource title
|
|
public var title: String?
|
|
/// The resource URI
|
|
public var uri: String
|
|
/// The resource description
|
|
public var description: String?
|
|
/// The resource MIME type
|
|
public var mimeType: String?
|
|
/// Raw content size in bytes
|
|
public var size: Int?
|
|
/// Annotations for this resource
|
|
public var annotations: Resource.Annotations?
|
|
/// Optional set of sized icons that the client can display in a user interface
|
|
public var icons: [Icon]?
|
|
/// Metadata fields for the resource (see spec for _meta usage)
|
|
public var _meta: Metadata?
|
|
|
|
public init(
|
|
name: String,
|
|
uri: String,
|
|
title: String? = nil,
|
|
description: String? = nil,
|
|
mimeType: String? = nil,
|
|
size: Int? = nil,
|
|
annotations: Resource.Annotations? = nil,
|
|
icons: [Icon]? = nil,
|
|
_meta: Metadata? = nil
|
|
) {
|
|
self.name = name
|
|
self.title = title
|
|
self.uri = uri
|
|
self.description = description
|
|
self.mimeType = mimeType
|
|
self.size = size
|
|
self.annotations = annotations
|
|
self.icons = icons
|
|
self._meta = _meta
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case name
|
|
case uri
|
|
case title
|
|
case description
|
|
case mimeType
|
|
case size
|
|
case annotations
|
|
case icons
|
|
case _meta
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
name = try container.decode(String.self, forKey: .name)
|
|
uri = try container.decode(String.self, forKey: .uri)
|
|
title = try container.decodeIfPresent(String.self, forKey: .title)
|
|
description = try container.decodeIfPresent(String.self, forKey: .description)
|
|
mimeType = try container.decodeIfPresent(String.self, forKey: .mimeType)
|
|
size = try container.decodeIfPresent(Int.self, forKey: .size)
|
|
annotations = try container.decodeIfPresent(Resource.Annotations.self, forKey: .annotations)
|
|
icons = try container.decodeIfPresent([Icon].self, forKey: .icons)
|
|
_meta = try container.decodeIfPresent(Metadata.self, forKey: ._meta)
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
try container.encode(name, forKey: .name)
|
|
try container.encode(uri, forKey: .uri)
|
|
try container.encodeIfPresent(title, forKey: .title)
|
|
try container.encodeIfPresent(description, forKey: .description)
|
|
try container.encodeIfPresent(mimeType, forKey: .mimeType)
|
|
try container.encodeIfPresent(size, forKey: .size)
|
|
try container.encodeIfPresent(annotations, forKey: .annotations)
|
|
try container.encodeIfPresent(icons, forKey: .icons)
|
|
try container.encodeIfPresent(_meta, forKey: ._meta)
|
|
}
|
|
|
|
/// Content of a resource.
|
|
public struct Content: Hashable, Codable, Sendable {
|
|
/// The resource URI
|
|
public let uri: String
|
|
/// The resource MIME type
|
|
public let mimeType: String?
|
|
/// The resource text content
|
|
public let text: String?
|
|
/// The resource binary content
|
|
public let blob: String?
|
|
/// Metadata fields (see spec for _meta usage)
|
|
public var _meta: Metadata?
|
|
|
|
public static func text(
|
|
_ content: String,
|
|
uri: String,
|
|
mimeType: String? = nil,
|
|
_meta: Metadata? = nil
|
|
) -> Self {
|
|
.init(uri: uri, mimeType: mimeType, text: content, _meta: _meta)
|
|
}
|
|
|
|
public static func binary(
|
|
_ data: Data,
|
|
uri: String,
|
|
mimeType: String? = nil,
|
|
_meta: Metadata? = nil
|
|
) -> Self {
|
|
.init(
|
|
uri: uri,
|
|
mimeType: mimeType,
|
|
blob: data.base64EncodedString(),
|
|
_meta: _meta
|
|
)
|
|
}
|
|
|
|
private init(
|
|
uri: String,
|
|
mimeType: String? = nil,
|
|
text: String? = nil,
|
|
_meta: Metadata? = nil
|
|
) {
|
|
self.uri = uri
|
|
self.mimeType = mimeType
|
|
self.text = text
|
|
self.blob = nil
|
|
self._meta = _meta
|
|
}
|
|
|
|
private init(
|
|
uri: String,
|
|
mimeType: String? = nil,
|
|
blob: String,
|
|
_meta: Metadata? = nil
|
|
) {
|
|
self.uri = uri
|
|
self.mimeType = mimeType
|
|
self.text = nil
|
|
self.blob = blob
|
|
self._meta = _meta
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case uri
|
|
case mimeType
|
|
case text
|
|
case blob
|
|
case _meta
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
uri = try container.decode(String.self, forKey: .uri)
|
|
mimeType = try container.decodeIfPresent(String.self, forKey: .mimeType)
|
|
text = try container.decodeIfPresent(String.self, forKey: .text)
|
|
blob = try container.decodeIfPresent(String.self, forKey: .blob)
|
|
_meta = try container.decodeIfPresent(Metadata.self, forKey: ._meta)
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
try container.encode(uri, forKey: .uri)
|
|
try container.encodeIfPresent(mimeType, forKey: .mimeType)
|
|
try container.encodeIfPresent(text, forKey: .text)
|
|
try container.encodeIfPresent(blob, forKey: .blob)
|
|
try container.encodeIfPresent(_meta, forKey: ._meta)
|
|
}
|
|
}
|
|
|
|
/// A resource template.
|
|
public struct Template: Hashable, Codable, Sendable {
|
|
/// The URI template pattern
|
|
public var uriTemplate: String
|
|
/// The template name
|
|
public var name: String
|
|
/// A human-readable template title
|
|
public var title: String?
|
|
/// The template description
|
|
public var description: String?
|
|
/// The resource MIME type
|
|
public var mimeType: String?
|
|
/// Annotations for this template
|
|
public var annotations: Resource.Annotations?
|
|
/// Optional set of sized icons that the client can display in a user interface
|
|
public var icons: [Icon]?
|
|
/// Metadata fields for the template (see spec for _meta usage)
|
|
public var _meta: Metadata?
|
|
|
|
public init(
|
|
uriTemplate: String,
|
|
name: String,
|
|
title: String? = nil,
|
|
description: String? = nil,
|
|
mimeType: String? = nil,
|
|
annotations: Resource.Annotations? = nil,
|
|
icons: [Icon]? = nil,
|
|
_meta: Metadata? = nil
|
|
) {
|
|
self.uriTemplate = uriTemplate
|
|
self.name = name
|
|
self.title = title
|
|
self.description = description
|
|
self.mimeType = mimeType
|
|
self.annotations = annotations
|
|
self.icons = icons
|
|
self._meta = _meta
|
|
}
|
|
}
|
|
|
|
// A resource annotation.
|
|
public struct Annotations: Hashable, Codable, Sendable {
|
|
/// The intended audience for this resource.
|
|
public enum Audience: String, Hashable, Codable, Sendable {
|
|
/// Content intended for end users.
|
|
case user = "user"
|
|
/// Content intended for AI assistants.
|
|
case assistant = "assistant"
|
|
}
|
|
|
|
/// An array indicating the intended audience(s) for this resource. For example, `[.user, .assistant]` indicates content useful for both.
|
|
public let audience: [Audience]?
|
|
/// A number from 0.0 to 1.0 indicating the importance of this resource. A value of 1 means "most important" (effectively required), while 0 means "least important".
|
|
public let priority: Double?
|
|
/// An ISO 8601 formatted timestamp indicating when the resource was last modified (e.g., "2025-01-12T15:00:58Z").
|
|
public let lastModified: String?
|
|
|
|
public init(
|
|
audience: [Audience]? = nil,
|
|
priority: Double? = nil,
|
|
lastModified: String? = nil
|
|
) {
|
|
self.audience = audience
|
|
self.priority = priority
|
|
self.lastModified = lastModified
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
/// To discover available resources, clients send a `resources/list` request.
|
|
/// - SeeAlso: https://modelcontextprotocol.io/specification/2025-11-25/server/resources/#listing-resources
|
|
public enum ListResources: Method {
|
|
public static let name: String = "resources/list"
|
|
|
|
public struct Parameters: NotRequired, Hashable, Codable, Sendable {
|
|
public let cursor: String?
|
|
|
|
public init() {
|
|
self.cursor = nil
|
|
}
|
|
|
|
public init(cursor: String) {
|
|
self.cursor = cursor
|
|
}
|
|
}
|
|
|
|
public struct Result: Hashable, Codable, Sendable {
|
|
public let resources: [Resource]
|
|
public let nextCursor: String?
|
|
public var _meta: Metadata?
|
|
|
|
public init(
|
|
resources: [Resource],
|
|
nextCursor: String? = nil,
|
|
_meta: Metadata? = nil
|
|
) {
|
|
self.resources = resources
|
|
self.nextCursor = nextCursor
|
|
self._meta = _meta
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey, CaseIterable {
|
|
case resources, nextCursor, _meta
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
try container.encode(resources, forKey: .resources)
|
|
try container.encodeIfPresent(nextCursor, forKey: .nextCursor)
|
|
try container.encodeIfPresent(_meta, forKey: ._meta)
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
resources = try container.decode([Resource].self, forKey: .resources)
|
|
nextCursor = try container.decodeIfPresent(String.self, forKey: .nextCursor)
|
|
_meta = try container.decodeIfPresent(Metadata.self, forKey: ._meta)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// To retrieve resource contents, clients send a `resources/read` request:
|
|
/// - SeeAlso: https://modelcontextprotocol.io/specification/2025-11-25/server/resources/#reading-resources
|
|
public enum ReadResource: Method {
|
|
public static let name: String = "resources/read"
|
|
|
|
public struct Parameters: Hashable, Codable, Sendable {
|
|
public let uri: String
|
|
|
|
public init(uri: String) {
|
|
self.uri = uri
|
|
}
|
|
}
|
|
|
|
public struct Result: Hashable, Codable, Sendable {
|
|
public let contents: [Resource.Content]
|
|
/// Optional metadata about this result
|
|
public var _meta: Metadata?
|
|
|
|
public init(
|
|
contents: [Resource.Content],
|
|
_meta: Metadata? = nil
|
|
) {
|
|
self.contents = contents
|
|
self._meta = _meta
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey, CaseIterable {
|
|
case contents, _meta
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
try container.encode(contents, forKey: .contents)
|
|
try container.encodeIfPresent(_meta, forKey: ._meta)
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
contents = try container.decode([Resource.Content].self, forKey: .contents)
|
|
_meta = try container.decodeIfPresent(Metadata.self, forKey: ._meta)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// To discover available resource templates, clients send a `resources/templates/list` request.
|
|
/// - SeeAlso: https://modelcontextprotocol.io/specification/2025-11-25/server/resources/#resource-templates
|
|
public enum ListResourceTemplates: Method {
|
|
public static let name: String = "resources/templates/list"
|
|
|
|
public struct Parameters: NotRequired, Hashable, Codable, Sendable {
|
|
public let cursor: String?
|
|
|
|
public init() {
|
|
self.cursor = nil
|
|
}
|
|
|
|
public init(cursor: String) {
|
|
self.cursor = cursor
|
|
}
|
|
}
|
|
|
|
public struct Result: Hashable, Codable, Sendable {
|
|
public let templates: [Resource.Template]
|
|
public let nextCursor: String?
|
|
/// Optional metadata about this result
|
|
public var _meta: Metadata?
|
|
|
|
public init(
|
|
templates: [Resource.Template],
|
|
nextCursor: String? = nil,
|
|
_meta: Metadata? = nil
|
|
) {
|
|
self.templates = templates
|
|
self.nextCursor = nextCursor
|
|
self._meta = _meta
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey, CaseIterable {
|
|
case templates = "resourceTemplates"
|
|
case nextCursor
|
|
case _meta
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
try container.encode(templates, forKey: .templates)
|
|
try container.encodeIfPresent(nextCursor, forKey: .nextCursor)
|
|
try container.encodeIfPresent(_meta, forKey: ._meta)
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
templates = try container.decode([Resource.Template].self, forKey: .templates)
|
|
nextCursor = try container.decodeIfPresent(String.self, forKey: .nextCursor)
|
|
_meta = try container.decodeIfPresent(Metadata.self, forKey: ._meta)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// When the list of available resources changes, servers that declared the listChanged capability SHOULD send a notification.
|
|
/// - SeeAlso: https://modelcontextprotocol.io/specification/2025-11-25/server/resources/#list-changed-notification
|
|
public struct ResourceListChangedNotification: Notification {
|
|
public static let name: String = "notifications/resources/list_changed"
|
|
|
|
public typealias Parameters = Empty
|
|
}
|
|
|
|
/// Clients can subscribe to specific resources and receive notifications when they change.
|
|
/// - SeeAlso: https://modelcontextprotocol.io/specification/2025-11-25/server/resources/#subscriptions
|
|
public enum ResourceSubscribe: Method {
|
|
public static let name: String = "resources/subscribe"
|
|
|
|
public struct Parameters: Hashable, Codable, Sendable {
|
|
public let uri: String
|
|
}
|
|
|
|
public typealias Result = Empty
|
|
}
|
|
|
|
/// Sent from the client to request cancellation of resources/updated notifications from the server. This should follow a previous resources/subscribe request.
|
|
/// - SeeAlso: https://modelcontextprotocol.io/specification/2025-06-18/schema#unsubscriberequest
|
|
public enum ResourceUnsubscribe: Method {
|
|
public static let name: String = "resources/unsubscribe"
|
|
|
|
public struct Parameters: Hashable, Codable, Sendable {
|
|
public let uri: String
|
|
}
|
|
|
|
public typealias Result = Empty
|
|
}
|
|
|
|
/// When a resource changes, servers that declared the updated capability SHOULD send a notification to subscribed clients.
|
|
/// - SeeAlso: https://modelcontextprotocol.io/specification/2025-11-25/server/resources/#subscriptions
|
|
public struct ResourceUpdatedNotification: Notification {
|
|
public static let name: String = "notifications/resources/updated"
|
|
|
|
public struct Parameters: Hashable, Codable, Sendable {
|
|
public let uri: String
|
|
|
|
public init(uri: String) {
|
|
self.uri = uri
|
|
}
|
|
}
|
|
}
|