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());
+       }
+}

Reply via email to