Skip to content

BETA BLOG - mcpServer-1.0 updates in 26.0.0.1-beta #33602

@ttmunyunguma

Description

@ttmunyunguma

The information you provide here will be included in the Open Liberty beta blog post (example), which will be published on openliberty.io/blog/, and potentially elsewhere, to promote this beta feature/function of Open Liberty. For this post to be included in the beta issue please make sure that this is completed by the end of Friday following the GM (Tuesday). The beta and release blogs are created using automation and rely on you following the template's structure. DO NOT REMOVE/ALTER THE <GHA> TAGS THROUGHOUT THIS TEMPLATE.

<GHA-BLOG-SUMMARY>

The Model Context Protocol (MCP) has emerged as the standard for AI applications to access real-time information from external sources. The Liberty MCP Server feature mcpServer-1.0 allows developers to expose the business logic of their applications, allowing it to be integrated into agentic AI workflows.

This beta release of Liberty includes important updates to the mcpServer-1.0 feature, including asynchronous tool support, stateless mode support and a few bug fixes

Prerequisites

To use the mcpServer-1.0 feature, it is required to have Java 17 installed on your system.

Update on the MCP JAR location

The directory in which the MCP server jar will be located has been updated from the previous lib folder, to the new location /dev/ibm/api. You can view the set up guide for complete instructions on how to build an MCP server in Liberty.

Asynchronous Tool Execution Support

Although MCP is built around synchronous operations, we can now support asynchronous operations. With this feature, tool users can wrap their tool responses in a CompletionStage.

This enables a non-blocking execution pattern for the server to kick off long running tools, which clients can then check back later for results when the stage completes. Your MCP server stays responsive and can handle multiple requests concurrently, making better use of resources while time-consuming operations run in the background.

Here is an example how that would look like:

@Tool()
public CompletionStage<String> toolName() {
//return your Completable Future or Managed service
}

Stateless Mode

As required by the MCP Specification, we now can distinguish clearly between stateless and stateful requests to a server by enabling stateless mode.

This mode removes the need for session affinity in a clustered environment, allowing any server instance to handle any request, simplifying load balancing and horizontal scaling. This is essential for simple clustering if you are running your MCP server on multiple nodes.

To enable stateless mode in your application, you'll need to add this configuration in your server.xml file:

<mcpServer stateless="true"/>

When stateless mode is enabled, you will not be able to cancel ongoing requests, therefore any notifications to cancel a request will be ignored, and by extension, you will not be able to delete a session.

Requests made in stateless mode should NOT provide a Mcp-Session-Id header. The Liberty server will automatically pick up the stateless mode configuration at startup, and any requests made with a session Id will be rejected.

If the above configuration is not enabled, all requests are assumed to be stateful and must provide a session ID for each request

Input and Output Schema support

We added support for schemas as specified by the MCP protocol. We can now generate input and output schemas for tools.

This will allow users to annotate complex types with @Schema that should be used to interpret its JSON representation. This will ensure that the server provides tools with a predictable structure of parameters, and a definite structure the tool returns, therefore preventing ambiguity and allowing for a reliable interaction between the client and server.

You can add the annotation on the type itself, or on the parameters of a complex type

@Schema("{\"properties\": {  \"streetName\": { \"type\": \"string\" }, \"roadType\": { \"type\": \"string\" } }" )
public record Street(String streetName, String roadType) {}

public record Company(String name, Address address, 
                                @Schema(value = "{\"properties\": {\"key\":{ \"type\": \"integer\" }, \"value\":{ \"$ref\": \"#/$defs/person\" }},\"required\": [ ], \"type\": \"object\"}") Optional<Map<String, Person>> shareholderRegistry) {};

An input schema can be configured as follows, with the input parameter @Schema() Street which was defined above.

@Tool()
public boolean toolName(@ToolArg(name = "...") Street street) {
//do something with your street object
}

For an output Schema, you need to indicate that the tool returns structured content and provide a schema for the output structure. With this, a serializable response will be returned in a JSON with the structured content field

@Tool(name = "...", structuredContent = true)
public @Schema(value = "{... your json schema...}") ToolResponse toolName() {
//return your tool response with the json object
}

Result:

{
 "id": 2,
 "jsonrpc": "2.0",
 "result": {
     "isError": false,
     "structuredContent": [
        {
	"street": {
 	  "streetName": "Poles Ln",
  	  "roadType": "n/a"
           }
        } ] }}

Allowing annotations in MCP Content objects

As specified in the MCP specification we now allow annotations to be created and returned by tools as Content objects. This provides hints to clients on how to use and display the content. Included in the annotation is:

  • audience field - indicating the intended audience for the content,
  • priority field - indication the importance of the content object, and
  • lastModified field - indicating when the content object was last modified.

This can be added to tools as represented below.

@Tool(name = "...")
public TextContent toolName(@ToolArg(name = "...", description = "...") String input) {
        Content.Annotations annotations = new Content.Annotations(Role, lastModified, priority)
	return new TextContent(text, _meta, annotations);
}

Result:

{
  "id": 1,
  "jsonrpc": "2.0",
  "result": {
    "content": [
     {
       "annotations": {
         "audience": "...",
         "lastModified": "...",
         "priority": 0.5
       },
       "text": "...",
       "type": "text"
       }
     ],
    "isError": false
    }
 }
</GHA-BLOG-SUMMARY>

What happens next?

  • Add the label to the blog issue for the beta you're targeting (e.g. target:YY00X-beta).
  • Make sure this blog post is linked back to the Epic for this feature/function.
  • Your paragraph will be included in the beta blog post. It might be edited for style and consistency.
  • You will be asked to review a draft before publication.
    • Once you've approved the code review, close this issue.
  • If you would also like to write a standalone blog post about your update (highly recommended), raise an issue on the Open Liberty blogs repo. State in the issue that the blog post relates to a specific release so that we can ensure it is published on an appropriate date (it won't be the same day as the beta blog post).

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions