Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ Four service classes expose MCP tools via `@McpTool` annotations:
- **CollectionService** (`collection/`) - List collections, get stats, health checks
- **SchemaService** (`schema/`) - Schema introspection and additive modification (add-fields, add-field-types)

### Solr Agent Skill

`src/main/resources/skills/solr/` holds an Agent Skill (`SKILL.md` + `references/`) about this
server's tools, not about Solr: the Solr Reference Guide covers Solr itself. Keep it to server
behaviour that the tool descriptions, prompts and server instructions do not already say.
`SolrSkillFormatTest` enforces the Agent Skills format and the SEP-2640 size limits. When tool
behaviour changes (error messages, response wording, id handling), update the skill in the same PR.
Serving it over MCP is tracked in #280.

### Document Creators and pass-throughs

Only the formats Solr cannot parse itself go through server-side document creators
Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,18 @@ Slash-command-style workflow templates that walk the assistant through a canonic
| `index-data` | `collection`, `format` (`json` / `csv` / `xml`), `sample` (optional) | Pick the right indexing tool and confirm the result |
| `search-collection` | `collection`, `question` | Translate a natural-language question into a Solr query |

### Skill

`src/main/resources/skills/solr/` is an [Agent Skill](https://agentskills.io/specification) for this
server's tools: what to know before writing data (shared `_default` configset, document ids, how field
names and values are stored), how the indexing tools respond, how to read the server's errors, and
what differs across Solr versions. It does not teach Solr itself; the Solr Reference Guide covers that.

