Files
spring-ai-examples/model-context-protocol/sampling/README.md
Christian Tzolov 04269db217 Add MCP Sampling capability with weather example
Adds MCP Sampling implementation that demonstrates how to delegate LLM requests to multiple providers.

- add a weather server that retrieves data and uses MCP Sampling to generate creative content
- add a client that routes requests to different LLM providers (OpenAI and Anthropic) based on model hints
- add README documentation explaining the MCP Sampling workflow and implementation details

The MCP Sampling capability enables applications to leverage multiple LLM providers within a single workflow,
allowing for creative content generation, model comparison, and specialized task delegation.

Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
2025-03-24 14:13:37 +01:00

7.8 KiB

Spring AI MCP Sampling Examples

This directory contains examples demonstrating the Model Context Protocol (MCP) Sampling capability in Spring AI. MCP Sampling allows an MCP server to delegate certain requests to LLM providers, enabling multi-model interactions and creative content generation.

Overview

The MCP Sampling examples showcase:

  • How an MCP server can delegate LLM requests to a client
  • How a client can route requests to different LLM providers based on model hints
  • Integration with multiple LLM providers (OpenAI and Anthropic)
  • Creative content generation using multiple models
  • Combining responses from different LLMs into a unified result

Projects

This directory contains two main projects:

  1. mcp-weather-webmvc-server: An MCP server that provides weather information and uses MCP Sampling to generate creative content
  2. mcp-sampling-client: An MCP client that handles sampling requests and routes them to different LLM providers

What is MCP Sampling?

MCP Sampling is a powerful capability of the Model Context Protocol that allows:

  • An MCP server to delegate certain requests to LLM providers
  • Specifying model preferences to target specific LLM providers
  • Routing requests based on model hints
  • Combining responses from multiple LLMs

This approach enables applications to leverage multiple LLM providers within a single workflow, allowing for creative content generation, model comparison, and specialized task delegation.

How It Works

The MCP Sampling workflow in these examples follows these steps:

  1. Client Initialization:

    • The client connects to the MCP Weather Server
    • It registers a sampling handler that can route requests to different LLM providers
  2. User Query:

    • The user sends a weather-related query to the client
    • The client forwards the query to the MCP Weather Server
  3. Server Processing:

    • The server retrieves weather data from the Open-Meteo API
    • It extracts the McpSyncServerExchange from the tool context
    • It creates sampling requests with different model preferences:
      • One targeting OpenAI with ModelPreferences.builder().addHint("openai").build()
      • One targeting Anthropic with ModelPreferences.builder().addHint("anthropic").build()
  4. Sampling Delegation:

    • The server sends the sampling requests back to the client
    • The client's sampling handler receives the requests
  5. Model Routing:

    • The client extracts the model hint from each request
    • It selects the appropriate LLM provider based on the hint
    • It forwards the prompt to the selected LLM
  6. Response Generation:

    • Each LLM generates a creative response (in this case, a poem about the weather)
    • The client returns the responses to the server
  7. Result Combination:

    • The server combines the responses from different LLMs
    • It returns the combined result to the user

Server Implementation

The MCP Weather Server implements the server-side of MCP Sampling:

public String callMcpSampling(ToolContext toolContext, WeatherResponse weatherResponse) {
    String openAiWeatherPoem = "<no OpenAI poem>";
    String anthropicWeatherPoem = "<no Anthropic poem>";

    if (toolContext != null && toolContext.getContext().containsKey("exchange")) {
        // Spring AI MCP Auto-configuration injects the McpSyncServerExchange into the ToolContext
        McpSyncServerExchange exchange = (McpSyncServerExchange) toolContext.getContext().get("exchange");
        if (exchange.getClientCapabilities().sampling() != null) {
            var messageRequestBuilder = McpSchema.CreateMessageRequest.builder()
                    .systemPrompt("You are a poet!")
                    .messages(List.of(new McpSchema.SamplingMessage(McpSchema.Role.USER,
                            new McpSchema.TextContent(
                                    "Please write a poem about this weather forecast (temperature is in Celsius)..."))));

            // Request poem from OpenAI
            var openAiLlmMessageRequest = messageRequestBuilder
                    .modelPreferences(ModelPreferences.builder().addHint("openai").build())
                    .build();
            CreateMessageResult openAiLlmResponse = exchange.createMessage(openAiLlmMessageRequest);
            openAiWeatherPoem = ((McpSchema.TextContent) openAiLlmResponse.content()).text();

            // Request poem from Anthropic
            var anthropicLlmMessageRequest = messageRequestBuilder
                    .modelPreferences(ModelPreferences.builder().addHint("anthropic").build())
                    .build();
            CreateMessageResult anthropicAiLlmResponse = exchange.createMessage(anthropicLlmMessageRequest);
            anthropicWeatherPoem = ((McpSchema.TextContent) anthropicAiLlmResponse.content()).text();
        }
    }

    // Combine responses
    return "OpenAI poem about the weather: " + openAiWeatherPoem + "\n\n" +
           "Anthropic poem about the weather: " + anthropicWeatherPoem + "\n" +
           ModelOptionsUtils.toJsonStringPrettyPrinter(weatherResponse);
}

Client Implementation

The MCP Sampling Client implements the client-side handling of sampling requests:

@Bean
McpSyncClientCustomizer samplingCustomizer(Map<String, ChatClient> chatClients) {
    return (name, spec) -> {
        spec.sampling(llmRequest -> {
            var userPrompt = ((McpSchema.TextContent) llmRequest.messages().get(0).content()).text();
            String modelHint = llmRequest.modelPreferences().hints().get(0).name();

            // Find the appropriate chat client based on the model hint
            ChatClient hintedChatClient = chatClients.entrySet().stream()
                    .filter(e -> e.getKey().contains(modelHint)).findFirst()
                    .orElseThrow().getValue();

            // Generate response using the selected model
            String response = hintedChatClient.prompt()
                    .system(llmRequest.systemPrompt())
                    .user(userPrompt)
                    .call()
                    .content();

            return CreateMessageResult.builder().content(new McpSchema.TextContent(response)).build();
        });
    };
}

Running the Examples

Prerequisites

  • Java 17 or later
  • Maven 3.6+
  • OpenAI API key
  • Anthropic API key

Step 1: Start the MCP Weather Server

cd mcp-weather-webmvc-server
./mvnw clean install -DskipTests
java -jar target/mcp-sampling-weather-server-0.0.1-SNAPSHOT.jar

Step 2: Set Environment Variables

export OPENAI_API_KEY=your-openai-key
export ANTHROPIC_API_KEY=your-anthropic-key

Step 3: Run the MCP Sampling Client

cd mcp-sampling-client
./mvnw clean install
java -Dai.user.input='What is the weather in Amsterdam right now?' -jar target/mcp-sampling-client-0.0.1-SNAPSHOT.jar

Sample Output

When you run the application, you'll see creative responses from both OpenAI and Anthropic models, each generating a poem about the current weather in Amsterdam.

Spring AI provides several MCP client and server implementations:

Additional Resources