Skip to content
Open
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
4 changes: 2 additions & 2 deletions Package.resolved

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

39 changes: 38 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,43 @@ do {
> [!NOTE]
> `Server` automatically handles batch requests from MCP clients.

#### Progress Notifications

MCP supports optional progress tracking for long-running operations through notifications. Either side can send progress notifications to provide updates about operation status.

For example, a client might wish to update its loading UI whenever the server sends a progress notification for a particular `CallTool` request:

```swift
let callToolProgressToken = "abc-123"

// Register notification handler before invoking callTool
await client.onNotification(ProgressNotification.self) { message in
// Access progress notifications for a particular request by filtering for that request's progressToken
guard message.params.progressToken == callToolProgressToken else { return }

if let progressTotal = message.params.total {
let percentProgress = message.params.progress / progressTotal
// update loading UI with percent progress
}

if let progressMessage = message.params.message {
// update loading UI with human-readable progress information
}
}

// Call a tool with a progressToken specified in the _meta arguments
try await client.callTool(
name: "content-recommender",
arguments: [
"prompt": "The cutest Samoyed accounts across all social media",
"_meta": [
"progressToken": callToolProgressToken
]
]
)
```


## Server Usage

The server component allows your application to host model capabilities and respond to client requests.
Expand Down Expand Up @@ -886,4 +923,4 @@ see the [GitHub Releases page](https://github.com/modelcontextprotocol/swift-sdk
This project is licensed under the MIT License.

[mcp]: https://modelcontextprotocol.io
[mcp-spec-2025-03-26]: https://modelcontextprotocol.io/specification/2025-03-26
[mcp-spec-2025-03-26]: https://modelcontextprotocol.io/specification/2025-03-26
28 changes: 28 additions & 0 deletions Sources/MCP/Base/Utilities/Progress.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/// The Model Context Protocol includes optional progress tracking that allows receive updates about the progress of a request.
/// - Important: When a party wants to receive progress updates for a request, it must (1) include a `progressToken` in that request's metadata and (2) subscribe to `ProgressNotification` via`onNotification`. See README.md for example usage.
/// - SeeAlso: https://modelcontextprotocol.io/specification/2025-03-26/basic/utilities/progress
public struct ProgressNotification: Notification {
public static let name: String = "notifications/progress"

public struct Parameters: Hashable, Codable, Sendable {
/// The original progress token.
public let progressToken: String

/// The current progress value so far.
public let progress: Double

/// An optional “total” progress value.
public let total: Double?

/// An optional “message” value.
public let message: String?

public init(progressToken: String, progress: Double, total: Double?, message: String?) {
self.progressToken = progressToken
self.progress = progress
self.total = total
self.message = message
}
}
}

37 changes: 37 additions & 0 deletions Tests/MCPTests/NotificationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,43 @@ struct NotificationTests {
#expect(decoded.params.uri == "test://resource")
}

@Test("Progress notification with parameters")
func testProgressNotification() throws {
let params = ProgressNotification.Parameters(
progressToken: "some-token",
progress: 20,
total: 25,
message: "beep boop bop"
)
let notification = ProgressNotification.message(params)

#expect(notification.method == ProgressNotification.name)
#expect(notification.params.progressToken == "some-token")
#expect(notification.params.progress == 20)
#expect(notification.params.total == 25)
#expect(notification.params.message == "beep boop bop")

let encoder = JSONEncoder()
let decoder = JSONDecoder()

let data = try encoder.encode(notification)

// Verify the exact JSON structure
let json = try JSONDecoder().decode([String: Value].self, from: data)
#expect(json["jsonrpc"] == "2.0")
#expect(json["method"] == "notifications/progress")
#expect(json["params"] != nil)
#expect(json.count == 3, "Should contain jsonrpc, method, and params fields")

// Verify we can decode it back
let decoded = try decoder.decode(Message<ProgressNotification>.self, from: data)
#expect(decoded.method == ProgressNotification.name)
#expect(decoded.params.progressToken == "some-token")
#expect(decoded.params.progress == 20)
#expect(decoded.params.total == 25)
#expect(decoded.params.message == "beep boop bop")
}

@Test("AnyNotification decoding - without params")
func testAnyNotificationDecodingWithoutParams() throws {
// Test decoding when params field is missing
Expand Down
18 changes: 18 additions & 0 deletions Tests/MCPTests/ProgressTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Testing

@testable import MCP

@Test("ProgressNotification parameters validation")
func testProgressNotification() throws {
let params = ProgressNotification.Parameters(
progressToken: "some-token",
progress: 20,
total: 25,
message: "beep boop bop"
)
#expect(params.progressToken == "some-token")
#expect(params.progress == 20)
#expect(params.total == 25)
#expect(params.message == "beep boop bop")
#expect(ProgressNotification.name == "notifications/progress")
}