This is an automated email from the ASF dual-hosted git repository.
epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-mcp.git
The following commit(s) were added to refs/heads/main by this push:
new a84033b feat(jspecify): complete the @NullMarked opt-in for test
packages (#179)
a84033b is described below
commit a84033b00cba6d0e8a9420bb4033499f48a75734
Author: Aditya Parikh <[email protected]>
AuthorDate: Tue Aug 18 18:02:55 2026 -0400
feat(jspecify): complete the @NullMarked opt-in for test packages (#179)
* chore(jspecify): roll @NullMarked out to all sub-packages
Background: prior to this change only the root package
`org.apache.solr.mcp.server` was @NullMarked via package-info.java. All
sub-packages — `.collection`, `.config`, `.indexing`,
`.indexing.documentcreator`, `.metadata`, `.search`, `.security`,
`.util`, plus the test-only `.containerization` and `.observability` —
were running unannotated, so NullAway (`OnlyNullMarked=true`) was
silently skipping them.
Changes:
1. Adds `package-info.java` with `@NullMarked` to every sub-package (10
files: 8 main + 2 test-only).
2. Annotates production code with `@Nullable` where return types,
parameters, or record components legitimately admit null. The
non-obvious nullable surfaces:
- `CollectionUtils.getLong/getFloat/getInteger` return null on missing
or unparseable keys.
- `CollectionService` cache/handler metrics methods return null when
the underlying Solr endpoint is unavailable (graceful degradation
by design).
- `SolrMetrics.cacheStats` / `handlerStats`, `CacheStats.*`,
`CacheInfo.*`, `HandlerStats.*`, `HandlerInfo.*`,
`SolrHealthStatus.errorMessage` / `responseTime` / `totalDocuments`
/ `solrVersion` / `status` — all documented as nullable, now
annotated.
- `IndexStats.numDocs` / `segmentCount` — Luke can return either
field as null.
- Optional `@McpToolParam(required = false)` parameters on
`CollectionService.createCollection` and `SearchService.search`.
- `JsonResponseParser.convertValue` — JSON null inputs.
3. `HttpSecurityConfiguration` Spring `@Value` fields initialised with
defaults (`""` for `issuerUrl` matching the property default,
`List.of()` for `allowedOrigins`) so the analyser can see they are
always non-null after construction.
4. NullAway is explicitly disabled on `compileTestJava` (with an
explanatory comment). The @NullMarked rollout exposes ~30 test sites
that unbox or dereference values now declared @Nullable in
production (Map.get returns, metric fields that are null when an
endpoint is unavailable, etc.). Each can be tightened by extracting
to a local + `assertNotNull`, but the volume makes it a deliberate
follow-up. Production is fully enforced.
Build verified green (compile + unit + integration).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Signed-off-by: adityamparikh <[email protected]>
* refactor(jspecify): drop unnecessary @Nullable on extractCacheStats
The method unconditionally returns `new CacheStats(...)`; null-wrapping
happens one level up in `fetchCacheMetrics` via the `isCacheStatsEmpty`
check. The @Nullable was misleading — readers expect a method marked
@Nullable to actually have a `return null` path.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Signed-off-by: adityamparikh <[email protected]>
* chore(jspecify): complete the @NullMarked opt-in for test packages
@NullMarked on a package-info.java applies to that package only - Java
packages are not hierarchical for annotation purposes. Combined with
NullAway:OnlyNullMarked=true, that means an unmarked package is exempt from
nullness checking even though NullAway is configured at error level.
#133 marked every main package and two test packages (containerization,
observability). This adds the six that were still unmarked, so every main
and test package is now null-marked and the opt-in is uniform.
NullAway remains disabled on compileTestJava, so this is declarative for
now. Enabling it surfaces 48 test sites (the note in build.gradle.kts
estimates ~30); that is left as a follow-up rather than folded in here.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Signed-off-by: Aditya Parikh <[email protected]>
---------
Signed-off-by: adityamparikh <[email protected]>
Signed-off-by: Aditya Parikh <[email protected]>
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Co-authored-by: Eric Pugh <[email protected]>
---
build.gradle.kts | 11 +++++
.../mcp/server/collection/CollectionService.java | 28 +++++++------
.../mcp/server/collection/CollectionUtils.java | 7 ++--
.../apache/solr/mcp/server/collection/Dtos.java | 49 +++++++++++-----------
.../solr/mcp/server/collection/package-info.java | 20 +++++++++
.../solr/mcp/server/config/JsonResponseParser.java | 3 +-
.../solr/mcp/server/config/package-info.java | 20 +++++++++
.../indexing/documentcreator/package-info.java | 20 +++++++++
.../solr/mcp/server/indexing/package-info.java | 20 +++++++++
.../solr/mcp/server/schema/package-info.java | 20 +++++++++
.../solr/mcp/server/search/SearchService.java | 15 ++++---
.../solr/mcp/server/search/package-info.java | 20 +++++++++
.../server/security/HttpSecurityConfiguration.java | 4 +-
.../solr/mcp/server/security/package-info.java | 20 +++++++++
.../apache/solr/mcp/server/util/package-info.java | 20 +++++++++
.../solr/mcp/server/collection/package-info.java | 20 +++++++++
.../solr/mcp/server/config/package-info.java | 20 +++++++++
.../mcp/server/containerization/package-info.java | 20 +++++++++
.../solr/mcp/server/indexing/package-info.java | 20 +++++++++
.../mcp/server/observability/package-info.java | 20 +++++++++
.../org/apache/solr/mcp/server/package-info.java | 20 +++++++++
.../solr/mcp/server/schema/package-info.java | 20 +++++++++
.../solr/mcp/server/search/package-info.java | 20 +++++++++
23 files changed, 389 insertions(+), 48 deletions(-)
diff --git a/build.gradle.kts b/build.gradle.kts
index f1df742..6a846c9 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -289,10 +289,21 @@ tasks.withType<JavaCompile>().configureEach {
options.errorprone {
disableAllChecks.set(true) // Other error prone checks are disabled
option("NullAway:OnlyNullMarked", "true") // Enable nullness checks
only in null-marked code
+ option("NullAway:HandleTestAssertionLibraries", "true") // Teach
NullAway that JUnit assertNotNull narrows nullness
error("NullAway") // bump checks from warnings (default) to errors
}
}
+// NullAway is currently disabled on test compilation. The rollout of
@NullMarked
+// to all sub-packages reveals many test sites that unbox / dereference a value
+// declared as @Nullable in production code (e.g. metrics fields that are null
+// when a Solr endpoint is unavailable). Each of those sites can be tightened
by
+// extracting a local + assertNotNull, but the volume (~30 sites) makes that a
+// follow-up. Production code is fully enforced.
+tasks.named<JavaCompile>("compileTestJava") {
+ options.errorprone.disable("NullAway")
+}
+
tasks.build {
dependsOn(tasks.spotlessApply)
}
diff --git
a/src/main/java/org/apache/solr/mcp/server/collection/CollectionService.java
b/src/main/java/org/apache/solr/mcp/server/collection/CollectionService.java
index 48c69b4..011d278 100644
--- a/src/main/java/org/apache/solr/mcp/server/collection/CollectionService.java
+++ b/src/main/java/org/apache/solr/mcp/server/collection/CollectionService.java
@@ -44,6 +44,7 @@ import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.mcp.server.config.SolrConfigurationProperties;
import org.apache.solr.mcp.server.util.PromptNames;
+import org.jspecify.annotations.Nullable;
import org.springaicommunity.mcp.annotation.McpArg;
import org.springaicommunity.mcp.annotation.McpComplete;
import org.springaicommunity.mcp.annotation.McpPrompt;
@@ -668,7 +669,7 @@ public class CollectionService {
* @see #extractCacheStats(NamedList)
* @see #isCacheStatsEmpty(CacheStats)
*/
- public CacheStats getCacheMetrics(String collection) throws
SolrServerException, IOException {
+ public @Nullable CacheStats getCacheMetrics(String collection) throws
SolrServerException, IOException {
String actualCollection = extractCollectionName(collection);
if (!validateCollectionExists(actualCollection)) {
@@ -682,7 +683,7 @@ public class CollectionService {
* Internal cache metrics fetch that assumes the collection has already
been
* validated and the name has been extracted from any shard identifier.
*/
- private CacheStats fetchCacheMetrics(String collection) {
+ private @Nullable CacheStats fetchCacheMetrics(String collection) {
try {
NamedList<Object> coreMetrics =
fetchMetrics(collection, CACHE_METRIC_PREFIX);
if (coreMetrics == null) {
@@ -708,7 +709,7 @@ public class CollectionService {
* the cache statistics to evaluate
* @return true if the stats are null or all cache types are null
*/
- private boolean isCacheStatsEmpty(CacheStats stats) {
+ private boolean isCacheStatsEmpty(@Nullable CacheStats stats) {
return stats == null
|| (stats.queryResultCache() == null &&
stats.documentCache() == null && stats.filterCache() == null);
}
@@ -727,7 +728,7 @@ public class CollectionService {
}
@SuppressWarnings("unchecked")
- private CacheInfo extractSingleCacheInfo(NamedList<Object> coreMetrics,
String key) {
+ private @Nullable CacheInfo extractSingleCacheInfo(NamedList<Object>
coreMetrics, String key) {
NamedList<Object> cache = (NamedList<Object>)
coreMetrics.get(key);
if (cache == null) {
return null;
@@ -784,7 +785,7 @@ public class CollectionService {
* @see #fetchFlatHandlerInfo(String, String, String)
* @see #isHandlerStatsEmpty(HandlerStats)
*/
- public HandlerStats getHandlerMetrics(String collection) throws
SolrServerException, IOException {
+ public @Nullable HandlerStats getHandlerMetrics(String collection)
throws SolrServerException, IOException {
String actualCollection = extractCollectionName(collection);
if (!validateCollectionExists(actualCollection)) {
@@ -798,7 +799,7 @@ public class CollectionService {
* Internal handler metrics fetch that assumes the collection has
already been
* validated and the name has been extracted from any shard identifier.
*/
- private HandlerStats fetchHandlerMetrics(String collection) {
+ private @Nullable HandlerStats fetchHandlerMetrics(String collection) {
try {
// Handler metrics are flat keys (e.g.
QUERY./select.requests) so we
// fetch each handler prefix separately and reconstruct
HandlerInfo
@@ -840,7 +841,8 @@ public class CollectionService {
* @return the core-level metrics NamedList, or null if unavailable
*/
@SuppressWarnings("unchecked")
- private NamedList<Object> fetchMetrics(String collection, String
prefix) throws SolrServerException, IOException {
+ private @Nullable NamedList<Object> fetchMetrics(String collection,
String prefix)
+ throws SolrServerException, IOException {
ModifiableSolrParams params = new ModifiableSolrParams();
params.set(GROUP_PARAM, CORE_GROUP);
params.set(PREFIX_PARAM, prefix);
@@ -885,7 +887,7 @@ public class CollectionService {
* {@code QUERY./select.})
* @return HandlerInfo with stats, or null if unavailable
*/
- private HandlerInfo fetchFlatHandlerInfo(String collection, String
metricPrefix, String keyPrefix)
+ private @Nullable HandlerInfo fetchFlatHandlerInfo(String collection,
String metricPrefix, String keyPrefix)
throws SolrServerException, IOException {
NamedList<Object> coreMetrics = fetchMetrics(collection,
metricPrefix);
if (coreMetrics == null) {
@@ -905,7 +907,7 @@ public class CollectionService {
* @return HandlerInfo reconstructed from flat keys, or null if no
requests key
* found
*/
- private HandlerInfo extractFlatHandlerInfo(NamedList<Object>
coreMetrics, String keyPrefix) {
+ private @Nullable HandlerInfo extractFlatHandlerInfo(NamedList<Object>
coreMetrics, String keyPrefix) {
Long requests = getLong(coreMetrics, keyPrefix +
REQUESTS_FIELD);
if (requests == null) {
return null;
@@ -1122,13 +1124,15 @@ public class CollectionService {
+ "configSet defaults to _default,
numShards and replicationFactor default to 1.")
public CollectionCreationResult createCollection(
@McpToolParam(description = "Name of the collection to
create") String name,
- @McpToolParam(description = "Configset name. Defaults
to _default.", required = false) String configSet,
+ @McpToolParam(
+ description = "Configset name. Defaults
to _default.",
+ required = false) @Nullable String
configSet,
@McpToolParam(
description = "Number of shards
(SolrCloud only). Defaults to 1.",
- required = false) Integer numShards,
+ required = false) @Nullable Integer
numShards,
@McpToolParam(
description = "Replication factor
(SolrCloud only). Defaults to 1.",
- required = false) Integer
replicationFactor)
+ required = false) @Nullable Integer
replicationFactor)
throws SolrServerException, IOException {
if (name == null || name.isBlank()) {
diff --git
a/src/main/java/org/apache/solr/mcp/server/collection/CollectionUtils.java
b/src/main/java/org/apache/solr/mcp/server/collection/CollectionUtils.java
index f64e878..a1ece62 100644
--- a/src/main/java/org/apache/solr/mcp/server/collection/CollectionUtils.java
+++ b/src/main/java/org/apache/solr/mcp/server/collection/CollectionUtils.java
@@ -17,6 +17,7 @@
package org.apache.solr.mcp.server.collection;
import org.apache.solr.common.util.NamedList;
+import org.jspecify.annotations.Nullable;
/**
* Utility class providing type-safe helper methods for extracting values from
@@ -113,7 +114,7 @@ public class CollectionUtils {
* @see Number#longValue()
* @see Long#parseLong(String)
*/
- public static Long getLong(NamedList<Object> response, String key) {
+ public static @Nullable Long getLong(NamedList<Object> response, String
key) {
Object value = response.get(key);
if (value == null)
return null;
@@ -172,7 +173,7 @@ public class CollectionUtils {
* @return the Float value if found and convertible, {@code null}
otherwise
* @see Number#floatValue()
*/
- public static Float getFloat(NamedList<Object> stats, String key) {
+ public static @Nullable Float getFloat(NamedList<Object> stats, String
key) {
Object value = stats.get(key);
if (value == null)
return null;
@@ -252,7 +253,7 @@ public class CollectionUtils {
* @see Integer#parseInt(String)
* @see #getLong(NamedList, String)
*/
- public static Integer getInteger(NamedList<Object> response, String
key) {
+ public static @Nullable Integer getInteger(NamedList<Object> response,
String key) {
Object value = response.get(key);
if (value == null)
return null;
diff --git a/src/main/java/org/apache/solr/mcp/server/collection/Dtos.java
b/src/main/java/org/apache/solr/mcp/server/collection/Dtos.java
index e5ab3f2..2df8568 100644
--- a/src/main/java/org/apache/solr/mcp/server/collection/Dtos.java
+++ b/src/main/java/org/apache/solr/mcp/server/collection/Dtos.java
@@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.time.Instant;
+import org.jspecify.annotations.Nullable;
/**
* Data Transfer Objects (DTOs) for the Apache Solr MCP Server.
@@ -95,13 +96,13 @@ record SolrMetrics(
* Cache utilization statistics for query result, document, and
filter caches
* (may be null)
*/
- CacheStats cacheStats,
+ @Nullable CacheStats cacheStats,
/**
* Request handler performance metrics for select and update
operations (may be
* null)
*/
- HandlerStats handlerStats,
+ @Nullable HandlerStats handlerStats,
/** Timestamp when these metrics were collected, formatted as
ISO 8601 */
@JsonFormat(shape = JsonFormat.Shape.STRING) Instant timestamp)
{
@@ -139,13 +140,13 @@ record SolrMetrics(
@JsonInclude(JsonInclude.Include.NON_NULL)
record IndexStats(
/** Total number of documents in the index (excluding deleted
documents) */
- Integer numDocs,
+ @Nullable Integer numDocs,
/**
* Number of Lucene segments in the index (lower numbers
generally indicate
* better performance)
*/
- Integer segmentCount) {
+ @Nullable Integer segmentCount) {
}
/**
@@ -223,13 +224,13 @@ record QueryStats(
@JsonInclude(JsonInclude.Include.NON_NULL)
record CacheStats(
/** Performance metrics for the query result cache */
- CacheInfo queryResultCache,
+ @Nullable CacheInfo queryResultCache,
/** Performance metrics for the document cache */
- CacheInfo documentCache,
+ @Nullable CacheInfo documentCache,
/** Performance metrics for the filter cache */
- CacheInfo filterCache) {
+ @Nullable CacheInfo filterCache) {
}
/**
@@ -262,28 +263,28 @@ record CacheStats(
@JsonInclude(JsonInclude.Include.NON_NULL)
record CacheInfo(
/** Total number of cache lookup requests */
- Long lookups,
+ @Nullable Long lookups,
/** Number of successful cache hits */
- Long hits,
+ @Nullable Long hits,
/**
* Cache hit ratio (hits/lookups) - higher values indicate
better cache
* performance
*/
- Float hitratio,
+ @Nullable Float hitratio,
/** Number of new entries added to the cache */
- Long inserts,
+ @Nullable Long inserts,
/**
* Number of entries removed due to cache size limits
(indicates memory
* pressure)
*/
- Long evictions,
+ @Nullable Long evictions,
/** Current number of entries stored in the cache */
- Long size) {
+ @Nullable Long size) {
}
/**
@@ -317,10 +318,10 @@ record CacheInfo(
@JsonInclude(JsonInclude.Include.NON_NULL)
record HandlerStats(
/** Performance metrics for the search/select request handler */
- HandlerInfo selectHandler,
+ @Nullable HandlerInfo selectHandler,
/** Performance metrics for the document update request handler
*/
- HandlerInfo updateHandler) {
+ @Nullable HandlerInfo updateHandler) {
}
/**
@@ -352,22 +353,22 @@ record HandlerStats(
@JsonInclude(JsonInclude.Include.NON_NULL)
record HandlerInfo(
/** Total number of requests processed by this handler */
- Long requests,
+ @Nullable Long requests,
/** Number of requests that resulted in errors */
- Long errors,
+ @Nullable Long errors,
/** Number of requests that exceeded timeout limits */
- Long timeouts,
+ @Nullable Long timeouts,
/** Cumulative time spent processing all requests
(milliseconds) */
- Long totalTime,
+ @Nullable Long totalTime,
/** Average time per request in milliseconds */
- Float avgTimePerRequest,
+ @Nullable Float avgTimePerRequest,
/** Average throughput in requests per second */
- Float avgRequestsPerSecond) {
+ @Nullable Float avgRequestsPerSecond) {
}
/**
@@ -412,13 +413,13 @@ record SolrHealthStatus(
boolean isHealthy,
/** Detailed error message when isHealthy is false, null when
healthy */
- String errorMessage,
+ @Nullable String errorMessage,
/** Response time in milliseconds for the health check ping
request */
- Long responseTime,
+ @Nullable Long responseTime,
/** Total number of documents currently indexed in the
collection */
- Long totalDocuments,
+ @Nullable Long totalDocuments,
/** Timestamp when this health check was performed, formatted
as ISO 8601 */
@JsonFormat(shape = JsonFormat.Shape.STRING) Instant
lastChecked,
diff --git
a/src/main/java/org/apache/solr/mcp/server/collection/package-info.java
b/src/main/java/org/apache/solr/mcp/server/collection/package-info.java
new file mode 100644
index 0000000..6c8a68a
--- /dev/null
+++ b/src/main/java/org/apache/solr/mcp/server/collection/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server.collection;
+
+import org.jspecify.annotations.NullMarked;
diff --git
a/src/main/java/org/apache/solr/mcp/server/config/JsonResponseParser.java
b/src/main/java/org/apache/solr/mcp/server/config/JsonResponseParser.java
index 235b57a..545d5e8 100644
--- a/src/main/java/org/apache/solr/mcp/server/config/JsonResponseParser.java
+++ b/src/main/java/org/apache/solr/mcp/server/config/JsonResponseParser.java
@@ -29,6 +29,7 @@ import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
+import org.jspecify.annotations.Nullable;
import org.springframework.http.MediaType;
/**
@@ -102,7 +103,7 @@ class JsonResponseParser extends ResponseParser {
return result;
}
- private Object convertValue(JsonNode node) {
+ private @Nullable Object convertValue(JsonNode node) {
if (node.isNull())
return null;
if (node.isBoolean())
diff --git a/src/main/java/org/apache/solr/mcp/server/config/package-info.java
b/src/main/java/org/apache/solr/mcp/server/config/package-info.java
new file mode 100644
index 0000000..a2951a6
--- /dev/null
+++ b/src/main/java/org/apache/solr/mcp/server/config/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server.config;
+
+import org.jspecify.annotations.NullMarked;
diff --git
a/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/package-info.java
b/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/package-info.java
new file mode 100644
index 0000000..60983ee
--- /dev/null
+++
b/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server.indexing.documentcreator;
+
+import org.jspecify.annotations.NullMarked;
diff --git
a/src/main/java/org/apache/solr/mcp/server/indexing/package-info.java
b/src/main/java/org/apache/solr/mcp/server/indexing/package-info.java
new file mode 100644
index 0000000..b89126a
--- /dev/null
+++ b/src/main/java/org/apache/solr/mcp/server/indexing/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server.indexing;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/src/main/java/org/apache/solr/mcp/server/schema/package-info.java
b/src/main/java/org/apache/solr/mcp/server/schema/package-info.java
new file mode 100644
index 0000000..dde06d6
--- /dev/null
+++ b/src/main/java/org/apache/solr/mcp/server/schema/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server.schema;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/src/main/java/org/apache/solr/mcp/server/search/SearchService.java
b/src/main/java/org/apache/solr/mcp/server/search/SearchService.java
index 57eaa6c..cff5168 100644
--- a/src/main/java/org/apache/solr/mcp/server/search/SearchService.java
+++ b/src/main/java/org/apache/solr/mcp/server/search/SearchService.java
@@ -33,6 +33,7 @@ import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.FacetParams;
import org.apache.solr.mcp.server.util.PromptNames;
+import org.jspecify.annotations.Nullable;
import org.springaicommunity.mcp.annotation.McpArg;
import org.springaicommunity.mcp.annotation.McpPrompt;
import org.springaicommunity.mcp.annotation.McpTool;
@@ -296,14 +297,16 @@ public class SearchService {
@McpToolParam(
description = "Solr q parameter. Lucene
syntax; supports local params such as"
+ " {!edismax qf='name
author'}. If none specified defaults to \"*:*\"",
- required = false) String query,
+ required = false) @Nullable String
query,
@McpToolParam(
description = "Solr fq parameter: list
of filter queries, one filter per entry",
- required = false) List<String>
filterQueries,
- @McpToolParam(description = "Solr facet fields",
required = false) List<String> facetFields,
- @McpToolParam(description = "Solr sort parameter",
required = false) List<Map<String, String>> sortClauses,
- @McpToolParam(description = "Starting offset for
pagination", required = false) Integer start,
- @McpToolParam(description = "Number of rows to return",
required = false) Integer rows)
+ required = false) @Nullable
List<String> filterQueries,
+ @McpToolParam(description = "Solr facet fields",
required = false) @Nullable List<String> facetFields,
+ @McpToolParam(
+ description = "Solr sort parameter",
+ required = false) @Nullable
List<Map<String, String>> sortClauses,
+ @McpToolParam(description = "Starting offset for
pagination", required = false) @Nullable Integer start,
+ @McpToolParam(description = "Number of rows to return",
required = false) @Nullable Integer rows)
throws SolrServerException, IOException {
// query
diff --git a/src/main/java/org/apache/solr/mcp/server/search/package-info.java
b/src/main/java/org/apache/solr/mcp/server/search/package-info.java
new file mode 100644
index 0000000..08a3041
--- /dev/null
+++ b/src/main/java/org/apache/solr/mcp/server/search/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server.search;
+
+import org.jspecify.annotations.NullMarked;
diff --git
a/src/main/java/org/apache/solr/mcp/server/security/HttpSecurityConfiguration.java
b/src/main/java/org/apache/solr/mcp/server/security/HttpSecurityConfiguration.java
index a8f0905..b0ab728 100644
---
a/src/main/java/org/apache/solr/mcp/server/security/HttpSecurityConfiguration.java
+++
b/src/main/java/org/apache/solr/mcp/server/security/HttpSecurityConfiguration.java
@@ -38,10 +38,10 @@ import
org.springframework.web.cors.UrlBasedCorsConfigurationSource;
class HttpSecurityConfiguration {
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri:}")
- private String issuerUrl;
+ private String issuerUrl = "";
@Value("${mcp.cors.allowed-origins}")
- private List<String> allowedOrigins;
+ private List<String> allowedOrigins = List.of();
@Bean
@ConditionalOnProperty(name = "http.security.enabled", havingValue =
"true", matchIfMissing = true)
diff --git
a/src/main/java/org/apache/solr/mcp/server/security/package-info.java
b/src/main/java/org/apache/solr/mcp/server/security/package-info.java
new file mode 100644
index 0000000..088cb55
--- /dev/null
+++ b/src/main/java/org/apache/solr/mcp/server/security/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server.security;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/src/main/java/org/apache/solr/mcp/server/util/package-info.java
b/src/main/java/org/apache/solr/mcp/server/util/package-info.java
new file mode 100644
index 0000000..614f24c
--- /dev/null
+++ b/src/main/java/org/apache/solr/mcp/server/util/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server.util;
+
+import org.jspecify.annotations.NullMarked;
diff --git
a/src/test/java/org/apache/solr/mcp/server/collection/package-info.java
b/src/test/java/org/apache/solr/mcp/server/collection/package-info.java
new file mode 100644
index 0000000..6c8a68a
--- /dev/null
+++ b/src/test/java/org/apache/solr/mcp/server/collection/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server.collection;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/src/test/java/org/apache/solr/mcp/server/config/package-info.java
b/src/test/java/org/apache/solr/mcp/server/config/package-info.java
new file mode 100644
index 0000000..a2951a6
--- /dev/null
+++ b/src/test/java/org/apache/solr/mcp/server/config/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server.config;
+
+import org.jspecify.annotations.NullMarked;
diff --git
a/src/test/java/org/apache/solr/mcp/server/containerization/package-info.java
b/src/test/java/org/apache/solr/mcp/server/containerization/package-info.java
new file mode 100644
index 0000000..b30fa1e
--- /dev/null
+++
b/src/test/java/org/apache/solr/mcp/server/containerization/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server.containerization;
+
+import org.jspecify.annotations.NullMarked;
diff --git
a/src/test/java/org/apache/solr/mcp/server/indexing/package-info.java
b/src/test/java/org/apache/solr/mcp/server/indexing/package-info.java
new file mode 100644
index 0000000..b89126a
--- /dev/null
+++ b/src/test/java/org/apache/solr/mcp/server/indexing/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server.indexing;
+
+import org.jspecify.annotations.NullMarked;
diff --git
a/src/test/java/org/apache/solr/mcp/server/observability/package-info.java
b/src/test/java/org/apache/solr/mcp/server/observability/package-info.java
new file mode 100644
index 0000000..5405596
--- /dev/null
+++ b/src/test/java/org/apache/solr/mcp/server/observability/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server.observability;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/src/test/java/org/apache/solr/mcp/server/package-info.java
b/src/test/java/org/apache/solr/mcp/server/package-info.java
new file mode 100644
index 0000000..0ff54ed
--- /dev/null
+++ b/src/test/java/org/apache/solr/mcp/server/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/src/test/java/org/apache/solr/mcp/server/schema/package-info.java
b/src/test/java/org/apache/solr/mcp/server/schema/package-info.java
new file mode 100644
index 0000000..dde06d6
--- /dev/null
+++ b/src/test/java/org/apache/solr/mcp/server/schema/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server.schema;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/src/test/java/org/apache/solr/mcp/server/search/package-info.java
b/src/test/java/org/apache/solr/mcp/server/search/package-info.java
new file mode 100644
index 0000000..08a3041
--- /dev/null
+++ b/src/test/java/org/apache/solr/mcp/server/search/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@NullMarked
+package org.apache.solr.mcp.server.search;
+
+import org.jspecify.annotations.NullMarked;