diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f8dc448..b479c634 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ The format is inspired by Keep a Changelog, and this project adheres to semantic ## [Unreleased] +## [2.4.1] - 2026-09-25 + +### Fixed +- `nostr_publish_event` dropped empty tag values and trimmed the rest. Tags are positional, so + `["p", pk, "", "mention"]` went out as `["p", pk, "mention"]`, putting the marker in the + relay-hint slot, and a signed event could differ from the one previewed for approval. Tag + values are now passed through verbatim. + ## [2.4.0] - 2026-09-21 ### Added diff --git a/docs/howto/multi-relay-publishing.md b/docs/howto/multi-relay-publishing.md index c43303a3..b30e2e2f 100644 --- a/docs/howto/multi-relay-publishing.md +++ b/docs/howto/multi-relay-publishing.md @@ -10,7 +10,7 @@ send a private direct message. xyz.tcheeric nostr-java-api - 2.4.0 + 2.4.1 ``` diff --git a/nostr-java-api/pom.xml b/nostr-java-api/pom.xml index 79233679..9f780eb6 100644 --- a/nostr-java-api/pom.xml +++ b/nostr-java-api/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric nostr-java - 2.4.0 + 2.4.1 ../pom.xml diff --git a/nostr-java-client/pom.xml b/nostr-java-client/pom.xml index acdb8ce5..70729801 100644 --- a/nostr-java-client/pom.xml +++ b/nostr-java-client/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric nostr-java - 2.4.0 + 2.4.1 ../pom.xml diff --git a/nostr-java-core/pom.xml b/nostr-java-core/pom.xml index 7ba8bb6b..176578e4 100644 --- a/nostr-java-core/pom.xml +++ b/nostr-java-core/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric nostr-java - 2.4.0 + 2.4.1 ../pom.xml diff --git a/nostr-java-event/pom.xml b/nostr-java-event/pom.xml index af4ca2bb..5dbe5df2 100644 --- a/nostr-java-event/pom.xml +++ b/nostr-java-event/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric nostr-java - 2.4.0 + 2.4.1 ../pom.xml diff --git a/nostr-java-identity/pom.xml b/nostr-java-identity/pom.xml index 7c257d5e..b0ae7b48 100644 --- a/nostr-java-identity/pom.xml +++ b/nostr-java-identity/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric nostr-java - 2.4.0 + 2.4.1 ../pom.xml diff --git a/nostr-java-mcp/pom.xml b/nostr-java-mcp/pom.xml index 7d40545b..a4621c9f 100644 --- a/nostr-java-mcp/pom.xml +++ b/nostr-java-mcp/pom.xml @@ -4,7 +4,7 @@ xyz.tcheeric nostr-java - 2.4.0 + 2.4.1 ../pom.xml diff --git a/nostr-java-mcp/src/main/java/nostr/mcp/argument/ToolArguments.java b/nostr-java-mcp/src/main/java/nostr/mcp/argument/ToolArguments.java index d8d143b5..0989781c 100644 --- a/nostr-java-mcp/src/main/java/nostr/mcp/argument/ToolArguments.java +++ b/nostr-java-mcp/src/main/java/nostr/mcp/argument/ToolArguments.java @@ -130,6 +130,9 @@ public Map> 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