JVM Source Lens

Resolves your Gradle project’s real classpath and returns Java source, method signatures, and class structure for any dependency class—using the version your build actually uses, not random files from ~/.gradle/caches.

JVM Source Lens (jvmsrc)

CI npm version License: MIT Node

Resolve a JVM project's actual classpath through Gradle, then read Java source, signatures, and structure for any class on that classpath — with the version your project uses, not whatever happens to sit in ~/.gradle/caches.

Built for CLI use and MCP agents (Cursor, Claude Code, Windsurf, etc.).

Contents: Why · How it works · Requirements · Limitations · Install · Quick start · MCP · Security · Troubleshooting · Building from source

Why this exists

Coding agents on JVM codebases often hit a library type that is not in the workspace. A common failure mode: see an import, try to open the class in the repo, fail, then panic — sibling repos, **/*.java globs, ~/.gradle/caches / ~/.m2 walks, or long shell chains (find, jar tf, javap), often on the wrong JAR because global caches hold many versions. In the worst case the agent guesses an API from the wrong artifact or from memory.

Even when something works, the cost is high: noisy output, many tool rounds, wasted tokens, and subprocesses on paths you did not mean to expose.

jvmsrc offers one path: ask Gradle for the resolved classpath for this project, then return source, signatures, or structure with provenance — or a clear error. Global cache directories are not a substitute: they store every version ever downloaded; only the build tool knows which one your module uses. Picking a JAR by name is guesswork; wrong output is worse than an error.

How it works

  1. Resolvegradlew / gradle runs with a bundled Groovy init script; result is cached until build inputs change.
  2. Locate class — inter-project src/ first, then dependency JARs on the chosen classpath.
  3. Read — sources JAR when available; otherwise CFR decompile into a global cache (nothing written under your project tree).

First resolution on a project is often 5–30s; later calls reuse the cache.

Compared to similar tools

ToolApproachGap
Cache indexers / ~/.gradle grepScan global cachesNo per-project resolved version
Static build.gradle parsersParse declarations onlyMiss transitives, BOMs, dynamic versions
mcp-javadc / path-only CFRUser supplies JAR pathsNo Gradle resolution
Gradle MCP (tooling API)Task/build focusedNot classpath-accurate source for arbitrary FQNs
jvmsrcgradlew + init script → resolved graphVersion-correct source/signatures for agents

Requirements

On your machine: Node.js ≥ 20 to run the published CLI; Java on PATH (CFR + javap). Developing jvmsrc uses Bun only (bun.lock).

Project types: JVM codebases (Java, Kotlin, Scala, Groovy, …). jvmsrc calls the build tool, not your editor.

Build systemStatus
GradleSupported — multimodule included
Maven, Bazel, …Planned (SPEC.md)

Point -p / projectRoot at the Gradle root (settings.gradle(.kts) or root build.gradle(.kts)). Uses ./gradlew when present, else gradle on PATH. Maven-only trees get an explicit unsupported error.

Known limitations

Early software; the supported path is narrow:

AreaToday
Build toolGradle only
IntegrationGroovy init script (--init-script) — not a Gradle Portal plugin
ClasspathsStandard JVM + Kotlin MPP jvm* configurations when Gradle exposes them
OutputJava-shaped .java text (sources JAR, inter-project src, or CFR)

Your repo may use build.gradle or build.gradle.kts; the jvmsrc side is still a Groovy init script. Composite builds, Android-only layouts, and exotic configurations are not fully validated. See ROADMAP.md.

Install

npm install -g jvmsrc
# or
npx jvmsrc <command>

Published package includes prebuilt dist/ and bundled CFR — Node ≥ 20.

Quick start

# 1. Install the agent skill for your AI tool
jvmsrc init --agent cursor          # Cursor (user skills)
jvmsrc init --agent claude          # Claude Code
jvmsrc init --agent cursor --scope project -p /path/to/gradle-project   # repo-shared

# 2. Paste-ready MCP config, then restart your AI tool
jvmsrc config --project /path/to/gradle-project

# 3. Fetch class source
jvmsrc get com.example.MyClass -p /path/to/gradle-project

Skill paths: ~/.cursor/skills/jvmsrc/SKILL.md, ~/.claude/skills/jvmsrc/SKILL.md, or .cursor/skills/jvmsrc/SKILL.md in a repo.

More CLI examples:

jvmsrc com.example.MyClass -p /path/to/gradle-project          # shorthand for get
jvmsrc get com.example.MyClass -p /path/to/project -q > MyClass.java
jvmsrc resolve -p /path/to/project --force-refresh

Useful flags: -p / --project, --module (:core:api), --configuration, --include-test, --force-refresh, --verbose, --method, --start-line / --end-line.

Repo fixture: test/fixtures/gradle-smoke — e.g. jvmsrc get com.smoke.Core -p test/fixtures/gradle-smoke --module :core.

MCP

After install, a typical server entry:

{
  "mcpServers": {
    "jvmsrc": {
      "command": "npx",
      "args": ["-y", "jvmsrc", "mcp"]
    }
  }
}

Restart the MCP host after upgrading jvmsrc.

Agents: copy or reference SKILL.md in your agent rules.

Tools

ToolUse when
search_classesUnknown FQN — substring or */? glob on classpath
get_method_signatureOverloads / parameters / throws (bytecodeOnly: true for strict javap)
get_class_structureFields, methods, hierarchy — without full source
find_in_class_sourceSearch inside one class
get_class_sourceFull body or excerpt (methodNames, line range) — last resort
resolve_dependenciesDependency graph, modules[], warm cache

