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
6 changes: 3 additions & 3 deletions ballerina/Ballerina.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
org = "ballerina"
name = "mcp"
version = "0.4.0"
version = "0.4.1"
authors = ["Ballerina"]
keywords = ["mcp"]
repository = "https://github.com/ballerina-platform/module-ballerina-mcp"
Expand All @@ -15,5 +15,5 @@ graalvmCompatible = true
[[platform.java21.dependency]]
groupId = "io.ballerina.stdlib."
artifactId = "mcp-native"
version = "0.4.0"
path = "../native/build/libs/mcp-native-0.4.0.jar"
version = "0.4.1"
path = "../native/build/libs/mcp-native-0.4.1-SNAPSHOT.jar"
8 changes: 6 additions & 2 deletions ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ dependencies = [
[[package]]
org = "ballerina"
name = "http"
version = "2.14.1"
version = "2.14.2"
dependencies = [
{org = "ballerina", name = "auth"},
{org = "ballerina", name = "cache"},
Expand Down Expand Up @@ -108,6 +108,9 @@ dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "lang.value"}
]
modules = [
{org = "ballerina", packageName = "io", moduleName = "io"}
]

[[package]]
org = "ballerina"
Expand Down Expand Up @@ -220,9 +223,10 @@ dependencies = [
[[package]]
org = "ballerina"
name = "mcp"
version = "0.4.0"
version = "0.4.1"
dependencies = [
{org = "ballerina", name = "http"},
{org = "ballerina", name = "io"},
{org = "ballerina", name = "jballerina.java"}
]
modules = [
Expand Down
36 changes: 24 additions & 12 deletions ballerina/client.bal
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,20 @@
// under the License.

# Configuration options for initializing an MCP client.
#
# + capabilities - Capabilities to be advertised by this client.
public type ClientConfiguration record {|
*ProtocolOptions;
*StreamableHttpClientTransportConfig;
# Client information such as name and version.
Implementation info;
# Client capabilities configuration.
ClientCapabilityConfiguration capabilityConfig?;
|};

# Configuration options for initializing an MCP client.
public type ClientCapabilityConfiguration record {|
# Capabilities to be advertised by this client.
ClientCapabilities capabilities?;
# Whether to enforce strict capabilities compliance.
boolean enforceStrictCapabilities?;
Comment on lines +30 to +31
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this defined in the spec?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes,

|};

# Represents an MCP client built on top of the Streamable HTTP transport.
Expand All @@ -45,10 +54,10 @@ public distinct isolated client class Client {
# + serverUrl - MCP server URL.
# + clientInfo - Client details, such as name and version.
# + config - Optional configuration containing client capabilities.
public isolated function init(string serverUrl, Implementation clientInfo, ClientConfiguration? config = ()) {
public isolated function init(string serverUrl, *ClientConfiguration config) {
self.serverUrl = serverUrl;
self.clientInfo = clientInfo.cloneReadOnly();
self.clientCapabilities = config?.capabilities.cloneReadOnly() ?: {};
self.clientInfo = config.info.cloneReadOnly();
self.clientCapabilities = (config.capabilityConfig?.capabilities).cloneReadOnly() ?: {};
}

# Establishes a connection to the MCP server and performs protocol initialization.
Expand Down Expand Up @@ -84,7 +93,9 @@ public distinct isolated client class Client {
// Validate protocol compatibility.
if (!SUPPORTED_PROTOCOL_VERSIONS.some(v => v == protocolVersion)) {
return error ProtocolVersionError(
string `Server protocol version '${protocolVersion}' is not supported. Supported versions: ${SUPPORTED_PROTOCOL_VERSIONS.toString()}.`
string `Server protocol version '${
protocolVersion}' is not supported. Supported versions: ${
SUPPORTED_PROTOCOL_VERSIONS.toString()}.`
);
}

Expand All @@ -99,7 +110,8 @@ public distinct isolated client class Client {
check self.sendNotificationMessage(initNotification);
} else {
return error ClientInitializationError(
string `Initialization failed: unexpected response type '${(typeof response).toString()}' received from server.`
string `Initialization failed: unexpected response type '${
(typeof response).toString()}' received from server.`
);
}
}
Expand All @@ -113,7 +125,7 @@ public distinct isolated client class Client {
StreamableHttpClientTransport? currentTransport = self.transport;
if currentTransport is () {
return error UninitializedTransportError(
"Subscription failed: client transport is not initialized. Call initialize() first."
"Subscription failed: client transport is not initialized."
);
}
return currentTransport.establishEventStream();
Expand Down Expand Up @@ -166,7 +178,7 @@ public distinct isolated client class Client {
StreamableHttpClientTransport? currentTransport = self.transport;
if currentTransport is () {
return error UninitializedTransportError(
"Closure failed: client transport is not initialized. Call initialize() first."
"Closure failed: client transport is not initialized."
);
}

Expand All @@ -193,7 +205,7 @@ public distinct isolated client class Client {
StreamableHttpClientTransport? currentTransport = self.transport;
if currentTransport is () {
return error UninitializedTransportError(
"Cannot send request: client transport is not initialized. Call initialize() first."
"Cannot send request: client transport is not initialized."
);
}

Expand Down Expand Up @@ -222,7 +234,7 @@ public distinct isolated client class Client {
StreamableHttpClientTransport? currentTransport = self.transport;
if currentTransport is () {
return error UninitializedTransportError(
"Cannot send notification: client transport is not initialized. Call initialize() first."
"Cannot send notification: client transport is not initialized."
);
}

Expand Down
3 changes: 0 additions & 3 deletions ballerina/error.bal
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ public type JsonRpcMessageTransformationError distinct StreamError;
# Error when required data is missing from an SSE event.
public type MissingSseDataError distinct JsonRpcMessageTransformationError;

# Error for JSON parsing failures within SSE event data.
public type JsonParsingError distinct JsonRpcMessageTransformationError;

# Error for failures converting JSON to JsonRpcMessage.
public type TypeConversionError distinct JsonRpcMessageTransformationError;

Expand Down
7 changes: 1 addition & 6 deletions ballerina/json_rpc_message_stream_transformer.bal
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,7 @@ isolated class JsonRpcMessageStreamTransformer {
return error MissingSseDataError("SSE event is missing the required 'data' field.");
}

json|error jsonData = eventData.fromJsonString();
if jsonData is error {
return error JsonParsingError(string `Failed to parse SSE event data as JSON: ${jsonData.message()}`);
}

JsonRpcMessage|error message = jsonData.cloneWithType(JsonRpcMessage);
JsonRpcMessage|error message = eventData.fromJsonStringWithType();
if message is error {
return error TypeConversionError(string `Failed to convert JSON data to JsonRpcMessage: ${message.message()}`);
}
Expand Down
28 changes: 0 additions & 28 deletions ballerina/protocol.bal

This file was deleted.

15 changes: 9 additions & 6 deletions ballerina/streamable_http.bal
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import ballerina/http;
#
# + sessionId - Optional session identifier for continued interactions.
type StreamableHttpClientTransportConfig record {|
string? sessionId = ();
*http:ClientConfiguration;
string sessionId?;
|};

# Provides HTTP-based client transport with support for streaming.
Expand All @@ -34,14 +35,16 @@ isolated class StreamableHttpClientTransport {
# + serverUrl - The URL of the server endpoint.
# + config - Optional configuration, such as session ID.
# + return - A StreamableHttpTransportError if initialization fails; otherwise, nil.
isolated function init(string serverUrl, StreamableHttpClientTransportConfig? config = ()) returns StreamableHttpTransportError? {
isolated function init(string serverUrl, *StreamableHttpClientTransportConfig config) returns StreamableHttpTransportError? {
self.serverUrl = serverUrl;

StreamableHttpClientTransportConfig {sessionId, ...clientConfig} = config;
do {
self.httpClient = check new (serverUrl);
self.httpClient = check new (serverUrl, clientConfig);
} on fail error e {
return error HttpClientError(string `Unable to initialize HTTP client for '${serverUrl}': ${e.message()}`);
}
self.sessionId = config?.sessionId;
self.sessionId = sessionId;
}

# Sends a JSON-RPC message to the server and returns the response.
Expand All @@ -65,7 +68,7 @@ isolated class StreamableHttpClientTransport {
}

// If response is 202 Accepted, there is no content to process.
if response.statusCode == 202 {
if response.statusCode == http:STATUS_ACCEPTED {
return;
}

Expand Down Expand Up @@ -134,7 +137,7 @@ isolated class StreamableHttpClientTransport {
}

# Returns the current session ID, or nil if no session is active.
#
#
# + return - The current session ID as a string, or nil if not set.
isolated function getSessionId() returns string? {
lock {
Expand Down
28 changes: 18 additions & 10 deletions ballerina/types.bal
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ public const SUPPORTED_PROTOCOL_VERSIONS = [

public const JSONRPC_VERSION = "2.0";

# Notification methods
public const NOTIFICATION_INITIALIZED = "notifications/initialized";
public const NOTIFICATION_CANCELLED = "notifications/cancelled";
public const NOTIFICATION_PROGRESS = "notifications/progress";
public const NOTIFICATION_RESOURCES_LIST_CHANGED = "notifications/resources/list_changed";
public const NOTIFICATION_RESOURCES_UPDATED = "notifications/resources/updated";
public const NOTIFICATION_PROMPTS_LIST_CHANGED = "notifications/prompts/list_changed";
public const NOTIFICATION_TOOLS_LIST_CHANGED = "notifications/tools/list_changed";
public const NOTIFICATION_MESSAGE = "notifications/message";
Comment on lines +29 to +37
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can also be an enum, but this is okay too.

Will need docs.


# A progress token, used to associate progress notifications with the original request.
public type ProgressToken string|int;

Expand Down Expand Up @@ -123,7 +133,7 @@ public type EmptyResult Result;
public type CancelledNotification record {|
*Notification;
# The method name for this notification
"notifications/cancelled" method;
NOTIFICATION_CANCELLED method;
# The parameters for the cancellation notification
record {|
# The ID of the request to cancel.
Expand Down Expand Up @@ -173,14 +183,12 @@ public type InitializeResult record {|
public type InitializedNotification record {|
*Notification;
# The method identifier for the notification, must be "notifications/initialized"
"notifications/initialized" method;
NOTIFICATION_INITIALIZED method;
|};

# Capabilities a client may support. Known capabilities are defined here, in this schema,
# but this is not a closed set: any client can define its own, additional capabilities.
public type ClientCapabilities record {
# Experimental, non-standard capabilities that the client supports.
record {|record {}...;|} experimental?;
# Present if the client supports listing roots.
record {|
# Whether the client supports notifications for changes to the roots list.
Expand Down Expand Up @@ -239,7 +247,7 @@ public type PingRequest record {|
public type ProgressNotification record {|
*Notification;
# The method name for the notification
"notifications/progress" method;
NOTIFICATION_PROGRESS method;
# The parameters for the progress notification
record {
# The progress token which was given in the initial request,
Expand Down Expand Up @@ -278,14 +286,14 @@ public type PaginatedResult record {|
public type ResourceListChangedNotification record {|
*Notification;
# The JSON-RPC method name for resource list changed notifications
"notifications/resources/list_changed" method;
NOTIFICATION_RESOURCES_LIST_CHANGED method;
|};

# A notification from the server to the client, informing it that a resource has changed and may need to be read again.
public type ResourceUpdatedNotification record {|
*Notification;
# The JSON-RPC method name for resource updated notifications
"notifications/resources/updated" method;
NOTIFICATION_RESOURCES_UPDATED method;
# The parameters for the resource updated notification
record {
# The URI of the resource that has been updated. This might be a sub-resource of the one
Expand Down Expand Up @@ -335,7 +343,7 @@ public type EmbeddedResource record {|
public type PromptListChangedNotification record {|
*Notification;
# The JSON-RPC method name for prompt list changed notifications
"notifications/prompts/list_changed" method;
NOTIFICATION_PROMPTS_LIST_CHANGED method;
|};

# Sent from the client to request a list of tools the server has.
Expand Down Expand Up @@ -382,7 +390,7 @@ public type CallToolParams record {|
public type ToolListChangedNotification record {|
*Notification;
# The JSON-RPC method name for tool list changed notifications
"notifications/tools/list_changed" method;
NOTIFICATION_TOOLS_LIST_CHANGED method;
|};

# Additional properties describing a Tool to clients.
Expand Down Expand Up @@ -433,7 +441,7 @@ public type Tool record {|
public type LoggingMessageNotification record {|
*Notification;
# The method name for the notification
"notifications/message" method;
NOTIFICATION_MESSAGE method;
# The parameters for the logging message notification
record {
# The severity of this log message.
Expand Down
1 change: 1 addition & 0 deletions ballerina/utils.bal
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,4 @@ isolated function extractResultFromMessage(JsonRpcMessage message) returns Serve
}
return error InvalidMessageTypeError("Received message from server is not a valid JsonRpcResponse.");
}