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 ac2b74e9439b28d1654b03884992ec42ded43482
Author: Andrus Adamchik <[email protected]>
AuthorDate: Sun May 24 15:39:49 2026 -0400

    CAY-2943 CayenneModeler MCP: open_project tool
    
    do not attempt to deal with sourtce trees, only locate the Modeler within 
distributions
---
 .../mcp/tools/openproject/McpJarLocator.java       | 41 +---------
 .../mcp/tools/openproject/ModelerDiscovery.java    | 92 ----------------------
 .../mcp/tools/openproject/OpenProjectTool.java     | 58 ++++++++++----
 .../protocol/OpenProjectDistribution.java          |  3 +-
 .../tools/openproject/ModelerDiscoveryTest.java    | 51 ++----------
 5 files changed, 51 insertions(+), 194 deletions(-)

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 3d0902eac..4bdaa664b 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
@@ -25,7 +25,6 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.security.CodeSource;
 import java.security.ProtectionDomain;
-import java.util.Optional;
 
 /**
  * Resolves the directory that contains the running MCP server jar. Used as the
@@ -35,46 +34,12 @@ 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, 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.
+     * Returns the directory containing the jar the given class was loaded 
from, or {@code null} in exotic launch configurations where the
+     * protection domain has no resolvable location.
      */
