This is an automated email from the ASF dual-hosted git repository. jamesbognar pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/juneau.git
commit 10cd81bea83eea86324bfd3924feeb5e33a14423 Author: James Bognar <[email protected]> AuthorDate: Tue Jul 28 16:44:49 2026 -0400 feat: add revision-neutral MCP tool/prompt/resource spec and outcome types Co-authored-by: Cursor <[email protected]> --- .../juneau/rest/server/mcp/McpPromptArgument.java | 90 ++++++++++++ .../juneau/rest/server/mcp/McpPromptMessage.java | 69 ++++++++++ .../juneau/rest/server/mcp/McpPromptOutcome.java | 72 ++++++++++ .../juneau/rest/server/mcp/McpPromptSpec.java | 93 +++++++++++++ .../juneau/rest/server/mcp/McpResourceOutcome.java | 51 +++++++ .../juneau/rest/server/mcp/McpResourceSpec.java | 153 +++++++++++++++++++++ .../juneau/rest/server/mcp/McpToolOutcome.java | 98 +++++++++++++ .../apache/juneau/rest/server/mcp/McpToolSpec.java | 92 +++++++++++++ .../rest/server/mcp/McpNeutralModel_Test.java | 117 ++++++++++++++++ 9 files changed, 835 insertions(+) diff --git a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptArgument.java b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptArgument.java new file mode 100644 index 0000000000..d897c5b027 --- /dev/null +++ b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptArgument.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.juneau.rest.server.mcp; + +/** + * Revision-neutral declared argument of a prompt. + * + * <p> + * Supersedes the wire-level {@code PromptArgument} bean. + */ +public class McpPromptArgument { + + private String name; + private String description; + private Boolean required; + + /** + * The argument name. + * + * @return The name, or <jk>null</jk> if not set. + */ + public String getName() { + return name; + } + + /** + * Sets the argument name. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpPromptArgument setName(String value) { + name = value; + return this; + } + + /** + * The human-readable argument description. + * + * @return The description, or <jk>null</jk> if not set. + */ + public String getDescription() { + return description; + } + + /** + * Sets the argument description. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpPromptArgument setDescription(String value) { + description = value; + return this; + } + + /** + * Whether the argument is required. + * + * @return The flag, or <jk>null</jk> if not set. + */ + public Boolean getRequired() { + return required; + } + + /** + * Sets whether the argument is required. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpPromptArgument setRequired(Boolean value) { + required = value; + return this; + } +} diff --git a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptMessage.java b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptMessage.java new file mode 100644 index 0000000000..ea4e8f970e --- /dev/null +++ b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptMessage.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.juneau.rest.server.mcp; + +/** + * Revision-neutral single message within a prompt result. + * + * <p> + * Supersedes the wire-level {@code PromptMessage} bean. + */ +public class McpPromptMessage { + + private McpRole role; + private McpContentBlock content; + + /** + * The message's role. + * + * @return The role, or <jk>null</jk> if not set. + */ + public McpRole getRole() { + return role; + } + + /** + * Sets the message's role. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpPromptMessage setRole(McpRole value) { + role = value; + return this; + } + + /** + * The message's content. + * + * @return The content, or <jk>null</jk> if not set. + */ + public McpContentBlock getContent() { + return content; + } + + /** + * Sets the message's content. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpPromptMessage setContent(McpContentBlock value) { + content = value; + return this; + } +} diff --git a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptOutcome.java b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptOutcome.java new file mode 100644 index 0000000000..ca2afabe07 --- /dev/null +++ b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptOutcome.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.juneau.rest.server.mcp; + +import java.util.*; + +/** + * Revision-neutral result of a {@code prompts/get} invocation. + * + * <p> + * Supersedes the wire-level {@code GetPromptResult} bean. + */ +public class McpPromptOutcome { + + private String description; + private List<McpPromptMessage> messages; + + /** + * The human-readable outcome description. + * + * @return The description, or <jk>null</jk> if not set. + */ + public String getDescription() { + return description; + } + + /** + * Sets the outcome description. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpPromptOutcome setDescription(String value) { + description = value; + return this; + } + + /** + * The prompt's rendered messages. + * + * @return The messages, or <jk>null</jk> if not set. A <jk>null</jk> value is distinct from an + * empty list: it means the wire property is omitted entirely. + */ + public List<McpPromptMessage> getMessages() { + return messages; + } + + /** + * Sets the prompt's rendered messages. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpPromptOutcome setMessages(List<McpPromptMessage> value) { + messages = value; + return this; + } +} diff --git a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptSpec.java b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptSpec.java new file mode 100644 index 0000000000..d689b37642 --- /dev/null +++ b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptSpec.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.juneau.rest.server.mcp; + +import java.util.*; + +/** + * Revision-neutral prompt descriptor returned from {@code prompts/list}. + * + * <p> + * Supersedes the wire-level {@code Prompt} bean. + */ +public class McpPromptSpec { + + private String name; + private String description; + private List<McpPromptArgument> arguments; + + /** + * The prompt name, used to route {@code prompts/get} requests. + * + * @return The name, or <jk>null</jk> if not set. + */ + public String getName() { + return name; + } + + /** + * Sets the prompt name. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpPromptSpec setName(String value) { + name = value; + return this; + } + + /** + * The human-readable prompt description. + * + * @return The description, or <jk>null</jk> if not set. + */ + public String getDescription() { + return description; + } + + /** + * Sets the prompt description. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpPromptSpec setDescription(String value) { + description = value; + return this; + } + + /** + * The prompt's declared arguments. + * + * @return The arguments, or <jk>null</jk> if not set. A <jk>null</jk> value is distinct from an + * empty list: it means the wire property is omitted entirely. + */ + public List<McpPromptArgument> getArguments() { + return arguments; + } + + /** + * Sets the prompt's declared arguments. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpPromptSpec setArguments(List<McpPromptArgument> value) { + arguments = value; + return this; + } +} diff --git a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpResourceOutcome.java b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpResourceOutcome.java new file mode 100644 index 0000000000..7ffcddcf82 --- /dev/null +++ b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpResourceOutcome.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.juneau.rest.server.mcp; + +import java.util.*; + +/** + * Revision-neutral result of a {@code resources/read} invocation. + * + * <p> + * Supersedes the wire-level {@code ReadResourceResult} bean. + */ +public class McpResourceOutcome { + + private List<McpResourceContents> contents; + + /** + * The resource's payload contents. + * + * @return The contents, or <jk>null</jk> if not set. A <jk>null</jk> value is distinct from an + * empty list: it means the wire property is omitted entirely. + */ + public List<McpResourceContents> getContents() { + return contents; + } + + /** + * Sets the resource's payload contents. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpResourceOutcome setContents(List<McpResourceContents> value) { + contents = value; + return this; + } +} diff --git a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpResourceSpec.java b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpResourceSpec.java new file mode 100644 index 0000000000..7c9ef3ac02 --- /dev/null +++ b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpResourceSpec.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.juneau.rest.server.mcp; + +/** + * Revision-neutral resource descriptor returned from {@code resources/list}. + * + * <p> + * Supersedes the wire-level {@code Resource} bean. + */ +public class McpResourceSpec { + + private String uri; + private String name; + private String title; + private String description; + private String mimeType; + private Long size; + + /** + * The resource URI. + * + * @return The URI, or <jk>null</jk> if not set. + */ + public String getUri() { + return uri; + } + + /** + * Sets the resource URI. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpResourceSpec setUri(String value) { + uri = value; + return this; + } + + /** + * The resource name. + * + * @return The name, or <jk>null</jk> if not set. + */ + public String getName() { + return name; + } + + /** + * Sets the resource name. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpResourceSpec setName(String value) { + name = value; + return this; + } + + /** + * The human-readable resource title. + * + * @return The title, or <jk>null</jk> if not set. + */ + public String getTitle() { + return title; + } + + /** + * Sets the resource title. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpResourceSpec setTitle(String value) { + title = value; + return this; + } + + /** + * The human-readable resource description. + * + * @return The description, or <jk>null</jk> if not set. + */ + public String getDescription() { + return description; + } + + /** + * Sets the resource description. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpResourceSpec setDescription(String value) { + description = value; + return this; + } + + /** + * The resource's media type. + * + * @return The media type, or <jk>null</jk> if not set. + */ + public String getMimeType() { + return mimeType; + } + + /** + * Sets the resource's media type. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpResourceSpec setMimeType(String value) { + mimeType = value; + return this; + } + + /** + * The resource's size in bytes. + * + * @return The size, or <jk>null</jk> if not set. + */ + public Long getSize() { + return size; + } + + /** + * Sets the resource's size in bytes. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpResourceSpec setSize(Long value) { + size = value; + return this; + } +} diff --git a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpToolOutcome.java b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpToolOutcome.java new file mode 100644 index 0000000000..923dfadc11 --- /dev/null +++ b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpToolOutcome.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.juneau.rest.server.mcp; + +import java.util.*; + +/** + * Revision-neutral result of a {@code tools/call} invocation. + * + * <p> + * Supersedes the wire-level {@code CallToolResult} bean. Deliberately absent: + * {@code structuredContent} and {@code cachePolicy}, both specific to later MCP revisions. + * + * <p> + * Note the field is named {@code error}, while the {@code 2025-06-18} wire property is + * {@code isError}; the rename happens at the adapter boundary and is intentional. Do not + * "fix" this field name to match the wire. + */ +public class McpToolOutcome { + + private List<McpContentBlock> content; + private Boolean error; + + /** + * Creates an outcome carrying a single text block. + * + * @param value The text. Can be <jk>null</jk>. + * @return A new outcome. Never <jk>null</jk>. + */ + public static McpToolOutcome text(String value) { + return new McpToolOutcome().setContent(List.of(McpContentBlock.text(value))); + } + + /** + * Creates an outcome carrying the supplied blocks. + * + * @param values The content blocks. Must not be <jk>null</jk>. + * @return A new outcome. Never <jk>null</jk>. + */ + public static McpToolOutcome of(McpContentBlock... values) { + return new McpToolOutcome().setContent(List.of(values)); + } + + /** + * The content blocks returned by the tool. + * + * @return The blocks, or <jk>null</jk> if not set. A <jk>null</jk> value is distinct from an empty + * list: it means the wire property is omitted entirely. + */ + public List<McpContentBlock> getContent() { + return content; + } + + /** + * Sets the content blocks. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpToolOutcome setContent(List<McpContentBlock> value) { + content = value; + return this; + } + + /** + * Whether the tool call represents an error result. + * + * @return The flag, or <jk>null</jk> if not set. + */ + public Boolean getError() { + return error; + } + + /** + * Sets the error flag. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpToolOutcome setError(Boolean value) { + error = value; + return this; + } +} diff --git a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpToolSpec.java b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpToolSpec.java new file mode 100644 index 0000000000..75df032c50 --- /dev/null +++ b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpToolSpec.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.juneau.rest.server.mcp; + +/** + * Revision-neutral tool descriptor returned from {@code tools/list}. + * + * <p> + * Supersedes the wire-level {@code Tool} bean. Deliberately absent: {@code outputSchema} and + * {@code title}, both of which are specific to later MCP revisions and belong on the revision-typed + * side of the adapter boundary. + */ +public class McpToolSpec { + + private String name; + private String description; + private McpSchema inputSchema; + + /** + * The tool name, used to route {@code tools/call} requests. + * + * @return The name, or <jk>null</jk> if not set. + */ + public String getName() { + return name; + } + + /** + * Sets the tool name. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpToolSpec setName(String value) { + name = value; + return this; + } + + /** + * The human-readable tool description. + * + * @return The description, or <jk>null</jk> if not set. + */ + public String getDescription() { + return description; + } + + /** + * Sets the tool description. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpToolSpec setDescription(String value) { + description = value; + return this; + } + + /** + * The tool's argument schema. + * + * @return The schema, or <jk>null</jk> if not set. + */ + public McpSchema getInputSchema() { + return inputSchema; + } + + /** + * Sets the tool's argument schema. + * + * @param value The new value. Can be <jk>null</jk> to unset the property. + * @return This object. + */ + public McpToolSpec setInputSchema(McpSchema value) { + inputSchema = value; + return this; + } +} diff --git a/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpNeutralModel_Test.java b/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpNeutralModel_Test.java new file mode 100644 index 0000000000..2944a8a0eb --- /dev/null +++ b/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpNeutralModel_Test.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.juneau.rest.server.mcp; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.*; + +import org.apache.juneau.marshall.collections.*; +import org.junit.jupiter.api.*; + +/** + * Coverage for the revision-neutral tool / prompt / resource spec and outcome types. + */ +class McpNeutralModel_Test { + + @Test + void a01_toolSpec_fluentSetters() { + var a = McpSchema.of(JsonMap.of("type", "object")); + var b = new McpToolSpec().setName("echo").setDescription("d").setInputSchema(a); + assertEquals("echo", b.getName()); + assertEquals("d", b.getDescription()); + assertSame(a, b.getInputSchema()); + } + + @Test + void a02_toolSpec_defaultsAreNull() { + var a = new McpToolSpec(); + assertNull(a.getName()); + assertNull(a.getDescription()); + assertNull(a.getInputSchema()); + } + + @Test + void b01_toolOutcome_textFactory() { + var a = McpToolOutcome.text("hi"); + assertEquals(1, a.getContent().size()); + assertEquals("hi", a.getContent().get(0).text()); + assertNull(a.getError()); + } + + @Test + void b02_toolOutcome_ofFactory() { + var a = McpToolOutcome.of(McpContentBlock.text("a"), McpContentBlock.image("AAA=", "image/png")); + assertEquals(2, a.getContent().size()); + } + + @Test + void b03_toolOutcome_defaultsAreNull_notEmpty() { + var a = new McpToolOutcome(); + assertNull(a.getContent(), "null content must stay null: an empty list would change the wire bytes"); + assertNull(a.getError()); + } + + @Test + void b04_toolOutcome_errorFlag() { + assertEquals(Boolean.TRUE, new McpToolOutcome().setError(true).getError()); + } + + @Test + void c01_promptFamily_fluentSetters() { + var a = new McpPromptArgument().setName("who").setDescription("d").setRequired(true); + var b = new McpPromptSpec().setName("greet").setDescription("pd").setArguments(List.of(a)); + assertEquals("greet", b.getName()); + assertEquals(1, b.getArguments().size()); + assertEquals("who", b.getArguments().get(0).getName()); + assertEquals(Boolean.TRUE, a.getRequired()); + } + + @Test + void c02_promptOutcome_fluentSetters() { + var a = new McpPromptMessage().setRole(McpRole.USER).setContent(McpContentBlock.text("hi")); + var b = new McpPromptOutcome().setDescription("d").setMessages(List.of(a)); + assertEquals("d", b.getDescription()); + assertEquals(McpRole.USER, b.getMessages().get(0).getRole()); + } + + @Test + void c03_promptOutcome_defaultsAreNull() { + var a = new McpPromptOutcome(); + assertNull(a.getDescription()); + assertNull(a.getMessages()); + } + + @Test + void d01_resourceSpec_hasAllSixFields() { + var a = new McpResourceSpec().setUri("file:///a").setName("n").setTitle("t") + .setDescription("d").setMimeType("text/plain").setSize(99L); + assertEquals("file:///a", a.getUri()); + assertEquals("n", a.getName()); + assertEquals("t", a.getTitle()); + assertEquals("d", a.getDescription()); + assertEquals("text/plain", a.getMimeType()); + assertEquals(Long.valueOf(99L), a.getSize()); + } + + @Test + void d02_resourceOutcome_fluentSetters() { + var a = new McpResourceOutcome().setContents(List.of(McpResourceContents.text("u", null, "x"))); + assertEquals(1, a.getContents().size()); + assertNull(new McpResourceOutcome().getContents()); + } +}
