Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
[![Project Status: Incubating](https://img.shields.io/badge/status-incubating-yellow.svg)](https://github.com/apache/solr-mcp)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=apache_solr-mcp&metric=alert_status)](https://sonarcloud.io/project/overview?id=apache_solr-mcp)
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=apache_solr-mcp&metric=security_rating)](https://sonarcloud.io/project/overview?id=apache_solr-mcp)
[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=apache_solr-mcp&metric=reliability_rating)](https://sonarcloud.io/project/overview?id=apache_solr-mcp)
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=apache_solr-mcp&metric=sqale_rating)](https://sonarcloud.io/project/overview?id=apache_solr-mcp)
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=apache_solr-mcp&metric=bugs)](https://sonarcloud.io/project/overview?id=apache_solr-mcp)
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=apache_solr-mcp&metric=vulnerabilities)](https://sonarcloud.io/project/overview?id=apache_solr-mcp)
[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=apache_solr-mcp&metric=code_smells)](https://sonarcloud.io/project/overview?id=apache_solr-mcp)
[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=apache_solr-mcp&metric=ncloc)](https://sonarcloud.io/project/overview?id=apache_solr-mcp)
[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=apache_solr-mcp&metric=duplicated_lines_density)](https://sonarcloud.io/project/overview?id=apache_solr-mcp)

# Solr MCP Server

Expand Down
7 changes: 7 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,13 @@ jib {
}
}

// Jib's tasks read Task.project at execution time, which the configuration cache rejects.
// Opt them out so jibDockerBuild/jib work with org.gradle.configuration-cache=true; every
// other task keeps using the cache.
tasks.withType<com.google.cloud.tools.jib.gradle.JibTask>().configureEach {
notCompatibleWithConfigurationCache("Jib reads Task.project at execution time")
}

tasks.bootBuildImage {
if (nativeBuild) {
imageName.set("solr-mcp:$version-native-$nativeProfile")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,15 @@ abstract class GenerateBinaryLicense : DefaultTask() {
private fun licensesOf(component: Component): List<License> {
val choice = component.licenses ?: return emptyList()
val out = LinkedHashMap<String, String?>()
for (license in choice.licenses.orEmpty()) {
val id = license.id
val label = id ?: license.name ?: "Unspecified"
out.putIfAbsent(label, license.url ?: id?.let { spdxUrl(it) })
for (item in choice.items.orEmpty()) {
val license = item.license
if (license != null) {
val id = license.id
val label = id ?: license.name ?: "Unspecified"
out.putIfAbsent(label, license.url ?: id?.let { spdxUrl(it) })
}
item.expression?.value?.let { out.putIfAbsent(it, null) }
}
choice.expression?.value?.let { out.putIfAbsent(it, null) }
return out.map { License(it.key, it.value) }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,24 @@ class LicenseNoticeTasksTest {
assertTrue(text.contains("License: BSD licence — http://antlr.org/license.html"))
}

@Test
fun `license reports an SPDX expression as written`() {
val task = licenseTask()
write("LICENSE", "BASE").let(task.baseLicense::set)
write(
"sbom.json",
"""{"components":[{"group":"org.example","name":"dual","version":"1.0",
"licenses":[{"expression":"EPL-2.0 OR GPL-2.0-with-classpath-exception"}]}]}""",
).let(task.sbom::set)
task.bundledCoordinates.set(listOf("org.example:dual:1.0"))
val out = File(tempDir, "out/LICENSE")
task.outputFile.set(out)

task.generate()

assertTrue(out.readText().contains("License: EPL-2.0 OR GPL-2.0-with-classpath-exception"))
}

@Test
fun `license gate fails when a bundled dependency is absent from the SBOM`() {
val task = licenseTask()
Expand Down
10 changes: 5 additions & 5 deletions dev-docs/graalvm-native-image.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,10 @@ from the `PROFILES` env var; the native image already made it at *build time*.

Spring AOT generates most hints automatically, but a few types are invisible to
it and must be registered by hand. They live in
`SolrConfig`'s sibling `SolrNativeHints.java`, a `@Configuration` annotated
`@ImportRuntimeHints(...)`. Registering hints through a `RuntimeHintsRegistrar`
(rather than scattering `@RegisterReflection` annotations) keeps the whole
reflective surface in one reviewable place. The class is registered
`SolrNativeHints.java`, a `RuntimeHintsRegistrar` that `SolrConfig` imports with
`@ImportRuntimeHints(SolrNativeHints.class)`. Registering hints through one
registrar (rather than scattering `@RegisterReflection` annotations) keeps the
whole reflective surface in one reviewable place. The registrar is imported
unconditionally; on the JVM path it is simply a no-op.

### Wire format choice underpins the small hint surface
Expand Down Expand Up @@ -175,7 +175,7 @@ container and value types.
1. Reproduce with `./gradlew nativeTest -Pnative` (or run the native binary and
trigger the failing path). GraalVM reports the missing reflective/resource
element.
2. Add a targeted registration in `SolrNativeHints.Registrar` — a single
2. Add a targeted registration in `SolrNativeHints` — a single
`registerType(...)` with `INVOKE_DECLARED_CONSTRUCTORS`,
`INVOKE_DECLARED_METHODS`, `DECLARED_FIELDS`, or a `registerPattern(...)` for
a resource. Prefer this over the tracing agent so the rule is explicit and
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/org/apache/solr/mcp/server/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@
public class Main {

/** Default constructor used by Spring Boot to bootstrap the application. */
public Main() {}
public Main() {
// Declared only to carry the Javadoc above; javadoc warns on an implicit constructor.
}

/**
* Logback's own system property naming the status listener to install during its
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -824,8 +824,8 @@ private boolean isHandlerStatsEmpty(HandlerStats stats) {
Long errors = getLong(coreMetrics, keyPrefix + ERRORS_FIELD);
Long timeouts = getLong(coreMetrics, keyPrefix + TIMEOUTS_FIELD);
Long totalTime = getLong(coreMetrics, keyPrefix + TOTAL_TIME_FIELD);
// avgTimePerRequest and avgRequestsPerSecond are not available as flat metrics;
// compute avgTimePerRequest from totalTime/requests when possible
// Solr has no flat metric for the average time per request or for requests per
// second, so the average time is derived from the total time and the request count.
Float avgTimePerRequest =
(requests > 0 && totalTime != null) ? (float) totalTime / requests : null;
return new HandlerInfo(requests, errors, timeouts, totalTime, avgTimePerRequest, null);
Expand Down Expand Up @@ -1051,7 +1051,7 @@ public String setupCollectionPrompt(
required = false)
@Nullable String purpose) {
String purposeLine =
(purpose == null || purpose.isBlank()) ? "" : "\nPurpose: %s\n".formatted(purpose.strip());
(purpose == null || purpose.isBlank()) ? "" : "\nPurpose: " + purpose.strip() + "\n";
return """
You are setting up a new Solr collection named `%s` through MCP tools.%s
1. Validate the name.
Expand Down
Loading