feat(mcp): prompts + completion#137
Merged
Merged
Conversation
Spec and plan for adding add-fields and add-field-types MCP tools per issue #30. See docs/superpowers/specs/ and docs/superpowers/plans/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
… tools Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
…oject DTO convention @JsonIgnoreProperties, @JsonInclude(NON_NULL), and @jsonformat on the timestamp field match the pattern used by every record in Dtos.java. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
Package previously named "metadata" only contained schema-related types (SchemaService, SchemaUpdateResult, and their tests). Renaming to "schema" makes the package name accurate to its contents. Moves preserve git history via git mv. Imports updated in Main, MainTest, and McpToolRegistrationTest. Spec and plan paths updated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
Closes part of #30. Adds one or more fields per call via SolrJ's SchemaRequest.MultiUpdate. Input is List<Map<String, Object>> matching the Solr Schema API add-field JSON shape; validation is limited to collection name and non-empty list (Solr returns clear errors for malformed field defs). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
String.valueOf(null) returns the literal "null"; direct cast yields a real null on missing key, which makes the result honest about input shape (Solr's error surfaces before any result is returned). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
…elper Supports single analyzer, separate index/query analyzers, and non-analyzer field types like DenseVectorField. Manual conversion from flat input map to SolrJ FieldTypeDefinition because name/class go into attributes map and analyzers are typed sub-objects. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
…es test Aligns addFieldTypes_blankCollection_throws with the parallel addFields_blankCollection_throws test which already covers null + empty + whitespace. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
…flection Same pattern as the other @mcptool response records — invisible to AOT because MCP dispatches via Object. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
End-to-end against real Solr via Testcontainers. Verifies schema round-trip, custom analyzer behavior, vector field type registration, and error propagation on duplicate field / unknown type. SolrJ's MultiUpdate.process() throws natively on Schema API errors, so no explicit response-body inspection was needed in SchemaService. Note: SolrJ 10 moved SolrQuery to org.apache.solr.client.solrj.request.SolrQuery; vectorDimension attribute is returned as String by the schema API, handled via toString/parseInt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
Adds ordered tests 16-18 exercising the add-fields → index → search workflow through the MCP protocol against both HTTP and stdio transports. Also asserts add-fields and add-field-types appear in listTools output. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
…n tools Reflects the metadata→schema package rename and the new add-fields and add-field-types capabilities. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
…reflection GraalVM native test caught that Jackson's convertValue(map, AnalyzerDefinition.class) in SchemaService.toAnalyzerDefinition fails at runtime without reflection metadata. The spec anticipated this; adding both SolrJ types defensively. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
The get-schema MCP tool returns SolrJ's SchemaRepresentation, which Spring AI serializes to JSON for MCP clients. Without reflection metadata in the native image, the JSON Spring AI produces is missing the fields/fieldTypes/dynamicFields/copyFields arrays — silently breaking any consumer that introspects the schema. JVM tests didn't catch this because the pre-existing get-schema test only asserts the response is non-empty. The new end-to-end shows workflow in the next commit parses the schema JSON, which surfaced the gap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
Adds 9 new ordered MCP-protocol tests (orders 19-27) covering the full workflow an LLM would drive against the shows-collection use case from issue #30: 1. create-collection → "shows" 2. add-fields → 16 user-defined fields (title, platform, genres, release_year, ..., imdb_rating, description, tags) 3. index-json-documents → 61 docs from src/test/resources/shows.json (Netflix, Prime Video, HBO Max, Disney+, Apple TV+, Hulu, Peacock, Paramount+) 4. search → numFound=61 5. search + facet → platform facet returns Netflix=20, Prime=20 6. search + filter → multi-valued genres:Sci-Fi 7. search + keyword → description:apocalyptic with platform filter 8. get-schema → all 16 added fields present 9. get-collection-stats → numDocs=61 Because the test base class is reused by both HTTP and stdio MCP client transports and is also compiled into the GraalVM native test binary, these tests exercise all four combinations: - JVM + stdio (McpClientStdioIntegrationTest) - JVM + HTTP (McpClientIntegrationTest) - Native + stdio (via nativeTest -Pnative) - Native + HTTP (via nativeTest -Pnative) The shows collection inherits the same _default configset that prior tests in this class have already modified via schemaless indexing and add-fields against mcp-client-test. addShowsSchema() calls get-schema first to filter the desired field list to only the fields not already present in the shared configset's managed-schema — which is exactly what the add-fields tool description tells the LLM to do. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
…keys Two correctness fixes flagged by the Solr-expert review of PR apache#131: 1. MultiUpdate IS transactional, not sequential Per the Solr Schema API reference guide and SolrJ's SchemaRequest.MultiUpdate Javadoc, all commands in a single call either succeed or fail together. The previous tool descriptions wrongly told the LLM "commands run in input order; if one fails mid-batch, prior commands remain applied" / "partial application possible on failure" — which would lead an LLM to issue partial- recovery commands that double-apply on retry. Updated both add-fields and add-field-types descriptions and the spec doc to say "Solr's Schema API is transactional — if any command in the batch fails, none are applied." 2. toAnalyzerDefinition silently dropped unknown analyzer keys SolrJ's AnalyzerDefinition only exposes typed setters for charFilters, tokenizer, and filters. A naive objectMapper.convertValue(raw, AnalyzerDefinition.class) silently drops every other top-level analyzer key (class, luceneMatchVersion, positionIncrementGap, ...). This broke the valid single-class analyzer form {"analyzer":{"class":"solr.WhitespaceAnalyzer"}} which the Solr Ref Guide documents for the StandardAnalyzer / WhitespaceAnalyzer / KeywordAnalyzer / per-language analyzer (ArabicAnalyzer etc.) patterns. Rewrote the helper to manually split the map: charFilters, tokenizer, filters go through the typed setters; everything else is preserved via setAttributes(). Added a unit-test regression guard that serializes the captured MultiUpdate's wire body and asserts the analyzer-level class key is present. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
Apply the same drop-noise treatment to SchemaUpdateResult that PR apache#132 applies to Dtos.java records: - success (boolean) — always true on return; failures throw before reaching the result. - timestamp (Date) — always "now"; sub-second operation, MCP host records call timing already. After this change, SchemaUpdateResult is just (collection, addedNames): both fields carry real information. addedNames echoes the field names back so the LLM can confirm what landed. java.util.Date and com.fasterxml.jackson.annotation.JsonFormat imports drop from the record entirely. Test assertions referencing the dropped fields are removed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
…lows Adds one @McpPrompt method per service so MCP clients can expose slash-command-style entry points to the existing tools: - explore-and-create-collections (CollectionService) - design-schema (SchemaService) - index-data (IndexingService) - search-collection (SearchService) Each prompt returns a String that the framework wraps as a single user-role PromptMessage. Templates point the LLM at the right tools in the right order, embed user-supplied arguments (collection name, question, sample document, etc.) where provided, and cross-reference sibling prompts for next-step workflows. Discovery is automatic via Spring component scanning — no MCP server wiring changes are required, matching how existing @mcptool / @McpResource / @McpComplete methods are registered. Tests: - Unit tests on each *ServiceTest assert prompt bodies mention the expected tool names, embed user inputs (where applicable), and branch correctly on enum-style args (json/csv/xml). - McpClientIntegrationTestBase gains orders 28–32: listPrompts() returns the four names; getPrompt(...) for each prompt returns a non-empty first PromptMessage whose text references the right tools. These run in both stdio and HTTP transports via the existing subclasses. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
Addresses three review threads on PR #86: 1. Split the previous `explore-and-create-collections` (the literal "AND" in the name was a code smell — two independent intents, different argument shapes) into: - `explore-collections` (no args, read-only) - `setup-collection` (args: name, optional purpose; cross-references `design-schema` for the next step) 2. Add `view-schema` — read-only schema introspection prompt that walks the LLM through summarising fields, types, dynamic fields, copy fields, and the unique key. Distinct from the mutating `design-schema`. 3. Conciseness / language-features pass: - Centralize prompt names in `PromptNames` so prompt bodies that cross-reference siblings cannot drift from the `@McpPrompt(name)` declarations. - Extract the duplicated "optional code-block section" pattern to `util.PromptText.optionalCodeBlock`. - Collapse the two parallel `switch` expressions in `indexDataPrompt` into one switch returning a `record IndexTool(name, paramName)`. Throw `IllegalArgumentException` for unknown formats instead of soft-warning in the prompt text. - Interpolate `DEFAULT_CONFIGSET` / `DEFAULT_NUM_SHARDS` / `DEFAULT_REPLICATION_FACTOR` into `setup-collection` so the prompt text and the tool stay in lockstep. - Replace `list.get(0)` with `list.getFirst()` in `extractFirstMessageText`, `extractText`, and `assertNotError`. Tests updated: - Unit tests reorganised around the new prompt set; added coverage for `view-schema`, `setup-collection` (with and without purpose), and the `IllegalArgumentException` on unknown format. - `McpClientIntegrationTestBase` orders 28–34 list and fetch all six prompts via the real MCP client, referencing `PromptNames` constants rather than literal strings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
…op redundant null checks Addresses three follow-up review points: 1. Drop PromptNames central class. Six string constants used in ~4 cross-references and 6 annotations is not enough complexity to justify a separate file + import everywhere. The prompt name IS the public MCP contract; if it changes, the change is intentional and grep finds the references. Inlining literals reads better at the call site. 2. Use Java text blocks for the JSON sample strings in the new prompt unit tests (IndexingServiceTest, SchemaServiceTest) so the JSON is readable instead of a sea of escaped quotes. Assert the embedded sample by direct equality with the source text block, not against a re-escaped literal. 3. Remove the redundant `assertNotNull(messages)` and `assertNotNull(text)` from `extractFirstMessageText`. These run inside the `@NullMarked` package `org.apache.solr.mcp.server`, where the McpSchema record accessor `result.messages()` and `TextContent.text()` are non-null by JSpecify semantics — the asserts were dead defensive code. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
The existing @McpComplete handler for solr://{collection}/schema returned
every collection regardless of what the user had typed, which defeats the
point of an autocomplete suggestion for clients with a resource-template
picker. Accept the CompleteRequest.CompleteArgument so the handler can
filter case-insensitively by prefix, sort for stable ordering, and cap the
response at 100 suggestions to avoid pathological payloads on large
clusters.
Adds unit tests for prefix matching, case insensitivity, the wrong
argument-name guard, null argument, the result cap, and the
listCollections failure path; an annotation-binding registration test;
and an end-to-end integration test that calls completeCompletion through
a real McpSyncClient and asserts the live collection appears in the
results.
Apply @PreAuthorize("isAuthenticated()") to all six @McpPrompt endpoints so they match the existing pattern on @mcptool, @McpResource, and @McpComplete. Spring's proxy-based security is bypassed by self-invocation, so each entry point must carry its own annotation. Without this, the sweeping invariant in McpToolRegistrationTest (introduced alongside the completion prefix-filter work) fails when the prompt and completion changes land together. Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com> # Conflicts: # src/test/java/org/apache/solr/mcp/server/McpClientIntegrationTestBase.java # src/test/java/org/apache/solr/mcp/server/collection/CollectionServiceTest.java
…dempotent)
MCP clients use these annotations to decide whether to prompt for user
approval before invoking a tool. Without them, every call has to be
treated as worst-case destructive, which produces consent fatigue and
discourages clients from auto-allowing safe reads.
Hints applied:
- search, list-collections, get-collection-stats, check-health,
get-schema → readOnlyHint=true
- create-collection → destructiveHint=false (additive provisioning)
- index-{json,csv,xml}-documents → idempotentHint=true (Solr overwrites
by uniqueKey, so re-posting the same payload leaves the index in the
same end state)
NSA's "Model Context Protocol: Security Design Considerations"
(U/OO/6030316-26, May 2026) flags poor approval workflows as a top
risk; exposing these hints is the server-side enabler clients need to
build sensible approval UX.
Test: extends McpClientIntegrationTestBase to assert each hint flows
through to listTools output, so both HTTP and stdio transports verify
the wire-level annotations.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
…prompts The existing @McpComplete bound only to the schema resource template, so MCP Inspector's completion/complete with a ref/prompt reference (e.g. search-collection) returned -32602 AsyncCompletionSpecification not found. Add @McpComplete(prompt = ...) handlers for the four prompts that take a 'collection' argument (search-collection, index-data, view-schema, design-schema), each delegating to the existing completeCollection logic. Route both @McpPrompt(name=...) and @McpComplete(prompt=...) through a new PromptNames constants class so a typo or rename surfaces as a compile error instead of a silent runtime failure. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
…ite hints Configure Spotless's Eclipse JDT formatter to wrap multi-argument annotations one-arg-per-line via M_ONE_PER_LINE_SPLIT (alignment value 48). Single-arg annotations stay on one line because M_FORCE is intentionally omitted. This makes @mcptool / @McpPrompt / @McpResource / @McpArg / @McpToolParam behavior hints and descriptive copy readable at a glance instead of being crammed onto a 120-column line. Side effect: a handful of @SpringBootTest annotations in tests rewrap the same way, which is consistent. The search() @mcptool's text block masks the formatter's column counter, so that single declaration is wrapped behind '// @Formatter:off' to match the others. Also corrects two missing behavior hints surfaced while auditing: - add-fields and add-field-types are additive-only schema operations. Per the MCP spec the default destructiveHint is true, so both tools were advertising 'may perform destructive updates' when they only ever add. Both now declare destructiveHint=false. Verified against the MCP tool annotation defaults: readOnlyHint = false (default) destructiveHint = true (default; meaningful only when readOnly=false) idempotentHint = false (default; meaningful only when readOnly=false) Final per-tool audit: list-collections, get-collection-stats, check-health, search, get-schema - readOnlyHint=true create-collection - destructiveHint=false (additive; second call fails) add-fields, add-field-types - destructiveHint=false (additive only) index-{json,csv,xml}-documents - idempotentHint=true (Solr upserts on id) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
…ured The secure-by-default change in 015eedd (PR apache#125) broke DockerImageHttpIntegrationTest under -Pnative -Pprofile=http: AOT bakes in the http.security.enabled=true SecurityFilterChain bean (the @ConditionalOnProperty decision is frozen at build time), and McpServerOAuth2Configurer.init() builds a NimbusJwtDecoder eagerly against the configured issuer URL during bean instantiation. With the placeholder default https://your-auth0-domain.auth0.com/, native-http exited code 1 before /actuator/health came up, while the JVM image was unaffected because its conditional is evaluated at runtime. Three changes work together: - application-http.properties: drop the placeholder Auth0 default. An unset OAUTH2_ISSUER_URI now resolves to an empty string instead of a URL that will never resolve. - HttpSecurityConfiguration: skip the .with(McpServerOAuth2Configurer ...) call when issuerUrl is blank. The authorizeHttpRequests() rules still apply, so every non-permitAll endpoint returns 401/403 — the chain is locked down, just without a bearer-token validator. Production deployments that actually configure OAUTH2_ISSUER_URI continue to wire the full validator path. - DockerImageHttpIntegrationTest: no longer needs an HTTP_SECURITY_ENABLED override (it would be ineffective in native anyway since the conditional is AOT-baked). Comment documents why no issuer URL is provided. Verified locally by rebuilding solr-mcp:1.0.0-SNAPSHOT-native-http and running it with no OAUTH2_ISSUER_URI — /actuator/health returns {"status":"UP"} where it previously crashed with "Unable to resolve the Configuration with the provided Issuer". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com> # Conflicts: # src/main/java/org/apache/solr/mcp/server/collection/CollectionService.java # src/main/java/org/apache/solr/mcp/server/indexing/IndexingService.java # src/main/java/org/apache/solr/mcp/server/schema/SchemaService.java # src/main/java/org/apache/solr/mcp/server/search/SearchService.java # src/test/java/org/apache/solr/mcp/server/McpClientIntegrationTestBase.java # src/test/java/org/apache/solr/mcp/server/schema/SchemaServiceTest.java
adityamparikh
added a commit
to adityamparikh/solr-mcp
that referenced
this pull request
Jun 5, 2026
Incorporates the upstream changes accumulated since the previous sb4 sync: - feat(mcp): prompts + completion (apache#137) — adapted McpPrompt/McpArg/McpComplete imports from org.springaicommunity.mcp.annotation.* to org.springframework.ai.mcp.annotation.* (Spring AI 2.0 package layout). - feat: schema modification tools add-fields / add-field-types (apache#131) — kept the upstream relocation from metadata/SchemaService.java to schema/SchemaService.java and re-applied the SB4 Jackson 3 (tools.jackson) + springframework.ai.mcp imports onto the new files. Old metadata/Schema* files removed. - feat(mcp): readOnly/destructive/idempotent tool behavior hints (apache#134). - Various security, indexing, and Solr-client fixes from upstream (apache#101, apache#107, apache#112, apache#120, apache#121, apache#123, apache#124, apache#125, apache#126, apache#129, apache#130, etc.). Also bumps Spring AI from 2.0.0-M7 to 2.0.0-M8 in libs.versions.toml. SB4-specific changes retained: - Spring Boot 4.0.6 (vs upstream's 3.5.14). - Jackson 3 — tools.jackson.databind throughout production and test code, including the new schema/, prompt/, and MCP-client test files imported from upstream. JsonDocumentCreator uses the injected ObjectMapper (upstream apache#101) resolved as tools.jackson.databind.ObjectMapper. - MCP annotation package springframework.ai.mcp.annotation (vs upstream's org.springaicommunity.mcp.annotation). - MCP STDIO client uses io.modelcontextprotocol.json.jackson3.JacksonMcpJsonMapper with tools.jackson.databind.json.JsonMapper instead of the Jackson 2 variant. - SB4 observability stack — spring-boot-starter-opentelemetry + the opentelemetry-logback-appender — kept; the OTel/Micrometer split from upstream SB 3.x was not re-introduced. AGENTS.md "Spring Boot 4 Notes" section preserved and updated to reference Spring AI 2.0.0-M8. - SolrConfig HTTP/1.1 + JSON response parser configuration kept (upstream's reorder was functionally equivalent). - SolrNativeHints retains the CoreStatusResponse and DefaultMetaProvider hints added on sb4; merged additively with upstream's new schema-request reflection hints (AnalyzerDefinition, FieldTypeDefinition, SchemaRepresentation). - DockerImageHttp/StdioIntegrationTest pick up upstream's image-tag suffix logic for native http/stdio variants while keeping the SB4 testHttpModeConfiguration TCP-reachability test. - build.gradle.kts adopts upstream's conditional (nativeBuild) graalvmNative block and per-profile native image tags (solr-mcp:<v>-native-stdio, solr-mcp:<v>-native-http) so the GraalVM section matches the docs. Test fix: CollectionServiceTest#completeCollection_WithNullValue_ReturnsAllSorted was rewritten as completeCollection_NullValueRejectedAtSdkBoundary — the MCP SDK in io.modelcontextprotocol >= 0.16 validates CompleteRequest.CompleteArgument.value as non-null at construction, so the previous test (which passed null to CollectionService#completeCollection) is no longer reachable through the SDK. ./gradlew build passes (346 tests, 7 skipped, 0 failed). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: adityamparikh <aditya.m.parikh@gmail.com>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
@McpPromptendpoints for the canonical Solr workflows and the matching@McpCompleteplumbing for both resource-template and prompt-argument completion, plus a small native-http boot fix that surfaced during integration testing.This consolidates work that lived in two fork-only PRs (adityamparikh/solr-mcp#86 + #87) plus the prompt-arg completion gap that only shows up once the two are combined:
@McpCompletewas only bound to the schema resource template, so MCP Inspector'scompletion/completewith aref/promptreference (e.g.search-collection) returned-32602 AsyncCompletionSpecification not found.What's in this PR
MCP prompts — guided workflows for the canonical Solr operations:
explore-collections,setup-collection(CollectionService)search-collection(SearchService)index-data(IndexingService)view-schema,design-schema(SchemaService)Every prompt has
@PreAuthorize("isAuthenticated()")so http-mode security applies; a registration test enforces that going forward.Resource-template completion —
@McpComplete(uri = "solr://{collection}/schema")filters collection names by the user-typed prefix (case-insensitive), sorts, and caps results at 100 so the suggestion list stays usable on large clusters.Prompt-argument completion —
@McpComplete(prompt = ...)handlers for every prompt that takes acollectionargument (search-collection,index-data,view-schema,design-schema). Each delegates to the existingcompleteCollectionlogic so behavior, sort order, and the 100-result cap are shared.setup-collection(new-name arg) andexplore-collections(no args) intentionally have no binding.PromptNamesconstants class — both@McpPrompt(name = …)and@McpComplete(prompt = …)reference the samepublic static final Stringconstant, so a typo or rename surfaces as a compile error instead of a silent runtime-32602. The registration test asserts the expected set of completion bindings against those same constants.Annotation formatting + behavior-hint audit — Spotless's Eclipse JDT formatter is configured to render multi-argument annotations one-arg-per-line (
alignment_for_arguments_in_annotation=48, no force flag, so single-arg annotations stay inline). Behavior-hint audit corrected two missing hints —add-fieldsandadd-field-typesnow declaredestructiveHint = falsebecause they're additive-only schema operations.Native-http boot fix —
015eedd(secure-by-default) interacted badly with Spring AOT:@ConditionalOnProperty(http.security.enabled, matchIfMissing=true)is evaluated at AOT build time, so the securedSecurityFilterChainbean is always selected in the native binary, andMcpServerOAuth2Configurer.init()buildsNimbusJwtDecodereagerly against the configured issuer URL — crashing native-http boot whenever the placeholderhttps://your-auth0-domain.auth0.com/default was in effect. JVM mode wasn't affected because its conditional runs at startup.Three coordinated changes:
application-http.properties— drop the placeholder Auth0 default; unsetOAUTH2_ISSUER_URInow resolves to empty.HttpSecurityConfiguration— guard the.with(McpServerOAuth2Configurer ...)call withStringUtils.hasText(issuerUrl). With no issuer set, every non-permitAll()endpoint still falls through to Spring Security's default 401/403, just without a bearer-token validator.DockerImageHttpIntegrationTest— comment documents the runtime-guard contract; noHTTP_SECURITY_ENABLED=falseoverride needed (it would be ineffective in native anyway).Test plan
./gradlew build— green locally../gradlew bootBuildImage -Pnative -Pprofile=http+ manualdocker run—/actuator/healthreturns{"status":"UP"}with noOAUTH2_ISSUER_URI(previously crashed with "Unable to resolve the Configuration with the provided Issuer").McpToolRegistrationTest—testCollectionCompletionsCoverSchemaResourceAndCollectionTakingPromptsasserts oneuribinding + fourpromptbindings viaPromptNamesconstants;everyMcpEndpointIsPreAuthorizedenforces@PreAuthorizeon every MCP entry point.McpClientIntegrationTest/McpClientStdioIntegrationTest— new end-to-end tests exercisePromptReference("search-collection")andPromptReference("view-schema")against a realMcpSyncClientand Testcontainers Solr. Both transports pass (39/39).🤖 Generated with Claude Code