> tagFilters(@NonNull String name) {
* A single flat list is read as one entry, since a model given an example of nested arrays
* will sometimes send just the inner one.
*
+ *
Values are kept verbatim, unlike {@link #texts}: tags are positional, so dropping an empty
+ * relay hint in {@code ["p", pk, "", "mention"]} would move the marker into its place.
+ *
* @param name the argument to read
* @return each inner list's values
*/
@@ -139,14 +142,18 @@ public List> nestedTexts(@NonNull String name) {
return List.of();
}
if (outer.stream().noneMatch(List.class::isInstance)) {
- return List.of(texts(name));
+ return List.of(verbatim(outer));
}
return outer.stream()
.filter(List.class::isInstance)
- .map(inner -> new ToolArguments(Map.of("v", inner)).texts("v"))
+ .map(inner -> verbatim((List>) inner))
.toList();
}
+ private static List verbatim(List> values) {
+ return values.stream().map(String::valueOf).toList();
+ }
+
private int parseInteger(String name, String text) {
try {
return (int) Double.parseDouble(text);
diff --git a/nostr-java-mcp/src/test/java/nostr/mcp/argument/ToolArgumentsTest.java b/nostr-java-mcp/src/test/java/nostr/mcp/argument/ToolArgumentsTest.java
new file mode 100644
index 00000000..c44f7321
--- /dev/null
+++ b/nostr-java-mcp/src/test/java/nostr/mcp/argument/ToolArgumentsTest.java
@@ -0,0 +1,39 @@
+package nostr.mcp.argument;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/** Verifies tool arguments are read without changing what the caller meant. */
+class ToolArgumentsTest {
+
+ // Verifies an empty tag value keeps its position, since NIP-01 tags are positional and
+ // dropping the empty relay hint in ["p", pk, "", "mention"] moves the marker into its place.
+ @Test
+ void nestedTextsKeepEmptyValuesInPlace() {
+ var arguments = new ToolArguments(Map.of("tags", List.of(List.of("p", "abc", "", "mention"))));
+
+ assertEquals(List.of(List.of("p", "abc", "", "mention")), arguments.nestedTexts("tags"));
+ }
+
+ // Verifies tag values are not trimmed, since a tag can carry quoted text that must go out
+ // byte-for-byte.
+ @Test
+ void nestedTextsDoNotTrim() {
+ var arguments = new ToolArguments(Map.of("tags", List.of(List.of("comment", " spaced \n"))));
+
+ assertEquals(List.of(List.of("comment", " spaced \n")), arguments.nestedTexts("tags"));
+ }
+
+ // Verifies a single flat tag is read the same way, since that path is taken when a model sends
+ // only the inner array.
+ @Test
+ void aFlatTagKeepsEmptyValuesInPlace() {
+ var arguments = new ToolArguments(Map.of("tags", List.of("p", "abc", "", "mention")));
+
+ assertEquals(List.of(List.of("p", "abc", "", "mention")), arguments.nestedTexts("tags"));
+ }
+}
diff --git a/pom.xml b/pom.xml
index 4101b78b..e7e31593 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
xyz.tcheeric
nostr-java
- 2.4.0
+ 2.4.1
pom
nostr-java