gradle-mcp-server
v0.6.0
Published
MCP server that resolves external Java/Kotlin classes from Gradle dependencies — returns artifact coordinates and class structure in one call.
Maintainers
Readme
gradle-mcp-server
A Model Context Protocol (MCP) server that lets AI agents look up external Java/Kotlin classes in a Gradle project in a single tool call — no more chains of find, grep, jar, and javap.
Given a fully-qualified class name, it:
- Resolves every source set's compile classpath via Gradle —
main,test, and any custom source sets — sotestImplementation/testFixturesdeps are included. Uses a bundled init script; your project is not modified. - Locates the JAR containing the class.
- Runs
javapand parses the output. - Returns the artifact coordinates plus the class's fields, methods, and modifiers.
Requirements
- Node.js 18+
- A JDK on
PATH(sojavapis available) - The target project must have a Gradle wrapper (
./gradlewon macOS/Linux,gradlew.baton Windows) - macOS, Linux, or Windows
Supported target versions
The server runs against your project's own Gradle wrapper and JDK, so it inherits their version support. CI exercises the full tool surface against both ends of the currently supported range:
| Gradle | JDK | Status | | ------ | --- | ------ | | 8.5 | 21 | Tested in CI | | 9.5.1 | 25 | Tested in CI |
Other versions within that range are expected to work but aren't pinned in CI.
The javap parsing supports class-file versions through Java 25 (major version 69).
Install / run
Recommended — from npm:
npx -y gradle-mcp-server@latestVersioned, signed with build provenance (you can verify it came from this repo's release workflow on the package's npm page). @latest makes npx re-check the registry each run so you pick up new versions automatically. Pin a specific version with [email protected] if you'd rather control upgrades.
Alternatives:
Straight from GitHub (no registry):
npx -y github:Jukipuki/gradle-mcp-server#v0.3.0Pin to a tag, otherwise npx's cache can serve stale commits. Requires a TS toolchain on the consumer side (the prepare script builds on install).
From a local checkout:
git clone https://github.com/Jukipuki/gradle-mcp-server.git
cd gradle-mcp-server && npm install && npm run build
node /absolute/path/to/gradle-mcp-server/dist/index.jsWire into Claude Desktop
Edit claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"gradle": {
"command": "npx",
"args": ["-y", "gradle-mcp-server@latest"]
}
}
}Or pin a specific version (e.g. [email protected]) if you'd rather control upgrades. To run straight from GitHub instead:
{
"mcpServers": {
"gradle": {
"command": "npx",
"args": ["-y", "github:Jukipuki/gradle-mcp-server#v0.3.0"]
}
}
}Or, from a local checkout:
{
"mcpServers": {
"gradle": {
"command": "node",
"args": ["/absolute/path/to/gradle-mcp-server/dist/index.js"]
}
}
}Restart Claude Desktop. The agent will see a tool named resolve_external_class.
Wire into Kiro
Kiro uses the same MCP server schema as Claude Desktop. Put the config in either:
- Workspace:
.kiro/settings/mcp.json(committed per-repo) - User-global:
~/.kiro/settings/mcp.json
{
"mcpServers": {
"gradle": {
"command": "npx",
"args": ["-y", "gradle-mcp-server@latest"],
"disabled": false,
"autoApprove": ["resolve_external_class"]
}
}
}autoApprove lets the tool run without a per-call confirmation prompt — useful since resolve_external_class is read-only. Remove that field if you'd rather approve each call. Reload the MCP servers from the Kiro command palette (or restart Kiro) after editing.
Local development & manual testing
From a local checkout, the easiest way to exercise the tools without wiring into an MCP client is the official MCP Inspector:
npm install && npm run build
npx @modelcontextprotocol/inspector node dist/index.jsThe Inspector opens a browser UI where you can list tools, fill in arguments, and invoke them against a real Gradle project. The classpath is cached per server process, so repeated calls in the same session are instant — restart the server (or change a build file) to pick up dependency changes.
Automated tests
npm testRuns against a minimal Gradle fixture at test/fixtures/sample-project/ (Java plugin + one Maven Central dep). The suite covers:
- The MCP handshake —
serverInfo.versionmatchespackage.json;tools/listreturns all six tools. - Every tool end-to-end against the fixture, including a regression guard that asserts the
mainsource set appears inlist_dependenciesoutput (this would have caught the silentcompileClasspathfilter bug).
Requires a JDK on PATH. First run downloads the Gradle distribution (~120 MB) into ~/.gradle/; subsequent runs reuse it. CI runs the same npm test on Ubuntu, macOS, and Windows via .github/workflows/ci.yml.
Tools
The server exposes six tools. All take projectPath as an absolute path to the Gradle project root (except inspect_class, which takes a jarPath directly).
Picking the right tool
| If you want to… | Use |
| --- | --- |
| Look up a class by FQCN when you don't know the JAR | resolve_external_class |
| Inspect a class when you already know the JAR path | inspect_class |
| List everything on the classpath | list_dependencies |
| Check "what version of X do I have?" | find_dependency_version |
| Understand why a dependency was pulled in | dependency_insight |
| See which dependencies have newer versions | check_outdated |
resolve_external_class
Resolve and inspect a class from external dependencies when you DON'T know which JAR contains it. Replaces manual find / grep / jar / javap chains.
Input: projectPath, className (FQCN), includePrivate (default false).
Example response:
{
"found": true,
"artifact": "com.elevate:commons-workflow:1.390.1",
"className": "com.elevate.workflows.prefund.dto.VoidPrefundReturnInvoicesDto",
"jarPath": "/Users/.../commons-workflow-1.390.1.jar",
"isRecord": true,
"isInterface": false,
"isAbstract": false,
"isFinal": true,
"superclass": "Record",
"hasBuilder": true,
"fields": [
{ "name": "organizationIds", "type": "List<Long>", "access": "private", "modifiers": ["final"] }
],
"methods": [
{ "name": "organizationIds", "returnType": "List<Long>", "parameters": [], "access": "public", "modifiers": [], "isConstructor": false }
]
}Miss: { "found": false, "className": "...", "searched": 142 }
Error: { "found": false, "className": "...", "error": "Gradle wrapper not found at ..." }
inspect_class
Same class structure as resolve_external_class, but takes a jarPath directly — skips Gradle classpath resolution. Use this when the JAR path is already known (e.g. from a previous resolve_external_class result or list_dependencies).
Input: jarPath, className (FQCN), includePrivate (default false).
Response shape is identical to resolve_external_class minus the artifact field.
list_dependencies
List all resolved external dependencies across every source set's compile classpath (main + test + custom). Each entry includes sourceSets, e.g. ["main"], ["test"], or ["main","test"].
Input: projectPath, directOnly (default false).
Example response:
{
"count": 142,
"totalResolved": 142,
"dependencies": [
{ "group": "com.fasterxml.jackson.core", "artifact": "jackson-databind", "version": "2.17.1", "jarPath": "/Users/.../jackson-databind-2.17.1.jar", "direct": true, "sourceSets": ["main", "test"] }
]
}find_dependency_version
Substring-match against group / artifact and return resolved versions. Case-insensitive.
Input: projectPath, query.
Example: query: "jackson" →
{
"query": "jackson",
"count": 6,
"matches": [
{ "group": "com.fasterxml.jackson.core", "artifact": "jackson-databind", "version": "2.17.1", "direct": true, "sourceSets": ["main", "test"] },
{ "group": "com.fasterxml.jackson.core", "artifact": "jackson-annotations", "version": "2.17.1", "direct": false, "sourceSets": ["main", "test"] }
]
}dependency_insight
Wraps ./gradlew dependencyInsight --configuration compileClasspath --dependency <query>. Returns the raw resolution chain and a parsed requestedBy list.
Input: projectPath, dependency (artifact name or group:artifact), optional subproject (e.g. ":app" — defaults to root).
Example response:
{
"found": true,
"dependency": "jackson-databind",
"subproject": null,
"raw": "...full dependencyInsight output...",
"truncated": false,
"requestedBy": ["+--- com.example:my-lib:1.0", "\\--- com.example:other-lib:2.3"]
}check_outdated
Compares resolved versions against the latest available on Maven Central and any other repositories the project declares — whether in a project repositories {} block or centrally in settings.gradle under dependencyResolutionManagement.repositories (the modern default). For a dependency Maven Central doesn't have (e.g. an internal artifact), each declared repo's maven-metadata.xml is queried instead. Credentials configured on a repo are forwarded — both username/password (basic auth) and HTTP-header credentials (e.g. an Authorization: Bearer <token> access token) — so private JFrog/Nexus repos resolve. Checks direct deps only by default.
Input: projectPath, includeTransitive (default false), limit (optional cap).
Example response:
{
"checked": 24,
"outdatedCount": 3,
"outdated": [
{ "group": "com.fasterxml.jackson.core", "artifact": "jackson-databind", "current": "2.17.1", "latest": "2.18.2", "outdated": true }
],
"unresolvedCount": 1,
"unresolved": [
{ "group": "com.internal", "artifact": "some-lib", "current": "1.0.0" }
],
"repositoriesQueried": [
"https://search.maven.org",
"https://repo.maven.apache.org/maven2/",
"https://internal.example.com/artifactory/libs-release/"
]
}unresolved lists deps that resolved on the classpath but couldn't be found in any queried repo (missing repo, wrong credentials, etc.) — this explains why checked may be lower than the total dependency count. repositoriesQueried shows exactly which repositories were consulted, for debugging.
Pre-release latestVersion values (SNAPSHOT, alpha, beta, rc, M*) are filtered out when the current version is stable.
How it works
The server invokes:
./gradlew -q --init-script <bundled-init-script> printGradleMcpInfoThe bundled Groovy init script registers printGradleMcpInfo on every project. For each resolvable *CompileClasspath configuration (so main, test, and any custom source sets), it emits GMCP|D|group:artifact:version|sourceSet1,sourceSet2|/path/to.jar for direct deps and GMCP|T|... for transitive. Entries appearing in multiple source sets are merged. Classpath output is cached per projectPath and invalidated when any of build.gradle.kts, build.gradle, settings.gradle.kts, or settings.gradle change.
The init script also emits a line for every Maven repository, collected from both project.repositories and settings.dependencyResolutionManagement.repositories: GMCP-REPO|<url> with no credentials, GMCP-REPO|<url>|basic|<base64(user:password)> for username/password auth, or GMCP-REPO|<url>|header|<base64(name)>|<base64(value)> for HTTP-header auth. These feed check_outdated.
dependency_insight is a separate ./gradlew dependencyInsight invocation. check_outdated queries https://search.maven.org/solrsearch/select (results cached in-memory for 10 minutes), falling back to each declared repo's maven-metadata.xml for artifacts Maven Central doesn't have.
Releases (maintainer notes)
Versioning follows semver: patch for bug fixes, minor for new tools or backwards-compatible additions, major for breaking changes to tool inputs/outputs.
Publishes are automated. Tagging a v* commit triggers .github/workflows/release.yml, which:
- Runs
npm testagainst the fixture project. If any test fails, the publish job is skipped. - Checks out the tagged commit.
- Installs deps and builds via the
preparescript. - Verifies the git tag matches
package.jsonversion (fails the run if they drift). - Runs
npm publish --provenance --access public. Authentication is via npm Trusted Publishers — no long-livedNPM_TOKENexists; the workflow exchanges a short-lived GitHub OIDC token for publish rights. Provenance attestations are signed and recorded in the sigstore transparency log, and npm displays a "Built and signed on GitHub Actions" badge on the package page.
To cut a release:
npm version patch -m "Release %s" # or minor / major; updates package.json, commits, tags
git push
git push origin v$(node -p "require('./package.json').version")Watch the run in the Actions tab. On success, npm view gradle-mcp-server reflects the new version within a minute.
If a release fails partway: fix the issue, push to main, and re-run the failed job from the Actions UI — the tag is unchanged, so the workflow will retry against the same ref. Avoid npm unpublish: it bans the version name permanently. Prefer rolling forward to the next patch.
Caveats
- Only compile classpaths are resolved (every source set's
*CompileClasspath).runtimeClasspathis out of scope, so runtime-only deps may be missing. - Source / Javadoc JARs are not used; class info comes from
javap. - Cache is in-memory (process-lifetime).
check_outdatedversion comparison is a simple numeric-aware split, not full MavenComparableVersion. Edge cases (timestamped snapshots, qualifier ordering) may be misclassified.
License
MIT
