This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit 40eb6c9ab677ca0eb185b9155b75cca101697be7 Author: Andrus Adamchik <[email protected]> AuthorDate: Sat May 23 13:25:44 2026 -0400 Packaging MCP server as CayenneMCPServer.jar for symmetry with CayenneModeler --- ai-plugin/README.md | 2 +- ai-plugin/references/mcp-tools.md | 2 +- cayenne-mcp-server/README.md | 20 +++++++++--------- cayenne-mcp-server/pom.xml | 1 + .../org/apache/cayenne/mcp/CayenneMcpMain.java | 2 +- .../mcp/tools/openproject/McpJarLocator.java | 19 +++++++++++++---- .../mcp/tools/openproject/ModelerDiscovery.java | 24 ++++++++++++++++------ .../mcp/tools/openproject/OpenProjectTool.java | 9 ++++---- 8 files changed, 52 insertions(+), 27 deletions(-) diff --git a/ai-plugin/README.md b/ai-plugin/README.md index 50914756a..a6868a480 100644 --- a/ai-plugin/README.md +++ b/ai-plugin/README.md @@ -43,7 +43,7 @@ The plugin is distributed from the Apache Cayenne GitHub repository: **https://g /plugin install apache-cayenne@apache-cayenne # Install the MCP server (requires a local CayenneModeler install) -claude mcp add cayenne --scope user -- java -jar /path/to/cayenne-mcp-server-<VERSION>.jar +claude mcp add cayenne --scope user -- java -jar /path/to/CayenneMCPServer.jar ``` Full MCP setup instructions: https://cayenne.apache.org/docs/5.0/cayenne-guide/installing-the-cayenne-mcp-server/ diff --git a/ai-plugin/references/mcp-tools.md b/ai-plugin/references/mcp-tools.md index c8ab872bc..f0a61506e 100644 --- a/ai-plugin/references/mcp-tools.md +++ b/ai-plugin/references/mcp-tools.md @@ -25,7 +25,7 @@ The Cayenne MCP server (`cayenne-mcp-server` module) exposes Cayenne operations Setup is documented in `cayenne-mcp-server/README.md` at the repo root. Quick form for Claude Code: ```bash -claude mcp add cayenne --scope user -- java -jar /path/to/cayenne-mcp-server-<VERSION>.jar +claude mcp add cayenne --scope user -- java -jar /path/to/CayenneMCPServer.jar ``` Verify the server is registered with `claude mcp list` — you should see an entry named `cayenne` (the MCP server alias from the `claude mcp add cayenne` command above) showing as connected. This is unrelated to the plugin name `apache-cayenne`. diff --git a/cayenne-mcp-server/README.md b/cayenne-mcp-server/README.md index 1464fda16..f776bc550 100644 --- a/cayenne-mcp-server/README.md +++ b/cayenne-mcp-server/README.md @@ -14,21 +14,21 @@ Downloads are available at https://cayenne.apache.org/download/. ### macOS (DMG) ``` -<install-dir>/CayenneModeler.app/Contents/Resources/mcp/cayenne-mcp-server-<VERSION>.jar +<install-dir>/CayenneModeler.app/Contents/Resources/mcp/CayenneMCPServer.jar # But typically, in: -/Applications/CayenneModeler.app/Contents/Resources/mcp/cayenne-mcp-server-<VERSION>.jar +/Applications/CayenneModeler.app/Contents/Resources/mcp/CayenneMCPServer.jar ``` ### Windows (ZIP) ``` -<install-dir>\bin\cayenne-mcp-server-<VERSION>.jar +<install-dir>\bin\CayenneMCPServer.jar ``` ### Linux / cross-platform (tar.gz) ``` -<install-dir>/bin/cayenne-mcp-server-<VERSION>.jar +<install-dir>/bin/CayenneMCPServer.jar ``` ### Development build (own Cayenne source) @@ -42,13 +42,13 @@ mvn clean package -pl cayenne-mcp-server -am -DskipTests The JAR is then at: ``` -cayenne-mcp-server/target/cayenne-mcp-server-<VERSION>.jar +cayenne-mcp-server/target/classes/CayenneMCPServer.jar ``` ## Configuring AI clients The server communicates over **stdio** and is launched on demand by the client. -Replace `/path/to/cayenne-mcp-server.jar` with the actual path from the section above. +Replace `/path/to/CayenneMCPServer.jar` with the actual path from the section above. ### Claude Code @@ -56,10 +56,10 @@ By default `claude mcp add` registers the server for the current project only. U ```bash # Current project only (default) -claude mcp add cayenne -- java -jar /path/to/cayenne-mcp-server-<VERSION>.jar +claude mcp add cayenne -- java -jar /path/to/CayenneMCPServer.jar # All projects for the current user -claude mcp add cayenne --scope user -- java -jar /path/to/cayenne-mcp-server-<VERSION>.jar +claude mcp add cayenne --scope user -- java -jar /path/to/CayenneMCPServer.jar ``` ### Cursor @@ -71,7 +71,7 @@ Edit `~/.cursor/mcp.json` (global, all projects) or `.cursor/mcp.json` in your p "mcpServers": { "cayenne": { "command": "java", - "args": ["-jar", "/path/to/cayenne-mcp-server-<VERSION>.jar"] + "args": ["-jar", "/path/to/CayenneMCPServer.jar"] } } } @@ -87,7 +87,7 @@ Add to `.vscode/mcp.json` in your project (per-project), or to your user `mcp.js "cayenne": { "type": "stdio", "command": "java", - "args": ["-jar", "/path/to/cayenne-mcp-server-<VERSION>.jar"] + "args": ["-jar", "/path/to/CayenneMCPServer.jar"] } } } diff --git a/cayenne-mcp-server/pom.xml b/cayenne-mcp-server/pom.xml index 7eca6077f..7df0f20c0 100644 --- a/cayenne-mcp-server/pom.xml +++ b/cayenne-mcp-server/pom.xml @@ -76,6 +76,7 @@ </dependencies> <build> + <finalName>CayenneMCPServer</finalName> <plugins> <plugin> diff --git a/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/CayenneMcpMain.java b/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/CayenneMcpMain.java index cb6fcfa0e..045a2e98d 100644 --- a/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/CayenneMcpMain.java +++ b/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/CayenneMcpMain.java @@ -33,7 +33,7 @@ public class CayenneMcpMain { String version = version(); if (opts.isHelp()) { - System.err.println("Usage: java -jar cayenne-mcp-server.jar [options]"); + System.err.println("Usage: java -jar CayenneMCPServer.jar [options]"); System.err.println(); System.err.println("Options:"); System.err.println(" -h, --help Print this help and exit."); diff --git a/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/McpJarLocator.java b/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/McpJarLocator.java index 4b6085a07..3d0902eac 100644 --- a/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/McpJarLocator.java +++ b/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/McpJarLocator.java @@ -20,6 +20,7 @@ package org.apache.cayenne.mcp.tools.openproject; import java.net.URL; import java.net.URISyntaxException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.CodeSource; @@ -34,15 +35,21 @@ import java.util.Optional; */ final class McpJarLocator { + /** Canonical name of the MCP server jar in a distribution (outside of dev builds). */ + static final String MCP_JAR_NAME = "CayenneMCPServer.jar"; + + record Located(Path dir, boolean isDistribution) {} + private McpJarLocator() { } /** * Locates the directory containing the jar (or class output dir) the given class - * was loaded from. Returns {@code Optional.empty()} in exotic launch configurations - * where the protection domain has no resolvable location. + * was loaded from, and whether that jar is the canonical distribution jar + * ({@value #MCP_JAR_NAME}). Returns {@code Optional.empty()} in exotic launch + * configurations where the protection domain has no resolvable location. */ - static Optional<Path> locate(Class<?> anchor) { + static Optional<Located> locate(Class<?> anchor) { try { ProtectionDomain pd = anchor.getProtectionDomain(); if (pd == null) { @@ -61,7 +68,11 @@ final class McpJarLocator { // For a class-file directory (IDE / surefire fork): location is the dir; // its parent is also a reasonable starting point (target/), but here we // want the dir that "would have been the jar's parent" — so use it directly. - return Optional.of(location.getParent() != null ? location.getParent() : location); + boolean isDistribution = Files.isRegularFile(location) + && MCP_JAR_NAME.equals(location.getFileName() != null + ? location.getFileName().toString() : ""); + Path dir = location.getParent() != null ? location.getParent() : location; + return Optional.of(new Located(dir, isDistribution)); } catch (URISyntaxException | RuntimeException e) { return Optional.empty(); } diff --git a/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/ModelerDiscovery.java b/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/ModelerDiscovery.java index d7cf007b0..56a3b8590 100644 --- a/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/ModelerDiscovery.java +++ b/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/ModelerDiscovery.java @@ -54,6 +54,16 @@ final class ModelerDiscovery { } static DiscoveryResult discover(Path mcpDir, OsKind osKind) { + return discover(mcpDir, osKind, false); + } + + /** + * @param isDistributionJar {@code true} when the MCP server is running from + * {@value McpJarLocator#MCP_JAR_NAME} — a distribution build. When {@code true}, + * the source-tree fallback is skipped: a production jar should not accidentally + * pick up a developer Modeler build. + */ + static DiscoveryResult discover(Path mcpDir, OsKind osKind, boolean isDistributionJar) { List<String> notes = new ArrayList<>(); if (osKind == OsKind.MAC) { @@ -78,13 +88,15 @@ final class ModelerDiscovery { } notes.add("generic: no " + GENERIC_JAR_NAME + " sibling of the MCP jar"); - Optional<Found> sourceTree = probeSourceTree(mcpDir, osKind); - if (sourceTree.isPresent()) { - return sourceTree.get(); + if (!isDistributionJar) { + Optional<Found> sourceTree = probeSourceTree(mcpDir, osKind); + if (sourceTree.isPresent()) { + return sourceTree.get(); + } + notes.add(""" + source_tree: no built CayenneModeler under <gitRoot>/modeler/cayenne-modeler-{mac,win,generic}/target/classes/ \ + (run `mvn -pl modeler/cayenne-modeler-<kind> -am package -P<kind>`)"""); } - notes.add(""" - source_tree: no built CayenneModeler under <gitRoot>/modeler/cayenne-modeler-{mac,win,generic}/target/classes/ \ - (run `mvn -pl modeler/cayenne-modeler-<kind> -am package -P<kind>`)"""); return new NotFound(List.copyOf(notes)); } diff --git a/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/OpenProjectTool.java b/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/OpenProjectTool.java index 6ab3e75d7..ced9e3a21 100644 --- a/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/OpenProjectTool.java +++ b/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/OpenProjectTool.java @@ -123,21 +123,22 @@ public class OpenProjectTool { } // Step 2 — locate the MCP jar's directory. - Optional<Path> mcpDir = McpJarLocator.locate(OpenProjectTool.class); - if (mcpDir.isEmpty()) { + Optional<McpJarLocator.Located> mcpLocated = McpJarLocator.locate(OpenProjectTool.class); + if (mcpLocated.isEmpty()) { return validationFailed(OpenProjectErrorCode.mcp_jar_location_unresolved, "Could not resolve the running MCP server jar's location", new OpenProjectValidation(true, false, null)); } + McpJarLocator.Located mcp = mcpLocated.get(); // Step 3 — discover a Modeler installation. OsKind osKind = OsKind.detect(); - DiscoveryResult discovery = ModelerDiscovery.discover(mcpDir.get(), osKind); + DiscoveryResult discovery = ModelerDiscovery.discover(mcp.dir(), osKind, mcp.isDistribution()); return switch (discovery) { case Found f -> launchAndAwait(f, projectFile); case NotFound nf -> validationFailed(OpenProjectErrorCode.modeler_not_found, "No CayenneModeler installation found relative to MCP jar at %s. Probes: %s" - .formatted(mcpDir.get(), String.join("; ", nf.probeNotes())), + .formatted(mcp.dir(), String.join("; ", nf.probeNotes())), new OpenProjectValidation(true, true, false)); }; }
