Skip to content

feat(mcp): prompts + completion#137

Merged
epugh merged 30 commits into
apache:mainfrom
adityamparikh:combine-86-87
May 27, 2026
Merged

feat(mcp): prompts + completion#137
epugh merged 30 commits into
apache:mainfrom
adityamparikh:combine-86-87

Conversation

@adityamparikh

Copy link
Copy Markdown
Contributor

Summary

Adds @McpPrompt endpoints for the canonical Solr workflows and the matching @McpComplete plumbing 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: @McpComplete was only bound 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.

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 a collection argument (search-collection, index-data, view-schema, design-schema). Each delegates to the existing completeCollection logic so behavior, sort order, and the 100-result cap are shared. setup-collection (new-name arg) and explore-collections (no args) intentionally have no binding.

PromptNames constants class — both @McpPrompt(name = …) and @McpComplete(prompt = …) reference the same public static final String constant, 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-fields and add-field-types now declare destructiveHint = false because they're additive-only schema operations.

Native-http boot fix015eedd (secure-by-default) interacted badly with Spring AOT: @ConditionalOnProperty(http.security.enabled, matchIfMissing=true) is evaluated at AOT build time, so the secured SecurityFilterChain bean is always selected in the native binary, and McpServerOAuth2Configurer.init() builds NimbusJwtDecoder eagerly against the configured issuer URL — crashing native-http boot whenever the placeholder https://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; unset OAUTH2_ISSUER_URI now resolves to empty.
  • HttpSecurityConfiguration — guard the .with(McpServerOAuth2Configurer ...) call with StringUtils.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; no HTTP_SECURITY_ENABLED=false override needed (it would be ineffective in native anyway).

Test plan

  • ./gradlew build — green locally.
  • ./gradlew bootBuildImage -Pnative -Pprofile=http + manual docker run/actuator/health returns {"status":"UP"} with no OAUTH2_ISSUER_URI (previously crashed with "Unable to resolve the Configuration with the provided Issuer").
  • McpToolRegistrationTesttestCollectionCompletionsCoverSchemaResourceAndCollectionTakingPrompts asserts one uri binding + four prompt bindings via PromptNames constants; everyMcpEndpointIsPreAuthorized enforces @PreAuthorize on every MCP entry point.
  • McpClientIntegrationTest / McpClientStdioIntegrationTest — new end-to-end tests exercise PromptReference("search-collection") and PromptReference("view-schema") against a real McpSyncClient and Testcontainers Solr. Both transports pass (39/39).
  • CI matrix on the fork (build-and-publish + native stdio + native http) — green after the native-http fix.

🤖 Generated with Claude Code

adityamparikh and others added 30 commits May 17, 2026 01:20
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 adityamparikh changed the title feat(mcp): prompts + prompt-arg completion (combines #86 + #87) feat(mcp): prompts + completion May 27, 2026
@epugh epugh merged commit 58c0a4e into apache:main May 27, 2026
4 checks passed
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants