From 37c34dc1c6a2b39920fc6d2a1adbc132ff6b7281 Mon Sep 17 00:00:00 2001 From: Eric Pugh Date: Sat, 13 Jun 2026 14:19:48 -0400 Subject: [PATCH 01/10] until we publish docker images.... lets show the clone --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 60b63569..f8ca624a 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ A Spring AI Model Context Protocol (MCP) server that provides tools for interact ## Get started (users) - Prerequisites: Java 25+, Docker (and Docker Compose), Git +- Clone the repo: + ```bash + git clone https://github.com/apache/solr-mcp.git && cd solr-mcp + ``` - Start Solr with sample data: ```bash docker compose up -d From ecf9c70bb20bf8b11a82ef14a4cf7ab5fca41b2b Mon Sep 17 00:00:00 2001 From: Eric Pugh Date: Sat, 13 Jun 2026 14:24:03 -0400 Subject: [PATCH 02/10] Move dev oritented docs on publishing to dev guide --- CONTRIBUTING.md | 20 -------------------- dev-docs/DEVELOPMENT.md | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4c957e08..3fbb5900 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,26 +38,6 @@ To keep this document concise, please see the Development Guide for all testing - Docker image tests: dev-docs/DEVELOPMENT.md#docker-integration-tests - Coverage reports: dev-docs/DEVELOPMENT.md#testing -## Publishing to Maven Local - -To install the project artifacts to your local Maven repository for testing or local development: - -```bash -./gradlew publishToMavenLocal -``` - -This publishes the following artifacts to `~/.m2/repository/org/apache/solr/solr-mcp/{version}/`: - -- `solr-mcp-{version}.jar` - Main application JAR -- `solr-mcp-{version}-sources.jar` - Source code for IDE navigation -- `solr-mcp-{version}-javadoc.jar` - API documentation -- `solr-mcp-{version}.pom` - Maven POM with dependencies - -This is useful when: -- Testing the library locally before publishing to a remote repository -- Sharing artifacts between local projects during development -- Verifying the published POM and artifact structure - ## Submitting Changes ### Pull Request Process diff --git a/dev-docs/DEVELOPMENT.md b/dev-docs/DEVELOPMENT.md index a9624c18..dd6d18e5 100644 --- a/dev-docs/DEVELOPMENT.md +++ b/dev-docs/DEVELOPMENT.md @@ -37,6 +37,26 @@ The build produces an executable JAR in `build/libs/`: - `solr-mcp-1.0.0-SNAPSHOT.jar` โ€” Spring Boot executable (fat) JAR +### Publishing to Maven Local + +To install the project artifacts to your local Maven repository for testing or local development: + +```bash +./gradlew publishToMavenLocal +``` + +This publishes the following artifacts to `~/.m2/repository/org/apache/solr/solr-mcp/{version}/`: + +- `solr-mcp-{version}.jar` - Main application JAR +- `solr-mcp-{version}-sources.jar` - Source code for IDE navigation +- `solr-mcp-{version}-javadoc.jar` - API documentation +- `solr-mcp-{version}.pom` - Maven POM with dependencies + +This is useful when: +- Testing the library locally before publishing to a remote repository +- Sharing artifacts between local projects during development +- Verifying the published POM and artifact structure + ## Running Locally ### Start Solr From c3dfaefa045d1421cb3e8c5b2c482ecd398e9f3c Mon Sep 17 00:00:00 2001 From: Eric Pugh Date: Sat, 13 Jun 2026 14:27:21 -0400 Subject: [PATCH 03/10] SBOM generation locally is a development task --- README.md | 7 +------ dev-docs/DEVELOPMENT.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f8ca624a..308b25b3 100644 --- a/README.md +++ b/README.md @@ -476,12 +476,7 @@ From a running HTTP-mode server: curl -s http://localhost:8080/actuator/sbom/application > application.cdx.json ``` -From the local build (no server required): - -```bash -./gradlew cyclonedxBom -cat build/reports/application.cdx.json -``` +To generate the SBOM locally from source, see [dev-docs/DEVELOPMENT.md#generating-the-sbom-locally](dev-docs/DEVELOPMENT.md#generating-the-sbom-locally). ### Scan the SBOM diff --git a/dev-docs/DEVELOPMENT.md b/dev-docs/DEVELOPMENT.md index dd6d18e5..9684dcb0 100644 --- a/dev-docs/DEVELOPMENT.md +++ b/dev-docs/DEVELOPMENT.md @@ -57,6 +57,17 @@ This is useful when: - Sharing artifacts between local projects during development - Verifying the published POM and artifact structure +### Generating the SBOM locally + +The build produces a [CycloneDX](https://cyclonedx.org/) 1.6 Software Bill of Materials. The Spring Boot Gradle plugin also embeds it in the bootJar (and therefore every Docker image) at `META-INF/sbom/application.cdx.json`. To generate it from source without running the server: + +```bash +./gradlew cyclonedxBom +cat build/reports/application.cdx.json +``` + +For consuming and scanning the SBOM (HTTP endpoint, GitHub Release attachment, Trivy/Grype), see the [Supply chain & SBOM](../README.md#supply-chain--sbom) section of the README. + ## Running Locally ### Start Solr From eebe9b4bd2b100ef7ce7ef4dab2e091df4921d65 Mon Sep 17 00:00:00 2001 From: Eric Pugh Date: Sat, 13 Jun 2026 14:32:49 -0400 Subject: [PATCH 04/10] be truthful about our lack of published images --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 308b25b3..ed1e906e 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,12 @@ A Spring AI Model Context Protocol (MCP) server that provides tools for interact - ๐Ÿ”ง Inspect schema - ๐Ÿ”Œ Transports: STDIO (Claude Desktop) and HTTP (MCP Inspector) - ๐Ÿ” OAuth2 security with Auth0 (HTTP mode only) -- ๐Ÿณ Docker images built with Jib +- ๐Ÿณ Docker images built locally with Jib (JVM) and Paketo (native) ## Get started (users) +> **Note:** Published container images are not yet available on a public registry. The `docker run` examples below use a **locally built** image โ€” build it first with `./gradlew jibDockerBuild` (produces `solr-mcp:latest`), or use the JAR path instead. See [Building Docker images](#building-docker-images). + - Prerequisites: Java 25+, Docker (and Docker Compose), Git - Clone the repo: ```bash @@ -39,7 +41,7 @@ A Spring AI Model Context Protocol (MCP) server that provides tools for interact ``` - Docker: ```bash - docker run -i --rm ghcr.io/apache/solr-mcp:latest + docker run -i --rm solr-mcp:latest ``` - **HTTP mode**: - Gradle: @@ -52,7 +54,7 @@ A Spring AI Model Context Protocol (MCP) server that provides tools for interact ``` - Docker: ```bash - docker run -p 8080:8080 --rm -e PROFILES=http ghcr.io/apache/solr-mcp:latest + docker run -p 8080:8080 --rm -e PROFILES=http solr-mcp:latest ``` For more options (custom SOLR_URL, Linux host networking) see the Deployment Guide: docs/DEPLOYMENT.md @@ -69,7 +71,7 @@ Using Docker: "mcpServers": { "solr-mcp": { "command": "docker", - "args": ["run", "-i", "--rm", "ghcr.io/apache/solr-mcp:latest"], + "args": ["run", "-i", "--rm", "solr-mcp:latest"], "env": { "SOLR_URL": "http://localhost:8983/solr/" } @@ -111,7 +113,7 @@ Using Docker: "-p", "8080:8080", "--rm", - "ghcr.io/apache/solr-mcp:latest" + "solr-mcp:latest" ], "env": { "PROFILES": "http", @@ -162,7 +164,7 @@ PROFILES=http java -jar build/libs/solr-mcp-1.0.0-SNAPSHOT.jar Running via Docker: ```bash -docker run -p 8080:8080 --rm -e PROFILES=http ghcr.io/apache/solr-mcp:latest +docker run -p 8080:8080 --rm -e PROFILES=http solr-mcp:latest ``` Then add to your `claude_desktop_config.json`: @@ -191,7 +193,7 @@ Add Solr MCP to [Claude Code](https://docs.anthropic.com/en/docs/claude-code) us Using Docker (CLI): ```bash -claude mcp add --transport stdio solr-mcp -- docker run -i --rm ghcr.io/apache/solr-mcp:latest +claude mcp add --transport stdio solr-mcp -- docker run -i --rm solr-mcp:latest ``` Using JAR (CLI): @@ -208,7 +210,7 @@ Using Docker: "solr-mcp": { "type": "stdio", "command": "docker", - "args": ["run", "-i", "--rm", "ghcr.io/apache/solr-mcp:latest"], + "args": ["run", "-i", "--rm", "solr-mcp:latest"], "env": { "SOLR_URL": "http://localhost:8983/solr/" } @@ -244,7 +246,7 @@ PROFILES=http ./gradlew bootRun PROFILES=http java -jar build/libs/solr-mcp-1.0.0-SNAPSHOT.jar # Docker -docker run -p 8080:8080 --rm -e PROFILES=http ghcr.io/apache/solr-mcp:latest +docker run -p 8080:8080 --rm -e PROFILES=http solr-mcp:latest ``` Then add to Claude Code: From a1b38f50ee298ba3a38feb88ce704d273eb06763 Mon Sep 17 00:00:00 2001 From: Eric Pugh Date: Sat, 13 Jun 2026 14:38:10 -0400 Subject: [PATCH 05/10] keep the good parts! --- TESTING_DISTRIBUTED_TRACING.md | 174 --------------------------------- dev-docs/DEVELOPMENT.md | 37 +++++++ 2 files changed, 37 insertions(+), 174 deletions(-) delete mode 100644 TESTING_DISTRIBUTED_TRACING.md diff --git a/TESTING_DISTRIBUTED_TRACING.md b/TESTING_DISTRIBUTED_TRACING.md deleted file mode 100644 index 07af83b6..00000000 --- a/TESTING_DISTRIBUTED_TRACING.md +++ /dev/null @@ -1,174 +0,0 @@ -# Distributed Tracing Test Implementation - Complete โœ… - -## Summary - -Successfully implemented comprehensive distributed tracing tests for Spring Boot 3.5 using SimpleTracer from micrometer-tracing-test. All distributed tracing unit tests are passing. - -## Test Results - -### DistributedTracingTest โœ… -**Status:** All 6 tests passing -**Execution time:** ~6 seconds -**Coverage:** -- โœ… `shouldCreateSpanForSearchServiceMethod()` - Verifies spans are created for @Observed methods -- โœ… `shouldIncludeSpanAttributes()` - Verifies span attributes/tags are set -- โœ… `shouldCreateSpanHierarchy()` - Verifies span creation -- โœ… `shouldSetCorrectSpanKind()` - Verifies span kinds -- โœ… `shouldIncludeServiceNameInResource()` - Verifies service name in spans -- โœ… `shouldRecordSpanDuration()` - Verifies span timing (start/end timestamps) - -## Key Implementation Details - -### 1. Test Configuration: OpenTelemetryTestConfiguration.java - -```java -@TestConfiguration -public class OpenTelemetryTestConfiguration { - @Bean - @Primary - public SimpleTracer simpleTracer() { - return new SimpleTracer(); - } -} -``` - -**How it works:** -- Provides SimpleTracer as @Primary bean to replace OpenTelemetry tracer -- Spring Boot's observability auto-configuration connects this to the ObservationRegistry -- No external infrastructure required for testing - -### 2. Test Approach - -**Spring Boot 3.5 Observability Stack:** -``` -@Observed annotation โ†’ Micrometer Observation API โ†’ Micrometer Tracing โ†’ SimpleTracer -``` - -**Key API differences:** -- Method: `tracer.getSpans()` (not `getFinishedSpans()`) -- Return type: `Deque` (not `List`) -- Span name format: `"search-service#search"` (kebab-case: `class-name#method-name`) - -### 3. Dependencies Added - -**Main dependencies** (build.gradle.kts): -```kotlin -implementation("io.micrometer:micrometer-tracing-bridge-otel") -implementation("org.springframework.boot:spring-boot-starter-aop") -``` - -**Test dependencies** (libs.versions.toml): -```kotlin -micrometer-tracing-test = { module = "io.micrometer:micrometer-tracing-test" } -awaitility = { module = "org.awaitility:awaitility", version.ref = "awaitility" } -``` - -### 4. Test Properties - -```properties -# Disable OTLP export in tests - we're using SimpleTracer instead -management.otlp.tracing.endpoint= -management.opentelemetry.logging.export.otlp.enabled=false - -# Ensure 100% sampling for tests -management.tracing.sampling.probability=1.0 - -# Enable @Observed annotation support -management.observations.annotations.enabled=true -``` - -## Known Issues - -### OtlpExportIntegrationTest โš ๏ธ -**Status:** Disabled -**Reason:** Jetty HTTP client ClassNotFoundException with LgtmStackContainer -**Impact:** Low - core distributed tracing functionality is fully tested - -The testcontainers-grafana module requires `org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP` which is not properly resolved with the current Jetty BOM configuration. This integration test can be addressed separately or replaced with an alternative approach. - -**Workaround options:** -1. Use a different HTTP client library (Apache HttpClient, OkHttp) -2. Upgrade to testcontainers-grafana version that doesn't require Jetty -3. Test OTLP export manually with LGTM Stack container -4. Use different testing approach (MockWebServer, WireMock) - -## Files Modified - -### Test Files -- `src/test/java/org/apache/solr/mcp/server/observability/DistributedTracingTest.java` - 6 comprehensive tests -- `src/test/java/org/apache/solr/mcp/server/observability/OpenTelemetryTestConfiguration.java` - SimpleTracer configuration -- `src/test/java/org/apache/solr/mcp/server/observability/OtlpExportIntegrationTest.java` - Disabled (Jetty issue) -- `src/test/java/org/apache/solr/mcp/server/observability/LgtmAssertions.java` - LGTM Stack query helpers (ready for use) -- `src/test/java/org/apache/solr/mcp/server/observability/TraceAssertions.java` - Span assertion utilities - -### Configuration Files -- `build.gradle.kts` - Added micrometer-tracing-bridge-otel and spring-boot-starter-aop -- `gradle/libs.versions.toml` - Added test dependencies (micrometer-tracing-test, awaitility, Jetty modules) - -### Main Code -- `src/main/java/org/apache/solr/mcp/server/search/SearchService.java` - Already has @Observed annotation (no changes needed) - -## How to Run Tests - -```bash -# Run distributed tracing tests only -./gradlew test --tests "org.apache.solr.mcp.server.observability.DistributedTracingTest" - -# Run all tests -./gradlew build - -# Run with verbose output -./gradlew test --tests "*.DistributedTracingTest" --info -``` - -## Example Span Output - -From test execution, SimpleTracer captures spans like: -```java -SimpleSpan{ - name='search-service#search', - tags={method=search, class=org.apache.solr.mcp.server.search.SearchService}, - startMillis=1770309759979, - endMillis=1770309759988, - traceId='72a53a4517951631', - spanId='72a53a4517951631' -} -``` - -## Spring Boot 3 vs Spring Boot 4 Differences - -| Aspect | Spring Boot 3.5 | Spring Boot 4 | -|--------|----------------|---------------| -| **Tracing API** | Micrometer Observation โ†’ Micrometer Tracing โ†’ OpenTelemetry | Direct OpenTelemetry integration | -| **Test Approach** | SimpleTracer from micrometer-tracing-test | InMemorySpanExporter from opentelemetry-sdk-testing | -| **Span Retrieval** | `tracer.getSpans()` | `spanExporter.getFinishedSpanItems()` | -| **Span Type** | `SimpleSpan` (Micrometer) | `SpanData` (OpenTelemetry) | -| **Bridge Dependency** | `micrometer-tracing-bridge-otel` required | Not required | -| **AspectJ Starter** | `spring-boot-starter-aop` | `spring-boot-starter-aspectj` | - -## Next Steps (Optional) - -1. โœ… Core distributed tracing tests - **COMPLETE** -2. โš ๏ธ LGTM Stack integration test - Jetty issue (optional to fix) -3. ๐Ÿ“ Consider adding more span attribute assertions -4. ๐Ÿ“ Consider testing span parent-child relationships explicitly -5. ๐Ÿ“ Consider adding tests for error scenarios (exceptions in @Observed methods) - -## References - -- [Micrometer Tracing Testing Documentation](https://docs.micrometer.io/tracing/reference/testing.html) -- [Spring Boot 3 Observability](https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.micrometer-tracing) -- [SimpleTracer API](https://github.com/micrometer-metrics/tracing/blob/main/micrometer-tracing-tests/micrometer-tracing-test/src/main/java/io/micrometer/tracing/test/simple/SimpleTracer.java) -- [Observability With Spring Boot | Baeldung](https://www.baeldung.com/spring-boot-3-observability) - -## Success Criteria Met โœ… - -- [x] Comprehensive distributed tracing test suite implemented -- [x] Tests adapted from Spring Boot 4 implementation (PR #23) -- [x] All unit tests passing (6/6 DistributedTracingTest) -- [x] No regressions (full build successful) -- [x] Spring Boot 3.5 architecture properly used (Micrometer Observation API) -- [x] SimpleTracer successfully capturing spans from @Observed annotations -- [x] Test documentation complete - -**Result:** Distributed tracing testing for Spring Boot 3.5 is fully functional and ready for use. โœ… diff --git a/dev-docs/DEVELOPMENT.md b/dev-docs/DEVELOPMENT.md index 9684dcb0..ea86470a 100644 --- a/dev-docs/DEVELOPMENT.md +++ b/dev-docs/DEVELOPMENT.md @@ -173,6 +173,43 @@ npx @modelcontextprotocol/inspector Then open the browser URL provided (typically http://localhost:6274) and connect to http://localhost:8080/mcp +### Distributed Tracing Tests + +`DistributedTracingTest` verifies that spans are produced for `@Observed` methods (e.g. +`SearchService#search`) without requiring any external tracing infrastructure. + +```bash +./gradlew test --tests "org.apache.solr.mcp.server.observability.DistributedTracingTest" +``` + +**How it works.** Spring Boot 3.5's observability stack is +`@Observed annotation โ†’ Micrometer Observation API โ†’ Micrometer Tracing โ†’ tracer`. The test +swaps in a `SimpleTracer` (from `micrometer-tracing-test`) as a `@Primary` bean via +`OpenTelemetryTestConfiguration`, so spans are captured in-memory. Spans are retrieved with +`tracer.getSpans()` (returns `Deque`) and named in kebab-case as +`class-name#method-name` (e.g. `search-service#search`). Test properties disable OTLP export, +force `management.tracing.sampling.probability=1.0`, and set +`management.observations.annotations.enabled=true`. + +**Known issue โ€” `OtlpExportIntegrationTest` is disabled.** The end-to-end OTLP export test +(via `LgtmStackContainer`/`testcontainers-grafana`) fails with a Jetty +`ClassNotFoundException` for `org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP` +under the current Jetty BOM. Core tracing is fully covered by `DistributedTracingTest`, so the +impact is low; fixing it would mean swapping the HTTP client (Apache HttpClient/OkHttp) or +upgrading `testcontainers-grafana`. + +**Spring Boot 3.5 vs 4 differences** (relevant if/when we upgrade โ€” SB4 drops the Micrometer +bridge for direct OpenTelemetry): + +| Aspect | Spring Boot 3.5 | Spring Boot 4 | +|--------|-----------------|---------------| +| Tracing API | Micrometer Observation โ†’ Micrometer Tracing โ†’ OpenTelemetry | Direct OpenTelemetry integration | +| Test approach | `SimpleTracer` (`micrometer-tracing-test`) | `InMemorySpanExporter` (`opentelemetry-sdk-testing`) | +| Span retrieval | `tracer.getSpans()` | `spanExporter.getFinishedSpanItems()` | +| Span type | `SimpleSpan` (Micrometer) | `SpanData` (OpenTelemetry) | +| Bridge dependency | `micrometer-tracing-bridge-otel` required | not required | +| AspectJ starter | `spring-boot-starter-aop` | `spring-boot-starter-aspectj` | + ## Code Quality ### Spotless Formatting From 8872e138d9229b0e47dcbd88ff2343881fe030b1 Mon Sep 17 00:00:00 2001 From: Eric Pugh Date: Sat, 13 Jun 2026 14:43:58 -0400 Subject: [PATCH 06/10] Add native compile docs --- dev-docs/DEVELOPMENT.md | 63 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/dev-docs/DEVELOPMENT.md b/dev-docs/DEVELOPMENT.md index ea86470a..6f5f28d4 100644 --- a/dev-docs/DEVELOPMENT.md +++ b/dev-docs/DEVELOPMENT.md @@ -274,6 +274,69 @@ export DOCKER_EXECUTABLE=/custom/path/to/docker ./gradlew jibDockerBuild ``` +### Native Image (GraalVM) + +Native compilation is opt-in behind the `-Pnative` Gradle property. It trades a +slower, RAM-hungry build for sub-second startup, much lower RSS, and a smaller, +JRE-free image โ€” most valuable for the local STDIO use case where Claude Desktop +launches a fresh container per session. See the [Image ร— Mode matrix](../AGENTS.md) +for which image serves which transport. + +**Prerequisites.** `nativeCompile`/`nativeTest` need a GraalVM JDK on `PATH` or +`JAVA_HOME` (the plugin does not auto-provision a toolchain). Install locally via +SDKMAN: + +```bash +sdk install java 25.0.2-graalce +``` + +or download from . CI provisions it with +`graalvm/setup-graalvm`. + +**Build and test.** + +```bash +# Compile a host-OS native binary +./gradlew nativeCompile -Pnative + +# Run the test suite as a native image (slow โ€” not part of ./gradlew build) +./gradlew nativeTest -Pnative + +# Build a native Docker image via Paketo buildpacks (compiles inside a Linux +# builder container, so it works on any host OS โ€” no cross-compilation) +./gradlew bootBuildImage -Pnative # stdio binary +./gradlew bootBuildImage -Pnative -Pprofile=http # http binary +``` + +`nativeTest` is intentionally excluded from `./gradlew build` (an image compile +per run is slow); it runs in the dedicated `native.yml` CI job instead. + +**Adding a reflection hint.** GraalVM's closed-world analysis can't see +reflective access, so when `nativeTest` fails with a missing-class/method or +resource error: + +1. Add a targeted hint to a `RuntimeHintsRegistrar` (we centralize these in + `SolrNativeHints.java`, registered via `@ImportRuntimeHints`) rather than + scattering `@Reflective` annotations โ€” the rules stay reviewable in one place. +2. Only if static analysis of the failures is too noisy, fall back to the + tracing agent (`-agentlib:native-image-agent`); commit its output under + `src/main/resources/META-INF/native-image/`. + +**Known gotchas.** + +- **Memory:** `nativeCompile` commonly needs 4โ€“8 GB RAM. Ensure local/CI runners + have headroom. +- **First Paketo build is large:** `bootBuildImage` downloads a ~1 GB builder on + first run; CI caching mitigates this. +- **OpenTelemetry build-time init:** the pinned OTel instrumentation BOM lacks + native metadata, so the build adds `--initialize-at-build-time` for four OTel + packages (see `SolrNativeHints`/`build.gradle.kts`). Do **not** add + `io.opentelemetry.instrumentation.spring` โ€” it contains CGLIB proxies that + cannot be build-time initialized. Bumping the OTel BOM to 2.26.1 currently + fails at AOT time (`io.opentelemetry.common.ComponentLoader` not found) because + it outpaces the OTel SDK that Spring Boot 3.5.x manages; revisit when Spring + Boot aligns its managed OTel version. + ## IDE Setup ### IntelliJ IDEA From c67f7eea6f248a686032a71be6179b088aee8e5f Mon Sep 17 00:00:00 2001 From: Eric Pugh Date: Sat, 13 Jun 2026 14:50:52 -0400 Subject: [PATCH 07/10] consoildate all security related docs under docs/security --- README.md | 7 ++++--- security-docs/AUTH0_SETUP.md => docs/security/auth0.md | 0 docs/security/http.md | 1 + {security-docs => docs/security}/keycloak.md | 0 docs/security/stdio.md | 3 ++- 5 files changed, 7 insertions(+), 4 deletions(-) rename security-docs/AUTH0_SETUP.md => docs/security/auth0.md (100%) rename {security-docs => docs/security}/keycloak.md (100%) diff --git a/README.md b/README.md index ed1e906e..244cd97a 100644 --- a/README.md +++ b/README.md @@ -280,7 +280,7 @@ MCP tools. ### Quick Setup -1. **Configure Auth0** (see detailed guide: [security-docs/AUTH0_SETUP.md](security-docs/AUTH0_SETUP.md)) +1. **Configure Auth0** (see detailed guide: [docs/security/auth0.md](docs/security/auth0.md)) - Create an Auth0 Application (Machine to Machine) - Create an Auth0 API with your audience identifier - Note your Domain, Client ID, Client Secret, and Audience @@ -310,7 +310,7 @@ MCP tools. http://localhost:8080/mcp ``` -For complete setup instructions, see [security-docs/AUTH0_SETUP.md](security-docs/AUTH0_SETUP.md) +For complete setup instructions, see [docs/security/auth0.md](docs/security/auth0.md) ## Available MCP tools @@ -494,7 +494,8 @@ Both tools natively consume CycloneDX 1.6 and report CVEs against the listed com ## Documentation -- [Auth0 Setup (OAuth2 configuration)](security-docs/AUTH0_SETUP.md) +- Security model: [STDIO transport](docs/security/stdio.md) ยท [HTTP transport](docs/security/http.md) +- OAuth2 provider setup: [Auth0](docs/security/auth0.md) ยท [Keycloak](docs/security/keycloak.md) - [GraalVM native image spec](docs/specs/graalvm-native-image.md) ## Contributing diff --git a/security-docs/AUTH0_SETUP.md b/docs/security/auth0.md similarity index 100% rename from security-docs/AUTH0_SETUP.md rename to docs/security/auth0.md diff --git a/docs/security/http.md b/docs/security/http.md index 7c823243..6add6df7 100644 --- a/docs/security/http.md +++ b/docs/security/http.md @@ -172,5 +172,6 @@ exists for browser-based tooling. ## Related documents - [STDIO transport security model](./stdio.md) +- OAuth2 provider setup: [Auth0](./auth0.md) ยท [Keycloak](./keycloak.md) - [GraalVM native image spec](../specs/graalvm-native-image.md) - [Logging architecture in `CLAUDE.md`](../../CLAUDE.md#logging-architecture) diff --git a/security-docs/keycloak.md b/docs/security/keycloak.md similarity index 100% rename from security-docs/keycloak.md rename to docs/security/keycloak.md diff --git a/docs/security/stdio.md b/docs/security/stdio.md index 74b35658..af49a0a7 100644 --- a/docs/security/stdio.md +++ b/docs/security/stdio.md @@ -93,6 +93,7 @@ that launched the process. No code changes are required for STDIO security. ## Related documents -- HTTP transport security model โ€” *planned, will live at `docs/security/http.md`* +- [HTTP transport security model](./http.md) +- OAuth2 provider setup (HTTP mode): [Auth0](./auth0.md) ยท [Keycloak](./keycloak.md) - [GraalVM native image spec](../specs/graalvm-native-image.md) - [Logging architecture in `CLAUDE.md`](../../CLAUDE.md#logging-architecture) From ae050faf0fc37d5fe26539d96e61590e1654fa71 Mon Sep 17 00:00:00 2001 From: Eric Pugh Date: Sat, 13 Jun 2026 16:19:56 -0400 Subject: [PATCH 08/10] from pr 95 --- CONTRIBUTING.md | 40 +++++++++-- README.md | 178 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 209 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3fbb5900..84695da0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,6 +28,10 @@ We use Spotless for code formatting and style enforcement. CI enforces `spotless - Include unit tests for new features - Keep methods focused and concise +### Null safety + +Every package is `@NullMarked` via `package-info.java`. Methods, parameters, and return types are non-null by default โ€” mark legitimate null surfaces with `@Nullable` (`org.jspecify.annotations.Nullable`). [NullAway](https://github.com/uber/NullAway) enforces the contract on `compileJava`; new code that violates it fails the build. Reference: [JSpecify](https://jspecify.dev/). + ## Testing To keep this document concise, please see the Development Guide for all testing workflows and tips: @@ -38,6 +42,26 @@ To keep this document concise, please see the Development Guide for all testing - Docker image tests: dev-docs/DEVELOPMENT.md#docker-integration-tests - Coverage reports: dev-docs/DEVELOPMENT.md#testing +## Publishing to Maven Local + +To install the project artifacts to your local Maven repository for testing or local development: + +```bash +./gradlew publishToMavenLocal +``` + +This publishes the following artifacts to `~/.m2/repository/org/apache/solr/solr-mcp/{version}/`: + +- `solr-mcp-{version}.jar` - Main application JAR +- `solr-mcp-{version}-sources.jar` - Source code for IDE navigation +- `solr-mcp-{version}-javadoc.jar` - API documentation +- `solr-mcp-{version}.pom` - Maven POM with dependencies + +This is useful when: +- Testing the library locally before publishing to a remote repository +- Sharing artifacts between local projects during development +- Verifying the published POM and artifact structure + ## Submitting Changes ### Pull Request Process @@ -117,15 +141,23 @@ For implementation details and examples, see the Development Guide: - Dependencies and version catalogs: dev-docs/DEVELOPMENT.md#build-system - Documentation practices: dev-docs/DEVELOPMENT.md#modifying-configuration +## Security Setup (HTTP Mode) + +For OAuth2 configuration with supported providers: + +- [Auth0 Setup Guide](docs/security/auth0.md) +- [Keycloak Setup Guide](docs/security/keycloak.md) + ## Questions or Need Help? -- Open an issue for bugs or feature requests -- Start a discussion for questions or ideas -- Check existing issues and discussions first +- **Slack:** [`#solr-mcp`](https://the-asf.slack.com/archives/C09TVG3BM1P) in the `the-asf` workspace +- **Issues:** [GitHub Issues](https://github.com/apache/solr-mcp/issues) for bugs or feature requests +- **Discussions:** [GitHub Discussions](https://github.com/apache/solr-mcp/discussions) for questions or ideas +- **Mailing lists:** Shared with Apache Solr โ€” see [mailing lists](https://solr.apache.org/community.html#mailing-lists-chat) ## Code of Conduct -Be respectful, inclusive, and professional. We're all here to build something great together. +As an Apache project, we follow the [Apache Code of Conduct](https://www.apache.org/foundation/policies/conduct). ## License diff --git a/README.md b/README.md index 244cd97a..3b8027a8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,24 @@ # Solr MCP Server -A Spring AI Model Context Protocol (MCP) server that provides tools for interacting with Apache Solr. Enables AI assistants like Claude to search, index, and manage Solr collections through the MCP protocol. +Search, index, and manage [Apache Solr](https://solr.apache.org/) collections using **natural language** โ€” no need to hand-craft Solr queries, build filter expressions, or memorize the admin API. + +Instead of writing: + +``` +q=title:"star wars" AND genre_s:"sci-fi"&fq=year_i:[2000 TO *]&facet=true&facet.field=genre_s&sort=score desc&rows=10 +``` + +Just ask your AI assistant: + +> *"Find sci-fi movies with 'star wars' in the title released after 2000, show me the genre breakdown, and sort by relevance."* + +This Spring AI [Model Context Protocol (MCP)](https://spec.modelcontextprotocol.io/) server exposes Solr operations as tools that any MCP-compatible AI client (Claude Desktop, Claude Code, VS Code/Copilot, Cursor, JetBrains) can invoke. + +**[Website](https://solr.apache.org/mcp)** ยท +**[Quick Start](https://solr.apache.org/mcp/quick-start.html)** ยท +**[Client Setup](https://solr.apache.org/mcp/clients/claude-desktop.html)** ยท +**[Features](https://solr.apache.org/mcp/features.html)** ## What's inside @@ -57,7 +74,7 @@ A Spring AI Model Context Protocol (MCP) server that provides tools for interact docker run -p 8080:8080 --rm -e PROFILES=http solr-mcp:latest ``` -For more options (custom SOLR_URL, Linux host networking) see the Deployment Guide: docs/DEPLOYMENT.md +For more options (custom SOLR_URL, Linux host networking) see the [Deployment Guide](dev-docs/DEPLOYMENT.md). ### Claude Desktop @@ -266,6 +283,139 @@ Or add to `.mcp.json`: } ``` +### Other clients + +
+VS Code / GitHub Copilot + +Create `.vscode/mcp.json` in your project root: + +**STDIO mode** (JAR): + +```json +{ + "servers": { + "solr-mcp": { + "type": "stdio", + "command": "java", + "args": ["-jar", "/absolute/path/to/solr-mcp-1.0.0-SNAPSHOT.jar"], + "env": { "SOLR_URL": "http://localhost:8983/solr/" } + } + } +} +``` + +**HTTP mode:** + +```json +{ + "servers": { + "solr-mcp": { + "type": "sse", + "url": "http://localhost:8080/mcp" + } + } +} +``` + +After adding the configuration, Solr MCP tools are available in GitHub Copilot Chat (Agent mode). + +
+ +
+Cursor + +Create `.cursor/mcp.json` in your project root: + +**STDIO mode** (JAR): + +```json +{ + "mcpServers": { + "solr-mcp": { + "command": "java", + "args": ["-jar", "/absolute/path/to/solr-mcp-1.0.0-SNAPSHOT.jar"], + "env": { "SOLR_URL": "http://localhost:8983/solr/" } + } + } +} +``` + +**HTTP mode:** + +```json +{ + "mcpServers": { + "solr-mcp": { + "url": "http://localhost:8080/mcp" + } + } +} +``` + +Or use **Cursor Settings > Features > MCP Servers > Add New MCP Server**. + +
+ +
+JetBrains IDEs + +Create `.junie/mcp.json` in your project root: + +**STDIO mode** (JAR): + +```json +{ + "mcpServers": { + "solr-mcp": { + "command": "java", + "args": ["-jar", "/absolute/path/to/solr-mcp-1.0.0-SNAPSHOT.jar"], + "env": { "SOLR_URL": "http://localhost:8983/solr/" } + } + } +} +``` + +**HTTP mode:** + +```json +{ + "mcpServers": { + "solr-mcp": { + "url": "http://localhost:8080/mcp" + } + } +} +``` + +Or use **Settings > Tools > AI Assistant > MCP Servers > Add**. + +
+ +
+MCP Inspector + +```bash +npx @modelcontextprotocol/inspector +``` + +**HTTP:** connect to `http://localhost:8080/mcp` + +**STDIO:** command `java`, arguments `-jar /absolute/path/to/solr-mcp-1.0.0-SNAPSHOT.jar` + +
+ +## Configuration + +| Variable | Description | Default | +|----------|-------------|---------| +| `SOLR_URL` | Solr base URL | `http://localhost:8983/solr/` | +| `PROFILES` | Transport mode: `stdio` or `http` | `stdio` | +| `SECURITY_ENABLED` | Enable OAuth2 authentication (HTTP only) | `false` | +| `OAUTH2_ISSUER_URI` | OAuth2 issuer URL (Auth0, Keycloak, Okta) | โ€” | +| `OTEL_SAMPLING_PROBABILITY` | Tracing sampling rate (0.0โ€“1.0) | `1.0` | +| `OTEL_TRACES_URL` | OTLP collector endpoint | `http://localhost:4317` | + ## Security (OAuth2) The Solr MCP server supports OAuth2 authentication when running in HTTP mode, providing secure access control for your @@ -345,6 +495,8 @@ For complete setup instructions, see [docs/security/auth0.md](docs/security/auth | `add-fields` | Add one or more fields to a Solr collection schema (additive only; existing fields cannot be modified) | | `get-schema` | Retrieve schema information for a collection | +Every tool advertises MCP behavior hints (`readOnlyHint`, `destructiveHint`, `idempotentHint`) so clients can build sensible approval UX โ€” `search` and the metadata tools are read-only, indexing is destructive but idempotent, schema modification is additive. + ## Available MCP Resources MCP Resources provide a way to expose data that can be read by MCP clients. The Solr MCP Server provides the following resources: @@ -362,6 +514,19 @@ The `solr://{collection}/schema` resource supports autocompletion for the `{coll ![MCP Inspector Resource Completion](images/mcp-inspector-resource-completion.png) +## Available MCP Prompts + +Slash-command-style workflow templates. The framework wraps each prompt's return value as a single user-role `PromptMessage` directing the LLM through a canonical Solr workflow. + +| Prompt | Arguments | Purpose | +|---|---|---| +| `explore-collections` | โ€” | Read-only walkthrough: list collections and characterise each by stats and health | +| `setup-collection` | `name`, `purpose` (optional) | Validate a name, pick configset / shards / replication factor, create the collection, verify it | +| `view-schema` | `collection` | Read-only schema walkthrough | +| `design-schema` | `collection`, `datasetDescription`, `sampleDocument` (optional) | Inspect schema, choose field types, apply additive schema changes | +| `index-data` | `collection`, `format` (`json` / `csv` / `xml`), `sample` (optional) | Verify the target schema, pick the right indexing tool, confirm the result | +| `search-collection` | `collection`, `question` | Translate a natural-language question into a Solr query | + ## Screenshots - Claude Desktop (STDIO): @@ -504,10 +669,13 @@ We welcome contributions! - Start here: [CONTRIBUTING.md](CONTRIBUTING.md) -## Support +## Community -- Issues: https://github.com/apache/solr-mcp/issues -- Discussions: https://github.com/apache/solr-mcp/discussions +- **Website:** https://solr.apache.org/mcp +- **Slack:** [`#solr-mcp`](https://the-asf.slack.com/archives/C09TVG3BM1P) in the `the-asf` workspace +- **Mailing lists:** Shared with Apache Solr โ€” see [mailing lists](https://solr.apache.org/community.html#mailing-lists-chat) +- **Issues:** https://github.com/apache/solr-mcp/issues +- **Discussions:** https://github.com/apache/solr-mcp/discussions ## License From a2691ec701c9cf463e409dfd3bb9794127236ee8 Mon Sep 17 00:00:00 2001 From: adityamparikh Date: Sun, 14 Jun 2026 00:39:36 -0400 Subject: [PATCH 09/10] docs: slim README to a "how to use" guide; relocate build/SBOM detail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refocuses README.md on end-user "how to use" content and moves developer/operational detail into the docs that own it, so the README stays scannable and the deep material has a single home. README.md - Lean structure: value prop -> Quick start (start Solr, build, connect a client, try it) -> Example prompts -> tools/resources/prompts tables -> essential config -> documentation hub -> community. - Ports the unique end-user content from #95: sample-data blurb (films/books) and the categorized Example prompts. - Defers the full per-client matrix to the in-repo site pages under docs/site/content/pages/mcp/ (from #143), security to docs/security/, observability to the site page, and build/Docker/native/SBOM to dev-docs. - Fixes carried over from the #147 review: HTTP_SECURITY_ENABLED (secured by default), not the non-existent SECURITY_ENABLED. CONTRIBUTING.md - Convert bare-path references to markdown links for consistency. - De-duplicate "Publishing to Maven Local" -> link the dev-docs section (CONTRIBUTING's own principle is "no duplication; detail lives in dev-docs"). dev-docs/DEPLOYMENT.md - New "Image variants โ€” why three images" section receiving the rationale moved out of the README: the image x mode matrix, run commands, why Jib builds the JVM image and Paketo the native variants (and why Paketo's JVM image breaks STDIO), and the native Claude Desktop config (latest-native-stdio). dev-docs/DEVELOPMENT.md - Move the consumer-facing SBOM content (where it ships, fetch via the actuator endpoint, scan with Trivy/Grype) here next to "Generating the SBOM locally", replacing the now-removed README section it linked to. Companion website-page fixes (.junie/mcp/mcp.json, VS Code type: http, HTTP_SECURITY_ENABLED) flow into #143. Depends on #143 for the docs/site/* links to resolve on main. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: adityamparikh --- CONTRIBUTING.md | 52 ++-- README.md | 673 +++++----------------------------------- dev-docs/DEPLOYMENT.md | 76 +++++ dev-docs/DEVELOPMENT.md | 27 +- 4 files changed, 194 insertions(+), 634 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 84695da0..e31558e7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,19 +6,19 @@ Thank you for your interest in contributing to the Solr MCP Server! This documen To avoid duplication, the environment setup, build/run/test workflows, and detailed developer guides live in the dev-docs folder: -- Development Guide (build, run, test, IDE, CI): dev-docs/DEVELOPMENT.md -- Architecture: dev-docs/ARCHITECTURE.md -- Deployment (Docker, HTTP vs STDIO): dev-docs/DEPLOYMENT.md -- Troubleshooting: dev-docs/TROUBLESHOOTING.md +- [Development Guide](dev-docs/DEVELOPMENT.md) โ€” build, run, test, IDE, CI +- [Architecture](dev-docs/ARCHITECTURE.md) โ€” project structure and design decisions +- [Deployment](dev-docs/DEPLOYMENT.md) โ€” Docker images, HTTP vs STDIO +- [Troubleshooting](dev-docs/TROUBLESHOOTING.md) โ€” common issues and solutions -If you're ready to contribute code, see Submitting Changes below. +If you're ready to contribute code, see [Submitting Changes](#submitting-changes) below. ## Code Style and Quality We use Spotless for code formatting and style enforcement. CI enforces `spotlessCheck` on pull requests. -- Commands and details: dev-docs/DEVELOPMENT.md#common-gradle-tasks -- Build system overview: dev-docs/DEVELOPMENT.md#build-system +- [Commands and details](dev-docs/DEVELOPMENT.md#common-gradle-tasks) +- [Build system overview](dev-docs/DEVELOPMENT.md#build-system) ### Coding Standards @@ -36,31 +36,15 @@ Every package is `@NullMarked` via `package-info.java`. Methods, parameters, and To keep this document concise, please see the Development Guide for all testing workflows and tips: -- Testing overview: dev-docs/DEVELOPMENT.md#testing -- Unit tests: dev-docs/DEVELOPMENT.md#unit-tests -- Integration tests: dev-docs/DEVELOPMENT.md#integration-tests -- Docker image tests: dev-docs/DEVELOPMENT.md#docker-integration-tests -- Coverage reports: dev-docs/DEVELOPMENT.md#testing +- [Testing overview](dev-docs/DEVELOPMENT.md#testing) +- [Unit tests](dev-docs/DEVELOPMENT.md#unit-tests) +- [Integration tests](dev-docs/DEVELOPMENT.md#integration-tests) +- [Docker image tests](dev-docs/DEVELOPMENT.md#docker-integration-tests) +- [Coverage reports](dev-docs/DEVELOPMENT.md#testing) ## Publishing to Maven Local -To install the project artifacts to your local Maven repository for testing or local development: - -```bash -./gradlew publishToMavenLocal -``` - -This publishes the following artifacts to `~/.m2/repository/org/apache/solr/solr-mcp/{version}/`: - -- `solr-mcp-{version}.jar` - Main application JAR -- `solr-mcp-{version}-sources.jar` - Source code for IDE navigation -- `solr-mcp-{version}-javadoc.jar` - API documentation -- `solr-mcp-{version}.pom` - Maven POM with dependencies - -This is useful when: -- Testing the library locally before publishing to a remote repository -- Sharing artifacts between local projects during development -- Verifying the published POM and artifact structure +To install the project artifacts (JAR, sources, javadoc, POM) to your local Maven repository for testing or local development, run `./gradlew publishToMavenLocal`. See [Publishing to Maven Local](dev-docs/DEVELOPMENT.md#publishing-to-maven-local) for the full artifact list and when it's useful. ## Submitting Changes @@ -135,11 +119,11 @@ test: add integration tests for collection service For implementation details and examples, see the Development Guide: -- Adding new MCP tools: dev-docs/DEVELOPMENT.md#adding-a-new-mcp-tool -- Adding a new document format: dev-docs/DEVELOPMENT.md#adding-a-new-document-format -- Project structure and architecture: dev-docs/ARCHITECTURE.md -- Dependencies and version catalogs: dev-docs/DEVELOPMENT.md#build-system -- Documentation practices: dev-docs/DEVELOPMENT.md#modifying-configuration +- [Adding new MCP tools](dev-docs/DEVELOPMENT.md#adding-a-new-mcp-tool) +- [Adding a new document format](dev-docs/DEVELOPMENT.md#adding-a-new-document-format) +- [Project structure and architecture](dev-docs/ARCHITECTURE.md) +- [Dependencies and version catalogs](dev-docs/DEVELOPMENT.md#build-system) +- [Documentation practices](dev-docs/DEVELOPMENT.md#modifying-configuration) ## Security Setup (HTTP Mode) diff --git a/README.md b/README.md index 3b8027a8..0fff0452 100644 --- a/README.md +++ b/README.md @@ -16,658 +16,140 @@ Just ask your AI assistant: This Spring AI [Model Context Protocol (MCP)](https://spec.modelcontextprotocol.io/) server exposes Solr operations as tools that any MCP-compatible AI client (Claude Desktop, Claude Code, VS Code/Copilot, Cursor, JetBrains) can invoke. -**[Website](https://solr.apache.org/mcp)** ยท -**[Quick Start](https://solr.apache.org/mcp/quick-start.html)** ยท -**[Client Setup](https://solr.apache.org/mcp/clients/claude-desktop.html)** ยท -**[Features](https://solr.apache.org/mcp/features.html)** - -## What's inside - -- ๐Ÿ” Search Solr collections with filtering, faceting, and pagination -- ๐Ÿ“ Index documents in JSON, CSV, and XML -- ๐Ÿ“ Create collections with configurable shards, replicas, and configsets -- ๐Ÿ“Š Manage collections and view statistics -- ๐Ÿ”ง Inspect schema -- ๐Ÿ”Œ Transports: STDIO (Claude Desktop) and HTTP (MCP Inspector) -- ๐Ÿ” OAuth2 security with Auth0 (HTTP mode only) -- ๐Ÿณ Docker images built locally with Jib (JVM) and Paketo (native) - -## Get started (users) - -> **Note:** Published container images are not yet available on a public registry. The `docker run` examples below use a **locally built** image โ€” build it first with `./gradlew jibDockerBuild` (produces `solr-mcp:latest`), or use the JAR path instead. See [Building Docker images](#building-docker-images). - -- Prerequisites: Java 25+, Docker (and Docker Compose), Git -- Clone the repo: - ```bash - git clone https://github.com/apache/solr-mcp.git && cd solr-mcp - ``` -- Start Solr with sample data: - ```bash - docker compose up -d - ``` -- Run the server: - - **STDIO mode (default)**: - - Gradle: - ```bash - ./gradlew bootRun - ``` - - JAR: - ```bash - ./gradlew build - java -jar build/libs/solr-mcp-1.0.0-SNAPSHOT.jar - ``` - - Docker: - ```bash - docker run -i --rm solr-mcp:latest - ``` - - **HTTP mode**: - - Gradle: - ```bash - PROFILES=http ./gradlew bootRun - ``` - - JAR: - ```bash - PROFILES=http java -jar build/libs/solr-mcp-1.0.0-SNAPSHOT.jar - ``` - - Docker: - ```bash - docker run -p 8080:8080 --rm -e PROFILES=http solr-mcp:latest - ``` - -For more options (custom SOLR_URL, Linux host networking) see the [Deployment Guide](dev-docs/DEPLOYMENT.md). - -### Claude Desktop - -Add this to your Claude Desktop config (macOS path shown); then restart Claude. - -**STDIO mode (default)** - -Using Docker: -```json -{ - "mcpServers": { - "solr-mcp": { - "command": "docker", - "args": ["run", "-i", "--rm", "solr-mcp:latest"], - "env": { - "SOLR_URL": "http://localhost:8983/solr/" - } - } - } -} -``` - -Using JAR: - -```json -{ - "mcpServers": { - "solr-mcp": { - "command": "java", - "args": [ - "-jar", - "/absolute/path/to/solr-mcp-1.0.0-SNAPSHOT.jar" - ], - "env": { - "SOLR_URL": "http://localhost:8983/solr/" - } - } - } -} -``` +## Quick start -**HTTP mode** - -Using Docker: - -```json -{ - "mcpServers": { - "solr-mcp": { - "command": "docker", - "args": [ - "run", - "-p", - "8080:8080", - "--rm", - "solr-mcp:latest" - ], - "env": { - "PROFILES": "http", - "SOLR_URL": "http://localhost:8983/solr/" - } - } - } -} -``` - -Using JAR: - -```json -{ - "mcpServers": { - "solr-mcp": { - "command": "java", - "args": [ - "-jar", - "/absolute/path/to/solr-mcp-1.0.0-SNAPSHOT.jar" - ], - "env": { - "PROFILES": "http", - "SOLR_URL": "http://localhost:8983/solr/" - } - } - } -} -``` +**Prerequisites:** Java 25+, [Docker](https://docs.docker.com/get-docker/) and Docker Compose, Git. -**Connecting to a running HTTP server** - -If you already have the MCP server running in HTTP mode (via Gradle, JAR, or Docker), you can connect Claude Desktop to -it using `mcp-remote`: - -Running via Gradle: +#### 1. Start Solr with sample data ```bash -PROFILES=http ./gradlew bootRun +git clone https://github.com/apache/solr-mcp.git +cd solr-mcp +docker compose up -d ``` -Running locally (JAR): - -```bash -PROFILES=http java -jar build/libs/solr-mcp-1.0.0-SNAPSHOT.jar -``` +This starts Solr in SolrCloud mode with two sample collections: **films** (1,100+ movies) and **books** (empty, ready for indexing). Wait ~30 seconds, then verify at . -Running via Docker: +#### 2. Build the server ```bash -docker run -p 8080:8080 --rm -e PROFILES=http solr-mcp:latest -``` - -Then add to your `claude_desktop_config.json`: - -```json -{ - "mcpServers": { - "solr-mcp-http": { - "command": "npx", - "args": [ - "mcp-remote", - "http://localhost:8080/mcp" - ] - } - } -} -``` - -More configuration options: see the **Building Docker images** section below. - -### Claude Code - -Add Solr MCP to [Claude Code](https://docs.anthropic.com/en/docs/claude-code) using the CLI or by adding a `.mcp.json` file to your project root. - -**STDIO mode (default)** - -Using Docker (CLI): -```bash -claude mcp add --transport stdio solr-mcp -- docker run -i --rm solr-mcp:latest -``` - -Using JAR (CLI): -```bash -claude mcp add --transport stdio -e SOLR_URL=http://localhost:8983/solr/ solr-mcp -- java -jar /absolute/path/to/solr-mcp-1.0.0-SNAPSHOT.jar -``` - -Or add to your project's `.mcp.json`: - -Using Docker: -```json -{ - "mcpServers": { - "solr-mcp": { - "type": "stdio", - "command": "docker", - "args": ["run", "-i", "--rm", "solr-mcp:latest"], - "env": { - "SOLR_URL": "http://localhost:8983/solr/" - } - } - } -} -``` - -Using JAR: -```json -{ - "mcpServers": { - "solr-mcp": { - "type": "stdio", - "command": "java", - "args": ["-jar", "/absolute/path/to/solr-mcp-1.0.0-SNAPSHOT.jar"], - "env": { - "SOLR_URL": "http://localhost:8983/solr/" - } - } - } -} -``` - -**HTTP mode** - -Start the server first (pick one): -```bash -# Gradle -PROFILES=http ./gradlew bootRun - -# JAR -PROFILES=http java -jar build/libs/solr-mcp-1.0.0-SNAPSHOT.jar - -# Docker -docker run -p 8080:8080 --rm -e PROFILES=http solr-mcp:latest -``` - -Then add to Claude Code: -```bash -claude mcp add --transport http solr-mcp http://localhost:8080/mcp -``` - -Or add to `.mcp.json`: -```json -{ - "mcpServers": { - "solr-mcp": { - "type": "http", - "url": "http://localhost:8080/mcp" - } - } -} -``` - -### Other clients - -
-VS Code / GitHub Copilot - -Create `.vscode/mcp.json` in your project root: - -**STDIO mode** (JAR): - -```json -{ - "servers": { - "solr-mcp": { - "type": "stdio", - "command": "java", - "args": ["-jar", "/absolute/path/to/solr-mcp-1.0.0-SNAPSHOT.jar"], - "env": { "SOLR_URL": "http://localhost:8983/solr/" } - } - } -} -``` - -**HTTP mode:** - -```json -{ - "servers": { - "solr-mcp": { - "type": "sse", - "url": "http://localhost:8080/mcp" - } - } -} -``` - -After adding the configuration, Solr MCP tools are available in GitHub Copilot Chat (Agent mode). - -
- -
-Cursor - -Create `.cursor/mcp.json` in your project root: - -**STDIO mode** (JAR): - -```json -{ - "mcpServers": { - "solr-mcp": { - "command": "java", - "args": ["-jar", "/absolute/path/to/solr-mcp-1.0.0-SNAPSHOT.jar"], - "env": { "SOLR_URL": "http://localhost:8983/solr/" } - } - } -} -``` - -**HTTP mode:** - -```json -{ - "mcpServers": { - "solr-mcp": { - "url": "http://localhost:8080/mcp" - } - } -} +./gradlew build ``` -Or use **Cursor Settings > Features > MCP Servers > Add New MCP Server**. - -
+This produces `build/libs/solr-mcp-1.0.0-SNAPSHOT.jar`. -
-JetBrains IDEs +#### 3. Connect your AI client -Create `.junie/mcp.json` in your project root: - -**STDIO mode** (JAR): +Add the server to your MCP client. For **Claude Desktop**, edit +`~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or +`%APPDATA%\Claude\claude_desktop_config.json` (Windows), then restart Claude: ```json { "mcpServers": { "solr-mcp": { "command": "java", - "args": ["-jar", "/absolute/path/to/solr-mcp-1.0.0-SNAPSHOT.jar"], + "args": ["-jar", "/absolute/path/to/solr-mcp/build/libs/solr-mcp-1.0.0-SNAPSHOT.jar"], "env": { "SOLR_URL": "http://localhost:8983/solr/" } } } } ``` -**HTTP mode:** +Using a different client, or want STDIO/HTTP/Docker options? See the per-client guides: +**[Claude Code](docs/site/content/pages/mcp/clients/claude-code.md)** ยท +**[VS Code / Copilot](docs/site/content/pages/mcp/clients/vs-code.md)** ยท +**[Cursor](docs/site/content/pages/mcp/clients/cursor.md)** ยท +**[JetBrains](docs/site/content/pages/mcp/clients/jetbrains.md)** ยท +**[MCP Inspector](docs/site/content/pages/mcp/clients/mcp-inspector.md)**. -```json -{ - "mcpServers": { - "solr-mcp": { - "url": "http://localhost:8080/mcp" - } - } -} -``` - -Or use **Settings > Tools > AI Assistant > MCP Servers > Add**. - -
- -
-MCP Inspector - -```bash -npx @modelcontextprotocol/inspector -``` +#### 4. Try it out -**HTTP:** connect to `http://localhost:8080/mcp` +- *"What collections are available in Solr?"* +- *"Search the films collection for movies directed by Steven Spielberg"* +- *"Show me the schema for the films collection"* +- *"Index this JSON into the books collection: [{"id": "1", "title": "The Great Gatsby", "author": "F. Scott Fitzgerald"}]"* -**STDIO:** command `java`, arguments `-jar /absolute/path/to/solr-mcp-1.0.0-SNAPSHOT.jar` - -
- -## Configuration - -| Variable | Description | Default | -|----------|-------------|---------| -| `SOLR_URL` | Solr base URL | `http://localhost:8983/solr/` | -| `PROFILES` | Transport mode: `stdio` or `http` | `stdio` | -| `SECURITY_ENABLED` | Enable OAuth2 authentication (HTTP only) | `false` | -| `OAUTH2_ISSUER_URI` | OAuth2 issuer URL (Auth0, Keycloak, Okta) | โ€” | -| `OTEL_SAMPLING_PROBABILITY` | Tracing sampling rate (0.0โ€“1.0) | `1.0` | -| `OTEL_TRACES_URL` | OTLP collector endpoint | `http://localhost:4317` | +## Example prompts -## Security (OAuth2) +**Searching** +- *"Find sci-fi movies released after 2000 and show the genre breakdown"* +- *"Search films for movies with 'war' in the title, sorted by year"* +- *"Show me the top 5 most recent films"* -The Solr MCP server supports OAuth2 authentication when running in HTTP mode, providing secure access control for your -MCP tools. +**Indexing** +- *"Index this JSON into the books collection: [{"id": "1", "title": "1984", "author": "George Orwell"}]"* +- *"Create a new collection called products"* -### Features +**Managing** +- *"Is the films collection healthy?"* +- *"How many documents are in the films collection?"* +- *"Show me the schema for the films collection"* -- **OAuth2 Resource Server**: JWT token validation using Auth0 (or any OAuth2 provider) -- **HTTP Mode Only**: Security is only active when using the `http` profile -- **CORS Support**: Enabled for MCP Inspector compatibility -- **Machine-to-Machine**: Uses Client Credentials flow for service authentication +## What you can do -### Quick Setup - -1. **Configure Auth0** (see detailed guide: [docs/security/auth0.md](docs/security/auth0.md)) - - Create an Auth0 Application (Machine to Machine) - - Create an Auth0 API with your audience identifier - - Note your Domain, Client ID, Client Secret, and Audience - -2. **Set Environment Variable**: - ```bash - export OAUTH2_ISSUER_URI=https://your-tenant.auth0.com/ - export PROFILES=http - ``` - -3. **Run the Server**: - ```bash - ./gradlew bootRun - ``` - -4. **Get Access Token** (using convenience script): - ```bash - ./scripts/get-auth0-token.sh --domain your-tenant.auth0.com \ - --client-id YOUR_CLIENT_ID \ - --client-secret YOUR_CLIENT_SECRET \ - --audience https://solr-mcp-api - ``` - -5. **Use the Token**: - ```bash - curl -H "Authorization: Bearer YOUR_TOKEN" \ - http://localhost:8080/mcp - ``` - -For complete setup instructions, see [docs/security/auth0.md](docs/security/auth0.md) - -## Available MCP tools - -### Search +### Tools | Tool | Description | |------|-------------| | `search` | Full-text search with filtering, faceting, sorting, and pagination | - -### Indexing - -| Tool | Description | -|------|-------------| -| `index-json-documents` | Index documents from a JSON string into a Solr collection | -| `index-csv-documents` | Index documents from a CSV string into a Solr collection | -| `index-xml-documents` | Index documents from an XML string into a Solr collection | - -### Collections - -| Tool | Description | -|------|-------------| -| `create-collection` | Create a new Solr collection (configSet, numShards, replicationFactor optional โ€” default to `_default`, `1`, `1`) | +| `index-json-documents` | Index documents from a JSON string into a collection | +| `index-csv-documents` | Index documents from a CSV string into a collection | +| `index-xml-documents` | Index documents from an XML string into a collection | +| `create-collection` | Create a collection (configSet, numShards, replicationFactor optional โ€” default `_default`, `1`, `1`) | | `list-collections` | List all available Solr collections | | `get-collection-stats` | Get statistics and metrics for a collection | | `check-health` | Check the health status of a collection | - -### Schema - -| Tool | Description | -|------|-------------| -| `add-field-types` | Add one or more field types to a Solr collection schema (supports custom analyzers, DenseVectorField for semantic search, etc.) | -| `add-fields` | Add one or more fields to a Solr collection schema (additive only; existing fields cannot be modified) | +| `add-fields` | Add fields to a collection schema (additive only; existing fields cannot be modified) | +| `add-field-types` | Add field types โ€” custom analyzers, `DenseVectorField` for semantic search, etc. | | `get-schema` | Retrieve schema information for a collection | Every tool advertises MCP behavior hints (`readOnlyHint`, `destructiveHint`, `idempotentHint`) so clients can build sensible approval UX โ€” `search` and the metadata tools are read-only, indexing is destructive but idempotent, schema modification is additive. -## Available MCP Resources - -MCP Resources provide a way to expose data that can be read by MCP clients. The Solr MCP Server provides the following resources: +### Resources | Resource URI | Description | |--------------|-------------| -| `solr://collections` | List of all Solr collections available in the cluster | -| `solr://{collection}/schema` | Schema definition for a specific collection (supports autocompletion) | - -### Resource Autocompletion +| `solr://collections` | List of all Solr collections in the cluster | +| `solr://{collection}/schema` | Schema definition for a collection (supports autocompletion) | -The `solr://{collection}/schema` resource supports autocompletion for the `{collection}` parameter. MCP clients can use the completion API to get a list of available collection names. +### Prompts -![MCP Inspector Resources](images/mcp-inspector-list-resources.png) - -![MCP Inspector Resource Completion](images/mcp-inspector-resource-completion.png) - -## Available MCP Prompts - -Slash-command-style workflow templates. The framework wraps each prompt's return value as a single user-role `PromptMessage` directing the LLM through a canonical Solr workflow. +Slash-command-style workflow templates that walk the assistant through a canonical Solr workflow. | Prompt | Arguments | Purpose | -|---|---|---| -| `explore-collections` | โ€” | Read-only walkthrough: list collections and characterise each by stats and health | -| `setup-collection` | `name`, `purpose` (optional) | Validate a name, pick configset / shards / replication factor, create the collection, verify it | +|--------|-----------|---------| +| `explore-collections` | โ€” | List collections and characterise each by stats and health | +| `setup-collection` | `name`, `purpose` (optional) | Pick configset / shards / replication factor, create the collection, verify it | | `view-schema` | `collection` | Read-only schema walkthrough | -| `design-schema` | `collection`, `datasetDescription`, `sampleDocument` (optional) | Inspect schema, choose field types, apply additive schema changes | -| `index-data` | `collection`, `format` (`json` / `csv` / `xml`), `sample` (optional) | Verify the target schema, pick the right indexing tool, confirm the result | +| `design-schema` | `collection`, `datasetDescription`, `sampleDocument` (optional) | Choose field types and apply additive schema changes | +| `index-data` | `collection`, `format` (`json` / `csv` / `xml`), `sample` (optional) | Pick the right indexing tool and confirm the result | | `search-collection` | `collection`, `question` | Translate a natural-language question into a Solr query | -## Screenshots - -- Claude Desktop (STDIO): - - ![Claude Desktop STDIO](images/claude-stdio.png) - -- MCP Inspector (HTTP): - - ![MCP Inspector HTTP](images/mcp-inspector-http.png) - -- MCP Inspector (HTTP with OAuth2 - Success): - - ![MCP Inspector HTTP OAuth Success](images/mcp-inspector-http-oauth-success.png) - -- MCP Inspector (HTTP with OAuth2 - Failure): - - ![MCP Inspector HTTP OAuth Failure](images/mcp-inspector-http-oauth-failure.png) - -- MCP Inspector (STDIO): - - ![MCP Inspector STDIO](images/mcp-inspector-stdio.png) - -## Building Docker images - -Three image artifacts cover the full transport ร— runtime matrix. The JVM -image is built with Jib (clean stdout, multi-arch); the native variants are -built with Paketo Cloud Native Buildpacks. - -| Image | Toolchain | Build command | STDIO | HTTP | -|------------------------------------|-----------|----------------------------------------------------------|-------|------| -| `solr-mcp:` | Jib | `./gradlew jibDockerBuild` | โœ… | โœ… | -| `solr-mcp:-native-stdio` | Paketo | `./gradlew bootBuildImage -Pnative` | โœ… | โŒ | -| `solr-mcp:-native-http` | Paketo | `./gradlew bootBuildImage -Pnative -Pprofile=http` | โŒ | โœ… | - -### Run commands - -```bash -# STDIO โ€” Jib JVM (default profile is stdio) -docker run -i --rm \ - -e SOLR_URL=http://host.docker.internal:8983/solr/ \ - solr-mcp:latest - -# STDIO โ€” native (faster startup, smaller image) -docker run -i --rm \ - -e SOLR_URL=http://host.docker.internal:8983/solr/ \ - solr-mcp:latest-native-stdio - -# HTTP โ€” Jib JVM -docker run -p 8080:8080 --rm \ - -e PROFILES=http \ - -e SOLR_URL=http://host.docker.internal:8983/solr/ \ - solr-mcp:latest - -# HTTP โ€” native -docker run -p 8080:8080 --rm \ - -e PROFILES=http \ - -e SOLR_URL=http://host.docker.internal:8983/solr/ \ - solr-mcp:latest-native-http -``` - -### Why three images - -- **Jib's JVM image is dual-mode** because Jib uses a clean `java -jar` - entrypoint with no launcher script. Stdout stays clean for MCP STDIO, - and runtime `PROFILES=http` switches to web mode. -- **Paketo's JVM image is unsuitable for stdio** โ€” its `libjvm` helpers - (memory calculator, NMT, ca-certificates) write 6 lines to stdout before - the JVM, breaking MCP's JSON-RPC stream. Verified end-to-end by - `DockerImageMcpClientStdioIntegrationTest` (Spring AI MCP client times - out on `initialize()`). Filed upstream as - [paketo-buildpacks/libjvm#482](https://github.com/paketo-buildpacks/libjvm/issues/482). - We use Jib for the JVM image instead. -- **Native images must AOT-pin to one profile.** Spring AOT bakes - `spring.main.web-application-type` into the binary at AOT time. Activating - both profiles picks `servlet` (http overrides stdio), which forces Tomcat - to start regardless of the runtime `PROFILES` value, breaking stdio. So - we ship one native image per transport. - -### Claude Desktop (native, STDIO) - -```json -{ - "mcpServers": { - "solr-mcp": { - "command": "docker", - "args": [ - "run", "-i", "--rm", - "-e", "SOLR_URL=http://host.docker.internal:8983/solr/", - "solr-mcp:latest-native" - ] - } - } -} -``` - -See [docs/specs/graalvm-native-image.md](docs/specs/graalvm-native-image.md) for the native image design and known risks. - -## Supply chain & SBOM - -Every released JAR and Docker image ships a [CycloneDX](https://cyclonedx.org/) 1.6 Software Bill of Materials so downstream consumers can audit and scan the dependency graph. - -### Where the SBOM lives - -- **Inside every JAR and image:** `META-INF/sbom/application.cdx.json` โ€” embedded by the Spring Boot Gradle plugin at build time. The Jib JVM image (`solr-mcp:`) and both Paketo native images (`solr-mcp:-native-stdio`, `solr-mcp:-native-http`) all package the bootJar contents, so the SBOM ships with every distribution channel. -- **HTTP endpoint** (`http` profile only): `GET /actuator/sbom/application` returns the same SBOM as `application/vnd.cyclonedx+json`. -- **GitHub Releases:** the release workflow attaches `solr-mcp-.cdx.json` to every official ASF release. -- **CI artifacts:** every `Build and Publish` run uploads `solr-mcp-sbom` (CycloneDX JSON) to the workflow run page; downloadable for 30 days. - -### Fetch the SBOM - -From a running HTTP-mode server: - -```bash -curl -s http://localhost:8080/actuator/sbom/application > application.cdx.json -``` - -To generate the SBOM locally from source, see [dev-docs/DEVELOPMENT.md#generating-the-sbom-locally](dev-docs/DEVELOPMENT.md#generating-the-sbom-locally). - -### Scan the SBOM +## Configuration -```bash -# Trivy -trivy sbom application.cdx.json +The server reads configuration from environment variables. The essentials: -# Grype -grype sbom:application.cdx.json -``` +| Variable | Description | Default | +|----------|-------------|---------| +| `SOLR_URL` | Solr base URL | `http://localhost:8983/solr/` | +| `PROFILES` | Transport mode: `stdio` (default, for Claude Desktop) or `http` (remote / multi-client) | `stdio` | -Both tools natively consume CycloneDX 1.6 and report CVEs against the listed components. +Running in **HTTP mode** โ€” OAuth2, CORS, and the `HTTP_SECURITY_ENABLED` toggle (secured by default) โ€” is covered in the [security docs](docs/security/). Tracing and metrics env vars (`OTEL_SAMPLING_PROBABILITY`, `OTEL_TRACES_URL`) are covered in [Observability](docs/site/content/pages/mcp/observability.md). ## Documentation -- Security model: [STDIO transport](docs/security/stdio.md) ยท [HTTP transport](docs/security/http.md) -- OAuth2 provider setup: [Auth0](docs/security/auth0.md) ยท [Keycloak](docs/security/keycloak.md) -- [GraalVM native image spec](docs/specs/graalvm-native-image.md) - -## Contributing +**Using it** +- [Quick start](docs/site/content/pages/mcp/quick-start.md) ยท [Client setup](docs/site/content/pages/mcp/clients/) โ€” Claude Desktop, Claude Code, VS Code, Cursor, JetBrains, MCP Inspector +- [Observability](docs/site/content/pages/mcp/observability.md) โ€” OpenTelemetry traces, metrics, logs +- Security: [STDIO model](docs/security/stdio.md) ยท [HTTP model](docs/security/http.md) ยท OAuth2 setup: [Auth0](docs/security/auth0.md) ยท [Keycloak](docs/security/keycloak.md) -We welcome contributions! +**Developing it** +- [Development guide](dev-docs/DEVELOPMENT.md) โ€” build, run, test, IDE, native image, SBOM ยท [Architecture](dev-docs/ARCHITECTURE.md) +- [Deployment](dev-docs/DEPLOYMENT.md) โ€” Docker images, the three-image matrix, registries, Kubernetes ยท [Troubleshooting](dev-docs/TROUBLESHOOTING.md) +- [GraalVM native image spec](docs/specs/graalvm-native-image.md) ยท [Contributing](CONTRIBUTING.md) -- Start here: [CONTRIBUTING.md](CONTRIBUTING.md) +> **Container images:** published images are not yet available on a public registry. The Docker examples in the client guides use a **locally built** image โ€” build it with `./gradlew jibDockerBuild` (produces `solr-mcp:latest`). See [Building Docker images](dev-docs/DEPLOYMENT.md#docker-images-with-jib). ## Community @@ -679,15 +161,8 @@ We welcome contributions! ## License -Apache License 2.0 โ€” see LICENSE +Apache License 2.0 โ€” see [LICENSE](LICENSE). ## Acknowledgments -Built with: - -- Spring AI MCP โ€” https://spring.io/projects/spring-ai -- Apache Solr โ€” https://solr.apache.org/ -- Jib โ€” https://github.com/GoogleContainerTools/jib -- Paketo Cloud Native Buildpacks โ€” https://paketo.io/ -- Testcontainers โ€” https://www.testcontainers.org/ -- Spring AI MCP Security โ€” https://github.com/spring-ai-community/mcp-security \ No newline at end of file +Built with [Spring AI MCP](https://spring.io/projects/spring-ai), [Apache Solr](https://solr.apache.org/), [Jib](https://github.com/GoogleContainerTools/jib), [Paketo Buildpacks](https://paketo.io/), [Testcontainers](https://www.testcontainers.org/), and [Spring AI MCP Security](https://github.com/spring-ai-community/mcp-security). diff --git a/dev-docs/DEPLOYMENT.md b/dev-docs/DEPLOYMENT.md index 5963bbe2..93d44ee1 100644 --- a/dev-docs/DEPLOYMENT.md +++ b/dev-docs/DEPLOYMENT.md @@ -57,6 +57,82 @@ Docker images are built with multi-platform support for: Jib automatically selects the appropriate platform or builds the first specified platform. +## Image variants โ€” why three images + +Three image artifacts cover the full transport ร— runtime matrix. The JVM image +is built with **Jib** (clean stdout, multi-arch); the two native variants are +built with **Paketo Cloud Native Buildpacks**. + +| Image | Toolchain | Build command | STDIO | HTTP | +|------------------------------------|-----------|----------------------------------------------------------|-------|------| +| `solr-mcp:` | Jib | `./gradlew jibDockerBuild` | โœ… | โœ… | +| `solr-mcp:-native-stdio` | Paketo | `./gradlew bootBuildImage -Pnative` | โœ… | โŒ | +| `solr-mcp:-native-http` | Paketo | `./gradlew bootBuildImage -Pnative -Pprofile=http` | โŒ | โœ… | + +### Run commands + +```bash +# STDIO โ€” Jib JVM (default profile is stdio) +docker run -i --rm \ + -e SOLR_URL=http://host.docker.internal:8983/solr/ \ + solr-mcp:latest + +# STDIO โ€” native (faster startup, smaller image) +docker run -i --rm \ + -e SOLR_URL=http://host.docker.internal:8983/solr/ \ + solr-mcp:latest-native-stdio + +# HTTP โ€” Jib JVM +docker run -p 8080:8080 --rm \ + -e PROFILES=http \ + -e SOLR_URL=http://host.docker.internal:8983/solr/ \ + solr-mcp:latest + +# HTTP โ€” native +docker run -p 8080:8080 --rm \ + -e PROFILES=http \ + -e SOLR_URL=http://host.docker.internal:8983/solr/ \ + solr-mcp:latest-native-http +``` + +### Why two toolchains and three images + +- **Jib's JVM image is dual-mode** because Jib uses a clean `java -jar` + entrypoint with no launcher script. Stdout stays clean for MCP STDIO, and + runtime `PROFILES=http` switches to web mode โ€” one image serves both. +- **Paketo's JVM image is unsuitable for stdio** โ€” its `libjvm` helpers + (memory calculator, NMT, ca-certificates) write 6 lines to stdout before the + JVM starts, breaking MCP's JSON-RPC stream. Verified end-to-end by + `DockerImageMcpClientStdioIntegrationTest` (the Spring AI MCP client times out + on `initialize()`). Filed upstream as + [paketo-buildpacks/libjvm#482](https://github.com/paketo-buildpacks/libjvm/issues/482). + So we use Jib for the JVM image and Paketo only for the native variants. +- **Native images must AOT-pin to one profile.** Spring AOT bakes + `spring.main.web-application-type` into the binary at AOT time. Activating both + profiles picks `servlet` (http overrides stdio), which forces Tomcat to start + regardless of the runtime `PROFILES` value, breaking stdio. So we ship one + native image per transport. + +### Claude Desktop (native, STDIO) + +```json +{ + "mcpServers": { + "solr-mcp": { + "command": "docker", + "args": [ + "run", "-i", "--rm", + "-e", "SOLR_URL=http://host.docker.internal:8983/solr/", + "solr-mcp:latest-native-stdio" + ] + } + } +} +``` + +See [docs/specs/graalvm-native-image.md](../docs/specs/graalvm-native-image.md) +for the native image design and known risks. + ## Running Docker Containers ### STDIO Mode (Default) diff --git a/dev-docs/DEVELOPMENT.md b/dev-docs/DEVELOPMENT.md index 6f5f28d4..c3f57385 100644 --- a/dev-docs/DEVELOPMENT.md +++ b/dev-docs/DEVELOPMENT.md @@ -66,7 +66,32 @@ The build produces a [CycloneDX](https://cyclonedx.org/) 1.6 Software Bill of Ma cat build/reports/application.cdx.json ``` -For consuming and scanning the SBOM (HTTP endpoint, GitHub Release attachment, Trivy/Grype), see the [Supply chain & SBOM](../README.md#supply-chain--sbom) section of the README. +### Where the SBOM ships + +Every released JAR and Docker image carries a CycloneDX 1.6 SBOM so downstream consumers can audit and scan the dependency graph: + +- **Inside every JAR and image:** `META-INF/sbom/application.cdx.json` โ€” embedded by the Spring Boot Gradle plugin at build time. The Jib JVM image (`solr-mcp:`) and both Paketo native images (`solr-mcp:-native-stdio`, `solr-mcp:-native-http`) all package the bootJar contents, so the SBOM ships with every distribution channel. +- **HTTP endpoint** (`http` profile only): `GET /actuator/sbom/application` returns the SBOM as `application/vnd.cyclonedx+json`. +- **GitHub Releases:** `release-publish.yml` attaches `solr-mcp-.cdx.json` to every official ASF release. +- **CI artifacts:** every `Build and Publish` run uploads `solr-mcp-sbom` (CycloneDX JSON) to the workflow run page, retained for 30 days. + +### Consuming and scanning the SBOM + +Fetch from a running HTTP-mode server: + +```bash +curl -s http://localhost:8080/actuator/sbom/application > application.cdx.json +``` + +Scan it for CVEs (both tools natively consume CycloneDX 1.6): + +```bash +# Trivy +trivy sbom application.cdx.json + +# Grype +grype sbom:application.cdx.json +``` ## Running Locally From fa5c5141c565e15b02103c1b7973b40db1201327 Mon Sep 17 00:00:00 2001 From: adityamparikh Date: Sun, 14 Jun 2026 01:17:10 -0400 Subject: [PATCH 10/10] docs: document MCP completions and Solr version compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README: add a "Completions" subsection (the `{collection}` resource argument and the search-collection/index-data/view-schema/design-schema prompt arguments all autocomplete to live collection names), and a one-line Solr compatibility note linking the dev-docs section. - dev-docs/DEVELOPMENT.md: add "Solr Version Compatibility" under Testing โ€” the `-Dsolr.test.image` override, the tested-version matrix (8.11, 9.4, 9.9, 9.10, 10), and the Solr 10 caveats (mbeans removed โ†’ null cache/handler stats; SolrJ 10.x not yet on Maven Central). Ported from AGENTS.md so it lives in the developer docs, not just the agent guide. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: adityamparikh --- README.md | 11 +++++++++++ dev-docs/DEVELOPMENT.md | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/README.md b/README.md index 0fff0452..80607b6b 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ This Spring AI [Model Context Protocol (MCP)](https://spec.modelcontextprotocol. **Prerequisites:** Java 25+, [Docker](https://docs.docker.com/get-docker/) and Docker Compose, Git. +**Compatibility:** works with Apache Solr **8.11โ€“10** (the test suite runs against 9.9 by default โ€” see [Solr version compatibility](dev-docs/DEVELOPMENT.md#solr-version-compatibility)). + #### 1. Start Solr with sample data ```bash @@ -126,6 +128,15 @@ Slash-command-style workflow templates that walk the assistant through a canonic | `index-data` | `collection`, `format` (`json` / `csv` / `xml`), `sample` (optional) | Pick the right indexing tool and confirm the result | | `search-collection` | `collection`, `question` | Translate a natural-language question into a Solr query | +### Completions + +The server implements MCP argument autocompletion, so clients can suggest valid values as you type: + +- **Resource argument** โ€” the `{collection}` segment of `solr://{collection}/schema` completes to live collection names. +- **Prompt arguments** โ€” the `collection` argument of the `search-collection`, `index-data`, `view-schema`, and `design-schema` prompts completes to live collection names. + +Suggestions are matched case-insensitively by prefix and capped per request. + ## Configuration The server reads configuration from environment variables. The essentials: diff --git a/dev-docs/DEVELOPMENT.md b/dev-docs/DEVELOPMENT.md index c3f57385..0cb766a9 100644 --- a/dev-docs/DEVELOPMENT.md +++ b/dev-docs/DEVELOPMENT.md @@ -184,6 +184,26 @@ This runs tests tagged with `@Tag("docker-integration")` which verify: - Container stability - Solr connectivity +### Solr Version Compatibility + +Tests run against `solr:9.9-slim` by default. Point them at another Solr version with the `solr.test.image` system property: + +```bash +./gradlew test -Dsolr.test.image=solr:8.11-slim # Solr 8.11 +./gradlew test -Dsolr.test.image=solr:9.4-slim # Solr 9.4 +./gradlew test -Dsolr.test.image=solr:9.9-slim # Solr 9.9 (default) +./gradlew test -Dsolr.test.image=solr:9.10-slim # Solr 9.10 +./gradlew test -Dsolr.test.image=solr:10-slim # Solr 10 +``` + +**Tested compatible versions:** 8.11, 9.4, 9.9, 9.10, 10. + +**Solr 10 notes.** Solr 10 is fully supported with the JSON wire format. The `/admin/mbeans` +endpoint was removed in Solr 10, so `getCacheMetrics()`/`getHandlerMetrics()` catch +`RuntimeException` and return `null` โ€” `cacheStats`/`handlerStats` from `get-collection-stats` +are therefore always `null` on Solr 10 (a future migration to `/admin/metrics` will restore +them). SolrJ 10.x is not yet on Maven Central, so tests run SolrJ 9.x against a Solr 10 server. + ### Test with MCP Inspector The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) provides a web UI for testing: