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 e4f223e feat(collection): add create-collection tool and dedicated
collection package (#57)
e4f223e is described below
commit e4f223e3552086d57f3b8730198e283e76790d57
Author: Aditya Parikh <[email protected]>
AuthorDate: Mon Mar 9 17:11:07 2026 -0400
feat(collection): add create-collection tool and dedicated collection
package (#57)
* feat(collection): add create-collection tool and dedicated package
Move CollectionService, CollectionUtils, and Dtos from the metadata
package into a new dedicated collection package. This separates
collection management from schema introspection (SchemaService stays
in metadata).
Add create-collection MCP tool to CollectionService:
- Accepts name (required), configSet, numShards, replicationFactor
- Defaults: configSet=_default, numShards=1, replicationFactor=1
- Uses CollectionAdminRequest.createCollection() for both SolrCloud
and standalone Solr via Http2SolrClient
- Returns CollectionCreationResult DTO with name, success, message,
and createdAt timestamp
Add CollectionCreationResult record to Dtos.java.
Update unit tests with correct 2-arg Mockito stubs to match
CollectionAdminRequest.process() call signature.
Add integration test asserting the new collection appears in
listCollections() after creation.
---------
Signed-off-by: adityamparikh <[email protected]>
Signed-off-by: Aditya Parikh <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
---
README.md | 33 +-
mydata/devnexus-2026.json | 1626 ++++++++++++++++++++
src/main/java/org/apache/solr/mcp/server/Main.java | 41 +-
.../CollectionService.java | 77 +-
.../{metadata => collection}/CollectionUtils.java | 9 +-
.../mcp/server/{metadata => collection}/Dtos.java | 26 +-
.../java/org/apache/solr/mcp/server/MainTest.java | 2 +-
.../solr/mcp/server/McpToolRegistrationTest.java | 2 +-
.../CollectionServiceIntegrationTest.java | 18 +-
.../CollectionServiceTest.java | 59 +-
.../CollectionUtilsTest.java | 2 +-
.../ConferenceEndToEndIntegrationTest.java | 161 ++
12 files changed, 1998 insertions(+), 58 deletions(-)
diff --git a/README.md b/README.md
index 9dfc30c..488254e 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@ A Spring AI Model Context Protocol (MCP) server that provides
tools for interact
- 🔍 Search Solr collections with filtering, faceting, and pagination
- 📝 Index documents in JSON, CSV, and XML
+- 📁 Create collections with configurable shards, replicas, and configsets
- 📊 Manage collections and view statistics
- 🔧 Inspect schema
- 🔌 Transports: STDIO (Claude Desktop) and HTTP (MCP Inspector)
@@ -307,14 +308,34 @@ For complete setup instructions, see
[docs/AUTH0_SETUP.md](docs/AUTH0_SETUP.md)
## Available MCP tools
+### Search
+
+| Tool | Description |
+|------|-------------|
+| `search` | Full-text search with filtering, faceting, sorting, and
pagination |
+
+### Indexing
+
+| Tool | Description |
+|------|-------------|
+| `index-json-documents` | Index documents from a JSON string into a Solr
collection |
+| `index-csv-documents` | Index documents from a CSV string into a Solr
collection |
+| `index-xml-documents` | Index documents from an XML string into a Solr
collection |
+
+### Collections
+
+| Tool | Description |
+|------|-------------|
+| `create-collection` | Create a new Solr collection (configSet, numShards,
replicationFactor optional — default to `_default`, `1`, `1`) |
+| `list-collections` | List all available Solr collections |
+| `get-collection-stats` | Get statistics and metrics for a collection |
+| `check-health` | Check the health status of a collection |
+
+### Schema
+
| Tool | Description |
|------|-------------|
-| `search` | Search Solr collections with advanced query options |
-| `index_documents` | Index documents from JSON, CSV, or XML |
-| `listCollections` | List all available Solr collections |
-| `getCollectionStats` | Get statistics and metrics for a collection |
-| `checkHealth` | Check the health status of a collection |
-| `getSchema` | Retrieve schema information for a collection |
+| `get-schema` | Retrieve schema information for a collection |
## Available MCP Resources
diff --git a/mydata/devnexus-2026.json b/mydata/devnexus-2026.json
new file mode 100644
index 0000000..8f7d050
--- /dev/null
+++ b/mydata/devnexus-2026.json
@@ -0,0 +1,1626 @@
+[
+ {
+ "id": "devnexus-2026-001",
+ "conference": "DevNexus 2026",
+ "title": "Event Driven Architecture & Event Streaming Workshop",
+ "speakers": ["Daniel Hinojosa"],
+ "track": "Workshop",
+ "room": "TBD",
+ "day": "Day 0",
+ "date": "2026-03-04",
+ "start_time": "09:00",
+ "start_datetime": "2026-03-04T09:00:00Z",
+ "session_type": "Workshop",
+ "tags": ["event-driven", "event-streaming", "architecture", "workshop"]
+ },
+ {
+ "id": "devnexus-2026-002",
+ "conference": "DevNexus 2026",
+ "title": "Fundamentals of Software Engineering In the Age of AI Workshop",
+ "speakers": ["Nathaniel Schutta", "Dan Vega"],
+ "track": "Practices",
+ "room": "302",
+ "day": "Day 0",
+ "date": "2026-03-04",
+ "start_time": "09:00",
+ "start_datetime": "2026-03-04T09:00:00Z",
+ "session_type": "Workshop",
+ "tags": ["software-engineering", "ai", "practices", "workshop"]
+ },
+ {
+ "id": "devnexus-2026-003",
+ "conference": "DevNexus 2026",
+ "title": "Hands-On: Building Agents with Spring AI, MCP, Java, and Amazon
Bedrock",
+ "speakers": ["James Ward", "Josh Long"],
+ "track": "Production Ready Spring",
+ "room": "312",
+ "day": "Day 0",
+ "date": "2026-03-04",
+ "start_time": "09:00",
+ "start_datetime": "2026-03-04T09:00:00Z",
+ "session_type": "Workshop",
+ "tags": ["spring-ai", "mcp", "java", "amazon-bedrock", "agents",
"workshop"]
+ },
+ {
+ "id": "devnexus-2026-004",
+ "conference": "DevNexus 2026",
+ "title": "Agentic, Assistive & Predictive AI Patterns",
+ "speakers": ["Rohit Bhardwaj"],
+ "track": "AI in Practice",
+ "room": "311",
+ "day": "Day 0",
+ "date": "2026-03-04",
+ "start_time": "13:00",
+ "start_datetime": "2026-03-04T13:00:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "agents", "patterns", "predictive-ai"]
+ },
+ {
+ "id": "devnexus-2026-005",
+ "conference": "DevNexus 2026",
+ "title": "Cruising Along with Java: From Java 9 to 25",
+ "speakers": ["Venkat Subramaniam"],
+ "track": "Core Java",
+ "room": "301",
+ "day": "Day 0",
+ "date": "2026-03-04",
+ "start_time": "13:00",
+ "start_datetime": "2026-03-04T13:00:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "java-25", "language-evolution", "jvm"]
+ },
+ {
+ "id": "devnexus-2026-006",
+ "conference": "DevNexus 2026",
+ "title": "Modernize Your Apps in Days with AI Agents",
+ "speakers": ["Brian Benz", "Ayan Gupta"],
+ "track": "Gen AI",
+ "room": "316",
+ "day": "Day 0",
+ "date": "2026-03-04",
+ "start_time": "13:00",
+ "start_datetime": "2026-03-04T13:00:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "agents", "modernization", "generative-ai"]
+ },
+ {
+ "id": "devnexus-2026-007",
+ "conference": "DevNexus 2026",
+ "title": "Networking Happy Hour",
+ "speakers": [],
+ "track": "Social",
+ "room": "TBD",
+ "day": "Day 0",
+ "date": "2026-03-04",
+ "start_time": "18:15",
+ "start_datetime": "2026-03-04T18:15:00Z",
+ "session_type": "Social",
+ "tags": ["networking", "social"]
+ },
+ {
+ "id": "devnexus-2026-008",
+ "conference": "DevNexus 2026",
+ "title": "It's Up To Java Developers to Fix Enterprise AI",
+ "speakers": ["Rod Johnson"],
+ "track": "Keynote",
+ "room": "411/412",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "09:00",
+ "start_datetime": "2026-03-05T09:00:00Z",
+ "session_type": "Keynote",
+ "tags": ["keynote", "java", "ai", "enterprise"]
+ },
+ {
+ "id": "devnexus-2026-009",
+ "conference": "DevNexus 2026",
+ "title": "Design Patterns for Multi-Agent Systems",
+ "speakers": ["Brian Sam-Bodden"],
+ "track": "AI Tools",
+ "room": "315",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-05T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "agents", "design-patterns", "multi-agent"]
+ },
+ {
+ "id": "devnexus-2026-010",
+ "conference": "DevNexus 2026",
+ "title": "AI for Busy Java Developers",
+ "speakers": ["Frank Greco"],
+ "track": "AI in Practice",
+ "room": "311",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-05T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "java", "practical"]
+ },
+ {
+ "id": "devnexus-2026-011",
+ "conference": "DevNexus 2026",
+ "title": "Unbundling of the Cloud Data Warehouse: Open Source Databases
and Data Lakes",
+ "speakers": ["Zoe Steinkamp"],
+ "track": "Architecture",
+ "room": "314",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-05T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["architecture", "data-warehouse", "open-source", "data-lakes",
"cloud"]
+ },
+ {
+ "id": "devnexus-2026-012",
+ "conference": "DevNexus 2026",
+ "title": "To Java 26 and Beyond!",
+ "speakers": ["Billy Korando"],
+ "track": "Core Java",
+ "room": "301",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-05T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "java-26", "language-evolution", "jvm"]
+ },
+ {
+ "id": "devnexus-2026-013",
+ "conference": "DevNexus 2026",
+ "title": "What's New in Spring Boot 4.0 & What's Arriving in 4.1",
+ "speakers": ["Phil Webb"],
+ "track": "Frameworks",
+ "room": "313",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-05T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "spring-boot", "spring-boot-4", "frameworks"]
+ },
+ {
+ "id": "devnexus-2026-014",
+ "conference": "DevNexus 2026",
+ "title": "Agents, Tools, and MCP, Oh My! Next-Level AI Concepts for
Developers",
+ "speakers": ["Jennifer Reif"],
+ "track": "Gen AI",
+ "room": "316",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-05T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "mcp", "agents", "generative-ai", "tools"]
+ },
+ {
+ "id": "devnexus-2026-015",
+ "conference": "DevNexus 2026",
+ "title": "The Mentorship Hub",
+ "speakers": ["Bruno Souza", "Luiz Real"],
+ "track": "Community",
+ "room": "Open-Source Cafe",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "09:00",
+ "start_datetime": "2026-03-05T09:00:00Z",
+ "session_type": "Community",
+ "tags": ["mentorship", "community", "career"]
+ },
+ {
+ "id": "devnexus-2026-016",
+ "conference": "DevNexus 2026",
+ "title": "Refactoring RN",
+ "speakers": ["Aaron McClennen"],
+ "track": "Practices",
+ "room": "302",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-05T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["refactoring", "practices", "code-quality"]
+ },
+ {
+ "id": "devnexus-2026-017",
+ "conference": "DevNexus 2026",
+ "title": "Life After Dad Joke Apps - Building \"Enterprise Grade\" AI
Frameworks for Java",
+ "speakers": ["Kevin Strohmeyer", "Josh Long", "Rod Johnson", "Adib
Saikali"],
+ "track": "Production Ready Spring",
+ "room": "312",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-05T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "java", "ai", "enterprise", "frameworks"]
+ },
+ {
+ "id": "devnexus-2026-018",
+ "conference": "DevNexus 2026",
+ "title": "Zero Migration Java: Stay Current Without Breaking Your App",
+ "speakers": ["Yee-Kang Chang"],
+ "track": "Security",
+ "room": "304",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-05T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "migration", "security", "compatibility"]
+ },
+ {
+ "id": "devnexus-2026-019",
+ "conference": "DevNexus 2026",
+ "title": "Building Engineers or Teaching Technicians?",
+ "speakers": ["Jonathon Graf"],
+ "track": "Tech Leadership",
+ "room": "303",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-05T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["leadership", "career", "education", "engineering-culture"]
+ },
+ {
+ "id": "devnexus-2026-020",
+ "conference": "DevNexus 2026",
+ "title": "How I Automated My Life with MCP Servers",
+ "speakers": ["Cedric Clyburn"],
+ "track": "Tools and Techniques",
+ "room": "305",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-05T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["mcp", "automation", "tools", "productivity"]
+ },
+ {
+ "id": "devnexus-2026-021",
+ "conference": "DevNexus 2026",
+ "title": "Brokk: An AI-Native Code Platform for Java, in Java",
+ "speakers": ["Jonathan Ellis"],
+ "track": "AI Tools",
+ "room": "315",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-05T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "java", "tools", "code-platform"]
+ },
+ {
+ "id": "devnexus-2026-022",
+ "conference": "DevNexus 2026",
+ "title": "The Wrong Reasons to Build an MCP Server",
+ "speakers": ["Daniel Oh"],
+ "track": "AI in Practice",
+ "room": "311",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-05T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["mcp", "ai", "practical", "architecture"]
+ },
+ {
+ "id": "devnexus-2026-023",
+ "conference": "DevNexus 2026",
+ "title": "JUnit 6 + Exploring the Testing Ecosystem",
+ "speakers": ["Jeanne Boyarsky"],
+ "track": "Core Java",
+ "room": "301",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-05T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "testing", "junit", "junit-6"]
+ },
+ {
+ "id": "devnexus-2026-024",
+ "conference": "DevNexus 2026",
+ "title": "Spring Data 4: Data Access Revisited",
+ "speakers": ["Chris Bono"],
+ "track": "Frameworks",
+ "room": "313",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-05T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "spring-data", "frameworks", "data-access"]
+ },
+ {
+ "id": "devnexus-2026-025",
+ "conference": "DevNexus 2026",
+ "title": "10 Tools & Tips to Upgrade Your Java Code with AI",
+ "speakers": ["Vinicius Senger", "Jonathan Vogel"],
+ "track": "Gen AI",
+ "room": "316",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-05T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "java", "tools", "generative-ai", "productivity"]
+ },
+ {
+ "id": "devnexus-2026-026",
+ "conference": "DevNexus 2026",
+ "title": "Lean Software Development",
+ "speakers": ["Leigh Griffin"],
+ "track": "Practices",
+ "room": "302",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-05T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["lean", "practices", "software-development", "agile"]
+ },
+ {
+ "id": "devnexus-2026-027",
+ "conference": "DevNexus 2026",
+ "title": "Modern API Versioning and Enterprise-Ready AI Enablement with
Spring Cloud Gateway",
+ "speakers": ["Spencer Gibb", "Chris Sterling"],
+ "track": "Production Ready Spring",
+ "room": "312",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-05T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "spring-cloud", "api", "api-versioning", "gateway",
"enterprise"]
+ },
+ {
+ "id": "devnexus-2026-028",
+ "conference": "DevNexus 2026",
+ "title": "Bootiful Spring Security",
+ "speakers": ["Josh Long", "Rob Winch"],
+ "track": "Security",
+ "room": "304",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-05T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "spring-security", "security"]
+ },
+ {
+ "id": "devnexus-2026-029",
+ "conference": "DevNexus 2026",
+ "title": "Modern Architectures for Software Development Leaders",
+ "speakers": ["Venkat Subramaniam"],
+ "track": "Tech Leadership",
+ "room": "303",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-05T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["architecture", "leadership", "software-development"]
+ },
+ {
+ "id": "devnexus-2026-030",
+ "conference": "DevNexus 2026",
+ "title": "Unlocking Engineering Productivity with IDE-Based Coding Agents",
+ "speakers": ["Josh Kurz"],
+ "track": "Tools and Techniques",
+ "room": "305",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-05T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "agents", "ide", "productivity", "tools"]
+ },
+ {
+ "id": "devnexus-2026-031",
+ "conference": "DevNexus 2026",
+ "title": "Fundamentals of Software Engineering In the Age of AI",
+ "speakers": ["Nathaniel Schutta", "Dan Vega"],
+ "track": "AI Tools",
+ "room": "315",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-05T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "software-engineering", "practices", "tools"]
+ },
+ {
+ "id": "devnexus-2026-032",
+ "conference": "DevNexus 2026",
+ "title": "10 Things I Hate About AI",
+ "speakers": ["Cody Frenzel", "Laurie Lay"],
+ "track": "AI in Practice",
+ "room": "311",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-05T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "practical", "critique"]
+ },
+ {
+ "id": "devnexus-2026-033",
+ "conference": "DevNexus 2026",
+ "title": "Java Cloud Optimization: Serverless, Native, and CRaC – Unleash
Peak Performance",
+ "speakers": ["Rustam Mehmandarov"],
+ "track": "Architecture",
+ "room": "314",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-05T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "cloud", "serverless", "native", "crac", "performance",
"architecture"]
+ },
+ {
+ "id": "devnexus-2026-034",
+ "conference": "DevNexus 2026",
+ "title": "Java Performance: Beyond Simple Request Latencies",
+ "speakers": ["John Ceccarelli", "Simon Ritter"],
+ "track": "Core Java",
+ "room": "301",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-05T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "performance", "jvm", "latency"]
+ },
+ {
+ "id": "devnexus-2026-035",
+ "conference": "DevNexus 2026",
+ "title": "Beyond SWE-Bench: Enterprise Java AI Agents and Real-World
Development Benchmarks",
+ "speakers": ["Mark Pollack"],
+ "track": "Frameworks",
+ "room": "313",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-05T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "agents", "java", "enterprise", "benchmarks", "spring"]
+ },
+ {
+ "id": "devnexus-2026-036",
+ "conference": "DevNexus 2026",
+ "title": "Building AI Agents with Spring & MCP",
+ "speakers": ["James Ward", "Josh Long"],
+ "track": "Gen AI",
+ "room": "316",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-05T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "spring-ai", "mcp", "agents", "generative-ai"]
+ },
+ {
+ "id": "devnexus-2026-037",
+ "conference": "DevNexus 2026",
+ "title": "Git Features You Aren't Using",
+ "speakers": ["Raju Gandhi"],
+ "track": "Practices",
+ "room": "302",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-05T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["git", "practices", "tools", "version-control"]
+ },
+ {
+ "id": "devnexus-2026-038",
+ "conference": "DevNexus 2026",
+ "title": "The Modern Spring Workflow: Enterprise-Ready Delivery and
AI-Boosted Coding with Tanzu",
+ "speakers": ["Chris Sterling", "Cora Iberkleid"],
+ "track": "Production Ready Spring",
+ "room": "312",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-05T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "tanzu", "ai", "enterprise", "cloud-native"]
+ },
+ {
+ "id": "devnexus-2026-039",
+ "conference": "DevNexus 2026",
+ "title": "Building Trustworthy and Reliable LLM Applications",
+ "speakers": ["Alex Soto", "Markus Eisele"],
+ "track": "Security",
+ "room": "304",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-05T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["llm", "ai", "security", "reliability", "trust"]
+ },
+ {
+ "id": "devnexus-2026-040",
+ "conference": "DevNexus 2026",
+ "title": "The Accidental Leader: How to Succeed When You Weren't Planning
to Lead",
+ "speakers": ["Emily Harden"],
+ "track": "Tech Leadership",
+ "room": "303",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-05T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["leadership", "career", "management"]
+ },
+ {
+ "id": "devnexus-2026-041",
+ "conference": "DevNexus 2026",
+ "title": "Hands-on Embabel",
+ "speakers": ["Rod Johnson"],
+ "track": "AI Tools",
+ "room": "315",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "14:50",
+ "start_datetime": "2026-03-05T14:50:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "embabel", "tools", "agents"]
+ },
+ {
+ "id": "devnexus-2026-042",
+ "conference": "DevNexus 2026",
+ "title": "Inside MCP: Live Protocol Messages, Real-Time Flows, and Smarter
Agents",
+ "speakers": ["David Parry"],
+ "track": "AI in Practice",
+ "room": "311",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "14:50",
+ "start_datetime": "2026-03-05T14:50:00Z",
+ "session_type": "Talk",
+ "tags": ["mcp", "ai", "agents", "protocol"]
+ },
+ {
+ "id": "devnexus-2026-043",
+ "conference": "DevNexus 2026",
+ "title": "Modular Monoliths: A Happy Middle",
+ "speakers": ["Raju Gandhi"],
+ "track": "Architecture",
+ "room": "314",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "14:50",
+ "start_datetime": "2026-03-05T14:50:00Z",
+ "session_type": "Talk",
+ "tags": ["architecture", "modular-monolith", "microservices"]
+ },
+ {
+ "id": "devnexus-2026-044",
+ "conference": "DevNexus 2026",
+ "title": "Beyond Default Settings: Optimizing Java on K8s with AI-Driven
Performance Tuning",
+ "speakers": ["Stefano Doni"],
+ "track": "Core Java",
+ "room": "301",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "14:50",
+ "start_datetime": "2026-03-05T14:50:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "kubernetes", "performance", "ai", "jvm"]
+ },
+ {
+ "id": "devnexus-2026-045",
+ "conference": "DevNexus 2026",
+ "title": "Supercharge Your Applications with Java, Graphs, and a Touch of
AI",
+ "speakers": ["Jennifer Reif", "Erin Schnabel"],
+ "track": "Frameworks",
+ "room": "313",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "14:50",
+ "start_datetime": "2026-03-05T14:50:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "graphs", "ai", "frameworks", "neo4j"]
+ },
+ {
+ "id": "devnexus-2026-046",
+ "conference": "DevNexus 2026",
+ "title": "From Monolith to AI Agent: Modernizing Java Systems with MCP",
+ "speakers": ["Theo Lebrun"],
+ "track": "Gen AI",
+ "room": "316",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "14:50",
+ "start_datetime": "2026-03-05T14:50:00Z",
+ "session_type": "Talk",
+ "tags": ["mcp", "ai", "java", "modernization", "agents", "generative-ai"]
+ },
+ {
+ "id": "devnexus-2026-047",
+ "conference": "DevNexus 2026",
+ "title": "Clear Up Messy Code with Refactoring Maneuvers in IntelliJ IDEA",
+ "speakers": ["Ted M. Young"],
+ "track": "Practices",
+ "room": "302",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "14:50",
+ "start_datetime": "2026-03-05T14:50:00Z",
+ "session_type": "Talk",
+ "tags": ["refactoring", "intellij", "practices", "code-quality", "ide"]
+ },
+ {
+ "id": "devnexus-2026-048",
+ "conference": "DevNexus 2026",
+ "title": "Building AI Agents with Spring AI and Tanzu Platform",
+ "speakers": ["Josh Long", "Adib Saikali"],
+ "track": "Production Ready Spring",
+ "room": "312",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "14:50",
+ "start_datetime": "2026-03-05T14:50:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "spring-ai", "agents", "tanzu", "ai"]
+ },
+ {
+ "id": "devnexus-2026-049",
+ "conference": "DevNexus 2026",
+ "title": "The Missing Protocol: How MCP Bridges LLMs and Data Streams",
+ "speakers": ["Viktor Gamov"],
+ "track": "Security",
+ "room": "304",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "14:50",
+ "start_datetime": "2026-03-05T14:50:00Z",
+ "session_type": "Talk",
+ "tags": ["mcp", "llm", "data-streaming", "kafka", "security"]
+ },
+ {
+ "id": "devnexus-2026-050",
+ "conference": "DevNexus 2026",
+ "title": "How to Run a 1 on 1 for Everyone (Not Just Managers!)",
+ "speakers": ["Alex Riviere"],
+ "track": "Tech Leadership",
+ "room": "303",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "14:50",
+ "start_datetime": "2026-03-05T14:50:00Z",
+ "session_type": "Talk",
+ "tags": ["leadership", "management", "one-on-one", "career"]
+ },
+ {
+ "id": "devnexus-2026-051",
+ "conference": "DevNexus 2026",
+ "title": "The Engineer's Guide to Socialization",
+ "speakers": ["Nerando Johnson"],
+ "track": "Tools and Techniques",
+ "room": "305",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "14:50",
+ "start_datetime": "2026-03-05T14:50:00Z",
+ "session_type": "Talk",
+ "tags": ["soft-skills", "career", "communication"]
+ },
+ {
+ "id": "devnexus-2026-052",
+ "conference": "DevNexus 2026",
+ "title": "The Evolution of Memory in Humans and AI Agents",
+ "speakers": ["Raphael De Lio"],
+ "track": "AI Tools",
+ "room": "315",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "15:50",
+ "start_datetime": "2026-03-05T15:50:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "agents", "memory", "tools"]
+ },
+ {
+ "id": "devnexus-2026-053",
+ "conference": "DevNexus 2026",
+ "title": "When One Agent Isn't Enough: Experiments with Multi-Agent AI",
+ "speakers": ["Kenneth Kousen"],
+ "track": "AI in Practice",
+ "room": "311",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "15:50",
+ "start_datetime": "2026-03-05T15:50:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "multi-agent", "agents", "practical"]
+ },
+ {
+ "id": "devnexus-2026-054",
+ "conference": "DevNexus 2026",
+ "title": "From Legacy to Cloud-Native: Effective and Scalable Java
Modernization with Automation",
+ "speakers": ["Emily Jiang"],
+ "track": "Architecture",
+ "room": "314",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "15:50",
+ "start_datetime": "2026-03-05T15:50:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "cloud-native", "modernization", "automation",
"architecture"]
+ },
+ {
+ "id": "devnexus-2026-055",
+ "conference": "DevNexus 2026",
+ "title": "Developer Career Masterplan: 15 Steps to Grow Beyond Senior
Developer",
+ "speakers": ["Bruno Souza"],
+ "track": "Core Java",
+ "room": "301",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "15:50",
+ "start_datetime": "2026-03-05T15:50:00Z",
+ "session_type": "Talk",
+ "tags": ["career", "java", "developer", "growth"]
+ },
+ {
+ "id": "devnexus-2026-056",
+ "conference": "DevNexus 2026",
+ "title": "What Every Spring Developer Should Know About Jakarta EE",
+ "speakers": ["Ivar Grimstad"],
+ "track": "Frameworks",
+ "room": "313",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "15:50",
+ "start_datetime": "2026-03-05T15:50:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "jakarta-ee", "frameworks", "java"]
+ },
+ {
+ "id": "devnexus-2026-057",
+ "conference": "DevNexus 2026",
+ "title": "Architecting Microservices for Agentic AI Integration",
+ "speakers": ["Rohit Bhardwaj"],
+ "track": "Gen AI",
+ "room": "316",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "15:50",
+ "start_datetime": "2026-03-05T15:50:00Z",
+ "session_type": "Talk",
+ "tags": ["microservices", "ai", "agents", "architecture", "generative-ai"]
+ },
+ {
+ "id": "devnexus-2026-058",
+ "conference": "DevNexus 2026",
+ "title": "The Ultimate Showdown of Database Migration Tools",
+ "speakers": ["Pasha Finkelshteyn", "Anton Arhipov"],
+ "track": "Practices",
+ "room": "302",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "15:50",
+ "start_datetime": "2026-03-05T15:50:00Z",
+ "session_type": "Talk",
+ "tags": ["database", "migration", "liquibase", "flyway", "practices"]
+ },
+ {
+ "id": "devnexus-2026-059",
+ "conference": "DevNexus 2026",
+ "title": "Enabling High-Throughput, Low-Latency Inference for Your AI
Applications",
+ "speakers": ["Cora Iberkleid"],
+ "track": "Production Ready Spring",
+ "room": "312",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "15:50",
+ "start_datetime": "2026-03-05T15:50:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "inference", "performance", "spring", "llm"]
+ },
+ {
+ "id": "devnexus-2026-060",
+ "conference": "DevNexus 2026",
+ "title": "Privacy in Design (PbD) in DevSecOps",
+ "speakers": ["Anitha Dakamarri"],
+ "track": "Security",
+ "room": "304",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "15:50",
+ "start_datetime": "2026-03-05T15:50:00Z",
+ "session_type": "Talk",
+ "tags": ["security", "privacy", "devsecops", "practices"]
+ },
+ {
+ "id": "devnexus-2026-061",
+ "conference": "DevNexus 2026",
+ "title": "Thriving in an Evolving Software Industry",
+ "speakers": ["Nathaniel Schutta", "Glenn Renfro"],
+ "track": "Tech Leadership",
+ "room": "303",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "15:50",
+ "start_datetime": "2026-03-05T15:50:00Z",
+ "session_type": "Talk",
+ "tags": ["career", "leadership", "software-industry"]
+ },
+ {
+ "id": "devnexus-2026-062",
+ "conference": "DevNexus 2026",
+ "title": "Developer Experience != Developer Productivity",
+ "speakers": ["Jeremy Meiss"],
+ "track": "Tools and Techniques",
+ "room": "305",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "15:50",
+ "start_datetime": "2026-03-05T15:50:00Z",
+ "session_type": "Talk",
+ "tags": ["developer-experience", "productivity", "tools"]
+ },
+ {
+ "id": "devnexus-2026-063",
+ "conference": "DevNexus 2026",
+ "title": "The OffHeap Podcast. Devnexus Edition (Now with AI Agents)",
+ "speakers": ["Freddy Guime"],
+ "track": "Core Java",
+ "room": "301",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "16:50",
+ "start_datetime": "2026-03-05T16:50:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "podcast", "ai", "agents", "community"]
+ },
+ {
+ "id": "devnexus-2026-064",
+ "conference": "DevNexus 2026",
+ "title": "Conference Reception",
+ "speakers": [],
+ "track": "Social",
+ "room": "TBD",
+ "day": "Day 1",
+ "date": "2026-03-05",
+ "start_time": "17:30",
+ "start_datetime": "2026-03-05T17:30:00Z",
+ "session_type": "Social",
+ "tags": ["networking", "social"]
+ },
+ {
+ "id": "devnexus-2026-065",
+ "conference": "DevNexus 2026",
+ "title": "Hacking AI - How to Survive the AI Uprising",
+ "speakers": ["Gant Laborde"],
+ "track": "Keynote",
+ "room": "411/412",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "09:00",
+ "start_datetime": "2026-03-06T09:00:00Z",
+ "session_type": "Keynote",
+ "tags": ["keynote", "ai", "security", "hacking"]
+ },
+ {
+ "id": "devnexus-2026-066",
+ "conference": "DevNexus 2026",
+ "title": "Connecting the Dots with Context Graphs",
+ "speakers": ["Stephen Chin"],
+ "track": "AI Tools",
+ "room": "315",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-06T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "graphs", "context", "rag", "tools"]
+ },
+ {
+ "id": "devnexus-2026-067",
+ "conference": "DevNexus 2026",
+ "title": "Spec Driven Development: Why Your Prompt Chaos Won't Scale",
+ "speakers": ["Simon Maple"],
+ "track": "AI in Practice",
+ "room": "311",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-06T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "llm", "prompting", "practical", "development"]
+ },
+ {
+ "id": "devnexus-2026-068",
+ "conference": "DevNexus 2026",
+ "title": "Introduction to Cell Architectures",
+ "speakers": ["Christopher Curtin"],
+ "track": "Architecture",
+ "room": "314",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-06T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["architecture", "cell-architecture", "distributed-systems"]
+ },
+ {
+ "id": "devnexus-2026-069",
+ "conference": "DevNexus 2026",
+ "title": "Scotty I Need Warp Speed - Ways to Improve JVM Startup",
+ "speakers": ["Gerrit Grunwald"],
+ "track": "Core Java",
+ "room": "301",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-06T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "jvm", "startup", "performance", "graalvm"]
+ },
+ {
+ "id": "devnexus-2026-070",
+ "conference": "DevNexus 2026",
+ "title": "API Versioning in Spring",
+ "speakers": ["Spencer Gibb"],
+ "track": "Frameworks",
+ "room": "313",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-06T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "api", "api-versioning", "frameworks"]
+ },
+ {
+ "id": "devnexus-2026-071",
+ "conference": "DevNexus 2026",
+ "title": "OK, But What About Predictive AI?",
+ "speakers": ["Brayan Muñoz V.", "José R. Almonte C."],
+ "track": "Gen AI",
+ "room": "316",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-06T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "predictive-ai", "machine-learning", "generative-ai"]
+ },
+ {
+ "id": "devnexus-2026-072",
+ "conference": "DevNexus 2026",
+ "title": "Maven's Hidden Secrets to Speed Up Your Build",
+ "speakers": ["Ko Turk"],
+ "track": "Practices",
+ "room": "302",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-06T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["maven", "build", "practices", "performance"]
+ },
+ {
+ "id": "devnexus-2026-073",
+ "conference": "DevNexus 2026",
+ "title": "Spring Cloud Supercharged for Production-Ready Apps",
+ "speakers": ["Ryan Baxter", "Chris Sterling"],
+ "track": "Production Ready Spring",
+ "room": "312",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-06T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "spring-cloud", "production", "cloud-native"]
+ },
+ {
+ "id": "devnexus-2026-074",
+ "conference": "DevNexus 2026",
+ "title": "The Hidden Security Hazards in Your Java Stack",
+ "speakers": ["Brian Vermeer"],
+ "track": "Security",
+ "room": "304",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-06T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "security", "vulnerabilities", "devsecops"]
+ },
+ {
+ "id": "devnexus-2026-075",
+ "conference": "DevNexus 2026",
+ "title": "Back to the Future of Software: How to Survive the AI Apocalypse
with Tests, Prompts, and Specs",
+ "speakers": ["Baruch Sadogursky", "Leonid Igolnik"],
+ "track": "Tech Leadership",
+ "room": "303",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-06T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "testing", "leadership", "prompting",
"software-engineering"]
+ },
+ {
+ "id": "devnexus-2026-076",
+ "conference": "DevNexus 2026",
+ "title": "BoxLang vs the World: From Zero to Stable in 20 Months",
+ "speakers": ["Luis Majano", "Brad Wood"],
+ "track": "Tools and Techniques",
+ "room": "305",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "10:00",
+ "start_datetime": "2026-03-06T10:00:00Z",
+ "session_type": "Talk",
+ "tags": ["boxlang", "jvm-languages", "tools"]
+ },
+ {
+ "id": "devnexus-2026-077",
+ "conference": "DevNexus 2026",
+ "title": "Performant GraphRAG: Improving Neo4j to Drive Autonomous AI",
+ "speakers": ["Brandon Tylke", "Jon Gentsch"],
+ "track": "AI Tools",
+ "room": "315",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-06T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "rag", "neo4j", "graphs", "tools", "llm"]
+ },
+ {
+ "id": "devnexus-2026-078",
+ "conference": "DevNexus 2026",
+ "title": "From Microservices to Agent-Services: Architectural Patterns for
Autonomous System Boundaries",
+ "speakers": ["Mo Haghighi", "Prasanth Kumar Pari"],
+ "track": "AI in Practice",
+ "room": "311",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-06T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "agents", "microservices", "architecture", "practical"]
+ },
+ {
+ "id": "devnexus-2026-079",
+ "conference": "DevNexus 2026",
+ "title": "Breaking the Monolith Mindset: Hexagonal Architecture vs
Traditional Layers in Java",
+ "speakers": ["Emmanuel Guzmán", "Jorge Cajas"],
+ "track": "Architecture",
+ "room": "314",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-06T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["architecture", "hexagonal", "java", "monolith",
"layered-architecture"]
+ },
+ {
+ "id": "devnexus-2026-080",
+ "conference": "DevNexus 2026",
+ "title": "Trash Talk - Exploring the Memory Management in the JVM",
+ "speakers": ["Gerrit Grunwald"],
+ "track": "Core Java",
+ "room": "301",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-06T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "jvm", "memory-management", "garbage-collection",
"performance"]
+ },
+ {
+ "id": "devnexus-2026-081",
+ "conference": "DevNexus 2026",
+ "title": "Autoscaling Spring Boot Apps in Kubernetes with KEDA",
+ "speakers": ["John Coyne"],
+ "track": "Frameworks",
+ "room": "313",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-06T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "spring-boot", "kubernetes", "keda", "autoscaling",
"cloud-native"]
+ },
+ {
+ "id": "devnexus-2026-082",
+ "conference": "DevNexus 2026",
+ "title": "Integrating LLMs in Java: A Practical Guide to Model Context
Protocol",
+ "speakers": ["Dan Vega"],
+ "track": "Gen AI",
+ "room": "316",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-06T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["llm", "java", "mcp", "spring-ai", "generative-ai"]
+ },
+ {
+ "id": "devnexus-2026-083",
+ "conference": "DevNexus 2026",
+ "title": "Sociotechnical Platform Engineering",
+ "speakers": ["Chris Corriere"],
+ "track": "Practices",
+ "room": "302",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-06T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["platform-engineering", "practices", "devops", "culture"]
+ },
+ {
+ "id": "devnexus-2026-084",
+ "conference": "DevNexus 2026",
+ "title": "Refactoring the ROI of Software: Continuous Modernization with
Tanzu Platform",
+ "speakers": ["DaShaun Carter"],
+ "track": "Production Ready Spring",
+ "room": "312",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-06T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "tanzu", "modernization", "refactoring", "cloud"]
+ },
+ {
+ "id": "devnexus-2026-085",
+ "conference": "DevNexus 2026",
+ "title": "Implementing MCP Authorization Using Spring Security OAuth 2.1
Capabilities",
+ "speakers": ["Joe Grandja"],
+ "track": "Security",
+ "room": "304",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-06T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["mcp", "spring-security", "oauth2", "security", "authorization"]
+ },
+ {
+ "id": "devnexus-2026-086",
+ "conference": "DevNexus 2026",
+ "title": "Delivering Value Through Software: A Practical Guide for Tech
Leads",
+ "speakers": ["Sujith Paul"],
+ "track": "Tech Leadership",
+ "room": "303",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-06T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["leadership", "career", "tech-lead", "management"]
+ },
+ {
+ "id": "devnexus-2026-087",
+ "conference": "DevNexus 2026",
+ "title": "A Data-Oriented Programming Approach to REST APIs",
+ "speakers": ["Kenneth Kousen"],
+ "track": "Tools and Techniques",
+ "room": "305",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "11:20",
+ "start_datetime": "2026-03-06T11:20:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "rest", "api", "data-oriented-programming", "tools"]
+ },
+ {
+ "id": "devnexus-2026-088",
+ "conference": "DevNexus 2026",
+ "title": "Agentic AI for Java Microservices: Cloud Performance
Optimization at FinTech Scale",
+ "speakers": ["Sibasis Padhi"],
+ "track": "AI Tools",
+ "room": "315",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-06T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "agents", "java", "microservices", "fintech", "cloud",
"performance"]
+ },
+ {
+ "id": "devnexus-2026-089",
+ "conference": "DevNexus 2026",
+ "title": "Conversational AI Semantic Search for E-commerce Using
Elasticsearch + RAG",
+ "speakers": ["Karthik Govardhanan"],
+ "track": "AI in Practice",
+ "room": "311",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-06T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "rag", "semantic-search", "elasticsearch", "e-commerce"]
+ },
+ {
+ "id": "devnexus-2026-090",
+ "conference": "DevNexus 2026",
+ "title": "Durable Execution: Building Apps That Refuse to Die",
+ "speakers": ["Sam Dengler"],
+ "track": "Architecture",
+ "room": "314",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-06T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["architecture", "resilience", "durable-execution", "reliability"]
+ },
+ {
+ "id": "devnexus-2026-091",
+ "conference": "DevNexus 2026",
+ "title": "Just-in-Time Compilation Isn't Magic",
+ "speakers": ["Douglas Hawkins"],
+ "track": "Core Java",
+ "room": "301",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-06T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "jvm", "jit", "performance", "compilation"]
+ },
+ {
+ "id": "devnexus-2026-092",
+ "conference": "DevNexus 2026",
+ "title": "I Can See Clearly Now: Observability of JVM & Spring Boot 2-3-4
Apps",
+ "speakers": ["Jonatan Ivanov"],
+ "track": "Frameworks",
+ "room": "313",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-06T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "spring-boot", "observability", "monitoring", "jvm",
"opentelemetry"]
+ },
+ {
+ "id": "devnexus-2026-093",
+ "conference": "DevNexus 2026",
+ "title": "From Context Windows to Context Graphs: The Next Generation of
AI Systems",
+ "speakers": ["Medha Chakraborty", "Srijani Dey"],
+ "track": "Gen AI",
+ "room": "316",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-06T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "graphs", "llm", "generative-ai", "context"]
+ },
+ {
+ "id": "devnexus-2026-094",
+ "conference": "DevNexus 2026",
+ "title": "Better Assertions with AssertJ",
+ "speakers": ["Tim te Beek"],
+ "track": "Practices",
+ "room": "302",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-06T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["testing", "assertj", "java", "practices"]
+ },
+ {
+ "id": "devnexus-2026-095",
+ "conference": "DevNexus 2026",
+ "title": "The Golden Path Starts at Home: Engineering Developer Experience
from Laptop to Production",
+ "speakers": ["Tim Sparg", "DaShaun Carter"],
+ "track": "Production Ready Spring",
+ "room": "312",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-06T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "developer-experience", "production",
"platform-engineering"]
+ },
+ {
+ "id": "devnexus-2026-096",
+ "conference": "DevNexus 2026",
+ "title": "The Responsible Java Developer: Trustworthy GenAI in Practice",
+ "speakers": ["Brian Benz"],
+ "track": "Security",
+ "room": "304",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-06T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "ai", "generative-ai", "security", "responsible-ai"]
+ },
+ {
+ "id": "devnexus-2026-097",
+ "conference": "DevNexus 2026",
+ "title": "Beyond Code: Behavioral Architecture (Small Changes, Big Wins)",
+ "speakers": ["Sandeep Adinarayana"],
+ "track": "Tech Leadership",
+ "room": "303",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-06T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["leadership", "architecture", "culture", "organizational-change"]
+ },
+ {
+ "id": "devnexus-2026-098",
+ "conference": "DevNexus 2026",
+ "title": "Advancing with Java",
+ "speakers": ["Rodrigo Graciano", "Chandra Guntur"],
+ "track": "Tools and Techniques",
+ "room": "305",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "13:20",
+ "start_datetime": "2026-03-06T13:20:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "career", "tools"]
+ },
+ {
+ "id": "devnexus-2026-099",
+ "conference": "DevNexus 2026",
+ "title": "Stop Fighting Your AI: Engineering Prompts That Actually Work",
+ "speakers": ["Martin Rojas"],
+ "track": "AI Tools",
+ "room": "315",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "14:40",
+ "start_datetime": "2026-03-06T14:40:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "prompting", "prompt-engineering", "tools", "llm"]
+ },
+ {
+ "id": "devnexus-2026-100",
+ "conference": "DevNexus 2026",
+ "title": "Real-Time Fraud Detection in Java with Kafka, Streams & Vector
Similarity",
+ "speakers": ["Tim Kelly", "Ricardo Mello"],
+ "track": "AI in Practice",
+ "room": "311",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "14:40",
+ "start_datetime": "2026-03-06T14:40:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "kafka", "streaming", "fraud-detection", "vector-search",
"ai", "practical"]
+ },
+ {
+ "id": "devnexus-2026-101",
+ "conference": "DevNexus 2026",
+ "title": "Isolation Isn't All Bad, For Your Database",
+ "speakers": ["Sean McNealy"],
+ "track": "Architecture",
+ "room": "314",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "14:40",
+ "start_datetime": "2026-03-06T14:40:00Z",
+ "session_type": "Talk",
+ "tags": ["database", "isolation", "architecture", "transactions"]
+ },
+ {
+ "id": "devnexus-2026-102",
+ "conference": "DevNexus 2026",
+ "title": "Java's Asynchronous Ecosystem",
+ "speakers": ["Daniel Hinojosa"],
+ "track": "Core Java",
+ "room": "301",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "14:40",
+ "start_datetime": "2026-03-06T14:40:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "async", "reactive", "virtual-threads", "jvm"]
+ },
+ {
+ "id": "devnexus-2026-103",
+ "conference": "DevNexus 2026",
+ "title": "Extend Your JPA Applications with Relational JSON Documents",
+ "speakers": ["Anders Swanson"],
+ "track": "Frameworks",
+ "room": "313",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "14:40",
+ "start_datetime": "2026-03-06T14:40:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "jpa", "json", "database", "spring", "frameworks"]
+ },
+ {
+ "id": "devnexus-2026-104",
+ "conference": "DevNexus 2026",
+ "title": "Choose Your Fighter: Spring AI vs LangChain4j",
+ "speakers": ["Malavika Balamurali"],
+ "track": "Gen AI",
+ "room": "316",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "14:40",
+ "start_datetime": "2026-03-06T14:40:00Z",
+ "session_type": "Talk",
+ "tags": ["spring-ai", "langchain4j", "java", "generative-ai", "llm"]
+ },
+ {
+ "id": "devnexus-2026-105",
+ "conference": "DevNexus 2026",
+ "title": "Grow Beyond Senior Through Java Contributions: JUGs, the JCP and
OpenJDK",
+ "speakers": ["Bruno Souza", "Heather VanCura"],
+ "track": "Practices",
+ "room": "302",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "14:40",
+ "start_datetime": "2026-03-06T14:40:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "open-source", "community", "jcp", "openjdk", "career"]
+ },
+ {
+ "id": "devnexus-2026-106",
+ "conference": "DevNexus 2026",
+ "title": "Staying Current: A Journey in Software Maintenance",
+ "speakers": ["Shriyu Gaglani", "Ho Jong Yu"],
+ "track": "Production Ready Spring",
+ "room": "312",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "14:40",
+ "start_datetime": "2026-03-06T14:40:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "maintenance", "upgrades", "production"]
+ },
+ {
+ "id": "devnexus-2026-107",
+ "conference": "DevNexus 2026",
+ "title": "Deep Dive Into Data Streaming Security",
+ "speakers": ["Olena Kutsenko"],
+ "track": "Security",
+ "room": "304",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "14:40",
+ "start_datetime": "2026-03-06T14:40:00Z",
+ "session_type": "Talk",
+ "tags": ["security", "kafka", "data-streaming", "encryption"]
+ },
+ {
+ "id": "devnexus-2026-108",
+ "conference": "DevNexus 2026",
+ "title": "Engineering the Shift: How Technical Leaders Drive Enduring
Change",
+ "speakers": ["Niranjan Prithviraj"],
+ "track": "Tech Leadership",
+ "room": "303",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "14:40",
+ "start_datetime": "2026-03-06T14:40:00Z",
+ "session_type": "Talk",
+ "tags": ["leadership", "management", "organizational-change", "tech-lead"]
+ },
+ {
+ "id": "devnexus-2026-109",
+ "conference": "DevNexus 2026",
+ "title": "How I AI: Building Custom GPTs to Supercharge Everyday Work",
+ "speakers": ["Daneez Zamangil"],
+ "track": "Tools and Techniques",
+ "room": "305",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "14:40",
+ "start_datetime": "2026-03-06T14:40:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "gpt", "productivity", "tools", "automation"]
+ },
+ {
+ "id": "devnexus-2026-110",
+ "conference": "DevNexus 2026",
+ "title": "AI Agents for Java Devs: From Demo to Deployment",
+ "speakers": ["Brian Benz"],
+ "track": "AI Tools",
+ "room": "315",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "15:40",
+ "start_datetime": "2026-03-06T15:40:00Z",
+ "session_type": "Talk",
+ "tags": ["ai", "agents", "java", "tools", "deployment"]
+ },
+ {
+ "id": "devnexus-2026-111",
+ "conference": "DevNexus 2026",
+ "title": "Microservices for Pragmatists",
+ "speakers": ["Hazel Bohon"],
+ "track": "Architecture",
+ "room": "314",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "15:40",
+ "start_datetime": "2026-03-06T15:40:00Z",
+ "session_type": "Talk",
+ "tags": ["microservices", "architecture", "pragmatic"]
+ },
+ {
+ "id": "devnexus-2026-112",
+ "conference": "DevNexus 2026",
+ "title": "Zero to C-Speed with Only Java",
+ "speakers": ["David Vlijmincx"],
+ "track": "Core Java",
+ "room": "301",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "15:40",
+ "start_datetime": "2026-03-06T15:40:00Z",
+ "session_type": "Talk",
+ "tags": ["java", "performance", "jvm", "native", "panama"]
+ },
+ {
+ "id": "devnexus-2026-113",
+ "conference": "DevNexus 2026",
+ "title": "Debugging with IntelliJ IDEA",
+ "speakers": ["Anton Arhipov"],
+ "track": "Practices",
+ "room": "302",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "15:40",
+ "start_datetime": "2026-03-06T15:40:00Z",
+ "session_type": "Talk",
+ "tags": ["intellij", "debugging", "ide", "practices", "java"]
+ },
+ {
+ "id": "devnexus-2026-114",
+ "conference": "DevNexus 2026",
+ "title": "Stop Getting Shift Left'ed On",
+ "speakers": ["DaShaun Carter", "Kevin Strohmeyer"],
+ "track": "Production Ready Spring",
+ "room": "312",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "15:40",
+ "start_datetime": "2026-03-06T15:40:00Z",
+ "session_type": "Talk",
+ "tags": ["spring", "security", "shift-left", "devops", "production"]
+ },
+ {
+ "id": "devnexus-2026-115",
+ "conference": "DevNexus 2026",
+ "title": "Code Your Way to Quantum-Safe Development by Solving Tomorrow's
Encryption Crisis",
+ "speakers": ["Barry Burd"],
+ "track": "Security",
+ "room": "304",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "15:40",
+ "start_datetime": "2026-03-06T15:40:00Z",
+ "session_type": "Talk",
+ "tags": ["security", "quantum", "encryption", "post-quantum",
"cryptography"]
+ },
+ {
+ "id": "devnexus-2026-116",
+ "conference": "DevNexus 2026",
+ "title": "Prize Giveaways and Conference Close!",
+ "speakers": [],
+ "track": "Social",
+ "room": "411/412",
+ "day": "Day 2",
+ "date": "2026-03-06",
+ "start_time": "16:40",
+ "start_datetime": "2026-03-06T16:40:00Z",
+ "session_type": "Closing",
+ "tags": ["closing", "social"]
+ }
+]
diff --git a/src/main/java/org/apache/solr/mcp/server/Main.java
b/src/main/java/org/apache/solr/mcp/server/Main.java
index 943d3e7..f2970f5 100644
--- a/src/main/java/org/apache/solr/mcp/server/Main.java
+++ b/src/main/java/org/apache/solr/mcp/server/Main.java
@@ -16,8 +16,8 @@
*/
package org.apache.solr.mcp.server;
+import org.apache.solr.mcp.server.collection.CollectionService;
import org.apache.solr.mcp.server.indexing.IndexingService;
-import org.apache.solr.mcp.server.metadata.CollectionService;
import org.apache.solr.mcp.server.metadata.SchemaService;
import org.apache.solr.mcp.server.search.SearchService;
import org.springframework.boot.SpringApplication;
@@ -107,44 +107,7 @@ import
org.springframework.boot.autoconfigure.SpringBootApplication;
*/
@SpringBootApplication
public class Main {
-
- /**
- * Main application entry point that starts the Spring Boot application.
- *
- * <p>
- * This method initializes the Spring application context, configures
all
- * service beans, establishes Solr connectivity, and begins listening
for MCP
- * client connections via standard input/output.
- *
- * <p>
- * <strong>Startup Process:</strong>
- *
- * <ol>
- * <li>Initialize Spring Boot application context
- * <li>Load configuration properties from various sources
- * <li>Create and configure SolrClient bean
- * <li>Initialize all service beans with dependency injection
- * <li>Register MCP tools from service methods
- * <li>Start MCP server listening on stdio
- * </ol>
- *
- * <p>
- * <strong>Error Handling:</strong>
- *
- * <p>
- * Startup failures typically indicate configuration issues such as:
- *
- * <ul>
- * <li>Missing or invalid Solr URL configuration
- * <li>Network connectivity issues to Solr server
- * <li>Missing required dependencies or classpath issues
- * </ul>
- *
- * @param args
- * command-line arguments passed to the application
- * @see SpringApplication#run(Class, String...)
- */
- public static void main(String[] args) {
+ static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
diff --git
a/src/main/java/org/apache/solr/mcp/server/metadata/CollectionService.java
b/src/main/java/org/apache/solr/mcp/server/collection/CollectionService.java
similarity index 91%
rename from
src/main/java/org/apache/solr/mcp/server/metadata/CollectionService.java
rename to
src/main/java/org/apache/solr/mcp/server/collection/CollectionService.java
index e7912e7..c121914 100644
--- a/src/main/java/org/apache/solr/mcp/server/metadata/CollectionService.java
+++ b/src/main/java/org/apache/solr/mcp/server/collection/CollectionService.java
@@ -14,11 +14,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.solr.mcp.server.metadata;
+package org.apache.solr.mcp.server.collection;
-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.collection.CollectionUtils.getFloat;
+import static org.apache.solr.mcp.server.collection.CollectionUtils.getInteger;
+import static org.apache.solr.mcp.server.collection.CollectionUtils.getLong;
import static org.apache.solr.mcp.server.util.JsonUtils.toJson;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -49,6 +49,7 @@ 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.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
/**
@@ -251,6 +252,18 @@ public class CollectionService {
/** Error message prefix for collection not found exceptions */
private static final String COLLECTION_NOT_FOUND_ERROR = "Collection
not found: ";
+ /** Default configset name used when none is specified */
+ private static final String DEFAULT_CONFIGSET = "_default";
+
+ /** Default number of shards for new collections */
+ private static final int DEFAULT_NUM_SHARDS = 1;
+
+ /** Default replication factor for new collections */
+ private static final int DEFAULT_REPLICATION_FACTOR = 1;
+
+ /** Error message for blank collection name validation */
+ private static final String BLANK_COLLECTION_NAME_ERROR = "Collection
name must not be blank";
+
/** SolrJ client for communicating with Solr server */
private final SolrClient solrClient;
@@ -1040,4 +1053,60 @@ public class CollectionService {
return new SolrHealthStatus(false, e.getMessage(),
null, null, new Date(), null, null, null);
}
}
+
+ /**
+ * Creates a new Solr collection (SolrCloud) or core (standalone Solr).
+ *
+ * <p>
+ * Automatically detects the deployment type and uses the appropriate
API:
+ *
+ * <p>
+ * Uses the Collections API, which works with any SolrClient pointing
to a
+ * SolrCloud deployment.
+ *
+ * <p>
+ * Optional parameters default to sensible values when not provided by
the MCP
+ * client: configSet defaults to {@value #DEFAULT_CONFIGSET}, numShards
and
+ * replicationFactor both default to 1.
+ *
+ * @param name
+ * the name of the collection to create (must not be blank)
+ * @param configSet
+ * the configset name (optional, defaults to
+ * {@value #DEFAULT_CONFIGSET})
+ * @param numShards
+ * number of shards (optional, defaults to 1)
+ * @param replicationFactor
+ * replication factor (optional, defaults to 1)
+ * @return result describing the outcome of the creation operation
+ * @throws IllegalArgumentException
+ * if the collection name is blank
+ * @throws SolrServerException
+ * if Solr returns an error
+ * @throws IOException
+ * if there are I/O errors during communication
+ */
+ @PreAuthorize("isAuthenticated()")
+ @McpTool(name = "create-collection", description = "Create a new Solr
collection. "
+ + "configSet defaults to _default, numShards and
replicationFactor default to 1.")
+ public CollectionCreationResult createCollection(
+ @McpToolParam(description = "Name of the collection to
create") String name,
+ @McpToolParam(description = "Configset name. Defaults
to _default.", required = false) String configSet,
+ @McpToolParam(description = "Number of shards
(SolrCloud only). Defaults to 1.", required = false) Integer numShards,
+ @McpToolParam(description = "Replication factor
(SolrCloud only). Defaults to 1.", required = false) Integer replicationFactor)
+ throws SolrServerException, IOException {
+
+ if (name == null || name.isBlank()) {
+ throw new
IllegalArgumentException(BLANK_COLLECTION_NAME_ERROR);
+ }
+
+ String effectiveConfigSet = configSet != null ? configSet :
DEFAULT_CONFIGSET;
+ int effectiveShards = numShards != null ? numShards :
DEFAULT_NUM_SHARDS;
+ int effectiveRf = replicationFactor != null ? replicationFactor
: DEFAULT_REPLICATION_FACTOR;
+
+ CollectionAdminRequest.createCollection(name,
effectiveConfigSet, effectiveShards, effectiveRf)
+ .process(solrClient);
+
+ return new CollectionCreationResult(name, true, "Collection
created successfully", new Date());
+ }
}
diff --git
a/src/main/java/org/apache/solr/mcp/server/metadata/CollectionUtils.java
b/src/main/java/org/apache/solr/mcp/server/collection/CollectionUtils.java
similarity index 98%
rename from
src/main/java/org/apache/solr/mcp/server/metadata/CollectionUtils.java
rename to
src/main/java/org/apache/solr/mcp/server/collection/CollectionUtils.java
index bd6e20a..d79ea24 100644
--- a/src/main/java/org/apache/solr/mcp/server/metadata/CollectionUtils.java
+++ b/src/main/java/org/apache/solr/mcp/server/collection/CollectionUtils.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.solr.mcp.server.metadata;
+package org.apache.solr.mcp.server.collection;
import org.apache.solr.common.util.NamedList;
@@ -65,6 +65,9 @@ import org.apache.solr.common.util.NamedList;
*/
public class CollectionUtils {
+ private CollectionUtils() {
+ }
+
/**
* Extracts a Long value from a NamedList using the specified key with
robust
* type conversion.
@@ -122,7 +125,7 @@ public class CollectionUtils {
try {
return Long.parseLong(value.toString());
- } catch (NumberFormatException e) {
+ } catch (NumberFormatException _) {
return null;
}
}
@@ -258,7 +261,7 @@ public class CollectionUtils {
try {
return Integer.parseInt(value.toString());
- } catch (NumberFormatException e) {
+ } catch (NumberFormatException _) {
return null;
}
}
diff --git a/src/main/java/org/apache/solr/mcp/server/metadata/Dtos.java
b/src/main/java/org/apache/solr/mcp/server/collection/Dtos.java
similarity index 94%
rename from src/main/java/org/apache/solr/mcp/server/metadata/Dtos.java
rename to src/main/java/org/apache/solr/mcp/server/collection/Dtos.java
index 7a04ac3..d5569b6 100644
--- a/src/main/java/org/apache/solr/mcp/server/metadata/Dtos.java
+++ b/src/main/java/org/apache/solr/mcp/server/collection/Dtos.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.solr.mcp.server.metadata;
+package org.apache.solr.mcp.server.collection;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -477,3 +477,27 @@ record SolrHealthStatus(
/** Additional status information or state description */
String status) {
}
+
+/**
+ * Result of a collection creation operation.
+ *
+ * <p>
+ * Returned by the {@code create-collection} MCP tool to communicate the
outcome
+ * of a collection creation request. On success, {@code success} is {@code
true}
+ * and {@code createdAt} records when the operation completed.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+record CollectionCreationResult(
+ /** Name of the collection that was created */
+ String name,
+
+ /** Whether the collection was successfully created */
+ boolean success,
+
+ /** Optional message describing the outcome */
+ String message,
+
+ /** Timestamp when the collection was created, formatted as ISO
8601 */
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern =
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") Date createdAt) {
+}
diff --git a/src/test/java/org/apache/solr/mcp/server/MainTest.java
b/src/test/java/org/apache/solr/mcp/server/MainTest.java
index db6fdb7..1b9ae42 100644
--- a/src/test/java/org/apache/solr/mcp/server/MainTest.java
+++ b/src/test/java/org/apache/solr/mcp/server/MainTest.java
@@ -16,8 +16,8 @@
*/
package org.apache.solr.mcp.server;
+import org.apache.solr.mcp.server.collection.CollectionService;
import org.apache.solr.mcp.server.indexing.IndexingService;
-import org.apache.solr.mcp.server.metadata.CollectionService;
import org.apache.solr.mcp.server.metadata.SchemaService;
import org.apache.solr.mcp.server.search.SearchService;
import org.junit.jupiter.api.Test;
diff --git
a/src/test/java/org/apache/solr/mcp/server/McpToolRegistrationTest.java
b/src/test/java/org/apache/solr/mcp/server/McpToolRegistrationTest.java
index c0861c9..af79754 100644
--- a/src/test/java/org/apache/solr/mcp/server/McpToolRegistrationTest.java
+++ b/src/test/java/org/apache/solr/mcp/server/McpToolRegistrationTest.java
@@ -22,8 +22,8 @@ import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.List;
+import org.apache.solr.mcp.server.collection.CollectionService;
import org.apache.solr.mcp.server.indexing.IndexingService;
-import org.apache.solr.mcp.server.metadata.CollectionService;
import org.apache.solr.mcp.server.metadata.SchemaService;
import org.apache.solr.mcp.server.search.SearchService;
import org.junit.jupiter.api.Test;
diff --git
a/src/test/java/org/apache/solr/mcp/server/metadata/CollectionServiceIntegrationTest.java
b/src/test/java/org/apache/solr/mcp/server/collection/CollectionServiceIntegrationTest.java
similarity index 92%
rename from
src/test/java/org/apache/solr/mcp/server/metadata/CollectionServiceIntegrationTest.java
rename to
src/test/java/org/apache/solr/mcp/server/collection/CollectionServiceIntegrationTest.java
index 0fa9684..c520bd0 100644
---
a/src/test/java/org/apache/solr/mcp/server/metadata/CollectionServiceIntegrationTest.java
+++
b/src/test/java/org/apache/solr/mcp/server/collection/CollectionServiceIntegrationTest.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.solr.mcp.server.metadata;
+package org.apache.solr.mcp.server.collection;
import static org.junit.jupiter.api.Assertions.*;
@@ -243,4 +243,20 @@ class CollectionServiceIntegrationTest {
assertEquals("", collectionService.extractCollectionName(""),
"Empty string should return empty string");
}
+
+ @Test
+ void createCollection_createsAndListable() throws Exception {
+ String name = "mcp_test_create_" + System.currentTimeMillis();
+
+ CollectionCreationResult result =
collectionService.createCollection(name, null, null, null);
+
+ assertTrue(result.success(), "Collection creation should
succeed");
+ assertEquals(name, result.name(), "Result should contain the
collection name");
+ assertNotNull(result.createdAt(), "Creation timestamp should be
set");
+
+ List<String> collections = collectionService.listCollections();
+ boolean collectionExists = collections.contains(name)
+ || collections.stream().anyMatch(col ->
col.startsWith(name + "_shard"));
+ assertTrue(collectionExists, "Newly created collection should
appear in list (found: " + collections + ")");
+ }
}
diff --git
a/src/test/java/org/apache/solr/mcp/server/metadata/CollectionServiceTest.java
b/src/test/java/org/apache/solr/mcp/server/collection/CollectionServiceTest.java
similarity index 93%
rename from
src/test/java/org/apache/solr/mcp/server/metadata/CollectionServiceTest.java
rename to
src/test/java/org/apache/solr/mcp/server/collection/CollectionServiceTest.java
index 19888ae..ed5bca8 100644
---
a/src/test/java/org/apache/solr/mcp/server/metadata/CollectionServiceTest.java
+++
b/src/test/java/org/apache/solr/mcp/server/collection/CollectionServiceTest.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.solr.mcp.server.metadata;
+package org.apache.solr.mcp.server.collection;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
@@ -894,4 +894,61 @@ class CollectionServiceTest {
return mbeans;
}
+
+ // createCollection tests
+ @Test
+ void createCollection_success_cloudClient() throws Exception {
+ CloudSolrClient cloudClient = mock(CloudSolrClient.class);
+ when(cloudClient.request(any(), any())).thenReturn(new
NamedList<>());
+
+ CollectionService service = new CollectionService(cloudClient,
objectMapper);
+ CollectionCreationResult result =
service.createCollection("new_collection", "_default", 1, 1);
+
+ assertNotNull(result);
+ assertTrue(result.success());
+ assertEquals("new_collection", result.name());
+ assertNotNull(result.createdAt());
+ }
+
+ @Test
+ void createCollection_success_standaloneClient() throws Exception {
+ when(solrClient.request(any(), isNull())).thenReturn(new
NamedList<>());
+
+ CollectionCreationResult result =
collectionService.createCollection("new_core", null, null, null);
+
+ assertNotNull(result);
+ assertTrue(result.success());
+ assertEquals("new_core", result.name());
+ assertNotNull(result.createdAt());
+ }
+
+ @Test
+ void createCollection_defaultsApplied() throws Exception {
+ CloudSolrClient cloudClient = mock(CloudSolrClient.class);
+ when(cloudClient.request(any(), any())).thenReturn(new
NamedList<>());
+
+ CollectionService service = new CollectionService(cloudClient,
objectMapper);
+ CollectionCreationResult result =
service.createCollection("defaults_collection", null, null, null);
+
+ assertTrue(result.success());
+ assertEquals("defaults_collection", result.name());
+ }
+
+ @Test
+ void createCollection_blankName_throwsIllegalArgument() {
+ assertThrows(IllegalArgumentException.class, () ->
collectionService.createCollection(" ", null, null, null));
+ }
+
+ @Test
+ void createCollection_emptyName_throwsIllegalArgument() {
+ assertThrows(IllegalArgumentException.class, () ->
collectionService.createCollection("", null, null, null));
+ }
+
+ @Test
+ void createCollection_solrException_propagates() throws Exception {
+ when(solrClient.request(any(), isNull())).thenThrow(new
SolrServerException("Solr error"));
+
+ assertThrows(SolrServerException.class,
+ () ->
collectionService.createCollection("fail_core", null, null, null));
+ }
}
diff --git
a/src/test/java/org/apache/solr/mcp/server/metadata/CollectionUtilsTest.java
b/src/test/java/org/apache/solr/mcp/server/collection/CollectionUtilsTest.java
similarity index 99%
rename from
src/test/java/org/apache/solr/mcp/server/metadata/CollectionUtilsTest.java
rename to
src/test/java/org/apache/solr/mcp/server/collection/CollectionUtilsTest.java
index 2365494..a4f9a7d 100644
--- a/src/test/java/org/apache/solr/mcp/server/metadata/CollectionUtilsTest.java
+++
b/src/test/java/org/apache/solr/mcp/server/collection/CollectionUtilsTest.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.solr.mcp.server.metadata;
+package org.apache.solr.mcp.server.collection;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
diff --git
a/src/test/java/org/apache/solr/mcp/server/collection/ConferenceEndToEndIntegrationTest.java
b/src/test/java/org/apache/solr/mcp/server/collection/ConferenceEndToEndIntegrationTest.java
new file mode 100644
index 0000000..94f17d4
--- /dev/null
+++
b/src/test/java/org/apache/solr/mcp/server/collection/ConferenceEndToEndIntegrationTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.collection;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+import org.apache.solr.mcp.server.TestcontainersConfiguration;
+import org.apache.solr.mcp.server.indexing.IndexingService;
+import org.apache.solr.mcp.server.search.SearchResponse;
+import org.apache.solr.mcp.server.search.SearchService;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Import;
+import org.testcontainers.junit.jupiter.Testcontainers;
+
+/**
+ * End-to-end integration test that exercises the full create, index, and
search
+ * workflow using the DevNexus 2026 conference schedule sample data.
+ */
+@SpringBootTest
+@Import(TestcontainersConfiguration.class)
+@Testcontainers(disabledWithoutDocker = true)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+class ConferenceEndToEndIntegrationTest {
+
+ private static final String COLLECTION = "conferences";
+
+ private static final int TOTAL_SESSIONS = 116;
+
+ @Autowired
+ private CollectionService collectionService;
+
+ @Autowired
+ private IndexingService indexingService;
+
+ @Autowired
+ private SearchService searchService;
+
+ @BeforeAll
+ void createAndIndexConferences() throws Exception {
+ CollectionCreationResult result =
collectionService.createCollection(COLLECTION, null, null, null);
+ assertTrue(result.success(), "Collection creation should
succeed: " + result.message());
+
+ String json =
Files.readString(Path.of("mydata/devnexus-2026.json"));
+ indexingService.indexJsonDocuments(COLLECTION, json);
+ }
+
+ @Test
+ @Order(1)
+ void collectionAppearsInList() {
+ List<String> collections = collectionService.listCollections();
+ boolean found = collections.contains(COLLECTION)
+ || collections.stream().anyMatch(c ->
c.startsWith(COLLECTION + "_shard"));
+ assertTrue(found, "conferences collection should be listed,
found: " + collections);
+ }
+
+ @Test
+ @Order(2)
+ void collectionHealthIsHealthy() {
+ SolrHealthStatus health =
collectionService.checkHealth(COLLECTION);
+ assertTrue(health.isHealthy(), "conferences collection should
be healthy");
+ assertEquals(Long.valueOf(TOTAL_SESSIONS),
health.totalDocuments(), "Should contain 116 conference sessions");
+ }
+
+ @Test
+ @Order(3)
+ void searchAllDocumentsReturns116() throws Exception {
+ SearchResponse response = searchService.search(COLLECTION,
"*:*", null, null, null, 0, 0);
+ assertEquals(TOTAL_SESSIONS, response.numFound(), "Total
results should be 116");
+ }
+
+ @Test
+ @Order(4)
+ void searchByTrackReturnsFilteredResults() throws Exception {
+ SearchResponse response = searchService.search(COLLECTION,
"*:*", List.of("track:Workshop"), null, null, 0, 10);
+ assertTrue(response.numFound() > 0, "Should find workshop
sessions");
+ for (Map<String, Object> doc : response.documents()) {
+ Object track = doc.get("track");
+ String trackValue = track instanceof List ? ((List<?>)
track).get(0).toString() : track.toString();
+ assertEquals("Workshop", trackValue, "All results
should be workshops");
+ }
+ }
+
+ @Test
+ @Order(5)
+ void searchByKeywordFindsMatchingSessions() throws Exception {
+ SearchResponse response = searchService.search(COLLECTION,
"title:Spring", null, null, null, 0, 50);
+ assertTrue(response.numFound() > 0, "Should find sessions with
'Spring' in the title");
+ for (Map<String, Object> doc : response.documents()) {
+ Object title = doc.get("title");
+ String titleValue = title instanceof List ? ((List<?>)
title).get(0).toString() : title.toString();
+ assertTrue(titleValue.toLowerCase().contains("spring"),
"Title should contain 'spring': " + titleValue);
+ }
+ }
+
+ @Test
+ @Order(6)
+ void facetByIdReturnsResults() throws Exception {
+ // Facet on 'id' which is always a string type in Solr's
_default configset;
+ // schema-less text fields (track, day) are tokenized and
return empty facets.
+ SearchResponse response = searchService.search(COLLECTION,
"*:*", null, List.of("id"), null, 0, 0);
+ assertNotNull(response.facets(), "Facets should not be null");
+ assertTrue(response.facets().containsKey("id"), "Should have id
facet");
+
+ Map<String, Long> idFacets = response.facets().get("id");
+ assertFalse(idFacets.isEmpty(), "Id facets should not be
empty");
+ // Solr default facet.limit is 100, so we get at most 100
entries
+ assertTrue(idFacets.size() >= 100, "Should have facet entries
for sessions");
+ }
+
+ @Test
+ @Order(7)
+ void paginationWorks() throws Exception {
+ SearchResponse page1 = searchService.search(COLLECTION, "*:*",
null, null, null, 0, 10);
+ SearchResponse page2 = searchService.search(COLLECTION, "*:*",
null, null, null, 10, 10);
+
+ assertEquals(10, page1.documents().size(), "Page 1 should have
10 documents");
+ assertEquals(10, page2.documents().size(), "Page 2 should have
10 documents");
+ assertEquals(TOTAL_SESSIONS, page1.numFound(), "Total should be
116 across pages");
+
+ Object id1 = page1.documents().get(0).get("id");
+ Object id2 = page2.documents().get(0).get("id");
+ assertNotEquals(id1, id2, "Pages should return different
documents");
+ }
+
+ @Test
+ @Order(8)
+ void collectionStatsShowIndexedDocuments() throws Exception {
+ SolrMetrics metrics =
collectionService.getCollectionStats(COLLECTION);
+ assertNotNull(metrics, "Metrics should not be null");
+ assertNotNull(metrics.indexStats(), "Index stats should not be
null");
+ assertEquals(Integer.valueOf(TOTAL_SESSIONS),
metrics.indexStats().numDocs(),
+ "Should report 116 indexed documents");
+ }
+
+}