diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4c957e08..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 @@ -28,35 +28,23 @@ 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: -- 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 @@ -131,21 +119,29 @@ 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) + +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 60b63569..80607b6b 100644 --- a/README.md +++ b/README.md @@ -2,522 +2,178 @@ # 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. - -## 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 with Jib - -## Get started (users) - -- Prerequisites: Java 25+, Docker (and Docker Compose), Git -- 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 ghcr.io/apache/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 ghcr.io/apache/solr-mcp:latest - ``` - -For more options (custom SOLR_URL, Linux host networking) see the Deployment Guide: 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", "ghcr.io/apache/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/" - } - } - } -} -``` - -**HTTP mode** +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. -Using Docker: +Instead of writing: -```json -{ - "mcpServers": { - "solr-mcp": { - "command": "docker", - "args": [ - "run", - "-p", - "8080:8080", - "--rm", - "ghcr.io/apache/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/" - } - } - } -} +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 ``` -**Connecting to a running HTTP server** +Just ask your AI assistant: -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`: +> *"Find sci-fi movies with 'star wars' in the title released after 2000, show me the genre breakdown, and sort by relevance."* -Running via Gradle: +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. -```bash -PROFILES=http ./gradlew bootRun -``` +## Quick start -Running locally (JAR): +**Prerequisites:** Java 25+, [Docker](https://docs.docker.com/get-docker/) and Docker Compose, Git. -```bash -PROFILES=http java -jar build/libs/solr-mcp-1.0.0-SNAPSHOT.jar -``` +**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)). -Running via Docker: +#### 1. Start Solr with sample data ```bash -docker run -p 8080:8080 --rm -e PROFILES=http ghcr.io/apache/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" - ] - } - } -} +git clone https://github.com/apache/solr-mcp.git +cd solr-mcp +docker compose up -d ``` -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. +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 . -**STDIO mode (default)** +#### 2. Build the server -Using Docker (CLI): ```bash -claude mcp add --transport stdio solr-mcp -- docker run -i --rm ghcr.io/apache/solr-mcp:latest +./gradlew build ``` -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 -``` +This produces `build/libs/solr-mcp-1.0.0-SNAPSHOT.jar`. -Or add to your project's `.mcp.json`: +#### 3. Connect your AI client -Using Docker: -```json -{ - "mcpServers": { - "solr-mcp": { - "type": "stdio", - "command": "docker", - "args": ["run", "-i", "--rm", "ghcr.io/apache/solr-mcp:latest"], - "env": { - "SOLR_URL": "http://localhost:8983/solr/" - } - } - } -} -``` +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: -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/" - } + "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** - -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 +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)**. -# Docker -docker run -p 8080:8080 --rm -e PROFILES=http ghcr.io/apache/solr-mcp:latest -``` +#### 4. Try it out -Then add to Claude Code: -```bash -claude mcp add --transport http solr-mcp 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"}]"* -Or add to `.mcp.json`: -```json -{ - "mcpServers": { - "solr-mcp": { - "type": "http", - "url": "http://localhost:8080/mcp" - } - } -} -``` +## 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: [security-docs/AUTH0_SETUP.md](security-docs/AUTH0_SETUP.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 [security-docs/AUTH0_SETUP.md](security-docs/AUTH0_SETUP.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 | -## Available MCP Resources +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. -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 - -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. - -![MCP Inspector Resources](images/mcp-inspector-list-resources.png) - -![MCP Inspector Resource Completion](images/mcp-inspector-resource-completion.png) +| `solr://collections` | List of all Solr collections in the cluster | +| `solr://{collection}/schema` | Schema definition for a collection (supports autocompletion) | -## Screenshots +### Prompts -- Claude Desktop (STDIO): +Slash-command-style workflow templates that walk the assistant through a canonical Solr workflow. - ![Claude Desktop STDIO](images/claude-stdio.png) +| Prompt | Arguments | Purpose | +|--------|-----------|---------| +| `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) | 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 | -- MCP Inspector (HTTP): +### Completions - ![MCP Inspector HTTP](images/mcp-inspector-http.png) +The server implements MCP argument autocompletion, so clients can suggest valid values as you type: -- MCP Inspector (HTTP with OAuth2 - Success): +- **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. - ![MCP Inspector HTTP OAuth Success](images/mcp-inspector-http-oauth-success.png) +Suggestions are matched case-insensitively by prefix and capped per request. -- MCP Inspector (HTTP with OAuth2 - Failure): +## Configuration - ![MCP Inspector HTTP OAuth Failure](images/mcp-inspector-http-oauth-failure.png) +The server reads configuration from environment variables. The essentials: -- 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 -``` +| 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` | -### 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 -``` - -From the local build (no server required): - -```bash -./gradlew cyclonedxBom -cat build/reports/application.cdx.json -``` - -### Scan the SBOM - -```bash -# Trivy -trivy sbom application.cdx.json - -# Grype -grype sbom:application.cdx.json -``` - -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 -- [Auth0 Setup (OAuth2 configuration)](security-docs/AUTH0_SETUP.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). -## 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 -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/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/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 a9624c18..0cb766a9 100644 --- a/dev-docs/DEVELOPMENT.md +++ b/dev-docs/DEVELOPMENT.md @@ -37,6 +37,62 @@ 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 + +### 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 +``` + +### 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 ### Start Solr @@ -128,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: @@ -142,6 +218,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 @@ -206,6 +319,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 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)