This is an automated email from the ASF dual-hosted git repository.
epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-mcp.git
The following commit(s) were added to refs/heads/main by this push:
new f7c02d2 feat: add MCP resource and completion for listing Solr
collections and their schema (#31)
f7c02d2 is described below
commit f7c02d2cb466a69d1b5c93fce0224818aa4ebf8d
Author: Aditya Parikh <[email protected]>
AuthorDate: Wed Jan 7 06:38:46 2026 -0400
feat: add MCP resource and completion for listing Solr collections and
their schema (#31)
* feat: add MCP Resources for collections and schema
Add @McpResource and @McpComplete annotations from spring-ai-mcp-annotations
library to expose Solr metadata as MCP Resources:
- solr://collections: Lists all available Solr collections
- solr://{collection}/schema: Returns schema definition for a collection
The schema resource supports autocompletion for the {collection} parameter
using @McpComplete, allowing MCP clients to discover available collections.
Also adds JsonUtils utility class for consistent JSON serialization in
MCP Resource responses.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* docs: add MCP Inspector screenshots for resources
Add screenshots showing:
- MCP Inspector listing available resources
- Resource autocompletion for collection names
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* chore: remove useless comments
---------
Co-authored-by: Claude Opus 4.5 <[email protected]>
---
README.md | 17 ++++++
images/mcp-inspector-list-resources.png | Bin 0 -> 140711 bytes
images/mcp-inspector-resource-completion.png | Bin 0 -> 111983 bytes
.../mcp/server/metadata/CollectionService.java | 62 ++++++++++++++++++---
.../solr/mcp/server/metadata/SchemaService.java | 36 +++++++++++-
.../org/apache/solr/mcp/server/util/JsonUtils.java | 58 +++++++++++++++++++
.../mcp/server/metadata/CollectionServiceTest.java | 17 +++---
.../mcp/server/metadata/SchemaServiceTest.java | 12 ++--
8 files changed, 181 insertions(+), 21 deletions(-)
diff --git a/README.md b/README.md
index 011f94a..e50085b 100644
--- a/README.md
+++ b/README.md
@@ -235,6 +235,23 @@ For complete setup instructions, see
[docs/AUTH0_SETUP.md](docs/AUTH0_SETUP.md)
| `checkHealth` | Check the health status of a collection |
| `getSchema` | Retrieve schema information for a collection |
+## Available MCP Resources
+
+MCP Resources provide a way to expose data that can be read by MCP clients.
The Solr MCP Server provides the following resources:
+
+| 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.
+
+
+
+
+
## Screenshots
- Claude Desktop (STDIO):
diff --git a/images/mcp-inspector-list-resources.png
b/images/mcp-inspector-list-resources.png
new file mode 100644
index 0000000..0507164
Binary files /dev/null and b/images/mcp-inspector-list-resources.png differ
diff --git a/images/mcp-inspector-resource-completion.png
b/images/mcp-inspector-resource-completion.png
new file mode 100644
index 0000000..6ac2d54
Binary files /dev/null and b/images/mcp-inspector-resource-completion.png differ
diff --git
a/src/main/java/org/apache/solr/mcp/server/metadata/CollectionService.java
b/src/main/java/org/apache/solr/mcp/server/metadata/CollectionService.java
index 165b10b..34a9a59 100644
--- a/src/main/java/org/apache/solr/mcp/server/metadata/CollectionService.java
+++ b/src/main/java/org/apache/solr/mcp/server/metadata/CollectionService.java
@@ -16,12 +16,7 @@
*/
package org.apache.solr.mcp.server.metadata;
-import static org.apache.solr.mcp.server.metadata.CollectionUtils.*;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest;
@@ -31,15 +26,31 @@ import
org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.client.solrj.request.CoreAdminRequest;
import org.apache.solr.client.solrj.request.GenericSolrRequest;
import org.apache.solr.client.solrj.request.LukeRequest;
-import org.apache.solr.client.solrj.response.*;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+import org.apache.solr.client.solrj.response.CoreAdminResponse;
+import org.apache.solr.client.solrj.response.LukeResponse;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.client.solrj.response.SolrPingResponse;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.mcp.server.config.SolrConfigurationProperties;
+import org.springaicommunity.mcp.annotation.McpComplete;
+import org.springaicommunity.mcp.annotation.McpResource;
import org.springaicommunity.mcp.annotation.McpTool;
import org.springaicommunity.mcp.annotation.McpToolParam;
import org.springframework.stereotype.Service;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import static org.apache.solr.mcp.server.metadata.CollectionUtils.getFloat;
+import static org.apache.solr.mcp.server.metadata.CollectionUtils.getInteger;
+import static org.apache.solr.mcp.server.metadata.CollectionUtils.getLong;
+import static org.apache.solr.mcp.server.util.JsonUtils.toJson;
+
/**
* Spring Service providing comprehensive Solr collection management and
* monitoring capabilities for Model Context Protocol (MCP) clients.
@@ -242,6 +253,8 @@ public class CollectionService {
/** SolrJ client for communicating with Solr server */
private final SolrClient solrClient;
+ private final ObjectMapper objectMapper;
+
/**
* Constructs a new CollectionService with the required dependencies.
*
@@ -251,11 +264,44 @@ public class CollectionService {
*
* @param solrClient
* the SolrJ client instance for communicating with Solr
+ * @param objectMapper
+ * the Jackson ObjectMapper for JSON serialization
* @see SolrClient
* @see SolrConfigurationProperties
*/
- public CollectionService(SolrClient solrClient) {
+ public CollectionService(SolrClient solrClient, ObjectMapper
objectMapper) {
this.solrClient = solrClient;
+ this.objectMapper = objectMapper;
+ }
+
+ /**
+ * MCP Resource endpoint that returns a list of all available Solr
collections.
+ *
+ * <p>
+ * This resource provides a simple way for MCP clients to discover what
+ * collections are available in the Solr cluster. The returned JSON
contains an
+ * array of collection names.
+ *
+ * @return JSON string containing the list of collections
+ */
+ @McpResource(uri = "solr://collections", name = "solr-collections",
description = "List of all Solr collections available in the cluster", mimeType
= "application/json")
+ public String getCollectionsResource() {
+ return toJson(objectMapper, listCollections());
+ }
+
+ /**
+ * MCP Completion endpoint for collection name autocompletion.
+ *
+ * <p>
+ * Provides autocompletion support for the collection parameter in the
schema
+ * resource URI template. Returns all available collection names that
MCP
+ * clients can use to complete the {collection} placeholder.
+ *
+ * @return list of available collection names for autocompletion
+ */
+ @McpComplete(uri = "solr://{collection}/schema")
+ public List<String> completeCollectionForSchema() {
+ return listCollections();
}
/**
diff --git
a/src/main/java/org/apache/solr/mcp/server/metadata/SchemaService.java
b/src/main/java/org/apache/solr/mcp/server/metadata/SchemaService.java
index ea131f4..68e30a6 100644
--- a/src/main/java/org/apache/solr/mcp/server/metadata/SchemaService.java
+++ b/src/main/java/org/apache/solr/mcp/server/metadata/SchemaService.java
@@ -16,12 +16,16 @@
*/
package org.apache.solr.mcp.server.metadata;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.request.schema.SchemaRequest;
import org.apache.solr.client.solrj.response.schema.SchemaRepresentation;
+import org.springaicommunity.mcp.annotation.McpResource;
import org.springaicommunity.mcp.annotation.McpTool;
import org.springframework.stereotype.Service;
+import static org.apache.solr.mcp.server.util.JsonUtils.toJson;
+
/**
* Spring Service providing schema introspection and management capabilities
for
* Apache Solr collections.
@@ -122,8 +126,10 @@ public class SchemaService {
/** SolrJ client for communicating with Solr server */
private final SolrClient solrClient;
+ private final ObjectMapper objectMapper;
+
/**
- * Constructs a new SchemaService with the required SolrClient
dependency.
+ * Constructs a new SchemaService with the required dependencies.
*
* <p>
* This constructor is automatically called by Spring's dependency
injection
@@ -132,10 +138,36 @@ public class SchemaService {
*
* @param solrClient
* the SolrJ client instance for communicating with Solr
+ * @param objectMapper
+ * the Jackson ObjectMapper for JSON serialization
* @see SolrClient
*/
- public SchemaService(SolrClient solrClient) {
+ public SchemaService(SolrClient solrClient, ObjectMapper objectMapper) {
this.solrClient = solrClient;
+ this.objectMapper = objectMapper;
+ }
+
+ /**
+ * MCP Resource endpoint that returns the schema for a specified Solr
+ * collection.
+ *
+ * <p>
+ * This resource uses a URI template with {collection} placeholder that
can be
+ * completed using the {@code @McpComplete} endpoint in
CollectionService. The
+ * returned JSON contains the complete schema definition including
fields, field
+ * types, dynamic fields, and copy fields.
+ *
+ * @param collection
+ * the name of the collection to retrieve schema for
+ * @return JSON string containing the schema representation
+ */
+ @McpResource(uri = "solr://{collection}/schema", name =
"solr-collection-schema", description = "Schema definition for a Solr
collection including fields, field types, and copy fields", mimeType =
"application/json")
+ public String getSchemaResource(String collection) {
+ try {
+ return toJson(objectMapper, getSchema(collection));
+ } catch (Exception e) {
+ return "{\"error\": \"" + e.getMessage() + "\"}";
+ }
}
/**
diff --git a/src/main/java/org/apache/solr/mcp/server/util/JsonUtils.java
b/src/main/java/org/apache/solr/mcp/server/util/JsonUtils.java
new file mode 100644
index 0000000..6ecc3bc
--- /dev/null
+++ b/src/main/java/org/apache/solr/mcp/server/util/JsonUtils.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.mcp.server.util;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Utility class for JSON serialization operations.
+ *
+ * <p>
+ * Provides common JSON conversion methods used throughout the MCP server,
+ * particularly for MCP Resource responses that need to return JSON strings.
+ *
+ * @version 0.0.1
+ * @since 0.0.1
+ */
+public final class JsonUtils {
+
+ private JsonUtils() {
+ // Utility class - prevent instantiation
+ }
+
+ /**
+ * Converts an object to its JSON string representation.
+ *
+ * <p>
+ * Used by MCP Resource methods that need to return serialized JSON
responses.
+ * On serialization failure, returns an error JSON object.
+ *
+ * @param objectMapper
+ * the Jackson ObjectMapper for serialization
+ * @param obj
+ * the object to serialize
+ * @return JSON string representation, or error JSON on failure
+ */
+ public static String toJson(ObjectMapper objectMapper, Object obj) {
+ try {
+ return objectMapper.writeValueAsString(obj);
+ } catch (JsonProcessingException e) {
+ return "{\"error\": \"Failed to serialize response\"}";
+ }
+ }
+}
diff --git
a/src/test/java/org/apache/solr/mcp/server/metadata/CollectionServiceTest.java
b/src/test/java/org/apache/solr/mcp/server/metadata/CollectionServiceTest.java
index a5afc42..19888ae 100644
---
a/src/test/java/org/apache/solr/mcp/server/metadata/CollectionServiceTest.java
+++
b/src/test/java/org/apache/solr/mcp/server/metadata/CollectionServiceTest.java
@@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
+import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
@@ -61,9 +62,11 @@ class CollectionServiceTest {
private CollectionService collectionService;
+ private final ObjectMapper objectMapper = new ObjectMapper();
+
@BeforeEach
void setUp() {
- collectionService = new CollectionService(solrClient);
+ collectionService = new CollectionService(solrClient,
objectMapper);
}
// Constructor tests
@@ -76,7 +79,7 @@ class CollectionServiceTest {
void listCollections_WithCloudSolrClient_ShouldReturnCollections()
throws Exception {
// Given - This test verifies the service can be constructed
with
// CloudSolrClient
- CollectionService cloudService = new
CollectionService(cloudSolrClient);
+ CollectionService cloudService = new
CollectionService(cloudSolrClient, objectMapper);
// Note: This test cannot fully exercise listCollections()
because it requires
// mocking static methods in CollectionAdminRequest which
requires PowerMock or
@@ -702,7 +705,7 @@ class CollectionServiceTest {
when(cloudClient.request(any(), any())).thenReturn(response);
- CollectionService service = new CollectionService(cloudClient);
+ CollectionService service = new CollectionService(cloudClient,
objectMapper);
List<String> result = service.listCollections();
assertNotNull(result);
@@ -720,7 +723,7 @@ class CollectionServiceTest {
when(cloudClient.request(any(), any())).thenReturn(response);
- CollectionService service = new CollectionService(cloudClient);
+ CollectionService service = new CollectionService(cloudClient,
objectMapper);
List<String> result = service.listCollections();
assertNotNull(result);
@@ -732,7 +735,7 @@ class CollectionServiceTest {
CloudSolrClient cloudClient = mock(CloudSolrClient.class);
when(cloudClient.request(any(), any())).thenThrow(new
SolrServerException("Connection error"));
- CollectionService service = new CollectionService(cloudClient);
+ CollectionService service = new CollectionService(cloudClient,
objectMapper);
List<String> result = service.listCollections();
assertNotNull(result);
@@ -755,7 +758,7 @@ class CollectionServiceTest {
// Mock the solrClient request to return the response
when(solrClient.request(any(), any())).thenReturn(response);
- CollectionService service = new CollectionService(solrClient);
+ CollectionService service = new CollectionService(solrClient,
objectMapper);
List<String> result = service.listCollections();
assertNotNull(result);
@@ -768,7 +771,7 @@ class CollectionServiceTest {
void listCollections_NonCloudClient_Error() throws Exception {
when(solrClient.request(any(), any())).thenThrow(new
IOException("IO error"));
- CollectionService service = new CollectionService(solrClient);
+ CollectionService service = new CollectionService(solrClient,
objectMapper);
List<String> result = service.listCollections();
assertNotNull(result);
diff --git
a/src/test/java/org/apache/solr/mcp/server/metadata/SchemaServiceTest.java
b/src/test/java/org/apache/solr/mcp/server/metadata/SchemaServiceTest.java
index 56321b8..964c3a5 100644
--- a/src/test/java/org/apache/solr/mcp/server/metadata/SchemaServiceTest.java
+++ b/src/test/java/org/apache/solr/mcp/server/metadata/SchemaServiceTest.java
@@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
+import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrServerException;
@@ -43,6 +44,9 @@ class SchemaServiceTest {
@Mock
private SolrClient solrClient;
+ @Mock
+ private ObjectMapper objectMapper;
+
@Mock
private SchemaResponse schemaResponse;
@@ -53,13 +57,13 @@ class SchemaServiceTest {
@BeforeEach
void setUp() {
- schemaService = new SchemaService(solrClient);
+ schemaService = new SchemaService(solrClient, objectMapper);
}
@Test
void testSchemaService_InstantiatesCorrectly() {
// Given/When
- SchemaService service = new SchemaService(solrClient);
+ SchemaService service = new SchemaService(solrClient,
objectMapper);
// Then
assertNotNull(service, "SchemaService should be instantiated
correctly");
@@ -132,7 +136,7 @@ class SchemaServiceTest {
@Test
void testConstructor() {
// Test that constructor properly initializes the service
- SchemaService service = new SchemaService(solrClient);
+ SchemaService service = new SchemaService(solrClient,
objectMapper);
assertNotNull(service);
}
@@ -140,7 +144,7 @@ class SchemaServiceTest {
void testConstructor_WithNullClient() {
// Test constructor with null client
assertDoesNotThrow(() -> {
- new SchemaService(null);
+ new SchemaService(null, objectMapper);
});
}
}