You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2026/05/23 17:40:53 UTC
(cayenne) 01/02: Packaging MCP server as CayenneMCPServer.jar for symmetry with CayenneModeler
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 <an...@objectstyle.com>
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));
};
}