Skip to content

Documentation, MCP server non-reference #8493

Description

@TheoGkoumas

Feature epic details

Operating systems

Does the documentation apply to all operating systems?

  • Yes

Summary

Provide the ability for Open Liberty to act as an MCP Server, giving developers the ability to define Tools, Resources, Prompts for use by AI Apps.

Model Context Protocol (MCP) is an open standard that enables AI applications to interact with and utilise external systems. The mcp-1.0 feature for Open Liberty allows developers to expose the business logic of their applications, making it discoverable, understandable, and invocable by AI applications.

Configuration

The mcp-1.0 feature uses the https://github.com/mcp-java/java-mcp-annotations[`mcp-java` API] (org.mcpjava:mcp-server-api), which is available on Maven Central.

Add the following dependency to your pom.xml:

<!-- MCP Server API -->
<dependency>
    <groupId>org.mcpjava</groupId>
    <artifactId>mcp-server-api</artifactId>
    <version>1.0.0</version>
    <scope>provided</scope>
</dependency>

Some features - such as @Schema, DefaultValueConverter, ToolManager, and ToolResponseEncoder - are provided by the io.openliberty.mcp jar that ships with Liberty. To make these available on the build path, you need to add a system-scoped dependency in your pom.xml that points to this jar.

First, locate the io.openliberty.mcp_*.jar in <wlp>/dev/api/ibm/ and note the version suffix (e.g. 1.0.106). Then define the following properties in your pom.xml:

<properties>
    <wlp-dir-path>replace-with-path-to-wlp-dir</wlp-dir-path>
    <mcp-jar-version>replace-with-version-number</mcp-jar-version>
</properties>

Then add the dependency:

<!-- Liberty MCP extensions (io.openliberty.mcp) -->
<dependency>
    <groupId>io.openliberty.mcp</groupId>
    <artifactId>mcp-core</artifactId>
    <version>${mcp-jar-version}</version>
    <scope>system</scope>
    <systemPath>${wlp-dir-path}/dev/api/ibm/io.openliberty.mcp_${mcp-jar-version}.jar</systemPath>
</dependency>

=== Enable the feature

Add mcp-1.0 to your server.xml:

<featureManager>
    <feature>servlet-6.0</feature>
    <feature>cdi-4.0</feature>
    <feature>mcp-1.0</feature>
</featureManager> 

Updates to existing topics

It should not update any existing topics.

Create a new topic

This documentation should act as a concise tutorial for the MCP feature.
We should discuss with the docs team to identify what documentation is expected for liberty features.
As a starting point, we should have a step-by-step guide to getting started and registering a tool, which we can then build on to explain more advanced features.

In principle, the list of actions/functionalities that need to be cover is the following:

  • registering tools
  • providing tool arguments
  • using structured content
  • registering encoders
  • throwing exceptions
  • handling cancellation
  • writing asynchronous tools
  • programmatically registering tools
  • stateless mode
  • endpoint configuration

Each entry should also note whether there are any restrictions (e.g. cancellation does not work in stateless mode, structured content only works for clients which support version 2025-06-18 or later)

