Skip to content

Commit 70d150c

Browse files
committed
Add sampling documentation
1 parent e45bbb9 commit 70d150c

File tree

3 files changed

+231
-0
lines changed

3 files changed

+231
-0
lines changed

docs/mcp-declarative/README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,90 @@ List<McpToolContent> cancellationTool(McpCancellation cancellation) {
494494
}
495495
```
496496

497+
### Sampling
498+
499+
The MCP Sampling feature provides a standardized mechanism that allows servers to request LLM sampling operations from language
500+
models through connected clients. It enables servers to seamlessly integrate AI capabilities into their workflows without
501+
requiring API keys. Like other MCP features, sampling can be accessed via the MCP request features.
502+
Sampling support is optional for clients, and servers can verify its availability using the `enabled` method:
503+
504+
```java
505+
var sampling = request.features().sampling();
506+
if (!sampling.enabled()) {
507+
}
508+
```
509+
510+
If the client supports sampling, you can send a sampling request using the request method. A builder is provided to configure
511+
and customize the sampling request as needed:
512+
513+
```java
514+
McpSamplingRequest request = McpSamplingRequest.builder()
515+
.maxTokens(1)
516+
.temperature(0.1)
517+
.costPriority(0.1)
518+
.speedPriority(0.1)
519+
.hints(List.of("hint1"))
520+
.metadata(JsonValue.TRUE)
521+
.intelligencePriority(0.1)
522+
.systemPrompt("system prompt")
523+
.timeout(Duration.ofSeconds(10))
524+
.stopSequences(List.of("stop1"))
525+
.includeContext(McpIncludeContext.NONE)
526+
.addMessage(McpSamplingMessages.textContent("text", McpRole.USER))
527+
.build();
528+
```
529+
530+
Once your request is built, send it using the sampling feature. The request method may throw an `McpSamplingException` if an
531+
error occurs during processing. On success, it returns an McpSamplingResponse containing the response message, the model used,
532+
and optionally a stop reason.
533+
534+
```java
535+
try {
536+
McpSamplingResponse response = sampling.request(req -> req.addMessage(message));
537+
} catch(McpSamplingException exception) {
538+
// Manage error
539+
}
540+
```
541+
542+
The messages you send are prompts to the language model, and they follow the same structure as MCP prompts. You can use the
543+
`McpSamplingMessages` utility class to create different types of messages for the client model:
544+
545+
```java
546+
var text = McpSamplingMessages.textContent("Explain Helidon MCP in one paragraph.", McpRole.USER);
547+
var image = McpSamplingMessages.imageContent(pngBytes, MediaTypes.create("image/png"), McpRole.USER);
548+
var audio = McpSamplingMessages.audioContent(wavBytes, MediaTypes.create("audio/wav"), McpRole.USER);
549+
```
550+
551+
#### Example
552+
553+
Below is an example of a tool that uses the Sampling feature. If the connected client does not support sampling, the tool
554+
throws a `McpToolErrorException`.
555+
556+
```java
557+
@Mcp.Tool("Uses MCP Sampling to ask the connected client model.")
558+
List<McpToolContent> samplingTool(McpSampling sampling) {
559+
if (!sampling.enabled()) {
560+
throw new McpToolErrorException("This tool requires sampling feature");
561+
}
562+
563+
try {
564+
McpSamplingResponse response = sampling.request(req -> req
565+
.timeout(Duration.ofSeconds(10))
566+
.systemPrompt("You are a concise, helpful assistant.")
567+
.addMessage(McpSamplingMessages.textContent("Write a 3-line summary of Helidon MCP Sampling.", McpRole.USER)));
568+
569+
McpSamplingMessage message = response.message();
570+
if (message instanceof McpSamplingTextContent text) {
571+
return List.of(McpToolContents.textContent(text.text()));
572+
}
573+
throw new McpToolErrorException("Received non-text sampling content.");
574+
} catch (McpSamplingException e) {
575+
throw new McpToolErrorException(e.getMessage());
576+
}
577+
}
578+
```
579+
580+
497581
## References
498582

499583
- [MCP Specification](https://modelcontextprotocol.io/introduction)

docs/mcp/README.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,110 @@ private class CancellationTool implements McpTool {
659659
}
660660
```
661661

