Skip to content
Merged
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: 8 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,11 @@ ij_continuation_indent_size = 8

ij_any_keep_line_breaks = true

# Keep trailing whitespace and EOL settings untouched to minimize changes
# Keep trailing whitespace and EOL settings untouched to minimize changes

# Java is formatted by google-java-format (./gradlew spotlessApply): 2-space
# indent, 4-space continuation, 100 columns.
[*.java]
indent_size = 2
ij_continuation_indent_size = 4
max_line_length = 100
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,8 @@ See [infra.apache.org/licensing-howto](https://infra.apache.org/licensing-howto.
`build.gradle.kts` only applies the plugin. The tasks are unit-tested in
`buildSrc/src/test/kotlin/.../LicenseNoticeTasksTest.kt` (appendix listing, SBOM
name/URL handling, the completeness gate, and NOTICE de-duplication); `buildSrc`'s
`test` runs as part of `./gradlew build`.
`test` runs as part of `./gradlew build`, because `buildSrc/build.gradle.kts` finalizes its
`jar` with `test` (Gradle 8+ otherwise builds only the jar the root build needs).

## Testing Structure

Expand Down
14 changes: 2 additions & 12 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -386,18 +386,8 @@ tasks.wrapper {
spotless {
java {
target("src/**/*.java")
// Use Eclipse JDT formatter to avoid google-java-format's incompatibility
// with cutting-edge JDKs (e.g., 25) which can trigger NoSuchMethodError
// against internal javac classes. Override only the annotation-argument
// alignment so multi-arg @Mcp* annotations render one-arg-per-line.
// Pinned to the JDT version Spotless 7.0.2 defaulted to: Spotless 8's
// default JDT (4.39) collapses Javadoc <pre>{@code} blocks and folds
// @param/@see tags into the preceding paragraph.
eclipse("4.34").configFile("config/spotless/eclipse-java-formatter.properties")
removeUnusedImports()
trimTrailingWhitespace()
endWithNewline()
importOrder()
// Pinned so a Spotless upgrade cannot reformat the codebase on its own.
googleJavaFormat("1.35.0")
formatAnnotations()
}
kotlinGradle {
Expand Down
9 changes: 9 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,12 @@ dependencies {
tasks.withType<Test>().configureEach {
useJUnitPlatform()
}

// Since Gradle 8 the root build asks buildSrc only for the jar it puts on the build script
// classpath, so buildSrc's tests no longer run. Finalizing the jar with them brings them
// back: any root invocation, `./gradlew build` included, fails when the license, NOTICE or
// RAT tasks regress, and the tests are up to date unless buildSrc changed. It can't be
// `dependsOn`: compiling the Kotlin tests needs the jar, which would be a cycle.
tasks.jar {
finalizedBy(tasks.test)
}
16 changes: 0 additions & 16 deletions config/spotless/eclipse-java-formatter.properties

This file was deleted.

146 changes: 63 additions & 83 deletions src/main/java/org/apache/solr/mcp/server/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,77 +25,62 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* Main Spring Boot application class for the Apache Solr Model Context Protocol
* (MCP) Server.
* Main Spring Boot application class for the Apache Solr Model Context Protocol (MCP) Server.
*
* <p>
* This class serves as the entry point for the Solr MCP Server application,
* which provides a bridge between AI clients (such as Claude Desktop) and
* Apache Solr search and indexing capabilities through the Model Context
* Protocol specification.
* <p>This class serves as the entry point for the Solr MCP Server application, which provides a
* bridge between AI clients (such as Claude Desktop) and Apache Solr search and indexing
* capabilities through the Model Context Protocol specification.
*
* <p>
* <strong>Application Architecture:</strong>
* <p><strong>Application Architecture:</strong>
*
* <p>
* The application follows a service-oriented architecture where each major Solr
* operation category is encapsulated in its own service class:
* <p>The application follows a service-oriented architecture where each major Solr operation
* category is encapsulated in its own service class:
*
* <ul>
* <li><strong>SearchService</strong>: Search operations, faceting, sorting,
* pagination
* <li><strong>IndexingService</strong>: Document indexing, schema-less
* ingestion, batch processing
* <li><strong>CollectionService</strong>: Collection management, metrics,
* health monitoring
* <li><strong>SchemaService</strong>: Schema introspection and field management
* <li><strong>SearchService</strong>: Search operations, faceting, sorting, pagination
* <li><strong>IndexingService</strong>: Document indexing, schema-less ingestion, batch
* processing
* <li><strong>CollectionService</strong>: Collection management, metrics, health monitoring
* <li><strong>SchemaService</strong>: Schema introspection and field management
* </ul>
*
* <p>
* <strong>Spring Boot Features:</strong>
* <p><strong>Spring Boot Features:</strong>
*
* <ul>
* <li><strong>Auto-Configuration</strong>: Automatic setup of Solr client and
* service beans
* <li><strong>Property Management</strong>: Externalized configuration through
* application.properties
* <li><strong>Dependency Injection</strong>: Automatic wiring of service
* dependencies
* <li><strong>Component Scanning</strong>: Automatic discovery of service
* classes
* <li><strong>Auto-Configuration</strong>: Automatic setup of Solr client and service beans
* <li><strong>Property Management</strong>: Externalized configuration through
* application.properties
* <li><strong>Dependency Injection</strong>: Automatic wiring of service dependencies
* <li><strong>Component Scanning</strong>: Automatic discovery of service classes
* </ul>
*
* <p>
* <strong>Communication Flow:</strong>
* <p><strong>Communication Flow:</strong>
*
* <ol>
* <li>AI client connects to MCP server via stdio
* <li>Client discovers available tools through MCP protocol
* <li>Client invokes tools with natural language parameters
* <li>Server routes requests to appropriate service methods
* <li>Services interact with Solr via SolrJ client library
* <li>Results are serialized and returned to AI client
* <li>AI client connects to MCP server via stdio
* <li>Client discovers available tools through MCP protocol
* <li>Client invokes tools with natural language parameters
* <li>Server routes requests to appropriate service methods
* <li>Services interact with Solr via SolrJ client library
* <li>Results are serialized and returned to AI client
* </ol>
*
* <p>
* <strong>Configuration Requirements:</strong>
* <p><strong>Configuration Requirements:</strong>
*
* <p>
* The application requires the following configuration properties:
* <p>The application requires the following configuration properties:
*
* <pre>{@code
* # application.properties
* solr.url=http://localhost:8983
* }</pre>
*
* <p>
* <strong>Deployment Considerations:</strong>
* <p><strong>Deployment Considerations:</strong>
*
* <ul>
* <li>Ensure Solr server is running and accessible at configured URL
* <li>Verify network connectivity between MCP server and Solr
* <li>Configure appropriate timeouts for production workloads
* <li>Monitor application logs for connection and performance issues
* <li>Ensure Solr server is running and accessible at configured URL
* <li>Verify network connectivity between MCP server and Solr
* <li>Configure appropriate timeouts for production workloads
* <li>Monitor application logs for connection and performance issues
* </ul>
*
* @see SearchService
Expand All @@ -107,43 +92,38 @@
@SpringBootApplication
public class Main {

/** Default constructor used by Spring Boot to bootstrap the application. */
public Main() {
}
/** Default constructor used by Spring Boot to bootstrap the application. */
public Main() {}

Check failure on line 96 in src/main/java/org/apache/solr/mcp/server/Main.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a nested comment explaining why this method is empty, throw an UnsupportedOperationException or complete the implementation.

See more on https://sonarcloud.io/project/issues?id=apache_solr-mcp&issues=AaE7LNbDaREm7r37DomA&open=AaE7LNbDaREm7r37DomA&pullRequest=266

/**
* Logback's own system property naming the status listener to install during
* its self-initialization.
*/
static final String LOGBACK_STATUS_LISTENER_PROPERTY = "logback.statusListenerClass";
/**
* Logback's own system property naming the status listener to install during its
* self-initialization.
*/
static final String LOGBACK_STATUS_LISTENER_PROPERTY = "logback.statusListenerClass";

public static void main(String[] args) {
silenceLogbackStatusOutput();
SpringApplication.run(Main.class, args);
}
public static void main(String[] args) {
silenceLogbackStatusOutput();
SpringApplication.run(Main.class, args);
}

/**
* Keeps logback's internal status messages off stdout, which the STDIO
* transport reserves for JSON-RPC.
*
* <p>
* Logback initializes itself on the first {@code LoggerFactory} touch, long
* before Spring Boot's logging system runs. In a native image it cannot read
* its version from the manifest, raises a {@code |-WARN}, and
* {@code LogbackServiceProvider} then prints its whole status list to stdout -
* unless a status listener is already installed. It installs one from this
* system property before deciding whether to print, so setting it here, before
* anything can touch a logger, is the earliest and only hook. Spring Boot's
* later configuration ({@code logback-spring.xml}) is unaffected.
*
* <p>
* An explicit {@code -Dlogback.statusListenerClass=...} on the command line
* wins, so logback's own configuration can still be debugged with
* {@code OnConsoleStatusListener}.
*/
static void silenceLogbackStatusOutput() {
if (System.getProperty(LOGBACK_STATUS_LISTENER_PROPERTY) == null) {
System.setProperty(LOGBACK_STATUS_LISTENER_PROPERTY, NopStatusListener.class.getName());
}
}
/**
* Keeps logback's internal status messages off stdout, which the STDIO transport reserves for
* JSON-RPC.
*
* <p>Logback initializes itself on the first {@code LoggerFactory} touch, long before Spring
* Boot's logging system runs. In a native image it cannot read its version from the manifest,
* raises a {@code |-WARN}, and {@code LogbackServiceProvider} then prints its whole status list
* to stdout - unless a status listener is already installed. It installs one from this system
* property before deciding whether to print, so setting it here, before anything can touch a
* logger, is the earliest and only hook. Spring Boot's later configuration ({@code
* logback-spring.xml}) is unaffected.
*
* <p>An explicit {@code -Dlogback.statusListenerClass=...} on the command line wins, so logback's
* own configuration can still be debugged with {@code OnConsoleStatusListener}.
*/
static void silenceLogbackStatusOutput() {
if (System.getProperty(LOGBACK_STATUS_LISTENER_PROPERTY) == null) {
System.setProperty(LOGBACK_STATUS_LISTENER_PROPERTY, NopStatusListener.class.getName());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,19 @@
/**
* Result record for alias management operations.
*
* <p>
* Returned by {@link AliasService} methods to communicate the outcome of
* create, update, and delete operations on Solr aliases.
* <p>Returned by {@link AliasService} methods to communicate the outcome of create, update, and
* delete operations on Solr aliases.
*
* @param aliasName
* the alias that was operated on
* @param collections
* the target collection(s) (null for delete operations)
* @param success
* whether the operation completed successfully
* @param message
* human-readable description of the outcome
* @param timestamp
* when the operation was performed
* @param aliasName the alias that was operated on
* @param collections the target collection(s) (null for delete operations)
* @param success whether the operation completed successfully
* @param message human-readable description of the outcome
* @param timestamp when the operation was performed
*/
public record AliasResult(String aliasName, @Nullable String collections, boolean success, String message,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") Date timestamp) {
}
public record AliasResult(
String aliasName,
@Nullable String collections,
boolean success,
String message,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
Date timestamp) {}
Loading