Skip to content

Commit 8ab7f8b

Browse files
committed
Adding tests
1 parent 2d05331 commit 8ab7f8b

File tree

9 files changed

+561
-39
lines changed

9 files changed

+561
-39
lines changed

integrations/mcp/server/src/main/java/io/helidon/integrations/mcp/server/McpHttpFeature.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ private JsonObject toolsCall(JsonObject call) {
179179
Optional<Tool> tool = this.config.tools().stream()
180180
.filter(t -> call.getString("name").equals(t.name()))
181181
.findAny();
182-
McpParameters parameters = new McpParameters(call.getJsonObject("arguments"));
182+
McpParameters parameters = new McpParameters(call.getJsonObject("arguments"), "arguments");
183183
return tool.map(value -> value.process(parameters))
184184
.map(Jsonable::json)
185185
.map(result -> Json.createObjectBuilder()
@@ -239,7 +239,7 @@ private JsonObject promptsGet(JsonObject params) {
239239
.filter(p -> Objects.equals(p.name(), params.getString("name")))
240240
.findFirst();
241241

242-
McpParameters parameters = new McpParameters(params.getJsonObject("arguments"));
242+
McpParameters parameters = new McpParameters(params.getJsonObject("arguments"), "arguments");
243243
return prompt.map(value -> Json.createObjectBuilder()
244244
.add("description", value.description())
245245
.add("messages", Json.createArrayBuilder()
@@ -262,7 +262,7 @@ private JsonObject completion(JsonObject parameter) {
262262
Optional<Completion> completion = config.completions().stream()
263263
.filter(it -> it.name().equals(name))
264264
.findFirst();
265-
McpParameters parameters = new McpParameters(parameter.getJsonObject("argument"));
265+
McpParameters parameters = new McpParameters(parameter.getJsonObject("argument"), "argument");
266266
return completion.map(it -> it.complete(parameters))
267267
.map(result -> Json.createObjectBuilder()
268268
.add("completion", Json.createObjectBuilder()

integrations/mcp/server/src/main/java/io/helidon/integrations/mcp/server/McpParameters.java

Lines changed: 144 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,162 @@
1616

1717
package io.helidon.integrations.mcp.server;
1818

19+
import java.util.ArrayList;
20+
import java.util.List;
21+
import java.util.NoSuchElementException;
1922
import java.util.Optional;
23+
import java.util.function.Function;
2024

25+
import io.helidon.common.GenericType;
26+
import io.helidon.common.mapper.MapperException;
27+
import io.helidon.common.mapper.Mappers;
28+
import io.helidon.common.mapper.OptionalValue;
29+
import io.helidon.config.Config;
30+
31+
import jakarta.json.JsonArray;
32+
import jakarta.json.JsonNumber;
2133
import jakarta.json.JsonObject;
34+
import jakarta.json.JsonString;
2235
import jakarta.json.JsonValue;
2336

2437
/**
2538
* Mcp parameters provided to {@link Tool} and {@link Prompt}.
2639
*/
2740
public class McpParameters {
28-
private final JsonObject root;
41+
private static final Mappers MAPPERS = Mappers.create();
42+
private static final EmptyValue EMPTY_VALUE = new EmptyValue();
43+
private static final McpParameters EMPTY = new McpParameters(JsonValue.NULL, "null");
44+
45+
private final JsonValue value;
46+
private final String key;
47+
48+
McpParameters(JsonValue root, String key) {
49+
this.value = root;
50+
this.key = key;
51+
}
52+
53+
@SuppressWarnings("unchecked")
54+
private static <T> OptionalValue<T> empty() {
55+
return (OptionalValue<T>) EMPTY_VALUE;
56+
}
57+
58+
public McpParameters get(String key) {
59+
if (value instanceof JsonObject jsonObject) {
60+
JsonValue v = jsonObject.get(key);
61+
if (v != null) {
62+
return new McpParameters(v, key);
63+
}
64+
return EMPTY;
65+
}
66+
if (value == JsonValue.NULL) {
67+
return EMPTY;
68+
}
69+
throw new IllegalStateException("Cannot get " + value.getValueType() + " as an object");
70+
}
71+
72+
public OptionalValue<String> asString() {
73+
if (value instanceof JsonString jsonString) {
74+
return OptionalValue.create(MAPPERS, key, jsonString.getString());
75+
}
76+
if (value == JsonValue.NULL) {
77+
return empty();
78+
}
79+
throw new IllegalStateException("Cannot get " + value.getValueType() + " as a string");
80+
}
81+
82+
public OptionalValue<Integer> asInt() {
83+
if (value instanceof JsonNumber number) {
84+
return OptionalValue.create(MAPPERS, key, number.intValue());
85+
}
86+
if (value == JsonValue.NULL) {
87+
return empty();
88+
}
89+
throw new IllegalStateException("Cannot get " + value.getValueType() + "as an integer");
90+
}
91+
92+
public OptionalValue<Boolean> asBoolean() {
93+
if (value == JsonValue.TRUE) {
94+
return OptionalValue.create(MAPPERS, key, true);
95+
}
96+
if (value == JsonValue.FALSE) {
97+
return OptionalValue.create(MAPPERS, key, false);
98+
}
99+
if (value == JsonValue.NULL) {
100+
return empty();
101+
}
102+
throw new IllegalStateException("Cannot get " + value.getValueType() + "as a boolean");
103+
}
104+
105+
public OptionalValue<List<McpParameters>> asList() {
106+
if (value instanceof JsonArray array) {
107+
List<McpParameters> list = new ArrayList<>();
108+
int i = 0;
109+
for (JsonValue value : array) {
110+
list.add(new McpParameters(value, key + "-" + i++));
111+
}
112+
return OptionalValue.create(MAPPERS, key, list);
113+
}
114+
if (value == JsonValue.NULL) {
115+
return empty();
116+
}
117+
throw new IllegalStateException("Cannot get " + value.getValueType() + "as a list");
118+
}
119+
120+
public <T> OptionalValue<T> as(Function<McpParameters, T> function) {
121+
if (value == JsonValue.NULL) {
122+
return empty();
123+
}
124+
return OptionalValue.create(MAPPERS, key, function.apply(this));
125+
}
126+
127+
public <T> OptionalValue<T> as(Class<T> clazz) {
128+
if (value == JsonValue.NULL) {
129+
return empty();
130+
}
131+
return OptionalValue.create(MAPPERS, key, clazz);
132+
}
29133

30-
McpParameters(JsonObject root) {
31-
this.root = root;
134+
public <T> OptionalValue<T> as(GenericType<T> type) {
135+
if (value == JsonValue.NULL) {
136+
return empty();
137+
}
138+
return OptionalValue.create(MAPPERS, key, type);
32139
}
33140

34-
public Optional<JsonValue> jsonValue(String name) {
35-
return Optional.ofNullable(root.get(name));
141+
private static final class EmptyValue implements OptionalValue<Object> {
142+
143+
@SuppressWarnings("unchecked")
144+
@Override
145+
public <N> OptionalValue<N> as(Class<N> type) {
146+
return (OptionalValue<N>) this;
147+
}
148+
149+
@SuppressWarnings("unchecked")
150+
@Override
151+
public <N> OptionalValue<N> as(GenericType<N> type) {
152+
return (OptionalValue<N>) this;
153+
}
154+
155+
@SuppressWarnings("unchecked")
156+
@Override
157+
public <N> OptionalValue<N> as(Function<? super Object, ? extends N> mapper) {
158+
return (OptionalValue<N>) this;
159+
}
160+
161+
@Override
162+
public Optional<Object> asOptional() throws MapperException {
163+
return Optional.empty();
164+
}
165+
166+
@Override
167+
public String name() {
168+
return "empty";
169+
}
170+
171+
@Override
172+
public Object get() {
173+
throw new NoSuchElementException();
174+
}
36175
}
37176

38177
}

integrations/mcp/server/src/main/java/io/helidon/integrations/mcp/server/ResourceListContent.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import jakarta.json.JsonArrayBuilder;
2626
import jakarta.json.JsonObjectBuilder;
2727

28-
public class ResourceListContent implements ResourceContent {
28+
class ResourceListContent implements ResourceContent {
2929
List<ResourceContent> resources = new LinkedList<>();
3030

3131
public ResourceListContent(ResourceContent content) {
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
17+
package io.helidon.integrations.mcp.server;
18+
19+
import java.util.List;
20+
21+
import io.helidon.common.mapper.OptionalValue;
22+
23+
import jakarta.json.Json;
24+
import jakarta.json.JsonValue;
25+
import org.junit.jupiter.api.Test;
26+
27+
import static org.hamcrest.MatcherAssert.assertThat;
28+
import static org.hamcrest.Matchers.is;
29+
30+
class McpParametersTest {
31+
private static final String KEY = "key";
32+
33+
@Test
34+
void testSimpleString() {
35+
JsonValue object = Json.createObjectBuilder()
36+
.add("foo", "bar")
37+
.build();
38+
39+
McpParameters parameters = new McpParameters(object, KEY);
40+
String foo = parameters.get("foo").asString().orElse(null);
41+
42+
assertThat(foo, is("bar"));
43+
}
44+
45+
@Test
46+
void testSimpleBoolean() {
47+
JsonValue object = Json.createObjectBuilder()
48+
.add("foo", true)
49+
.build();
50+
51+
McpParameters parameters = new McpParameters(object, KEY);
52+
Boolean foo = parameters.get("foo").asBoolean().orElse(null);
53+
54+
assertThat(foo, is(true));
55+
}
56+
57+
@Test
58+
void testSimpleInteger() {
59+
JsonValue object = Json.createObjectBuilder()
60+
.add("foo", 1)
61+
.build();
62+
63+
McpParameters parameters = new McpParameters(object, KEY);
64+
int foo = parameters.get("foo").asInt().orElse(null);
65+
66+
assertThat(foo, is(1));
67+
}
68+
69+
@Test
70+
void testSimpleList() {
71+
JsonValue object = Json.createObjectBuilder()
72+
.add("foo", Json.createArrayBuilder()
73+
.add("foo1")
74+
.add("foo2"))
75+
.build();
76+
77+
McpParameters parameters = new McpParameters(object, KEY);
78+
List<String> foo = parameters.get("foo")
79+
.asList()
80+
.get()
81+
.stream()
82+
.map(McpParameters::asString)
83+
.map(OptionalValue::get)
84+
.toList();
85+
86+
assertThat(foo, is(List.of("foo1", "foo2")));
87+
}
88+
}

0 commit comments

Comments
 (0)