From 8c3beb915bc6bd015c80c02c9d803c35fec6629a Mon Sep 17 00:00:00 2001 From: drompincen Date: Sun, 29 Mar 2026 08:31:21 -0600 Subject: [PATCH 1/2] Fix JBang MCP server: bean name conflict, SDK imports, and API compat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename FileWatcher bean to "javaDuckerFileWatcher" to avoid collision with Spring Boot SslAutoConfiguration's own fileWatcher bean - Update MCP SDK dep from 1.1.0 to 0.8.1 (fat jar with all classes) - Fix imports: io.modelcontextprotocol.sdk → spec/server packages - Use CallToolResult record constructor instead of missing builder() - Verified: 49 tools register, health/stats/search/index_health all work Co-Authored-By: Claude Opus 4.6 (1M context) --- JavaDuckerMcpServer.java | 18 ++++++------------ .../server/ingestion/FileWatcher.java | 2 +- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/JavaDuckerMcpServer.java b/JavaDuckerMcpServer.java index 09189f8..763fd50 100644 --- a/JavaDuckerMcpServer.java +++ b/JavaDuckerMcpServer.java @@ -1,13 +1,13 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? //JAVA 21 -//DEPS io.modelcontextprotocol.sdk:mcp:1.1.0 +//DEPS io.modelcontextprotocol.sdk:mcp:0.8.1 //DEPS org.slf4j:slf4j-nop:2.0.16 import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import io.modelcontextprotocol.sdk.McpSchema; -import io.modelcontextprotocol.sdk.McpServer; -import io.modelcontextprotocol.sdk.server.transport.StdioServerTransportProvider; +import io.modelcontextprotocol.spec.McpSchema; +import io.modelcontextprotocol.server.McpServer; +import io.modelcontextprotocol.server.transport.StdioServerTransportProvider; import java.io.ByteArrayOutputStream; import java.net.Socket; @@ -711,15 +711,9 @@ static McpSchema.Tool tool(String name, String description, String schemaJson) { static McpSchema.CallToolResult call(ThrowingSupplier fn) { try { String json = MAPPER.writeValueAsString(fn.get()); - return McpSchema.CallToolResult.builder() - .content(List.of(new McpSchema.TextContent(json))) - .isError(false) - .build(); + return new McpSchema.CallToolResult(List.of(new McpSchema.TextContent(json)), false); } catch (Exception e) { - return McpSchema.CallToolResult.builder() - .content(List.of(new McpSchema.TextContent("Error: " + e.getMessage()))) - .isError(true) - .build(); + return new McpSchema.CallToolResult(List.of(new McpSchema.TextContent("Error: " + e.getMessage())), true); } } diff --git a/src/main/java/com/javaducker/server/ingestion/FileWatcher.java b/src/main/java/com/javaducker/server/ingestion/FileWatcher.java index 49a2d5b..2857a47 100644 --- a/src/main/java/com/javaducker/server/ingestion/FileWatcher.java +++ b/src/main/java/com/javaducker/server/ingestion/FileWatcher.java @@ -12,7 +12,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -@Component +@Component("javaDuckerFileWatcher") public class FileWatcher { private static final Logger log = LoggerFactory.getLogger(FileWatcher.class); From 382122705843feccd1aabd25f75ed7e6e01c121a Mon Sep 17 00:00:00 2001 From: drompincen Date: Sun, 29 Mar 2026 08:35:23 -0600 Subject: [PATCH 2/2] Document multi-instance setup for running per-project servers - Add "Running Multiple Instances" section with port/db/intake isolation - Show CLI client --port flag for targeting specific instances - Add "Multiple MCP Instances" section with per-project env config - Explain PROJECT_ROOT env var usage for blame/related/stale tools Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 80 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 9f53d9c..181edce 100644 --- a/README.md +++ b/README.md @@ -61,27 +61,50 @@ java -jar target/javaducker-1.0.0.jar \ --javaducker.intake-dir=temp/intake ``` -### Use the CLI Client +### Running Multiple Instances + +Each project can have its own JavaDucker instance. Use different ports, database files, and intake directories to run multiple instances on the same machine: ```bash -# Check health -./run-client.sh health +# Instance 1: project-alpha on port 8080 +java -jar target/javaducker-1.0.0.jar \ + --server.port=8080 \ + --javaducker.db-path=/data/alpha/javaducker.duckdb \ + --javaducker.intake-dir=/data/alpha/intake + +# Instance 2: project-beta on port 8081 +java -jar target/javaducker-1.0.0.jar \ + --server.port=8081 \ + --javaducker.db-path=/data/beta/javaducker.duckdb \ + --javaducker.intake-dir=/data/beta/intake + +# Instance 3: project-gamma on port 8082 +java -jar target/javaducker-1.0.0.jar \ + --server.port=8082 \ + --javaducker.db-path=/data/gamma/javaducker.duckdb \ + --javaducker.intake-dir=/data/gamma/intake +``` + +Each instance is fully isolated — its own DuckDB database, its own intake directory, its own port. The CLI client and MCP server connect to a specific instance via `--port` or `HTTP_PORT`. -# Upload one file -./run-client.sh upload-file --file ./docs/architecture.md +### Use the CLI Client -# Upload a directory -./run-client.sh upload-dir --root ./repo --ext .java,.xml,.md,.yml,.pdf +Point the CLI at the correct port for your instance: -# Search +```bash +# Default instance (port 8080) +./run-client.sh health ./run-client.sh find --phrase "@Transactional" --mode exact -./run-client.sh find --phrase "how onboarding approvals work" --mode hybrid -# Index health check -./run-client.sh index-health +# Targeting a specific instance +./run-client.sh --port 8081 health +./run-client.sh --port 8081 upload-dir --root /projects/beta --ext .java,.xml,.md +./run-client.sh --port 8081 find --phrase "how onboarding approvals work" --mode hybrid -# View stats -./run-client.sh stats +# Other commands +./run-client.sh --port 8082 upload-file --file ./docs/architecture.md +./run-client.sh --port 8082 index-health +./run-client.sh --port 8082 stats ``` ## Features @@ -152,7 +175,7 @@ JavaDucker ships a JBang-based MCP server (`JavaDuckerMcpServer.java`) that expo ./run-server.sh # or run-server.cmd on Windows ``` -2. Register the MCP server in your Claude Code config: +2. Register the MCP server in your Claude Code config (`.claude/settings.json` or `claude_desktop_config.json`): ```json { "mcpServers": { @@ -171,6 +194,35 @@ JavaDucker ships a JBang-based MCP server (`JavaDuckerMcpServer.java`) that expo JAVADUCKER_STALENESS_CHECK=true (default: true, set false to disable) ``` +### Multiple MCP Instances (per-project) + +Register separate MCP servers for different projects, each pointing to its own JavaDucker instance: + +```json +{ + "mcpServers": { + "javaducker-alpha": { + "command": "/path/to/code-helper/run-mcp.sh", + "env": { + "HTTP_PORT": "8080", + "PROJECT_ROOT": "/projects/alpha" + } + }, + "javaducker-beta": { + "command": "/path/to/code-helper/run-mcp.sh", + "env": { + "HTTP_PORT": "8081", + "PROJECT_ROOT": "/projects/beta" + } + } + } +} +``` + +Each MCP server connects to a different backend via `HTTP_PORT`. Start the corresponding Spring Boot instances first (see [Running Multiple Instances](#running-multiple-instances)). + +The `PROJECT_ROOT` env var tells the MCP server where the git repo root is — used by `javaducker_blame`, `javaducker_related`, and `javaducker_stale` (git diff mode). + ### MCP Tools #### Indexing & Search