Add support for JSON-RPC batching (#67)

* Update StdioTransport documentation comments to indicate support for JSON-RPC batches

* Add server support for JSON-RPC batching

* Add client support for JSON-RPC batching

* Fix bug that caused error messages to be wrapped unnecessarily
This commit is contained in:
Mattt 2025-04-10 12:13:41 -07:00 • committed by GitHub
commit 82f4fd29b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -17,6 +17,12 @@ import struct Foundation.Data
#if canImport(Darwin) || canImport(Glibc)
/// Standard input/output transport implementation
///
/// This transport supports JSON-RPC 2.0 messages, including individual requests,
/// notifications, responses, and batches containing multiple requests/notifications.
///
/// Messages are delimited by newlines and must not contain embedded newlines.
/// Each message must be a complete, valid JSON object or array (for batches).
public actor StdioTransport: Transport {
private let input: FileDescriptor
private let output: FileDescriptor
@ -131,6 +137,13 @@ import struct Foundation.Data
logger.info("Transport disconnected")
}
/// Sends a message over the transport.
///
/// This method supports sending both individual JSON-RPC messages and JSON-RPC batches.
/// Batches should be encoded as a JSON array containing multiple request/notification objects
/// according to the JSON-RPC 2.0 specification.
///
/// - Parameter message: The message data to send (without a trailing newline)
public func send(_ message: Data) async throws {
guard isConnected else {
throw MCPError.transportError(Errno(rawValue: ENOTCONN))
@ -158,6 +171,11 @@ import struct Foundation.Data
}
}
/// Receives messages from the transport.
///
/// Messages may be individual JSON-RPC requests, notifications, responses,
/// or batches containing multiple requests/notifications encoded as JSON arrays.
/// Each message is guaranteed to be a complete JSON object or array.
public func receive() -> AsyncThrowingStream<Data, Swift.Error> {
return AsyncThrowingStream { continuation in
Task {