Activity

  1. changed the title [-]Documentation, MCP server non-reference documentation[/-] [+]Documentation, MCP server non-reference[/+] on Aug 10, 2026
  2. TheoGkoumas commented on Aug 12, 2026

    @TheoGkoumas
    MemberAuthor

    New entry added (named as MCP Servers) on navigation pane, under Development, pointing to the new doc.

  3. ramkumar-k-9286 commented on Aug 19, 2026

    @ramkumar-k-9286
    Contributor

    Hi @TheoGkoumas

    I moved the information in the PR you created into one of my PR and merged the information.

    Please check and let me know.
    Draft site link:
    https://draft-openlibertyio.mqj6zf7jocq.us-south.codeengine.appdomain.cloud/docs/latest/liberty-mcp-server.html

    You can close your PR.

    Regards,
    Ramkumar

    CC @jantley-ibm @chirp1

  4. IssacAX123 commented on Aug 21, 2026

    @IssacAX123
    Member

    @ramkumar-k-9286 I have directly made changes in this PR #8500. There's a new security docs page and the main mcp server page has minor content removals and rewrites

  5. ramkumar-k-9286 commented on Aug 21, 2026

    @ramkumar-k-9286
    Contributor
  6. Azquelt commented on Aug 21, 2026

    @Azquelt
    Member

    Thanks for pulling in the updates, I've been held up sorting out a build issue but I will review and make comments here.

  7. Azquelt commented on Aug 24, 2026

    @Azquelt
    Member

    The PR has some changes to the main page which aren't yet reflected in the draft.

    Can you make the changes to liberty-mcp-server.adoc from these commits, aside from the one that I've left a comment on please? https://github.com/OpenLiberty/docs/pull/8500/changes/7af6f0f0eb0cf12a0795e1ec33bd5b43c5b08d85..8b6cc66eea8c7cfc4d809fe6293d91d9ce873f47#diff-b8f19b52f8eba5d170bd3eb4d69a3ec6a6eceacea0fe0563a76945ed96295700

    I'll continue reviewing, assuming those changes will be made.

  8. Azquelt commented on Aug 24, 2026

    @Azquelt
    Member

    I think we should improve the flow of the getting started section and separate it from the more in-depth documentation about tools.

    Please make the following changes:

    • At the end of the section "Enable the feature", add this as a Note:
      The mcp-1.0 feature is compatible with Jakarta EE 10 and EE 11 features

    • Before the section "Find your MCP endpoint URL", add a new section "Register a tool":

      Tools are registered by creating a public method on a CDI bean and annotating it with @Tool

      import java.time.LocalDateTime;
      
      import org.mcpjava.server.tools.Tool;
      
      import jakarta.enterprise.context.ApplicationScoped;
      
      @ApplicationScoped
      public class McpTools {
      
          @Tool(description = "Gets the current time")
          public String getTime() {
              return LocalDateTime.now().toString();
          }
      
      }
      
    • Move the section "Test with the MCP Inspector" before "Connect an AI client"

    • In "Connect an AI client", I would suggest we separate the example from the instructive text. Start a new paragraph at "For example".

    • Add a new level 2 heading "Developing tools" before the level 3 heading "Registering tools"

    After these changes, the outline should look like this:

    • Getting started
      • Add dependencies
      • Enable the feature
      • Register a tool
      • Find your MCP endpoint
      • Test with the MCP Inspector
      • Connect an AI client
    • Developing tools
      • Registering tools
      • Providing tool arguments
      • Using structured content
      • Registering encoders
      • Throwing exceptions
      • Handling cancellation
      • Writing asynchronous tools
      • Programmatically registering tools
      • Stateless mode
    • Configuration
      • Custom endpoint path
      • Server description and metadata
      • Async tool timeout
      • EAR deployments with multiple modules
    • Supported MCP protocol versions and features
      • Protocol versions
      • Supported features
  9. Azquelt commented on Aug 26, 2026

    @Azquelt
    Member

    Ok, I've gone through it again and picked up some more changes:

    • before the "Getting started" section, add a NOTE: the MCP protocol includes various features, but the mcp-1.0 feature only supports Tools. For more information see <>
    • In "Add the MCP API dependency", change "The mcp-1.0 feature uses the mcp-java API (org.mcpjava:mcp-server-api)" to "The mcp-1.0 feature uses the org.mcpjava API"
    • In "Add the MCP API dependency", is it possible to also include a gradle example with a tab controls, like we do for Windows/Mac/Linux command line examples in the guides here? If so I'll provide a gradle example too.
    • The text "For tool methods that return a full ToolResponse, you can also signal a business-logic error to the AI without throwing an exception:" and the following example is in the wrong place, take it out of here.
    • For the section "Throwing exceptions":
      • Rename the section to "Handling errors"
      • Reorganise the structure under "Handling errors" to
        • Error responses
          • An error response indicates that the tool couldn't be called. The error response is sent back to the AI agent so that it can decide what to do next. If the error indicates that the AI agent has provided invalid arguments, this gives it the opportunity to call the tool again with different arguments.
            You an send an error response by returning a ToolResponse. (Include the example above here)
        • ToolCallException
          • It's also possible to return an error response by throwing ToolCallException. In this case the tool method does not need to return ToolResponse. The message of the ToolCallException will be return to the client as the error message.
            (ToolCallException example here)
            Other exceptions can also be automatically wrapped by a ToolCallException by annotating the method with @WrapBusinessException. The message of the exception will still be return directly to the client as the error message, so check carefully whether the error message makes sense to the client and that it doesn't reveal any sensitive information from the server. In many cases, it may be better to catch and handle the exception yourself and create a better error message.
            (WrapBusinessException example here)
        • Unhandled exceptions
          • If your method throws an exception that isn't converted to a ToolCallException, then a general "Server error" response is sent to the client and details of the exception will be logged to the server log.
    • In "Handling cancellation
      • I think the heading would be better as just "Cancellation"
      • Change
        "Call cancellation.check().isRequested() periodically and throw Cancellation.OperationCancelledException to stop execution cleanly"
        to
        "Call cancellation.skipProcessingIfCancelled() periodically which will throw Cancellation.OperationCancelledException if the tool call has been cancelled. It's also possible to call cancellation.check() to handle cancellation yourself."
      • Update the example to match:
            for (String chunk : dataService.getChunks(datasetId)) {
                // Check for cancellation before each chunk
                cancellation.skipProcessingIfCancelled();
                results.add(process(chunk));
            }
    • In "Writing asynchronous tools" I need to work out a better example, the current one isn't wrong but it doesn't make sense. I'll provide this later.
    • In "Writing asynchronous tools" change the NOTE to a TIP
    • In "Programmatically registering tools" I'm not sure the code is quite correct, I'm testing it and may provide a new one.
    • In "Stateless mode"
      • put 2025-11-25 in code tags
      • the NOTE here appears to just repeat the previous line? Should we remove one or the other? Should this be a CAUTION? wdyt?
    • In "Configuration" every instance of <mcpServer> needs changed to <mcp> - we changed the name of this config element in Update feature name and config element to mcp-1.0 open-liberty#35365
    • In "Server description and metadata" change NOTE to TIP
    • In some cases we have text that is both code and a link (for example Cancellation in the cancellation section). These render fine on github but don't render well on docs site. Unless we can get the styling changed, please change these to be just a link without code tags.
  10. added a commit that references this issue on Aug 28, 2026
  11. ramkumar-k-9286 commented on Aug 28, 2026

    @ramkumar-k-9286
    Contributor

    Hi @Azquelt

    @TheoGkoumas @IssacAX123

    I have incorporated the comments from the other PR as well as the previous comments mentioned in this issue.

    Draft site link:

    https://docs-draft-openlibertyio.mqj6zf7jocq.us-south.codeengine.appdomain.cloud/docs/latest/liberty-mcp-server.html

    https://docs-draft-openlibertyio.mqj6zf7jocq.us-south.codeengine.appdomain.cloud/docs/latest/liberty-mcp-server-security.html

    Regards,
    Ramkumar

    I have used BOB for grammar and style checks on these pages. You may find sentences that have been rephrased. FYI

    Pending Items as mentioned in comments from @Azquelt

    1. Gradle dependency example with tab controls
    2. Replacement example for "Writing asynchronous tools"
    3. Corrected code for "Programmatically registering tools"
  12. Azquelt commented on Sep 1, 2026

    @Azquelt
    Member

    I had a quick read through and spotted this:

    In "Securing an MCP server" - "Controlling access per tool"

    @PermitAll - All authenticated callers are permitted, regardless of role.

    Should be

    @PermitAll - All callers are permitted

    And in the following code sample

    // Any authenticated user can call this tool

    Should be

    // Any user can call this tool

    I'll get the updated examples to you tomorrow morning.

  13. Azquelt commented on Sep 2, 2026

    @Azquelt
    Member

    New example for asynchronous tools: (The old example was from our tests. It wasn't wrong, but it didn't make sense as an example because there wasn't any need or benefit from it being asynchronous. This new example uses restfulWS async client which, in principle, saves server resources by being asynchronous.)

    import java.util.concurrent.CompletionStage;
    import org.mcpjava.server.tools.Tool;
    import org.mcpjava.server.tools.ToolArg;
    import jakarta.enterprise.context.ApplicationScoped;
    import jakarta.ws.rs.client.Client;
    import jakarta.ws.rs.client.ClientBuilder;
    import jakarta.ws.rs.core.MediaType;
    
    @ApplicationScoped
    public class RemoteTools {
    
        private Client client = ClientBuilder.newClient();
    
        @Tool
        public CompletionStage<String> fetchWidget(@ToolArg(name = "id", description = "widget id") String id) {
            validateWidgetId(id);
    
            return client.target("http://example.com/api/widgets/{id}")
                    .resolveTemplate("id", id)
                    .request(MediaType.APPLICATION_JSON)
                    .rx()
                    .get(Widget.class)
                    .thenApply(w -> createWidgetResponse(w));
        }
    }

    New example for programmatic registration: (it's the same, except it does the CDI lifecycle correctly)

    import io.openliberty.mcp.tools.ToolManager;
    import org.mcpjava.server.tools.ToolResponse;
    import jakarta.enterprise.context.ApplicationScoped;
    import jakarta.enterprise.event.Observes;
    import jakarta.enterprise.event.Startup;
    import jakarta.enterprise.inject.Instance;
    import jakarta.inject.Inject;
    
    @ApplicationScoped
    public class WeatherToolRegistrar {
    
        @Inject
        ToolManager toolManager;
    
        @Inject
        Instance<WeatherClient> weatherClientInstance;
    
        private void registerTools(@Observes Startup startup) {
            if (weatherClientInstance.isResolvable()) {
    
                toolManager.newTool("getForecast")
                        .setTitle("Weather Forecast Provider")
                        .setDescription("Get weather forecast for a location")
                        .addArgument("latitude",  "Latitude of the location",  true, Double.class)
                        .addArgument("longitude", "Longitude of the location", true, Double.class)
                        .setHandler(args -> {
                            WeatherClient weatherClient = weatherClientInstance.get();
                            try {
                                Double lat = (Double) args.args().get("latitude");
                                Double lon = (Double) args.args().get("longitude");
                                String result = weatherClient.getForecast(lat, lon, 4,
                                    "temperature_2m,snowfall,rain,precipitation,precipitation_probability");
                                    return ToolResponse.ofText(result);
                            } finally {
                                weatherClientInstance.destroy(weatherClient);
                            }
                        })
                        .register();
            }
        }
    }
  14. Azquelt commented on Sep 2, 2026

    @Azquelt
    Member
    1. Change

      Add the following dependency to your pom.xml:

      to

      Add the following dependency to your pom.xml or build.gradle:

    2. Add this gradle example for MCP Server API:

          providedCompile 'org.mcpjava:mcp-server-api:1.0.0'

      Add this gradle example for the Liberty MCP extensions:

          providedCompile 'io.openliberty.api:io.openliberty.mcp:1.0.117'
  15. added a commit that references this issue on Sep 2, 2026
  16. ramkumar-k-9286 commented on Sep 2, 2026

    @ramkumar-k-9286
    Contributor

    Hi @Azquelt @IssacAX123

    I have incorporated the comments from the other PR as well as the previous comments mentioned in this issue.

    Draft site link:

    https://docs-draft-openlibertyio.mqj6zf7jocq.us-south.codeengine.appdomain.cloud/docs/latest/liberty-mcp-server.html

    https://docs-draft-openlibertyio.mqj6zf7jocq.us-south.codeengine.appdomain.cloud/docs/latest/liberty-mcp-server-security.html

    Note: I have used BOB for grammar and style checks on these pages. You may find sentences that have been rephrased. FYI

    Regards,
    Ramkumar

  17. Azquelt commented on Sep 2, 2026

    @Azquelt
    Member

    The maven and gradle examples are both showing together, with a dark grey box around them, rather than in tabs.

    Image

    If we can't do tabs, then I think we would be better off just having them one after another without the big grey box if possible. It stands out to an extent that isn't justified by the content, unfortunately.

  18. Azquelt commented on Sep 2, 2026

    @Azquelt
    Member

    Alternatively, we can revert back to just the maven example if that's easier.

  19. Azquelt commented on Sep 2, 2026

    @Azquelt
    Member

    Other than that formatting issue, it looks good to me, so I'll tag with developer reviewed

  20. added a commit that references this issue on Sep 2, 2026
  21. Azquelt commented on Sep 3, 2026

    @Azquelt
    Member

    @ramkumar-k-9286 Looks good, thanks

  22. ramkumar-k-9286 commented on Sep 11, 2026

    @ramkumar-k-9286
    Contributor

    Published [26.0.0.9 release].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions