Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,11 @@ jobs:
java-version: ${{ matrix.java }}
cache: maven
- run: mvn -B test
- name: Test Gradle packaged-JAR example
if: runner.os != 'Windows'
working-directory: examples/gradle-packaged-jar
run: ./gradlew --no-daemon test
- name: Test Gradle packaged-JAR example on Windows
if: runner.os == 'Windows'
working-directory: examples/gradle-packaged-jar
run: .\gradlew.bat --no-daemon test
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ that is the `package` phase, which runs *after* surefire's `test` phase, so a te
`target/*.jar` belongs under failsafe (`*IT`, `mvn verify` — and `maven-failsafe-plugin` has to be
bound in your pom; it is not part of the default lifecycle, and surefire silently skips `*IT`
classes). With Gradle, make the test task depend on whichever task builds the jar (`jar` or
`bootJar`). To stay inside the plain `test` run instead: a Spring Boot server can use the `spring:`
`bootJar`); see the [packaged-JAR Gradle example](examples/gradle-packaged-jar). To stay inside
the plain `test` run instead: a Spring Boot server can use the `spring:`
mode below, and any server can be launched straight from the test classpath, no jar needed —
`command = {"${java.home}/bin/java", "-cp", "${java.class.path}", "com.example.MyServerMain"}`
(`${...}` expands system properties; this repo's own end-to-end test runs that way).
Expand Down
3 changes: 3 additions & 0 deletions examples/gradle-packaged-jar/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
gradlew text eol=lf
gradlew.bat text eol=crlf
*.jar binary
3 changes: 3 additions & 0 deletions examples/gradle-packaged-jar/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.gradle/
build/
!gradle/wrapper/gradle-wrapper.jar
42 changes: 42 additions & 0 deletions examples/gradle-packaged-jar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Gradle packaged-JAR example

A tiny Java stdio MCP server and one JUnit conformance test that launches its
executable JAR. Requires JDK 17+ with `java` on `PATH`; no Gradle or Maven installation
is needed. The checked-in wrapper downloads Gradle 9.7.1 and verifies its SHA-256
checksum. Dependencies come from Maven Central, including the released testkit 0.7.0;
you do not need to build or install the parent project first.

From this directory on Linux/macOS:

```sh
./gradlew test
```

On Windows (PowerShell):

```powershell
.\gradlew.bat test
```

Use `clean test` to repeat from an empty build directory. The test report is at
`build/reports/tests/test/index.html`.

The important wiring in [build.gradle](build.gradle) is:

```groovy
tasks.test {
dependsOn tasks.jar
useJUnitPlatform()
}
```

The `jar` task sets `Main-Class` and bundles Jackson into
`build/libs/gradle-packaged-jar.jar`. `PackagedJarConformanceTest` uses
`@McpServerTest(command = {"java", "-jar", "build/libs/gradle-packaged-jar.jar"})`
to start that artifact over stdio. Gradle runs tests from the project directory,
so the relative JAR path works on Linux and Windows. Without `dependsOn tasks.jar`,
`test` compiles the server but does not package it, and a clean run fails to launch it.

For a Spring Boot project, depend on `tasks.bootJar` instead and point the annotation
at that task's executable JAR. This example uses the plain Java plugin to keep the
packaging dependency visible.
38 changes: 38 additions & 0 deletions examples/gradle-packaged-jar/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
plugins {
id 'java'
}

repositories {
mavenCentral()
}

dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.22.2'
testImplementation 'io.github.senor14:mcp-java-testkit:0.7.0'
testImplementation platform('org.junit:junit-bom:5.14.4')
testImplementation 'org.junit.jupiter:junit-jupiter'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.withType(JavaCompile).configureEach {
options.release = 17
options.encoding = 'UTF-8'
}

tasks.jar {
archiveFileName = 'gradle-packaged-jar.jar'
manifest {
attributes 'Main-Class': 'example.GreetingServer'
}
// Include Jackson so the server can run with just java -jar.
from { configurations.runtimeClasspath.collect { zipTree(it) } }
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
exclude 'META-INF/*.SF', 'META-INF/*.RSA', 'META-INF/*.DSA', '**/module-info.class'
preserveFileTimestamps = false
reproducibleFileOrder = true
}

tasks.test {
dependsOn tasks.jar
useJUnitPlatform()
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=acd53f1edaf02f1a8ff99879f8a34b302661a057d9b063ae9e35b552f804d20a
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
248 changes: 248 additions & 0 deletions examples/gradle-packaged-jar/gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading