modelcontextprotocol/java-sdk

Invalid Automatic-Module-Name in mcp-core

Opened this issue ยท 6 comments

Hey there,

Just upgraded to 0.13.0 and ran into an invalid module name.

Current value:

Automatic-Module-Name:io.modelcontextprotocol.sdk.mcp-core

Module names may not contain dashes, so this should be:

Automatic-Module-Name:io.modelcontextprotocol.sdk.mcp.core

Can anybody fix this issue please? It is blocking updates from 0.12.1 to 0.13.0+ whenever people are using java modules

Seeing this as well (while building my https://github.com/enola-dev/enola and the https://github.com/bazel-contrib/rules_jvm_external tool logging the following message; but it's not specific to that build tool, but to this library, as reported in this issue):

INFO: From Creating compile jar for @@rules_jvm_external++maven+maven//:io_modelcontextprotocol_sdk_mcp_core: Automatic module name 'io.modelcontextprotocol.sdk.mcp-core' contains a character ('-') that may not be used in a java identifier: processed_mcp-core-0.14.1.jar

It looks like that Automatic-Module-Name is generated here. That seems wrong, and perhaps a PR to change and simply hard-code io.modelcontextprotocol.sdk.mcp.core instead of ${project.groupId}.${project.artifactId} would do the trick?

What surprises me is that the other (new, recently introduced) Maven artifacts mcp-json etc. don't even use the bnd-maven-plugin. So I am not sure that this library (as-is in 1.4.1) would fully work with Java modules, anyways; this might need a bit more work.

But I'm not actually using Java modules myself (just saw that message), so leaving this to someone more incentivized.

Hire my services if would like me to take care of this! ๐Ÿ˜†

That seems wrong, and perhaps a PR to change and simply hard-code io.modelcontextprotocol.sdk.mcp.core instead of ${project.groupId}.${project.artifactId} would do the trick?

Yup, that's exactly what my pull request does (#609), now we just cross our fingers for it to be merged. ๐Ÿ˜„

What surprises me is that the other (new, recently introduced) Maven artifacts mcp-json etc. don't even use the bnd-maven-plugin

The "mcp-core" jar includes all the classes from "mcp-json". I was never sure if this was a bug or a feature but as of right now you simply do not need "mcp-json".

Concerning other jars not using bnd-maven-plugin, see #562

Would be good to prioritize fixing this issue, breaks any downstream that is using Java Modules (--module-path). We are currently blocked from taking any version newer than 0.12.1.

I would recommend that as a modern and popular library this SDK should embrace Java Modules and use module-info.java. The UX of consuming this SDK in applications using Java Modules is sub-optimal because downstream module declarations end up having to add requires clauses for the onward dependencies of the SDK. If the SDK used module-info.java which had requires clauses this would not be necessary. See below for example of the verbosity/fragility imposed on downstream Java Modules due to this SDK not having its own module-info.java

To protect against this issue recurring a testcase should be added to confirm that the produced artefacts can be consumed as Java Modules. This would be a build time test, incorporated into the Maven build. If something regresses then this testcase would fail the build.

Below is a sample minimal project that could form the basis of this test:

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>mcp-jpms</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.release>21</maven.compiler.release>
        <compiler.version>3.14.1</compiler.version>
        <exec.version>3.6.1</exec.version>
        <mcp.version>0.12.1</mcp.version>
        <slf4j.version>2.0.16</slf4j.version>
        <module.name>org.example.mcp.jpms</module.name>
        <module.main.class>${module.name}.McpInJavaModule</module.main.class>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.modelcontextprotocol.sdk</groupId>
                <artifactId>mcp</artifactId>
                <version>${mcp.version}</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-simple</artifactId>
                <version>${slf4j.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>io.modelcontextprotocol.sdk</groupId>
            <artifactId>mcp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${compiler.version}</version>
                <configuration>
                    <release>${maven.compiler.release}</release>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>${exec.version}</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <executable>java</executable>
                    <arguments>
                        <argument>--module-path</argument>
                        <modulepath/>
                        <argument>--module</argument>
                        <argument>${module.name}/${module.main.class}</argument>
                    </arguments>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

src/main/java/module-info.java

module org.example.mcp.jpms {
    requires io.modelcontextprotocol.sdk.mcp;
    // require modules that MCP depends on
    requires org.slf4j;
    requires org.slf4j.simple;
    requires reactor.core;
    requires com.networknt.schema;
    requires com.fasterxml.jackson.databind;
}
  • Note how this module has to figure out and declare all the modules which io.modelcontextprotocol.sdk.mcp depends on and add requires clauses for them. This is the disadvantage of just using Automatic-Module-Name. Since this SDK has multiple dependencies, it would be better if it used module-info.java to declare its module name and use requires clauses to add its dependencies to the module graph.

src/main/java/org/example/mcp/jpms/McpInJavaModule.java

package org.example.mcp.jpms;

import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Duration;

public class McpInJavaModule {

    private static final Logger LOGGER = LoggerFactory.getLogger(McpInJavaModule.class);

    public static void main(String... args) {
        // If this compiles then Java Module config is good
        final var params =
                ServerParameters.builder("npx")
                        .args("-y", "@modelcontextprotocol/server-everything", "dir")
                        .build();
        final var transport = new StdioClientTransport(params);
        McpClient.sync(transport).requestTimeout(Duration.ofSeconds(10)).build();
        LOGGER.info("Mcp SDK working with Java modules");
    }
}

Here's a workaround for Gradle based builds.

Using the Extra Java Module Info Gradle plugin.

plugins {
    id("org.gradlex.extra-java-module-info")
}

dependencies {
    implementation("io.modelcontextprotocol.sdk:mcp-core:0.15.0")
}

extraJavaModuleInfo {
    module("io.modelcontextprotocol.sdk:mcp-core", "io.modelcontextprotocol.sdk.mcp.core") {
        overrideModuleName()
    }
    automaticModule("io.modelcontextprotocol.sdk:mcp-json", "io.modelcontextprotocol.sdk.mcp.json")
}