To use it today, copy the directory into your client's skills location, for example
`cp -r src/main/resources/skills/solr ~/.claude/skills/`. Serving it to clients over MCP itself, once
the protocol and SDK support the Skills extension, is tracked in
[#280](https://github.com/apache/solr-mcp/issues/280).

### Completions

The server implements MCP argument autocompletion, so clients can suggest valid values as you type:
Expand Down
10 changes: 7 additions & 3 deletions docs/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,13 @@ re-derive it imperfectly every call:

Keep the server for deterministic, security-sensitive, multi-step operations
and for non-Claude clients (it's an Apache incubating project for *any* MCP
client). Add a thin Solr skill for query and faceting know-how. The skill
makes the agent better at *using* Solr; the server makes the dangerous parts
*safe and repeatable*.
client). Pair it with a thin skill. The repo ships one
(`src/main/resources/skills/solr/`) that covers what is specific to this
server's tools; Solr itself (query syntax, analyzers, schema design) is what the
[Solr Reference Guide](https://solr.apache.org/guide/solr/latest/) is for, and
an LLM can reach it through its own knowledge, web search or a docs-lookup tool.
The skill makes the agent better at *using this server*; the server makes the
dangerous parts *safe and repeatable*.

## Doesn't an MCP server cost more context than a skill?

Expand Down
66 changes: 66 additions & 0 deletions src/main/resources/skills/solr/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
name: solr
description: Use when searching, indexing, or managing Apache Solr collections through the Solr MCP server (tools such as search, index-json-documents, get-schema, add-fields, create-collection, list-collections), or when the user asks about their Solr data, collections, or schema. Explains how the server's tools behave, what to know before writing data, how their results and errors read, and where results depend on the Solr version. It does not teach Solr itself; for query syntax, analysis, and schema design use the Solr Reference Guide.
license: Apache-2.0
metadata:
version: "1.0"
---

# Solr MCP server

This skill covers what is specific to this server's tools. It is not a Solr tutorial. For anything
about Solr itself (query parsers, analyzers, field types, faceting, relevance), consult the
[Solr Reference Guide](https://solr.apache.org/guide/solr/latest/) through your own knowledge,
web search, or a documentation-lookup tool, and match the guide version to the Solr in use.

## Which tool

| Task | Tool |
|---|---|
| See what collections exist | `list-collections` (also the `solr://collections` resource) |
| See fields and types | `get-schema` (also the `solr://{collection}/schema` resource) |
| Search, filter, facet, sort, page | `search` |
| Index JSON / CSV / Solr update XML / Markdown | `index-json-documents`, `index-csv-documents`, `index-xml-documents`, `index-markdown-documents` |
| Add fields or field types | `add-fields`, `add-field-types` |
| Create a collection | `create-collection` |
| Point a name at collections | `list-aliases`, `create-alias`, `delete-alias` |
| Size, query timings, cache and handler metrics | `get-collection-stats` |
| Is the collection reachable | `check-health` |

Pick the index tool by input format; do not convert JSON or CSV to Markdown to index it.

## Before you write data

1. **Collections share a schema by default.** `create-collection` uses the `_default` configset
unless told otherwise, and collections created that way share one schema. A field added with
`add-fields`, or created by Solr while indexing, on one collection shows up on the others, and a
later `add-fields` for the same name fails because the field already exists. Run `get-schema`
on a collection before assuming it is empty, and choose field names and types with the whole
cluster in mind.
2. **Give every document a stable `id`.** Without one, Solr's default update chain generates a random
id for JSON, CSV and XML documents, so sending the same payload twice creates duplicates.
Markdown documents without an `id` get one derived from their text: identical text replaces
itself, edited text becomes a new document.
3. **Field names and values are not always stored as sent.** Nested JSON objects are flattened with
underscores, and objects or arrays nested inside a JSON array are dropped. Fields that Solr
creates while indexing hold arrays when read back. Run `get-schema` before querying fields you
did not create. Details are in `references/indexing.md`.
4. **Each index call commits.** The documents are searchable when the call returns; there is no
commit tool and none is needed.

## Reading results

- `check-health` reports an unhealthy or unknown collection in its result, not as a tool error.
- `get-collection-stats` can return `null` for `cacheStats` and `handlerStats`. That is expected.
- A tool call that fails with a protocol-level error and no message usually means Solr could not be
reached or is misconfigured, not that the request was wrong.

## References

Open a reference only when the task needs it.

- `references/indexing.md` - what the `index-*` tools do to your data, how responses read, partial
failures, and field-type surprises.
- `references/troubleshooting.md` - the server's error messages, what each means, and the next call.
- `references/solr-versions.md` - what differs across the supported Solr versions (9.4 to 10) as
seen through the tools.
68 changes: 68 additions & 0 deletions src/main/resources/skills/solr/references/indexing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# What the index tools do to your data

The four `index-*` tools send the payload to Solr in one update request that also commits, so the
documents are searchable when the tool returns. For what fields, types and analyzers mean in Solr,
see the Solr Reference Guide.

## Ids and re-sending

- JSON, CSV and XML: whatever `id` you give is used. Without one, Solr's default update chain
assigns a random id, so re-sending creates new documents instead of replacing old ones.
- Markdown: a document without an `id` gets one derived from its text. The same text replaces
itself; an edited document is a new one. Put a stable `id` in the front matter to update in place.

## JSON

- Each object becomes one document. Field names are used as sent.
- Nested objects are flattened with underscores: `{"author": {"name": "X"}}` is indexed as
`author_name`.
- Inside an array, only scalar items are kept. Objects and arrays nested in an array are dropped
without a warning, and an array left with no scalars produces no field.
- The response ends with the indexed field names (at most 50, then a count of the rest). The list is
built from every submitted document, including any that Solr then rejected, so a listed name may
not exist in the schema.

## CSV and XML

Forwarded to Solr's own update handlers; the server does not parse the content.

- `index-xml-documents` takes Solr `<add>` blocks of `<doc>` elements only: send
`<add><doc><field name="...">` content and nothing else.
- Solr's response carries no document count, so none is reported. Confirm with `search` or
`get-collection-stats`.
- When Solr rejects a payload, fix it and re-send. The server does not retry these individually.

## Markdown

- A single string that begins with `---` and contains several front-matter blocks, each preceded by a
blank line, is split into several documents. The `M` in the response counts documents after
splitting, not array elements.
- An empty or blank element fails the whole call with `Markdown input cannot be null or empty`.
- Front matter values are indexed as text, whatever YAML would infer: `2024` and `true` arrive as
strings. Sequences become multi-valued fields and nested mappings are flattened with underscores.
- Extracted fields: `title` (front matter `title`, else the first level-1 heading), `headings`
(every heading), `content` (body without front matter).
- Unlike the JSON tool, the response does not list the indexed field names.

## Reading the response

- JSON and Markdown: `Successfully indexed N of M documents into collection '...'`.
- CSV and XML: `Solr accepted the ... and committed it (status S, T ms)`.

## Partial failures (JSON and Markdown)

If Solr rejects the batch with a 400 (usually an unknown field or a value of the wrong type), the
server retries the documents one at a time so the valid ones are indexed, then commits. The response
gives only the count; it does not say which documents failed or why. If `N` is less than `M`, run
`get-schema`, compare the failing fields with the schema, fix the data or add the missing field, and
re-send. Documents without an `id` that were already accepted before the failure may be indexed
again on the retry, so unique ids matter most here. Failures other than a 400 (unknown collection,
authorisation, Solr unreachable) are not retried and are returned as errors.

## Fields you did not create

On the `_default` configset, Solr creates a field the first time it sees it and picks the type from
that first value. Fields created this way hold arrays when read back. A wrong guess cannot be
changed with this server, which cannot alter existing fields, and because collections created with
defaults share one schema (see `SKILL.md`), it affects every collection on that configset. When a
field's type matters, call `add-fields` first, then index.
28 changes: 28 additions & 0 deletions src/main/resources/skills/solr/references/solr-versions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Solr versions as seen through the tools

The server supports Solr 9.4 to 10. No tool reports the Solr version, so ask the user or the operator
if it matters. The tools, parameters and response shapes are the same across the range, except for
the metrics figures below.

## `get-collection-stats`: `cacheStats` and `handlerStats`

These two figures can be `null`, and the rest of the result (index statistics and query timings) is
still filled in. Report them as unavailable, not as a fault. They are `null` when:

- **The Solr is version 10.** Solr 10's metrics endpoint does not return the JSON the server reads,
so these figures are never available there.
- **The node the server talks to holds no replica of the collection.** The figures are read from
that node.
- **No cache or handler activity has been recorded yet.** Run a search and ask again.

## Not version-dependent

- `search`, the four `index-*` tools, `get-schema`, `add-fields`, `add-field-types`,
`create-collection`, and the alias tools behave the same on every supported version.
- The server requires SolrCloud (collection listing uses the Collections API); standalone Solr is not
supported on any version.

## Version-specific Solr behaviour

How a Solr feature differs between versions (a query parser, a field type, a Schema API command) is
Reference Guide material. Read the guide for the version in use rather than assuming.
50 changes: 50 additions & 0 deletions src/main/resources/skills/solr/references/troubleshooting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Troubleshooting tool errors

The server rewrites most Solr failures into short messages that say what to do next. Match the
message and make the call it names.

## No message at all

A call that fails with a protocol-level error and no text, from `search`, the `index-*` tools or
`get-schema`, is a failure to reach Solr or to talk to it (connection refused, I/O error), not a
problem with your arguments. Retry once; if it persists, tell the user the operator must check the
Solr URL, network and credentials. The `Search failed: unable to communicate with Solr` message is
written for this case but may not reach you.

## `search`

| Message starts with | Meaning | Next call |
|---|---|---|
| `Search failed: the collection was not found` | The `collection` does not exist | `list-collections`, then retry with a listed name |
| `Search failed: a referenced field is not defined` | `query`, `filterQueries`, `facetFields` or `sortClauses` names a field the schema lacks | `get-schema`; fields Solr created while indexing may carry suffixes such as `name_s` |
| `Search failed: Solr could not parse the query or filters` | Lucene syntax error, or a bad `{!...}` local param | Quote or escape special characters; check local params |
| `Search failed: Solr rejected the request` | Another 400 | Re-check syntax, filters, facets, sort, pagination; `get-schema` to verify fields |
| `Search failed: Solr denied access` | Solr returned 401 or 403 | Not fixable from the tool; the operator must check Solr credentials and permissions |
| `Search failed: unable to process the Solr response` | Any unexpected failure reading the response | If `facetFields` were set, `get-schema` to check them or retry without; else tell the user the operator must check server logs |
| `Search failed: Solr could not complete the request` | Any other Solr error | Retry later; if it persists, the operator must check Solr health and logs |

## `get-collection-stats`, `check-health`

- `Collection not found: <name>. Hint: call list-collections ...`: use a name from `list-collections`.
- Both accept a shard-style name such as `films_shard1_replica_n1` and resolve it to the collection.
- `check-health` does not raise an error for a down or unknown collection; it returns an unhealthy
status with the failure message. Read the result.
- `get-collection-stats` returns `null` for `cacheStats` and `handlerStats` in several situations.
This is expected, not an error (see `references/solr-versions.md`).

## Indexing

- `Successfully indexed N of M` with N < M: some documents were rejected individually. See
"Partial failures" in `references/indexing.md`.
- `XML input must be a Solr <add> block containing <doc> elements ...`: the payload is not a Solr
`<add>` block. Send documents only.
- `Failed to parse XML document`: the XML is malformed, or carries a DOCTYPE, which is refused.
- `Markdown input cannot be null or empty`: one of the array elements is blank.
- A CSV or XML payload that Solr rejects returns Solr's error text. Fix the payload and re-send.

## Schema and collections

- `add-fields` fails with an "already exists" message when the field is already in the schema,
including when it was added or guessed through another collection on the same configset.
- `Each field definition requires a non-empty string 'name'`: a field map has no `name`.
- `Collection name must not be blank`, `Alias name must not be blank`: an argument was empty.
Loading