-    static Optional<Located> locate(Class<?> anchor) {
-        try {
-            ProtectionDomain pd = anchor.getProtectionDomain();
-            if (pd == null) {
-                return Optional.empty();
-            }
-            CodeSource cs = pd.getCodeSource();
-            if (cs == null) {
-                return Optional.empty();
-            }
-            URL url = cs.getLocation();
-            if (url == null) {
-                return Optional.empty();
-            }
-            Path location = Paths.get(url.toURI());
-            // For a jar: location is the jar itself; we want its parent 
directory.
-            // 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.
-            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 56a3b8590..c8ae42fe0 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
@@ -40,9 +40,6 @@ final class ModelerDiscovery {
     static final String WINDOWS_EXE_NAME = "CayenneModeler.exe";
     static final String GENERIC_JAR_NAME = "CayenneModeler.jar";
 
-    /** Max levels to climb when locating the source-tree git root. */
-    private static final int SOURCE_TREE_CLIMB_LIMIT = 8;
-
     sealed interface DiscoveryResult permits Found, NotFound {}
 
     record Found(OpenProjectDistribution distribution, LauncherKind 
launcherKind, Path launcher)
@@ -54,16 +51,6 @@ 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) {
@@ -88,16 +75,6 @@ final class ModelerDiscovery {
         }
         notes.add("generic: no " + GENERIC_JAR_NAME + " sibling of the MCP 
jar");
 
-        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>`)""");
-        }
-
         return new NotFound(List.copyOf(notes));
     }
 
@@ -151,73 +128,4 @@ final class ModelerDiscovery {
         return Files.isRegularFile(candidate) ? Optional.of(candidate) : 
Optional.empty();
     }
 
-    /**
-     * Source-tree probe — climbs to a {@code .git}-bearing root and looks 
under
-     * {@code modeler/cayenne-modeler-{mac,win,generic}/target/classes/} for 
the
-     * OS-appropriate launcher. The reported distribution is {@code 
source_tree}
-     * so callers can see when dev-mode discovery kicked in; the launcher kind
-     * still drives argv construction.
-     */
-    static Optional<Found> probeSourceTree(Path mcpDir, OsKind osKind) {
-        Path gitRoot = findGitRoot(mcpDir);
-        if (gitRoot == null) {
-            return Optional.empty();
-        }
-
-        if (osKind == OsKind.MAC) {
-            Path macClasses = 
gitRoot.resolve("modeler/cayenne-modeler-mac/target/classes");
-            Optional<Path> bundle = findAppBundle(macClasses);
-            if (bundle.isPresent()) {
-                return Optional.of(new Found(
-                        OpenProjectDistribution.source_tree, 
LauncherKind.MAC_APP, bundle.get()));
-            }
-        }
-
-        if (osKind == OsKind.WINDOWS) {
-            Path winExe = gitRoot.resolve(
-                    "modeler/cayenne-modeler-win/target/classes/" + 
WINDOWS_EXE_NAME);
-            if (Files.isRegularFile(winExe)) {
-                return Optional.of(new Found(
-                        OpenProjectDistribution.source_tree, 
LauncherKind.WINDOWS_EXE, winExe));
-            }
-        }
-
-        Path genericJar = gitRoot.resolve(
-                "modeler/cayenne-modeler-generic/target/classes/" + 
GENERIC_JAR_NAME);
-        if (Files.isRegularFile(genericJar)) {
-            return Optional.of(new Found(
-                    OpenProjectDistribution.source_tree, 
LauncherKind.GENERIC_JAR, genericJar));
-        }
-
-        return Optional.empty();
-    }
-
-    private static Path findGitRoot(Path start) {
-        if (start == null) {
-            return null;
-        }
-        Path current = start;
-        for (int i = 0; i < SOURCE_TREE_CLIMB_LIMIT && current != null; i++) {
-            if (Files.isDirectory(current.resolve(".git"))) {
-                return current;
-            }
-            current = current.getParent();
-        }
-        return null;
-    }
-
-    private static Optional<Path> findAppBundle(Path dir) {
-        if (!Files.isDirectory(dir)) {
-            return Optional.empty();
-        }
-        try (var stream = Files.list(dir)) {
-            return stream
-                    .filter(p -> p.getFileName() != null
-                            && p.getFileName().toString().endsWith(".app"))
-                    .filter(p -> 
Files.isDirectory(p.resolve("Contents/MacOS")))
-                    .findFirst();
-        } catch (java.io.IOException e) {
-            return Optional.empty();
-        }
-    }
 }
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 82de61236..3f83620ba 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
@@ -37,12 +37,16 @@ import 
org.apache.cayenne.mcp.tools.openproject.protocol.OpenProjectValidation;
 import org.apache.cayenne.modeler.pref.PrefsLocator;
 
 import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.CodeSource;
+import java.security.ProtectionDomain;
 import java.time.Duration;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.UUID;
 
 /**
@@ -131,22 +135,21 @@ public class OpenProjectTool {
         }
 
         // Step 2 — locate the MCP jar's directory.
-        Optional<McpJarLocator.Located> mcpLocated = 
McpJarLocator.locate(OpenProjectTool.class);
-        if (mcpLocated.isEmpty()) {
+        Path mcpDir = mcpJarDir();
+        if (mcpDir == null) {
             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(mcp.dir(), 
osKind, mcp.isDistribution());
+        DiscoveryResult discovery = ModelerDiscovery.discover(mcpDir, osKind);
         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(mcp.dir(), String.join("; ", 
nf.probeNotes())),
+                            .formatted(mcpDir, String.join("; ", 
nf.probeNotes())),
                     new OpenProjectValidation(true, true, false));
         };
     }
@@ -214,20 +217,20 @@ public class OpenProjectTool {
                                 "Spawned Modeler process exited before 
reporting handshake (%s)"
                                         .formatted(exit)));
             }
-            case TIMEOUT -> {
-                yield new OpenProjectResult(
-                        "error",
-                        resolved,
-                        allPassed,
-                        null,
-                        new 
OpenProjectError(OpenProjectErrorCode.launch_not_confirmed,
-                                "Handshake did not appear within 
%ds".formatted(HANDSHAKE_TIMEOUT.toSeconds())));
-            }
+            case TIMEOUT -> new OpenProjectResult(
+                    "error",
+                    resolved,
+                    allPassed,
+                    null,
+                    new 
OpenProjectError(OpenProjectErrorCode.launch_not_confirmed,
+                            "Handshake did not appear within 
%ds".formatted(HANDSHAKE_TIMEOUT.toSeconds())));
         };
     }
 
-    private static OpenProjectResult validationFailed(OpenProjectErrorCode 
code, String message,
-                                                      OpenProjectValidation 
validation) {
+    private static OpenProjectResult validationFailed(
+            OpenProjectErrorCode code,
+            String message,
+            OpenProjectValidation validation) {
         return new OpenProjectResult(
                 "validation_failed",
                 null,
@@ -236,4 +239,25 @@ public class OpenProjectTool {
                 new OpenProjectError(code, message)
         );
     }
+
+    static Path mcpJarDir() {
+        try {
+            ProtectionDomain pd = OpenProjectTool.class.getProtectionDomain();
+            if (pd == null) {
+                return null;
+            }
+            CodeSource cs = pd.getCodeSource();
+            if (cs == null) {
+                return null;
+            }
+            URL url = cs.getLocation();
+            if (url == null) {
+                return null;
+            }
+            Path location = Paths.get(url.toURI());
+            return Files.isRegularFile(location) && location.getParent() != 
null ? location.getParent() : location;
+        } catch (URISyntaxException | RuntimeException e) {
+            return null;
+        }
+    }
 }
diff --git 
a/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/protocol/OpenProjectDistribution.java
 
b/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/protocol/OpenProjectDistribution.java
index 3f3eb0f19..9985d8f16 100644
--- 
a/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/protocol/OpenProjectDistribution.java
+++ 
b/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/openproject/protocol/OpenProjectDistribution.java
@@ -26,6 +26,5 @@ package org.apache.cayenne.mcp.tools.openproject.protocol;
 public enum OpenProjectDistribution {
     mac,
     windows,
-    generic,
-    source_tree
+    generic
 }
diff --git 
a/cayenne-mcp-server/src/test/java/org/apache/cayenne/mcp/tools/openproject/ModelerDiscoveryTest.java
 
b/cayenne-mcp-server/src/test/java/org/apache/cayenne/mcp/tools/openproject/ModelerDiscoveryTest.java
index e4b133414..2f680e196 100644
--- 
a/cayenne-mcp-server/src/test/java/org/apache/cayenne/mcp/tools/openproject/ModelerDiscoveryTest.java
+++ 
b/cayenne-mcp-server/src/test/java/org/apache/cayenne/mcp/tools/openproject/ModelerDiscoveryTest.java
@@ -174,8 +174,8 @@ public class ModelerDiscoveryTest {
         DiscoveryResult result = ModelerDiscovery.discover(tmp, OsKind.MAC);
 
         NotFound nf = assertInstanceOf(NotFound.class, result);
-        // Mac eligible probes: mac, generic, source_tree (3)
-        assertEquals(3, nf.probeNotes().size());
+        // Mac eligible probes: mac, generic (2)
+        assertEquals(2, nf.probeNotes().size());
         // None of the notes should mention .exe (Windows probe wasn't 
eligible).
         assertTrue(nf.probeNotes().stream().noneMatch(n -> 
n.contains(".exe")));
     }
@@ -199,47 +199,8 @@ public class ModelerDiscoveryTest {
         DiscoveryResult result = ModelerDiscovery.discover(tmp, OsKind.OTHER);
 
         NotFound nf = assertInstanceOf(NotFound.class, result);
-        // OTHER eligible probes: generic, source_tree (2)
-        assertEquals(2, nf.probeNotes().size());
-    }
-
-    // -------- Source-tree fallback --------
-
-    @Test
-    public void sourceTreeMatchesGenericLauncher(@TempDir Path tmp) throws 
IOException {
-        Path root = tmp.resolve("repo");
-        Files.createDirectories(root.resolve(".git"));
-        Path genericClasses = 
root.resolve("modeler/cayenne-modeler-generic/target/classes");
-        Files.createDirectories(genericClasses);
-        
Files.createFile(genericClasses.resolve(ModelerDiscovery.GENERIC_JAR_NAME));
-
-        Path mcpDir = root.resolve("cayenne-mcp-server/target");
-        Files.createDirectories(mcpDir);
-
-        DiscoveryResult result = ModelerDiscovery.discover(mcpDir, 
OsKind.OTHER);
-
-        Found found = assertInstanceOf(Found.class, result);
-        assertEquals(OpenProjectDistribution.source_tree, 
found.distribution());
-        assertEquals(LauncherKind.GENERIC_JAR, found.launcherKind());
-    }
-
-    @Test
-    public void sourceTreeMatchesMacBundle(@TempDir Path tmp) throws 
IOException {
-        Path root = tmp.resolve("repo");
-        Files.createDirectories(root.resolve(".git"));
-        Path macClasses = 
root.resolve("modeler/cayenne-modeler-mac/target/classes");
-        Path bundle = macClasses.resolve("CayenneModeler.app");
-        Files.createDirectories(bundle.resolve("Contents/MacOS"));
-
-        Path mcpDir = root.resolve("cayenne-mcp-server/target");
-        Files.createDirectories(mcpDir);
-
-        DiscoveryResult result = ModelerDiscovery.discover(mcpDir, OsKind.MAC);
-
-        Found found = assertInstanceOf(Found.class, result);
-        assertEquals(OpenProjectDistribution.source_tree, 
found.distribution());
-        assertEquals(LauncherKind.MAC_APP, found.launcherKind());
-        assertEquals(bundle, found.launcher());
+        // OTHER eligible probes: generic (1)
+        assertEquals(1, nf.probeNotes().size());
     }
 
     @Test
@@ -247,8 +208,8 @@ public class ModelerDiscoveryTest {
         DiscoveryResult result = ModelerDiscovery.discover(tmp, OsKind.OTHER);
 
         NotFound nf = assertInstanceOf(NotFound.class, result);
-        assertEquals(2, nf.probeNotes().size(),
-                "OTHER has 2 eligible probes: generic and source_tree");
+        assertEquals(1, nf.probeNotes().size(),
+                "OTHER has 1 eligible probe: generic");
     }
 
     // -------- Helpers --------

Reply via email to