662+
### Sampling
663+
664+
The MCP Sampling feature provides a standardized mechanism that allows servers to request LLM sampling operations from language
665+
models through connected clients. It enables servers to seamlessly integrate AI capabilities into their workflows without
666+
requiring API keys. Like other MCP features, sampling can be accessed via the MCP request features.
667+
Sampling support is optional for clients, and servers can verify its availability using the `enabled` method:
668+
669+
```java
670+
var sampling = request.features().sampling();
671+
if (!sampling.enabled()) {
672+
}
673+
```
674+
675+
If the client supports sampling, you can send a sampling request using the request method. A builder is provided to configure
676+
and customize the sampling request as needed:
677+
678+
```java
679+
McpSamplingRequest request = McpSamplingRequest.builder()
680+
.maxTokens(1)
681+
.temperature(0.1)
682+
.costPriority(0.1)
683+
.speedPriority(0.1)
684+
.hints(List.of("hint1"))
685+
.metadata(JsonValue.TRUE)
686+
.intelligencePriority(0.1)
687+
.systemPrompt("system prompt")
688+
.timeout(Duration.ofSeconds(10))
689+
.stopSequences(List.of("stop1"))
690+
.includeContext(McpIncludeContext.NONE)
691+
.addMessage(McpSamplingMessages.textContent("text", McpRole.USER))
692+
.build();
693+
```
694+
695+
Once your request is built, send it using the sampling feature. The request method may throw an `McpSamplingException` if an
696+
error occurs during processing. On success, it returns an McpSamplingResponse containing the response message, the model used,
697+
and optionally a stop reason.
698+
699+
```java
700+
try {
701+
McpSamplingResponse response = sampling.request(req -> req.addMessage(message));
702+
} catch(McpSamplingException exception) {
703+
// Manage error
704+
}
705+
```
706+
707+
The messages you send are prompts to the language model, and they follow the same structure as MCP prompts. You can use the
708+
`McpSamplingMessages` utility class to create different types of messages for the client model:
709+
710+
```java
711+
var text = McpSamplingMessages.textContent("Explain Helidon MCP in one paragraph.", McpRole.USER);
712+
var image = McpSamplingMessages.imageContent(pngBytes, MediaTypes.create("image/png"), McpRole.USER);
713+
var audio = McpSamplingMessages.audioContent(wavBytes, MediaTypes.create("audio/wav"), McpRole.USER);
714+
```
715+
716+
#### Example
717+
718+
Below is an example of a tool that uses the Sampling feature. If the connected client does not support sampling, the tool
719+
throws a `McpToolErrorException`.
720+
721+
```java
722+
import java.time.Duration;
723+
724+
class SamplingTool implements McpTool {
725+
@Override
726+
public String name() {
727+
return "sampling-tool";
728+
}
729+
730+
@Override
731+
public String description() {
732+
return "Uses MCP Sampling to ask the connected client model.";
733+
}
734+
735+
@Override
736+
public String schema() {
737+
return "";
738+
}
739+
740+
@Override
741+
public List<McpToolContent> process(McpRequest request) {
742+
var sampling = request.features().sampling();
743+
744+
if (!sampling.enabled()) {
745+
throw new McpToolErrorException("This tool requires sampling feature");
746+
}
747+
748+
try {
749+
McpSamplingResponse response = sampling.request(req -> req
750+
.timeout(Duration.ofSeconds(10))
751+
.systemPrompt("You are a concise, helpful assistant.")
752+
.addMessage(McpSamplingMessages.textContent("Write a 3-line summary of Helidon MCP Sampling.", McpRole.USER)));
753+
754+
McpSamplingMessage message = response.message();
755+
if (message instanceof McpSamplingTextContent text) {
756+
return List.of(McpToolContents.textContent(text.text()));
757+
}
758+
throw new McpToolErrorException("Received non-text sampling content.");
759+
} catch (McpSamplingException e) {
760+
throw new McpToolErrorException(e.getMessage());
761+
}
762+
}
763+
}
764+
```
765+
662766
## Configuration
663767

664768
MCP server configuration can be defined using Helidon configuration files. Example in YAML:
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) 2025 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.helidon.extensions.mcp.server;
17+
18+
import org.junit.jupiter.api.Test;
19+
20+
import static org.hamcrest.MatcherAssert.assertThat;
21+
import static org.hamcrest.Matchers.instanceOf;
22+
import static org.hamcrest.Matchers.is;
23+
24+
class McpSamplingResponseTest {
25+
26+
@Test
27+
void testCustomValues() {
28+
McpSamplingResponse response = McpSamplingResponse.builder()
29+
.model("helidon-model")
30+
.stopReason(McpStopReason.END_TURN)
31+
.message(McpSamplingMessages.textContent("text", McpRole.USER))
32+
.build();
33+
34+
assertThat(response.stopReason().isPresent(), is(true));
35+
assertThat(response.stopReason().get(), is(McpStopReason.END_TURN));
36+
assertThat(response.model(), is("helidon-model"));
37+
assertThat(response.message(), instanceOf(McpSamplingTextContent.class));
38+
39+
McpSamplingTextContent text = (McpSamplingTextContent) response.message();
40+
assertThat(text.text(), is("text"));
41+
assertThat(text.role(), is(McpRole.USER));
42+
}
43+
}

0 commit comments

Comments
 (0)