Prefer get_method_signature or get_class_structure over get_class_source. Every source response has sourceAvailable: true = real sources; false = CFR (structure OK, names/Javadoc may be synthetic).

Security and privacy

  • No telemetry
  • Local only — caches and diagnostics on disk; never writes under the project root for resolution
  • Subprocesses: Gradle, java, javap via argv (no shell interpolation) — SECURITY.md
  • JVMSRC_ALLOWED_ROOTS — optional allowlist for projectRoot
  • Output cap — 512 KiB default (JVMSRC_MAX_SOURCE_OUTPUT_CHARS); use excerpts or structure tools for large types

Local data

Override with absolute paths: JVMSRC_CACHE_ROOT (resolution + decompile), JVMSRC_LOG_DIR (failure diagnostics).

Defaults (via env-paths): cache under ~/Library/Caches/jvmsrc (macOS), ~/.cache/jvmsrc (Linux), %LOCALAPPDATA%\jvmsrc\Cache (Windows); logs under ~/Library/Logs/jvmsrc, ~/.local/state/jvmsrc, or %LOCALAPPDATA%\jvmsrc\Logs.

Per-project buckets under projects/<id>/ (resolution.json, search index, …) and shared decompiled/<coordinates>/. Gradle's ~/.gradle is unchanged. Full layout: SPEC.md section 6.

jvmsrc diagnostics list
jvmsrc diagnostics show <diagnosticId>

Environment variables

VariablePurpose
JVMSRC_CACHE_ROOTCache root (absolute)
JVMSRC_LOG_DIRDiagnostic logs (absolute)
JVMSRC_ALLOWED_ROOTSAllowed projectRoot prefixes
JVMSRC_MAX_SOURCE_OUTPUT_CHARSMax source body size (default 524288)
JVMSRC_GRADLE_TIMEOUT_MSGradle timeout
JVMSRC_CFR_PATHCustom CFR JAR

More (CFR/javap capture limits, error codes): SPEC.md.

Troubleshooting

  • Resolution failures: jvmsrc diagnostics list / show <id>
  • After upgrade: reinstall or rebuild, then restart the MCP server
  • Stale classpath: jvmsrc resolve --force-refresh

Documentation

DocumentContents
SPEC.mdSchemas, contracts, CLI/MCP details
SKILL.mdAgent skill
CONTRIBUTING.mdBuild, test, PR notes
RELEASING.mdBranching, semver, npm releases
CHANGELOG.mdVersion history
ROADMAP.mdStatus and planned work
SECURITY.mdVulnerability reporting

Building from source

git clone https://github.com/Sintexer/jvm-source-lens.git
cd jvm-source-lens
bun install && bun run setup:cfr && bun run build
node dist/cli.js --version

Full contributor workflow: CONTRIBUTING.md.

License

MIT — see LICENSE.

Related Servers