diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c73d9f38..6e0889b0e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,7 @@ name: CI on: pull_request: {} + workflow_dispatch: {} jobs: build: @@ -11,6 +12,12 @@ jobs: - name: Checkout source code uses: actions/checkout@v4 + - name: Set up JDK 25 for Javadoc toolchain + uses: actions/setup-java@v4 + with: + java-version: '25' + distribution: 'temurin' + - name: Set up JDK 17 uses: actions/setup-java@v4 with: @@ -18,5 +25,12 @@ jobs: distribution: 'temurin' cache: 'maven' - - name: Build + - name: Build and test + env: + JAVA_HOME: ${{ env.JAVA_HOME_17_X64 }} run: mvn verify + + - name: Validate Javadoc generation + env: + JAVA_HOME: ${{ env.JAVA_HOME_17_X64 }} + run: mvn -Pjavadoc,javadoc-toolchain javadoc:aggregate diff --git a/.github/workflows/maven-central-release.yml b/.github/workflows/maven-central-release.yml index c6c9d3ab6..a2a67280b 100644 --- a/.github/workflows/maven-central-release.yml +++ b/.github/workflows/maven-central-release.yml @@ -8,8 +8,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - - name: Set up Java + + - name: Set up JDK 25 for Javadoc toolchain + uses: actions/setup-java@v4 + with: + java-version: '25' + distribution: 'temurin' + + - name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: '17' @@ -25,17 +31,20 @@ jobs: uses: actions/setup-node@v4 with: node-version: '20' - + - name: Build and Test + env: + JAVA_HOME: ${{ env.JAVA_HOME_17_X64 }} run: mvn clean verify - name: Publish to Maven Central - run: | - mvn --batch-mode \ - -Prelease \ - -Pjavadoc \ - deploy env: MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} MAVEN_GPG_PASSPHRASE: ${{ secrets.SIGNING_PASSPHRASE }} + JAVA_HOME: ${{ env.JAVA_HOME_17_X64 }} + run: | + mvn --batch-mode \ + -Prelease \ + -Pjavadoc,javadoc-toolchain \ + deploy diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml index 5d9b4aa39..1f9e9c4ed 100644 --- a/.github/workflows/publish-snapshot.yml +++ b/.github/workflows/publish-snapshot.yml @@ -12,6 +12,12 @@ jobs: - name: Checkout source code uses: actions/checkout@v4 + - name: Set up JDK 25 for Javadoc toolchain + uses: actions/setup-java@v4 + with: + java-version: '25' + distribution: 'temurin' + - name: Set up JDK 17 uses: actions/setup-java@v4 with: @@ -29,16 +35,14 @@ jobs: with: node-version: '20' - - name: Generate Java docs - run: mvn -Pjavadoc -B javadoc:aggregate - - name: Build with Maven and deploy to Sonatype snapshot repository env: MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} MAVEN_GPG_PASSPHRASE: ${{ secrets.SIGNING_PASSPHRASE }} + JAVA_HOME: ${{ env.JAVA_HOME_17_X64 }} run: | - mvn -Pjavadoc -Prelease --batch-mode --update-snapshots deploy + mvn -Pjavadoc,javadoc-toolchain -Prelease --batch-mode --update-snapshots deploy - name: Capture project version run: echo PROJECT_VERSION=$(mvn help:evaluate -Dexpression=project.version --quiet -DforceStdout) >> $GITHUB_ENV diff --git a/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/.gitkeep b/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/client.md b/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/client.md new file mode 100644 index 000000000..538cb4b0a --- /dev/null +++ b/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/client.md @@ -0,0 +1,644 @@ +# Java MCP Client + +## Client Features + +The MCP Client is a key component in the Model Context Protocol (MCP) architecture, responsible for establishing and managing connections with MCP servers. It implements the client-side of the protocol, handling: + +- Protocol version negotiation to ensure compatibility with servers +- Capability negotiation to determine available features +- Message transport and JSON-RPC communication +- Tool discovery and execution +- Resource access and management +- Prompt system interactions +- Optional features like roots management and sampling support + +> **Tip:** The core `io.modelcontextprotocol.sdk:mcp` module provides STDIO, Streamable-HTTP and SSE client transport implementations without requiring external web frameworks. +> +> Spring-specific transport implementations are available as an **optional** dependency `io.modelcontextprotocol.sdk:mcp-spring-webflux` for [Spring Framework](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-client-boot-starter-docs.html) users. + +> **Tip:** This [quickstart demo](https://modelcontextprotocol.io/quickstart/client), based on Spring AI MCP, will show you how to build an AI client that connects to MCP servers. + +The client provides both synchronous and asynchronous APIs for flexibility in different application contexts. + +### Sync API + +```java +// Create a sync client with custom configuration +McpSyncClient client = McpClient.sync(transport) + .requestTimeout(Duration.ofSeconds(10)) + .capabilities(ClientCapabilities.builder() + .roots(true) // Enable roots capability + .sampling() // Enable sampling capability + .elicitation() // Enable elicitation capability + .build()) + .sampling(request -> CreateMessageResult.builder()...build()) + .elicitation(elicitRequest -> ElicitResult.builder()...build()) + .toolsChangeConsumer((List tools) -> ...) + .resourcesChangeConsumer((List resources) -> ...) + .promptsChangeConsumer((List prompts) -> ...) + .loggingConsumer((LoggingMessageNotification logging) -> ...) + .progressConsumer((ProgressNotification progress) -> ...) + .build(); + +// Initialize connection +client.initialize(); + +// List available tools +ListToolsResult tools = client.listTools(); + +// Call a tool +CallToolResult result = client.callTool( + new CallToolRequest("calculator", + Map.of("operation", "add", "a", 2, "b", 3)) +); + +// List and read resources +ListResourcesResult resources = client.listResources(); +ReadResourceResult resource = client.readResource( + new ReadResourceRequest("resource://uri") +); + +// List and use prompts +ListPromptsResult prompts = client.listPrompts(); +GetPromptResult prompt = client.getPrompt( + new GetPromptRequest("greeting", Map.of("name", "Spring")) +); + +// Add/remove roots +client.addRoot(new Root("file:///path", "description")); +client.removeRoot("file:///path"); + +// Close client +client.closeGracefully(); +``` + +### Async API + +```java +// Create an async client with custom configuration +McpAsyncClient client = McpClient.async(transport) + .requestTimeout(Duration.ofSeconds(10)) + .capabilities(ClientCapabilities.builder() + .roots(true) // Enable roots capability + .sampling() // Enable sampling capability + .elicitation() // Enable elicitation capability + .build()) + .sampling(request -> Mono.just(new CreateMessageResult(response))) + .elicitation(elicitRequest -> Mono.just(ElicitResult.builder()...build())) + .toolsChangeConsumer(tools -> Mono.fromRunnable(() -> logger.info("Tools updated: {}", tools))) + .resourcesChangeConsumer(resources -> Mono.fromRunnable(() -> logger.info("Resources updated: {}", resources))) + .promptsChangeConsumer(prompts -> Mono.fromRunnable(() -> logger.info("Prompts updated: {}", prompts))) + .loggingConsumer(notification -> Mono.fromRunnable(() -> logger.info("Log: {}", notification.data()))) + .progressConsumer(progress -> Mono.fromRunnable(() -> logger.info("Progress update: {}", progress.data()))) + .build(); + +// Initialize connection and use features +client.initialize() + .flatMap(initResult -> client.listTools()) + .flatMap(tools -> { + return client.callTool(new CallToolRequest( + "calculator", + Map.of("operation", "add", "a", 2, "b", 3) + )); + }) + .flatMap(result -> { + return client.listResources() + .flatMap(resources -> + client.readResource(new ReadResourceRequest("resource://uri")) + ); + }) + .flatMap(resource -> { + return client.listPrompts() + .flatMap(prompts -> + client.getPrompt(new GetPromptRequest( + "greeting", + Map.of("name", "Spring") + )) + ); + }) + .flatMap(prompt -> { + return client.addRoot(new Root("file:///path", "description")) + .then(client.removeRoot("file:///path")); + }) + .doFinally(signalType -> { + client.closeGracefully().subscribe(); + }) + .subscribe(); +``` + +## Client Transport + +The transport layer handles the communication between MCP clients and servers, providing different implementations for various use cases. The client transport manages message serialization, connection establishment, and protocol-specific communication patterns. + +### STDIO Transport + +Creates transport for in-process based communication: + +```java +ServerParameters params = ServerParameters.builder("npx") + .args("-y", "@modelcontextprotocol/server-everything", "dir") + .build(); +McpTransport transport = new StdioClientTransport(params); +``` + +### HttpClient Transport + +#### Streamable-HTTP (HttpClient) + +Framework agnostic (only using JDK APIs) Streamable-HTTP client transport: + +```java +McpTransport transport = HttpClientStreamableHttpTransport + .builder("http://your-mcp-server") + .build(); +``` + +#### SSE (HttpClient) + +Framework agnostic (only using JDK APIs) SSE client transport: + +```java +McpTransport transport = HttpClientSseClientTransport + .builder("http://your-mcp-server") + .build(); +``` + +#### HttpClient: Customizing HTTP requests + +To customize the base HTTP request builder used for every request, provide a custom `HttpRequestBuilder`. +This is available in both Streamable HTTP and SSE transports. +When using a custom `HttpRequest.Builder`, every HTTP request issued by the transport +will use the hardcoded configuration from the builder. +For example, this is useful for providing a never-expiring security token in a header. +To add a `X-Custom-Header` header to every request, use: + +```java +var requestBuilder = HttpRequest + .newBuilder() + .header("X-Custom-Header", "some header value"); + +HttpClientStreamableHttpTransport + .builder("https://mcp.example.com") + .requestBuilder(requestBuilder) + .build(); +``` + +To dynamically modify HTTP request before they are issued, implement either `McpSyncHttpClientRequestCustomizer` or `McpAsyncHttpClientRequestCustomizer`. +Choose the request customizer matching the MCP Client type, sync or async. +Note that thread-locals may not be available in the customizers, context-related information +must be accessed through `McpTransportContext` (see [adding context information](#adding-context-information)). +Example implementations: + +```java +// Sync +class MyRequestCustomizer implements McpSyncHttpClientRequestCustomizer { + @Override + public void customize(HttpRequest.Builder builder, String method, + URI endpoint, String body, McpTransportContext context) { + // ... custom logic ... + var token = obtainAccessToken(context); + builder.header("Authorization", "Bearer " + token); + } +} + +// Async +class MyAsyncRequestCustomizer implements McpAsyncHttpClientRequestCustomizer { + @Override + public Publisher customize(HttpRequest.Builder builder, + String method, URI endpoint, String body, McpTransportContext context) { + // ... custom logic ... + Mono token = obtainAccessToken(context); + return token.map(t -> + builder.copy() + .header("Authorization", "Bearer " + t) + ); + } +} +``` + +The transports, both Streamable HTTP and SSE, can be configured to use a single customizer. + +```java +HttpClientStreamableHttpTransport + .builder("https://mcp.example.com") + .httpRequestCustomizer(new MyRequestCustomizer()) // sync + .asyncHttpRequestCustomizer(new MyAsyncRequestCustomizer()) // OR async + .build(); +``` + +To compose multiple customizers, use `DelegatingMcpSyncHttpClientRequestCustomizer` or `DelegatingMcpAsyncHttpClientRequestCustomizer`. + +### WebClient Transport + +WebClient-based client transport. Requires the `mcp-spring-webflux` dependency. + +#### Streamable-HTTP (WebClient) + +```java +WebClient.Builder webClientBuilder = WebClient.builder() + .baseUrl("http://your-mcp-server"); + +McpTransport transport = WebClientStreamableHttpTransport + .builder(webClientBuilder) + .build(); +``` + +#### SSE (WebClient) + +```java +WebClient.Builder webClientBuilder = WebClient.builder() + .baseUrl("http://your-mcp-server"); + +McpTransport transport = WebFluxSseClientTransport(webClientBuilder) + .builder(webClientBuilder) + .build(); +``` + +#### WebClient: Customizing HTTP requests + +To customize outgoing HTTP requests, provide a custom `WebClient.Builder`. +When using a custom builder, every request sent by the transport will be customized. +For example, this is useful for providing a never-expiring security token in a header. +To add a `X-Custom-Header` header to every request, use: + +```java +var webClientBuilder = WebClient.builder() + .defaultHeader("X-Custom-Header", "some header value"); + +McpTransport transport = WebClientStreamableHttpTransport + .builder(webClientBuilder) + .build(); +``` + +To dynamically modify HTTP request, the builder has a dedicated API, `ExchangeFilterFunction`. +In that function, the new request can be computed at run time, and additional information can +be obtained from the [Reactor context](https://projectreactor.io/docs/core/release/reference/advancedFeatures/context.html). +It is common to store context information in an `McpTransportContext` within the Reactor context, +typically in McpSyncClient, but any context key can be used. For example: + +```java +WebClient.builder() + .filter((request, next) -> { + return Mono.deferContextual(ctx -> { + var transportContext = ctx.get(McpTransportContext.KEY); + var otherInfo = ctx.get("your-context-key"); + Mono token = obtainAccessToken(transportContext, otherInfo); + return token.map(t -> { + var newRequest = ClientRequest.from(request) + .header("Authorization", "Bearer " + t) + .build(); + // Ensure you call next.exchange to execute the HTTP request + return next.exchange(newRequest); + }); + }); + }); +``` + +To learn how to populate context information, see [adding context information](#adding-context-information). + +## Client Capabilities + +The client can be configured with various capabilities: + +```java +var capabilities = ClientCapabilities.builder() + .roots(true) // Enable filesystem roots support with list changes notifications + .sampling() // Enable LLM sampling support + .elicitation() // Enable elicitation capability + .build(); +``` + +### Roots Support + +Roots define the boundaries of where servers can operate within the filesystem: + +```java +// Add a root dynamically +client.addRoot(new Root("file:///path", "description")); + +// Remove a root +client.removeRoot("file:///path"); + +// Notify server of roots changes +client.rootsListChangedNotification(); +``` + +The roots capability allows servers to: + +- Request the list of accessible filesystem roots +- Receive notifications when the roots list changes +- Understand which directories and files they have access to + +### Sampling Support + +Sampling enables servers to request LLM interactions ("completions" or "generations") through the client: + +```java +// Configure sampling handler +Function samplingHandler = request -> { + // Sampling implementation that interfaces with LLM + return new CreateMessageResult(response); +}; + +// Create client with sampling support +var client = McpClient.sync(transport) + .capabilities(ClientCapabilities.builder() + .sampling() + .build()) + .sampling(samplingHandler) + .build(); +``` + +This capability allows: + +- Servers to leverage AI capabilities without requiring API keys +- Clients to maintain control over model access and permissions +- Support for both text and image-based interactions +- Optional inclusion of MCP server context in prompts + +### Elicitation Support + +Elicitation enables servers to request specific information or clarification from the client: + +```java +// Configure elicitation handler +Function elicitationHandler = request -> { + // Elicitation implementation that interfaces with LLM + return ElicitResult.builder()...build(); +}; + +// Create client with elicitation support +var client = McpClient.sync(transport) + .capabilities(ClientCapabilities.builder() + .elicitation() // enable elicitation capability + .build()) + .elicitation(elicitationHandler) // register elicitation handler + .build(); +``` + +### Logging Support + +The client can register a logging consumer to receive log messages from the server and set the minimum logging level to filter messages: + +```java +var mcpClient = McpClient.sync(transport) + .loggingConsumer((LoggingMessageNotification notification) -> { + System.out.println("Received log message: " + notification.data()); + }) + .build(); + +mcpClient.initialize(); + +mcpClient.setLoggingLevel(McpSchema.LoggingLevel.INFO); + +// Call the tool that can sends logging notifications +CallToolResult result = mcpClient.callTool(new McpSchema.CallToolRequest("logging-test", Map.of())); +``` + +Clients can control the minimum logging level they receive through the `mcpClient.setLoggingLevel(level)` request. Messages below the set level will be filtered out. +Supported logging levels (in order of increasing severity): DEBUG (0), INFO (1), NOTICE (2), WARNING (3), ERROR (4), CRITICAL (5), ALERT (6), EMERGENCY (7) + +### Progress Support + +The client can register a progress consumer to receive progress updates from the server: + +```java +var mcpClient = McpClient.sync(transport) + .progressConsumer((ProgressNotification progress) -> { + System.out.println("Received progress update: " + progress.data()); + }) + .build(); + +mcpClient.initialize(); + +// Call the tool that can sends progress notifications +CallToolResult result = mcpClient.callTool(new McpSchema.CallToolRequest("progress-test", Map.of())); +``` + +### Change Notifications + +The client can register a change consumer to receive change notifications from the server about tools, resources, or prompts updates: + +```java + +var spec = McpClient.sync(transport); + +// Adds a consumer to be notified when the available tools change, such as tools +// being added or removed. +spec.toolsChangeConsumer((List tools) -> { + // Handle tools change +}); + +// Adds a consumer to be notified when the available resources change, such as resources +// being added or removed. +spec.resourcesChangeConsumer((List resources) -> { + // Handle resources change +}); + +// Adds a consumer to be notified when the available prompts change, such as prompts +// being added or removed. +spec.promptsChangeConsumer((List prompts) -> { + // Handle prompts change + +}); +``` + +## Using MCP Clients + +### Tool Execution + +Tools are server-side functions that clients can discover and execute. The MCP client provides methods to list available tools and execute them with specific parameters. Each tool has a unique name and accepts a map of parameters. + +#### Sync API + +```java +// List available tools and their names +var tools = client.listTools(); +tools.forEach(tool -> System.out.println(tool.getName())); + +// Execute a tool with parameters +var result = client.callTool("calculator", Map.of( + "operation", "add", + "a", 1, + "b", 2 +)); +``` + +#### Async API + +```java +// List available tools asynchronously +client.listTools() + .doOnNext(tools -> tools.forEach(tool -> + System.out.println(tool.getName()))) + .subscribe(); + +// Execute a tool asynchronously +client.callTool("calculator", Map.of( + "operation", "add", + "a", 1, + "b", 2 + )) + .subscribe(); +``` + +### Resource Access + +Resources represent server-side data sources that clients can access using URI templates. The MCP client provides methods to discover available resources and retrieve their contents through a standardized interface. + +#### Sync API + +```java +// List available resources and their names +var resources = client.listResources(); +resources.forEach(resource -> System.out.println(resource.getName())); + +// Retrieve resource content using a URI template +var content = client.getResource("file", Map.of( + "path", "/path/to/file.txt" +)); +``` + +#### Async API + +```java +// List available resources asynchronously +client.listResources() + .doOnNext(resources -> resources.forEach(resource -> + System.out.println(resource.getName()))) + .subscribe(); + +// Retrieve resource content asynchronously +client.getResource("file", Map.of( + "path", "/path/to/file.txt" + )) + .subscribe(); +``` + +### Prompt System + +The prompt system enables interaction with server-side prompt templates. These templates can be discovered and executed with custom parameters, allowing for dynamic text generation based on predefined patterns. + +#### Sync API + +```java +// List available prompt templates +var prompts = client.listPrompts(); +prompts.forEach(prompt -> System.out.println(prompt.getName())); + +// Execute a prompt template with parameters +var response = client.executePrompt("echo", Map.of( + "text", "Hello, World!" +)); +``` + +#### Async API + +```java +// List available prompt templates asynchronously +client.listPrompts() + .doOnNext(prompts -> prompts.forEach(prompt -> + System.out.println(prompt.getName()))) + .subscribe(); + +// Execute a prompt template asynchronously +client.executePrompt("echo", Map.of( + "text", "Hello, World!" + )) + .subscribe(); +``` + +### Using Completion + +As part of the [Completion capabilities](https://modelcontextprotocol.io/specification/latest/server/utilities/completion), MCP provides a standardized way for servers to offer argument autocompletion suggestions for prompts and resource URIs. + +Check the [Server Completion capabilities](server.html#completion-specification) to learn how to enable and configure completions on the server side. + +On the client side, the MCP client provides methods to request auto-completions: + +#### Sync API + +```java + +CompleteRequest request = new CompleteRequest( + new PromptReference("code_review"), + new CompleteRequest.CompleteArgument("language", "py")); + +CompleteResult result = syncMcpClient.completeCompletion(request); + +``` + +#### Async API + +```java + +CompleteRequest request = new CompleteRequest( + new PromptReference("code_review"), + new CompleteRequest.CompleteArgument("language", "py")); + +Mono result = mcpClient.completeCompletion(request); + +``` + +### Adding context information + +HTTP request sent through SSE or Streamable HTTP transport can be customized with +dedicated APIs (see [client transport](#client-transport)). These customizers may need +additional context-specific information that must be injected at the client level. + +#### Sync API + +The `McpSyncClient` is used in a blocking environment, and may rely on thread-locals to share information. +For example, some frameworks store the current server request or security tokens in a thread-local. +To make this type of information available to underlying transports, use `SyncSpec#transportContextProvider`: + +```java +McpClient.sync(transport) + .transportContextProvider(() -> { + var data = obtainDataFromThreadLocals(); + return McpTransportContext.create( + Map.of("some-data", data) + ); + }) + .build(); +``` + +This `McpTransportContext` will be available in HttpClient-based `McpSyncHttpClientRequestCustomizer` +and WebClient-based `ExchangeFilterFunction`. + +#### Async API + +The `McpAsyncClient` is used in a reactive environment. +Information is passed through the reactive chain using the [Reactor context](https://projectreactor.io/docs/core/release/reference/advancedFeatures/context.html). +To insert data in the Reactor context, use `contextWrite` when calling client operations: + +```java +var client = McpClient.async(transport) + // ... + .build(); + +Mono toolList = client + .listTools() + .contextWrite(ctx -> { + var transportContext = McpTransportContext.create( + Map.of("some-key", "some value") + ); + return ctx.put(McpTransportContext.KEY, transportContext) + .put("reactor-context-key", "some reactor value"); + }); +``` + +With this, the `McpTransportContext` will be available in HttpClient-based `McpAsyncHttpClientRequestCustomizer`. +The entire Reactor context is also available in both `McpAsyncHttpClientRequestCustomizer` +and WebClient's `ExchangeFilterFunction`, through `Mono.deferContextual`: + +```java +WebClient.builder() + .filter((request, next) -> { + return Mono.deferContextual(ctx -> { + var transportContext = ctx.get(McpTransportContext.KEY); + var someReactorValue = ctx.get("reactor-context-key"); + // ... use context values ... + }); + }); +``` diff --git a/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/getting-started.md b/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/getting-started.md new file mode 100644 index 000000000..a4f83f482 --- /dev/null +++ b/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/getting-started.md @@ -0,0 +1,114 @@ +# Getting Started + +Add the following dependencies to your project: + +## Maven + +The core MCP functionality: + +```xml + + io.modelcontextprotocol.sdk + mcp + +``` + +The core `mcp` module already includes default `STDIO`, `SSE` and `Streamable-HTTP` transport implementations and doesn't require external web frameworks. + +If you're using the Spring Framework and want to use Spring-specific transport implementations, add one of the following optional dependencies: + +```xml + + + io.modelcontextprotocol.sdk + mcp-spring-webflux + + + + + io.modelcontextprotocol.sdk + mcp-spring-webmvc + +``` + +## Gradle + +The core MCP functionality: + +```groovy +dependencies { + implementation platform("io.modelcontextprotocol.sdk:mcp") + //... +} +``` + +The core `mcp` module already includes default `STDIO`, `SSE` and `Streamable-HTTP` transport implementations and doesn't require external web frameworks. + +If you're using the Spring Framework and want to use Spring-specific transport implementations, add one of the following optional dependencies: + +```groovy +// Optional: Spring WebFlux-based SSE and Streamable-HTTP client and server transports +dependencies { + implementation platform("io.modelcontextprotocol.sdk:mcp-spring-webflux") +} + +// Optional: Spring WebMVC-based SSE and Streamable-HTTP server transports +dependencies { + implementation platform("io.modelcontextprotocol.sdk:mcp-spring-webmvc") +} +``` + +--- + +- `io.modelcontextprotocol.sdk:mcp-spring-webflux` - WebFlux-based Client and Server, `Streamable-HTTP` and `SSE` transport implementations. + The WebFlux implementation can be used in reactive applications while the WebClient-based MCP Client can be used in both reactive and imperative applications. + It is a highly scalable option and suitable and recommended for high-throughput scenarios. +- `io.modelcontextprotocol.sdk:mcp-spring-webmvc` - WebMVC-based Server, `Streamable-HTTP` and `SSE` transport implementation for servlet-based applications. + +## Bill of Materials (BOM) + +The Bill of Materials (BOM) declares the recommended versions of all the dependencies used by a given release. +Using the BOM from your application's build script avoids the need for you to specify and maintain the dependency versions yourself. +Instead, the version of the BOM you're using determines the utilized dependency versions. +It also ensures that you're using supported and tested versions of the dependencies by default, unless you choose to override them. + +Add the BOM to your project: + +### Maven + +```xml + + + + io.modelcontextprotocol.sdk + mcp-bom + 0.12.1 + pom + import + + + +``` + +### Gradle + +```groovy +dependencies { + implementation platform("io.modelcontextprotocol.sdk:mcp-bom:0.12.1") + //... +} +``` + +Gradle users can also use the MCP BOM by leveraging Gradle (5.0+) native support for declaring dependency constraints using a Maven BOM. +This is implemented by adding a 'platform' dependency handler method to the dependencies section of your Gradle build script. +As shown in the snippet above, this can then be followed by version-less declarations of the MCP SDK modules you wish to use. + +--- + +Replace the version number with the version of the BOM you want to use. + +## Additional Dependencies + +The following additional dependencies are available and managed by the BOM: + +- `io.modelcontextprotocol.sdk:mcp-test` - Testing utilities and support for MCP-based applications. diff --git a/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/images/java-mcp-client-architecture.jpg b/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/images/java-mcp-client-architecture.jpg new file mode 100644 index 000000000..688a2b4ad Binary files /dev/null and b/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/images/java-mcp-client-architecture.jpg differ diff --git a/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/images/java-mcp-server-architecture.jpg b/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/images/java-mcp-server-architecture.jpg new file mode 100644 index 000000000..4b05ca139 Binary files /dev/null and b/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/images/java-mcp-server-architecture.jpg differ diff --git a/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/server.md b/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/server.md new file mode 100644 index 000000000..3ba6e89ae --- /dev/null +++ b/mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/server.md @@ -0,0 +1,915 @@ +# Java MCP Server + +## Server Features + +The MCP Server is a foundational component in the Model Context Protocol (MCP) architecture that provides tools, resources, and capabilities to clients. It implements the server-side of the protocol, responsible for: + +- Exposing tools that clients can discover and execute. + Supports input and output schemas and returns structured and unstructured content types. +- Managing resources with URI-based access patterns +- Providing prompt templates and handling prompt requests +- Supporting capability negotiation with clients +- Implementing server-side protocol operations +- Managing concurrent client connections +- Providing structured logging, progress tracking, and notifications +- Provides `STDIO`, `Streamable-HTTP` and `SSE` server transport implementations without requiring external web frameworks. +- **Optional**, Spring-specific transport dependencies `io.modelcontextprotocol.sdk:mcp-spring-webflux`, `io.modelcontextprotocol.sdk:mcp-spring-webmvc` for [Spring AI](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html) users. + +> **Tip:** This [quickstart demo](https://modelcontextprotocol.io/quickstart/server), based on Spring AI MCP, will show you how to build an MCP server. + +The server supports both synchronous and asynchronous APIs, allowing for flexible integration in different application contexts. + +### Sync API + +```java +// Create a server with custom configuration +McpSyncServer syncServer = McpServer.sync(transportProvider) + .serverInfo("my-server", "1.0.0") + .capabilities(ServerCapabilities.builder() + .resources(false, true) // Enable resource support + .tools(true) // Enable tool support + .prompts(true) // Enable prompt support + .logging() // Enable logging support + .completions() // Enable completions support + .build()) + .build(); + +// Register tools, resources, and prompts +syncServer.addTool(syncToolSpecification); +syncServer.addResource(syncResourceSpecification); +syncServer.addPrompt(syncPromptSpecification); + +// Close the server when done +syncServer.close(); +``` + +#### Preserving thread-locals in handlers + +`McpSyncServer` delegates execution to an underlying `McpAsyncServer`. +Execution of handlers for tools, resources, etc, may not happen on the thread calling the method. +In that case, thread-locals are lost, which may break certain features in frameworks relying on thread-bound work. +To ensure execution happens on the calling thread, and that thread-locals remain available, set `McpServer.sync(...).immediateExecution(true)`. + +> **Note:** This is only relevant to Sync servers. Async servers use the reactive stack and should not rely on thread-locals. + +### Async API + +```java +// Create an async server with custom configuration +McpAsyncServer asyncServer = McpServer.async(transportProvider) + .serverInfo("my-server", "1.0.0") + .capabilities(ServerCapabilities.builder() + .resources(false, true) // Enable resource support + .tools(true) // Enable tool support + .prompts(true) // Enable prompt support + .logging() // Enable logging support + .completions() // Enable completions support + .build()) + .build(); + +// Register tools, resources, and prompts +asyncServer.addTool(asyncToolSpecification) + .doOnSuccess(v -> logger.info("Tool registered")) + .subscribe(); + +asyncServer.addResource(asyncResourceSpecification) + .doOnSuccess(v -> logger.info("Resource registered")) + .subscribe(); + +asyncServer.addPrompt(asyncPromptSpecification) + .doOnSuccess(v -> logger.info("Prompt registered")) + .subscribe(); + +// Close the server when done +asyncServer.close() + .doOnSuccess(v -> logger.info("Server closed")) + .subscribe(); +``` + +## Server Transport Providers + +The transport layer in the MCP SDK is responsible for handling the communication between clients and servers. +It provides different implementations to support various communication protocols and patterns. +The SDK includes several built-in transport provider implementations: + +### STDIO Transport + +Create in-process based transport: + +```java +StdioServerTransportProvider transportProvider = new StdioServerTransportProvider(new ObjectMapper()); +``` + +Provides bidirectional JSON-RPC message handling over standard input/output streams with non-blocking message processing, serialization/deserialization, and graceful shutdown support. + +Key features: +- Bidirectional communication through stdin/stdout +- Process-based integration support +- Simple setup and configuration +- Lightweight implementation + +### WebFlux Transport + +WebFlux-based SSE and Streamable-HTTP server transport. Requires the `mcp-spring-webflux` dependency (see [Getting Started](getting-started.html)). + +#### Streamable-HTTP (WebFlux) + +```java +@Configuration +class McpConfig { + @Bean + WebFluxStreamableServerTransportProvider webFluxStreamableServerTransportProvider(ObjectMapper mapper) { + return this.mcpStreamableServerTransportProvider = WebFluxStreamableServerTransportProvider.builder() + .objectMapper(mapper) + .messageEndpoint("/mcp") + .build(); + } + + @Bean + RouterFunction mcpRouterFunction(WebFluxStreamableServerTransportProvider transportProvider) { + return transportProvider.getRouterFunction(); + } +} +``` + +Implements the Streamable HTTP transport specification, providing: +- Bidirectional communication through Streamable-HTTP +- Reactive HTTP streaming with WebFlux +- Concurrent client connections through Streamable-HTTP endpoints +- Message routing and session management +- Graceful shutdown capabilities + +> **Note:** Current implementation lacks resumability due to lack of session storage + +> **Tip:** In distributed environments with multiple MCP Server instances, proper message routing is required. +> To achieve scalability without significant infrastructure overhead consider the `Stateless Streamable-HTTP` server transport implementation. + +#### Stateless Streamable-HTTP (WebFlux) + +Stateless MCP servers are designed for simplified deployments where session state is not maintained between requests. +They implement a subset of `Streamable-HTTP` specification that return `application/json` responses. +They are called stateless because unlike the standard Streamable-HTTP transport, they do not maintain session state between requests. +These servers are ideal for microservices architectures and cloud-native deployments. + +```java +@Configuration +class McpConfig { + @Bean + WebFluxStatelessServerTransport webFluxStatelessServerTransport(ObjectMapper mapper) { + return this.mcpStatelessServerTransport = WebFluxStatelessServerTransport.builder() + .objectMapper(mapper) + .messageEndpoint("/mcp") + .build(); + } + + @Bean + RouterFunction mcpRouterFunction(WebFluxStatelessServerTransport transportProvider) { + return transportProvider.getRouterFunction(); + } + + @Bean + public McpStatelessSyncServer prepareSyncServerBuilder(WebFluxStatelessServerTransport statelessServerTransport) { + return McpServer.sync(statelessServerTransport) + //... + .build(); + } + +} +``` + +Implements the MCP Stateless Streamable-HTTP transport specification, providing: +- Unidirectional (client to server) communication through Streamable-HTTP +- Reactive HTTP streaming with WebFlux +- Concurrent client connections through Stateless endpoints +- Message routing and session management +- Graceful shutdown capabilities + +> **Note:** Current implementation doesn't support sending Notifications back to the clients. + +#### SSE (WebFlux) + +```java +@Configuration +class McpConfig { + @Bean + WebFluxSseServerTransportProvider webFluxSseServerTransportProvider(ObjectMapper mapper) { + return new WebFluxSseServerTransportProvider(mapper, "/mcp/message"); + } + + @Bean + RouterFunction mcpRouterFunction(WebFluxSseServerTransportProvider transportProvider) { + return transportProvider.getRouterFunction(); + } +} +``` + +Implements the MCP SSE transport specification, providing: +- Reactive HTTP streaming with WebFlux +- Concurrent client connections through SSE endpoints +- Message routing and session management +- Graceful shutdown capabilities + +### WebMVC Transport + +Creates WebMvc-based SSE server transport. Requires the `mcp-spring-webmvc` dependency. + +#### Streamable-HTTP (WebMVC) + +```java +@Configuration +@EnableWebMvc +class McpConfig { + @Bean + WebMvcStreamableServerTransportProvider webMvcStreamableHttpServerTransportProvider(ObjectMapper mapper) { + return new WebMvcStreamableServerTransportProvider(mapper, "/mcp/message"); + } + + @Bean + RouterFunction mcpRouterFunction(WebMvcStreamableServerTransportProvider transportProvider) { + return transportProvider.getRouterFunction(); + } +} +``` + +Implements the Streamable HTTP transport specification, providing: +- Bidirectional communication through Streamable-HTTP +- Reactive HTTP streaming with WebFlux +- Concurrent client connections through Streamable-HTTP endpoints +- Message routing and session management +- Graceful shutdown capabilities + +> **Note:** Current implementation lacks resumability due to lack of session storage + +> **Tip:** In distributed environments with multiple MCP Server instances, proper message routing is required. +> To achieve scalability without significant infrastructure overhead consider the `Stateless Streamable-HTTP` server transport implementation. + +#### Stateless Streamable-HTTP (WebMVC) + +Stateless MCP servers are designed for simplified deployments where session state is not maintained between requests. +They implement a subset of `Streamable-HTTP` specification that return `application/json` responses. +They are called stateless because unlike the standard Streamable-HTTP transport, they do not maintain session state between requests. +These servers are ideal for microservices architectures and cloud-native deployments. + +```java +@Configuration +@EnableWebMvc +static class McpConfig { + + @Bean + public WebMvcStatelessServerTransport webMvcStatelessServerTransport() { + + return WebMvcStatelessServerTransport.builder() + .objectMapper(new ObjectMapper()) + .messageEndpoint(MESSAGE_ENDPOINT) + .build(); + + } + + @Bean + public RouterFunction routerFunction(WebMvcStatelessServerTransport statelessServerTransport) { + return statelessServerTransport.getRouterFunction(); + } + + @Bean + public McpStatelessSyncServer prepareSyncServerBuilder(WebMvcStatelessServerTransport statelessServerTransport) { + return McpServer.sync(statelessServerTransport) + //... + .build(); + } +} +``` + +Implements the MCP Streamable-HTTP, Stateless transport specification, providing: +- Unidirectional (client to server) communication through Streamable-HTTP +- Reactive HTTP streaming with WebFlux +- Concurrent client connections through Stateless endpoints +- Message routing and session management +- Graceful shutdown capabilities + +> **Note:** Current implementation doesn't support sending Notifications back to the clients. + +#### SSE (WebMVC) + +```java +@Configuration +@EnableWebMvc +class McpConfig { + @Bean + WebMvcSseServerTransportProvider webMvcSseServerTransportProvider(ObjectMapper mapper) { + return new WebMvcSseServerTransportProvider(mapper, "/mcp/message"); + } + + @Bean + RouterFunction mcpRouterFunction(WebMvcSseServerTransportProvider transportProvider) { + return transportProvider.getRouterFunction(); + } +} +``` + +Implements the MCP SSE transport specification, providing: +- Server-side event streaming +- Integration with Spring WebMVC +- Support for traditional web applications +- Synchronous operation handling + +### Servlet Transport + +Creates a Servlet-based `SSE`, `Streamable-HTTP`, and `Stateless` server transport. +It can be used with any Servlet container. + +#### Streamable-HTTP (Servlet) + +```java +@Configuration +@EnableWebMvc +public class McpServerConfig implements WebMvcConfigurer { + + @Bean + public HttpServletStreamableServerTransportProvider servletStreamableMcpSessionTransport() { + return HttpServletStreamableServerTransportProvider.builder() + .objectMapper(new ObjectMapper()) + .contextExtractor(TEST_CONTEXT_EXTRACTOR) + .mcpEndpoint(MESSAGE_ENDPOINT) + .keepAliveInterval(Duration.ofSeconds(1)) + .build(); + } + + //(Optionally) To use it with a Spring Web application, you can register it as a Servlet bean + @Bean + public ServletRegistrationBean customServletBean(HttpServletStreamableServerTransportProvider transportProvider) { + return new ServletRegistrationBean(transportProvider); + } +} +``` + +Implements the Streamable HTTP transport specification using the traditional Servlet API, providing: +- Bidirectional communication through Streamable-HTTP +- Asynchronous message handling using Servlet 6.0 async support +- Session management for multiple client connections +- One endpoint: + - Message endpoint (configurable) for client-to-server requests +- Error handling and response formatting +- Graceful shutdown support + +> **Note:** Current implementation lacks resumability due to lack of session storage + +> **Tip:** In distributed environments with multiple MCP Server instances, proper message routing is required. +> To achieve scalability without significant infrastructure overhead consider the `Stateless Streamable-HTTP` server transport implementation. + +#### Stateless Streamable-HTTP (Servlet) + +Stateless MCP servers are designed for simplified deployments where session state is not maintained between requests. +They implement a subset of `Streamable-HTTP` specification that return `application/json` responses. +They are called stateless because unlike the standard Streamable-HTTP transport, they do not maintain session state between requests. +These servers are ideal for microservices architectures and cloud-native deployments. + +```java +@Configuration +@EnableWebMvc +public class McpServerConfig implements WebMvcConfigurer { + + @Bean + public HttpServletStatelessServerTransport servletStatelessMcpSessionTransport() { + return HttpServletStatelessServerTransport.builder() + .objectMapper(new ObjectMapper()) + .mcpEndpoint(MESSAGE_ENDPOINT) + .keepAliveInterval(Duration.ofSeconds(1)) + .build(); + } + + + //(Optionally) To use it with a Spring Web application, you can register it as a Servlet bean + @Bean + public ServletRegistrationBean customServletBean(HttpServletStatelessServerTransport transportProvider) { + return new ServletRegistrationBean(transportProvider); + } + + @Bean + public McpStatelessSyncServer prepareSyncServerBuilder(HttpServletStatelessServerTransport statelessServerTransport) { + return McpServer.sync(statelessServerTransport) + .build(); + } + } +} +``` + +Implements the Streamable-HTTP Stateless transport specification using the traditional Servlet API, providing: +- Unidirectional (client to server) communication through Streamable-HTTP +- Asynchronous message handling using Servlet 6.0 async support +- Session management for multiple client connections +- One endpoint: + - Message endpoint (configurable) for client-to-server requests +- Error handling and response formatting +- Graceful shutdown support + +> **Note:** Current implementation doesn't support sending Notifications back to the clients. + +#### SSE (Servlet) + +```java +@Configuration +@EnableWebMvc +public class McpServerConfig implements WebMvcConfigurer { + + @Bean + public HttpServletSseServerTransportProvider servletSseServerTransportProvider() { + return HttpServletSseServerTransportProvider.builder() + .objectMapper(new ObjectMapper()) + .messageEndpoint("/mcp/message") + .keepAliveInterval(Duration.ofSeconds(1)) + .build(); + } + + //(Optionally) To use it with a Spring Web application, you can register it as a Servlet bean + @Bean + public ServletRegistrationBean customServletBean(HttpServletSseServerTransportProvider transportProvider) { + return new ServletRegistrationBean(transportProvider); + } +} +``` + +Implements the MCP SSE transport specification using the traditional Servlet API, providing: +- Asynchronous message handling using Servlet 6.0 async support +- Session management for multiple client connections +- Two types of endpoints: + - SSE endpoint (`/sse`) for server-to-client events + - Message endpoint (configurable) for client-to-server requests +- Error handling and response formatting +- Graceful shutdown support + +## Server Capabilities + +The server can be configured with various capabilities: + +```java +var capabilities = ServerCapabilities.builder() + .resources(false, true) // Resource support with list changes notifications + .tools(true) // Tool support with list changes notifications + .prompts(true) // Prompt support with list changes notifications + .logging() // Enable logging support (enabled by default with logging level INFO) + .build(); +``` + +### Tool Specification + +The Model Context Protocol allows servers to [expose tools](https://modelcontextprotocol.io/specification/latest/server/tools/) that can be invoked by language models. +The Java SDK allows implementing a Tool Specifications with their handler functions. +Tools enable AI models to perform calculations, access external APIs, query databases, and manipulate files: + +#### Sync + +```java +// Sync tool specification +var schema = """ + { + "type" : "object", + "id" : "urn:jsonschema:Operation", + "properties" : { + "operation" : { + "type" : "string" + }, + "a" : { + "type" : "number" + }, + "b" : { + "type" : "number" + } + } + } + """; +var syncToolSpecification = new McpServerFeatures.SyncToolSpecification( + new Tool("calculator", "Basic calculator", schema), + (exchange, arguments) -> { + // Tool implementation + return new CallToolResult(result, false); + } +); +``` + +#### Async + +```java +// Async tool specification +var schema = """ + { + "type" : "object", + "id" : "urn:jsonschema:Operation", + "properties" : { + "operation" : { + "type" : "string" + }, + "a" : { + "type" : "number" + }, + "b" : { + "type" : "number" + } + } + } + """; +var asyncToolSpecification = new McpServerFeatures.AsyncToolSpecification( + new Tool("calculator", "Basic calculator", schema), + (exchange, arguments) -> { + // Tool implementation + return Mono.just(new CallToolResult(result, false)); + } +); +``` + +The Tool specification includes a Tool definition with `name`, `description`, and `parameter schema` followed by a call handler that implements the tool's logic. +The function's first argument is `McpAsyncServerExchange` for client interaction, and the second is a map of tool arguments. + +### Resource Specification + +Specification of a resource with its handler function. +Resources provide context to AI models by exposing data such as: File contents, Database records, API responses, System information, Application state. +Example resource specification: + +#### Sync + +```java +// Sync resource specification +var syncResourceSpecification = new McpServerFeatures.SyncResourceSpecification( + new Resource("custom://resource", "name", "description", "mime-type", null), + (exchange, request) -> { + // Resource read implementation + return new ReadResourceResult(contents); + } +); +``` + +#### Async + +```java +// Async resource specification +var asyncResourceSpecification = new McpServerFeatures.AsyncResourceSpecification( + new Resource("custom://resource", "name", "description", "mime-type", null), + (exchange, request) -> { + // Resource read implementation + return Mono.just(new ReadResourceResult(contents)); + } +); +``` + +The resource specification comprised of resource definitions and resource read handler. +The resource definition including `name`, `description`, and `MIME type`. +The first argument of the function that handles resource read requests is an `McpAsyncServerExchange` upon which the server can +interact with the connected client. +The second arguments is an `McpSchema.ReadResourceRequest`. + +### Prompt Specification + +As part of the [Prompting capabilities](https://modelcontextprotocol.io/specification/latest/server/prompts/), MCP provides a standardized way for servers to expose prompt templates to clients. +The Prompt Specification is a structured template for AI model interactions that enables consistent message formatting, parameter substitution, context injection, response formatting, and instruction templating. + +#### Sync + +```java +// Sync prompt specification +var syncPromptSpecification = new McpServerFeatures.SyncPromptSpecification( + new Prompt("greeting", "description", List.of( + new PromptArgument("name", "description", true) + )), + (exchange, request) -> { + // Prompt implementation + return new GetPromptResult(description, messages); + } +); +``` + +#### Async + +```java +// Async prompt specification +var asyncPromptSpecification = new McpServerFeatures.AsyncPromptSpecification( + new Prompt("greeting", "description", List.of( + new PromptArgument("name", "description", true) + )), + (exchange, request) -> { + // Prompt implementation + return Mono.just(new GetPromptResult(description, messages)); + } +); +``` + +The prompt definition includes name (identifier for the prompt), description (purpose of the prompt), and list of arguments (parameters for templating). +The handler function processes requests and returns formatted templates. +The first argument is `McpAsyncServerExchange` for client interaction, and the second argument is a `GetPromptRequest` instance. + +### Completion Specification + +As part of the [Completion capabilities](https://modelcontextprotocol.io/specification/latest/server/utilities/completion), MCP provides a standardized way for servers to offer argument autocompletion suggestions for prompts and resource URIs. + +#### Sync + +```java +// Sync completion specification +var syncCompletionSpecification = new McpServerFeatures.SyncCompletionSpecification( + new McpSchema.PromptReference("code_review"), (exchange, request) -> { + + // completion implementation ... + + return new McpSchema.CompleteResult( + new CompleteResult.CompleteCompletion( + List.of("python", "pytorch", "pyside"), + 10, // total + false // hasMore + )); + } +); + +// Create a sync server with completion capabilities +var mcpServer = McpServer.sync(mcpServerTransportProvider) + .capabilities(ServerCapabilities.builder() + .completions() // enable completions support + // ... + .build()) + // ... + .completions(syncCompletionSpecification) // register completion specification + .build(); + +``` + +#### Async + +```java +// Async prompt specification +var asyncCompletionSpecification = new McpServerFeatures.AsyncCompletionSpecification( + new McpSchema.PromptReference("code_review"), (exchange, request) -> { + + // completion implementation ... + + return Mono.just(new McpSchema.CompleteResult( + new CompleteResult.CompleteCompletion( + List.of("python", "pytorch", "pyside"), + 10, // total + false // hasMore + ))); + } +); + +// Create a async server with completion capabilities +var mcpServer = McpServer.async(mcpServerTransportProvider) + .capabilities(ServerCapabilities.builder() + .completions() // enable completions support + // ... + .build()) + // ... + .completions(asyncCompletionSpecification) // register completion specification + .build(); + +``` + +The `McpSchema.CompletionReference` definition defines the type (`PromptReference` or `ResourceReference`) and the identifier for the completion specification (e.g handler). +The handler function processes requests and returns the completion response. +The first argument is `McpAsyncServerExchange` for client interaction, and the second argument is a `CompleteRequest` instance. + +Check the [using completion](client.html#using-completion) to learn how to use the completion capabilities on the client side. + +### Using Sampling from a Server + +To use [Sampling capabilities](https://modelcontextprotocol.io/specification/latest/client/sampling/), you need a compatible client that supports sampling. +No special server configuration is needed, but verify client sampling support before making requests. +Learn about [client sampling support](client.html#sampling-support). + +When a compatible client connects to a stateful server, the server can request language model generations: + +#### Sync API + +```java +// Create a server +McpSyncServer server = McpServer.sync(transportProvider) + .serverInfo("my-server", "1.0.0") + .build(); + +// Define a tool that uses sampling +var calculatorTool = new McpServerFeatures.SyncToolSpecification( + new Tool("ai-calculator", "Performs calculations using AI", schema), + (exchange, arguments) -> { + // Check if client supports sampling + if (exchange.getClientCapabilities().sampling() == null) { + return new CallToolResult("Client does not support AI capabilities", false); + } + + // Create a sampling request + McpSchema.CreateMessageRequest request = McpSchema.CreateMessageRequest.builder() + .messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER, + new McpSchema.TextContent("Calculate: " + arguments.get("expression"))) + .modelPreferences(McpSchema.ModelPreferences.builder() + .hints(List.of( + McpSchema.ModelHint.of("claude-3-sonnet"), + McpSchema.ModelHint.of("claude") + )) + .intelligencePriority(0.8) // Prioritize intelligence + .speedPriority(0.5) // Moderate speed importance + .build()) + .systemPrompt("You are a helpful calculator assistant. Provide only the numerical answer.") + .maxTokens(100) + .build(); + + // Request sampling from the client + McpSchema.CreateMessageResult result = exchange.createMessage(request); + + // Process the result + String answer = result.content().text(); + return new CallToolResult(answer, false); + } +); + +// Add the tool to the server +server.addTool(calculatorTool); +``` + +#### Async API + +```java +// Create a server +McpAsyncServer server = McpServer.async(transportProvider) + .serverInfo("my-server", "1.0.0") + .build(); + +// Define a tool that uses sampling +var calculatorTool = new McpServerFeatures.AsyncToolSpecification( + new Tool("ai-calculator", "Performs calculations using AI", schema), + (exchange, arguments) -> { + // Check if client supports sampling + if (exchange.getClientCapabilities().sampling() == null) { + return Mono.just(new CallToolResult("Client does not support AI capabilities", false)); + } + + // Create a sampling request + McpSchema.CreateMessageRequest request = McpSchema.CreateMessageRequest.builder() + .content(new McpSchema.TextContent("Calculate: " + arguments.get("expression"))) + .modelPreferences(McpSchema.ModelPreferences.builder() + .hints(List.of( + McpSchema.ModelHint.of("claude-3-sonnet"), + McpSchema.ModelHint.of("claude") + )) + .intelligencePriority(0.8) // Prioritize intelligence + .speedPriority(0.5) // Moderate speed importance + .build()) + .systemPrompt("You are a helpful calculator assistant. Provide only the numerical answer.") + .maxTokens(100) + .build(); + + // Request sampling from the client + return exchange.createMessage(request) + .map(result -> { + // Process the result + String answer = result.content().text(); + return new CallToolResult(answer, false); + }); + } +); + +// Add the tool to the server +server.addTool(calculatorTool) + .subscribe(); +``` + +The `CreateMessageRequest` object allows you to specify: `Content` - the input text or image for the model, +`Model Preferences` - hints and priorities for model selection, `System Prompt` - instructions for the model's behavior and +`Max Tokens` - maximum length of the generated response. + +### Using Elicitation from a Server + +To use [Elicitation capabilities](https://modelcontextprotocol.io/specification/latest/client/elicitation), you need a compatible client that supports elicitation. +No special server configuration is needed, but verify client elicitation support before making requests. +Learn about [client elicitation support](client.html#elicitation-support). + +When a compatible client connects to a stateful server, the server can request language model generations: + +#### Sync API + +```java +// Create a server +McpSyncServer server = McpServer.sync(transportProvider) + .serverInfo("my-server", "1.0.0") + .build(); + +// Define a tool that uses sampling +var calculatorTool = new McpServerFeatures.SyncToolSpecification( + new Tool("ai-calculator", "Performs calculations using AI", schema), + (exchange, arguments) -> { + // Check if client supports elicitation + if (exchange.getClientCapabilities().elicitation() == null) { + return new CallToolResult("Client does not support elicitation capabilities", false); + } + + // Create a elicitation request + McpSchema.ElicitRequest request = McpSchema.ElicitRequest.builder() + .message("Test message") + .requestedSchema( + Map.of("type", "object", "properties", Map.of("message", Map.of("type", "string")))) + .build(); + + // Request elicitation from the client + McpSchema.ElicitResult result = exchange.createElicitation(request); + + // Process the result + Map answer = result.content(); + return new CallToolResult(answer, false); + } +); + +// Add the tool to the server +server.addTool(calculatorTool); +``` + +#### Async API + +```java +// Create a server +McpAsyncServer server = McpServer.async(transportProvider) + .serverInfo("my-server", "1.0.0") + .build(); + +// Define a tool that uses elicitation +var calculatorTool = new McpServerFeatures.AsyncToolSpecification( + new Tool("ai-calculator", "Performs calculations using AI", schema), + (exchange, arguments) -> { + // Check if client supports elicitation + if (exchange.getClientCapabilities().elicitation() == null) { + return Mono.just(new CallToolResult("Client does not support elicitation capabilities", false)); + } + + // Create a elicitation request + McpSchema.ElicitRequest request = McpSchema.ElicitRequest.builder() + .message("Test message") + .requestedSchema( + Map.of("type", "object", "properties", Map.of("message", Map.of("type", "string")))) + .build(); + + // Request elicitation from the client + return exchange.createElicitation(request) + .map(result -> { + // Process the result + Map answer = result.content(); + return new CallToolResult(answer, false); + }); + } +); + +// Add the tool to the server +server.addTool(calculatorTool) + .subscribe(); +``` + +### Logging Support + +The server provides structured logging capabilities that allow sending log messages to clients with different severity levels. The +log notifications can only be sent from within an existing client session, such as tools, resources, and prompts calls. + +For example, we can send a log message from within a tool handler function. +On the client side, you can register a logging consumer to receive log messages from the server and set the minimum logging level to filter messages. + +```java +var mcpClient = McpClient.sync(transport) + .loggingConsumer(notification -> { + System.out.println("Received log message: " + notification.data()); + }) + .build(); + +mcpClient.initialize(); + +mcpClient.setLoggingLevel(McpSchema.LoggingLevel.INFO); + +// Call the tool that sends logging notifications +CallToolResult result = mcpClient.callTool(new McpSchema.CallToolRequest("logging-test", Map.of())); +``` + +The server can send log messages using the `McpAsyncServerExchange`/`McpSyncServerExchange` object in the tool/resource/prompt handler function: + +```java +var tool = new McpServerFeatures.AsyncToolSpecification( + new McpSchema.Tool("logging-test", "Test logging notifications", emptyJsonSchema), + (exchange, request) -> { + + exchange.loggingNotification( // Use the exchange to send log messages + McpSchema.LoggingMessageNotification.builder() + .level(McpSchema.LoggingLevel.DEBUG) + .logger("test-logger") + .data("Debug message") + .build()) + .block(); + + return Mono.just(new CallToolResult("Logging test completed", false)); + }); + +var mcpServer = McpServer.async(mcpServerTransportProvider) + .serverInfo("test-server", "1.0.0") + .capabilities( + ServerCapabilities.builder() + .logging() // Enable logging support + .tools(true) + .build()) + .tools(tool) + .build(); +``` + +Clients can control the minimum logging level they receive through the `mcpClient.setLoggingLevel(level)` request. Messages below the set level will be filtered out. +Supported logging levels (in order of increasing severity): DEBUG (0), INFO (1), NOTICE (2), WARNING (3), ERROR (4), CRITICAL (5), ALERT (6), EMERGENCY (7) + +## Error Handling + +The SDK provides comprehensive error handling through the McpError class, covering protocol compatibility, transport communication, JSON-RPC messaging, tool execution, resource management, prompt handling, timeouts, and connection issues. This unified error handling approach ensures consistent and reliable error management across both synchronous and asynchronous operations. diff --git a/pom.xml b/pom.xml index 67adb4d9a..0441fe99d 100644 --- a/pom.xml +++ b/pom.xml @@ -76,6 +76,7 @@ 3.1.2 3.5.2 3.11.2 + 25 3.3.0 0.8.10 1.5.0 @@ -266,6 +267,7 @@ + javadoc @@ -284,9 +286,13 @@ none -Xdoclint:none + --syntax-highlight ${java.version} true + true + ${project.basedir}/src/main/javadoc + ${project.basedir}/src/main/javadoc/overview.md @@ -308,6 +314,26 @@ + + + javadoc-toolchain + + false + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + ${javadoc.jdk.version} + + + + + + release diff --git a/src/main/javadoc/README.md b/src/main/javadoc/README.md new file mode 100644 index 000000000..f96c89a91 --- /dev/null +++ b/src/main/javadoc/README.md @@ -0,0 +1,52 @@ +# Javadoc Documentation + +This directory contains documentation that is included in the generated Javadoc. + +## Adding Documentation + +### Overview Page + +Edit `overview.md` in this directory for the main project overview page. + +### Additional Pages + +Place markdown files in `mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/`. + +Example: `mcp-core/src/main/javadoc/io/modelcontextprotocol/doc-files/getting-started.md` + +## Requirements + +- **JDK 25+** is required to generate javadocs with markdown and syntax highlighting support +- Files use CommonMark syntax with GitHub-flavored tables +- Code blocks with language hints (e.g., ` ```java `) get automatic syntax highlighting +- Generate docs locally: `mvn -Pjavadoc javadoc:aggregate` + +## Linking to Documentation + +From javadoc comments, link to doc-files: + +```java +/** + * See the Getting Started guide. + */ +``` + +Note: Link to `.html` even though source is `.md` - the javadoc tool converts them. + +From the overview page, use relative links: + +```markdown +See [Getting Started](io/modelcontextprotocol/doc-files/getting-started.html) +``` + +## Markdown Syntax + +The javadoc tool supports CommonMark with these extensions: + +- GitHub-flavored tables +- Fenced code blocks with syntax highlighting (via `--syntax-highlight` option using highlight.js) +- Links to Java elements: `[text][java.util.List]` + +Supported languages for syntax highlighting include Java, JSON, XML, HTML, and Properties. + +For full details, see [JEP 467: Markdown Documentation Comments](https://openjdk.org/jeps/467). diff --git a/src/main/javadoc/overview.md b/src/main/javadoc/overview.md new file mode 100644 index 000000000..a3312b38d --- /dev/null +++ b/src/main/javadoc/overview.md @@ -0,0 +1,82 @@ +# MCP Java SDK + +Java SDK for the [Model Context Protocol](https://modelcontextprotocol.io/) (MCP), enabling Java applications to interact with AI models and tools through a standardized interface. + +The source code is available at [github.com/modelcontextprotocol/java-sdk](https://github.com/modelcontextprotocol/java-sdk). + +## Modules + +- **mcp** - Convenience module that bundles core dependencies +- **mcp-core** - Core reference implementation +- **mcp-json** - JSON abstraction layer +- **mcp-json-jackson2** - Jackson JSON implementation +- **mcp-spring-webflux** - Spring WebFlux transport +- **mcp-spring-webmvc** - Spring WebMVC transport + +## Features + +- MCP Client and MCP Server implementations supporting: + - Protocol [version compatibility negotiation](https://modelcontextprotocol.io/specification/latest/basic/lifecycle#initialization) + - [Tool](https://modelcontextprotocol.io/specification/latest/server/tools/) discovery, execution, list change notifications + - [Resource](https://modelcontextprotocol.io/specification/latest/server/resources/) management with URI templates + - [Prompt](https://modelcontextprotocol.io/specification/latest/server/prompts/) handling and management + - [Completion](https://modelcontextprotocol.io/specification/latest/server/utilities/completion/) argument autocompletion suggestions for prompts and resource URIs + - [Progress](https://modelcontextprotocol.io/specification/latest/basic/utilities/progress/) progress tracking for long-running operations + - [Ping](https://modelcontextprotocol.io/specification/latest/basic/utilities/ping/) lightweight health check mechanism + - [Server Keepalive](https://modelcontextprotocol.io/specification/latest/basic/utilities/ping#implementation-considerations/) to maintain active server connections + - [Logging](https://modelcontextprotocol.io/specification/latest/server/utilities/logging/) for sending structured log messages to clients + - [Roots](https://modelcontextprotocol.io/specification/latest/client/roots/) list management and notifications + - [Sampling](https://modelcontextprotocol.io/specification/latest/client/sampling/) support for AI model interactions + - [Elicitation](https://modelcontextprotocol.io/specification/latest/client/elicitation/) for servers to request additional information from users through the client +- Multiple transport implementations: + - Default transports (included in core `mcp` module, no external web frameworks required): + - Stdio-based transport for process-based communication + - Java HttpClient-based `SSE` and `Streamable-HTTP` client transport + - Servlet-based `SSE` and `Streamable-HTTP` server transport + - Optional Spring-based transports (convenience if using Spring Framework): + - WebFlux `SSE` and `Streamable-HTTP` client and server transports + - WebMVC `SSE` and `Streamable-HTTP` transport for servlet-based HTTP streaming +- Supports Synchronous and Asynchronous programming paradigms + +> **Tip:** The core `io.modelcontextprotocol.sdk:mcp` module provides default `STDIO`, `SSE` and `Streamable-HTTP` client and server transport implementations without requiring external web frameworks. +> +> Spring-specific transports are available as optional dependencies for convenience when using the [Spring AI](https://docs.spring.io/spring-ai/reference/1.1-SNAPSHOT/api/mcp/mcp-overview.html) Framework. + +## Architecture + +The SDK follows a layered architecture with clear separation of concerns: + +- **Client/Server Layer (McpClient/McpServer)**: Both use McpSession for sync/async operations, + with McpClient handling client-side protocol operations and McpServer managing server-side protocol operations. +- **Session Layer (McpSession)**: Manages communication patterns and state using DefaultMcpSession implementation. +- **Transport Layer (McpTransport)**: Handles JSON-RPC message serialization/deserialization via: + - StdioTransport (stdin/stdout) in the core module + - HTTP `Streamable-HTTP` and `SSE` transports in dedicated transport modules (Java HttpClient, Spring WebFlux, Spring WebMVC) + +The [MCP Client](io/modelcontextprotocol/doc-files/client.html) is a key component in the Model Context Protocol (MCP) architecture, responsible for establishing and managing connections with MCP servers. +It implements the client-side of the protocol. + +Java MCP Client Architecture + +The [MCP Server](io/modelcontextprotocol/doc-files/server.html) is a foundational component in the Model Context Protocol (MCP) architecture that provides tools, resources, and capabilities to clients. +It implements the server-side of the protocol. + +Java MCP Server Architecture + +Key Interactions: + +- **Client/Server Initialization**: Transport setup, protocol compatibility check, capability negotiation, and implementation details exchange. +- **Message Flow**: JSON-RPC message handling with validation, type-safe response processing, and error handling. +- **Resource Management**: Resource discovery, URI template-based access, subscription system, and content retrieval. + +## SDK Documentation + +- [Getting Started](io/modelcontextprotocol/doc-files/getting-started.html) - Dependencies and setup +- [MCP Client](io/modelcontextprotocol/doc-files/client.html) - Client implementation and transport options +- [MCP Server](io/modelcontextprotocol/doc-files/server.html) - Server implementation and transport providers + +## Additional Documentation + +- [MCP documentation](https://modelcontextprotocol.io) +- [MCP specification](https://modelcontextprotocol.io/specification/latest) +- [Spring AI MCP Documentation](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html)