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
20 changes: 20 additions & 0 deletions .agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "spring-tools-local",
"interface": {
"displayName": "Spring Tools"
},
"plugins": [
{
"name": "spring-tools",
"source": {
"source": "local",
"path": "./agent-plugins/spring-tools"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Developer tools"
}
]
}
4 changes: 2 additions & 2 deletions .claude/skills/convert-openrewrite-to-jdt/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,12 @@ engine* differ between the two mechanisms:
./mvnw -q -o test -pl commons/commons-rewrite # if you removed a recipe/test from this module
```

12. **Plugin docs** — if `claude-plugins/spring-tools/explanations/<ProblemType code>.md`
12. **Plugin docs** — if `agent-plugins/spring-tools/explanations/<ProblemType code>.md`
exists for this diagnostic, check its before/after examples still match; usually no
change is needed since that file documents observable behavior, not implementation.

13. **Copyright header** — add/bump the current year in the EPL header of every file
you touch, per this repo's root `CLAUDE.md`.
you touch, per this repo's root `AGENTS.md`.

## Key files

Expand Down
155 changes: 155 additions & 0 deletions .github/workflows/agent-plugin-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
name: Agent Plugin Check

# Keeps the agent plugin under agent-plugins/spring-tools consistent with the
# language server: every diagnostic code has an explanation playbook, playbooks are
# well-formed, hooks, skills and agents only reference MCP tools that exist (and every
# tool is reachable through one of them), the plugin manifest validates, and the
# launcher/language server (including its file watcher) work end to end over MCP stdio.

on:
push:
branches:
- main
paths:
- 'agent-plugins/**'
- '.agents/plugins/marketplace.json'
- 'headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/**/*ProblemType.java'
- 'headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/AbstractDiagnosticValidator.java'
- 'headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlASTReconciler.java'
- 'headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/YamlSchemaProblems.java'
- 'headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/**'
- 'headless-services/spring-boot-language-server-standalone/src/main/java/**'
- '.github/workflows/agent-plugin-check.yml'
pull_request:
paths:
- 'agent-plugins/**'
- '.agents/plugins/marketplace.json'
- 'headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/**/*ProblemType.java'
- 'headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/validation/generations/AbstractDiagnosticValidator.java'
- 'headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlASTReconciler.java'
- 'headless-services/commons/commons-yaml/src/main/java/org/springframework/ide/vscode/commons/yaml/reconcile/YamlSchemaProblems.java'
- 'headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/**'
- 'headless-services/spring-boot-language-server-standalone/src/main/java/**'
- '.github/workflows/agent-plugin-check.yml'
workflow_dispatch:
inputs:
run-evals:
description: 'Also run the behavioral eval suite (claude plugin eval; needs the ANTHROPIC_API_KEY secret and costs model calls)'
type: boolean
default: false

permissions:
contents: read

concurrency:
group: agent-plugin-check-${{ github.ref }}
cancel-in-progress: true

jobs:
explanations-and-config:
name: Explanation playbooks and plugin configuration
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # 7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # 7.0.0
with:
node-version: 22
- name: Check explanation playbooks (coverage, structure, cross-references, coverage table)
run: node agent-plugins/tools/check-explanations.mjs --require-all --todo
- name: Check plugin configuration and cross-host adapters
run: node agent-plugins/tools/check-plugin-config.mjs --cross-host
- name: Run plugin tooling tests
run: node --test --test-reporter=spec 'agent-plugins/tools/test/*.test.mjs'

documentation-links:
name: Documentation links resolve
runs-on: ubuntu-latest
# Only on the main branch: external link checks depend on docs.spring.io availability.
if: github.event_name != 'pull_request'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # 7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # 7.0.0
with:
node-version: 22
- name: Check that every documentation link in the playbooks returns HTTP 200
run: node agent-plugins/tools/check-explanations.mjs --require-all --links

plugin-manifest:
name: Plugin manifest validates with the Claude Code CLI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # 7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # 7.0.0
with:
node-version: 22
- name: Install the Claude Code CLI into a temporary prefix
run: npm install --no-audit --no-fund --prefix "$RUNNER_TEMP/claude-code" @anthropic-ai/claude-code@latest
- name: Validate the plugin manifest (no login required)
run: |
"$RUNNER_TEMP/claude-code/node_modules/.bin/claude" --version
"$RUNNER_TEMP/claude-code/node_modules/.bin/claude" plugin validate ./agent-plugins/spring-tools --strict

mcp-smoke:
name: End-to-end MCP smoke test against a freshly built language server
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # 7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # 7.0.0
with:
node-version: 22
- uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # 5.6.0
with:
java-version: '25'
distribution: 'temurin'
cache: maven
cache-dependency-path: 'headless-services/**/pom.xml'
- name: Build the standalone language server and copy the JAR into the plugin
run: ./agent-plugins/update-local-jars.sh
- name: Start launcher.js over MCP stdio, index a sample project and exercise the tools the hooks and skills rely on
run: node agent-plugins/tools/smoke/mcp-smoke.mjs --keep
- name: Upload the language server log on failure
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # 7.0.1
with:
name: boot-ls-log
path: /tmp/spring-tools-smoke-*/data/boot-ls.log
if-no-files-found: ignore

plugin-evals:
name: Behavioral evals (claude plugin eval, on demand)
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' && inputs.run-evals
timeout-minutes: 60
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # 7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # 7.0.0
with:
node-version: 22
- name: Install the Claude Code CLI into a temporary prefix
run: npm install --no-audit --no-fund --prefix "$RUNNER_TEMP/claude-code" @anthropic-ai/claude-code@latest
- name: Run the eval suite against the recorded MCP mocks (the language server is not started)
working-directory: agent-plugins/spring-tools
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
if [ -z "$ANTHROPIC_API_KEY" ]; then
echo '::error::the ANTHROPIC_API_KEY secret is not set; the eval suite needs model access'
exit 1
fi
"$RUNNER_TEMP/claude-code/node_modules/.bin/claude" plugin eval . \
--trust-plugin \
--json "$RUNNER_TEMP/eval-results.json" \
--threshold 0.8 \
--judge-model claude-haiku-4-5 \
--no-publish \
--max-cost-usd 15
- name: Upload the eval results and report
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # 7.0.1
with:
name: plugin-eval-results
path: |
${{ runner.temp }}/eval-results.json
agent-plugins/spring-tools/evals/results/**/report.html
if-no-files-found: ignore
4 changes: 2 additions & 2 deletions .github/workflows/release-standalone-ls.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,15 @@ jobs:
--arg ref "$ref" \
'
.plugins |= if map(.name == $name) | any then
map(if .name == $name then .version=$version | .source.ref=$ref else . end)
map(if .name == $name then .version=$version | .source.ref=$ref | .source.path="agent-plugins/spring-tools" else . end)
else
. + [{
"name": $name,
"version": $version,
"source": {
"source": "git-subdir",
"url": "https://github.com/spring-projects/spring-tools.git",
"path": "claude-plugins/spring-tools",
"path": "agent-plugins/spring-tools",
"ref": $ref
},
"description": "Spring Boot Language Server",
Expand Down
18 changes: 11 additions & 7 deletions .github/workflows/snapshot-standalone-ls.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
echo "S3 path: ${s3_path}"
aws s3 cp ./standalone-ls-dist/$jar_file s3://$AWS_S3_BUCKET/$s3_path/$jar_file --no-progress --checksum-algorithm CRC32
aws s3 cp ./standalone-ls-dist/$sha_file s3://$AWS_S3_BUCKET/$s3_path/$sha_file --no-progress --checksum-algorithm CRC32
- name: Update plugin.json for Snapshot
- name: Update plugin manifest versions for Snapshot
id: update-plugin
run: |
git config --global user.name "@spring-projects/spring-ide-admin"
Expand All @@ -52,12 +52,16 @@ jobs:
# Create or reset a local branch to the current commit (which is what triggered the action)
git checkout -B claude-snapshot

# Update the version in plugin.json
# Keep both the Claude Code manifest and the portable Agent Plugins manifest in sync.
tmp=$(mktemp)
jq --arg v "$version" '.version = $v' claude-plugins/spring-tools/.claude-plugin/plugin.json > "$tmp" && mv "$tmp" claude-plugins/spring-tools/.claude-plugin/plugin.json
jq --arg v "$version" '.version = $v' agent-plugins/spring-tools/.claude-plugin/plugin.json > "$tmp" && mv "$tmp" agent-plugins/spring-tools/.claude-plugin/plugin.json
tmp=$(mktemp)
jq --arg v "$version" '.version = $v' agent-plugins/spring-tools/plugin.json > "$tmp" && mv "$tmp" agent-plugins/spring-tools/plugin.json

git add claude-plugins/spring-tools/.claude-plugin/plugin.json
git commit -m "Update Claude plugin version to $version"
git add agent-plugins/spring-tools/.claude-plugin/plugin.json agent-plugins/spring-tools/plugin.json
if ! git diff --cached --quiet; then
git commit -m "Update agent plugin versions to $version"
fi

# Push to the remote claude-snapshot branch forcefully.
# If it doesn't exist, this creates it. If it does exist, it completely overwrites it
Expand All @@ -78,15 +82,15 @@ jobs:
--arg sha "$sha" \
'
.plugins |= if map(.name == $name) | any then
map(if .name == $name then .version=$version | .source.sha=$sha | .source.ref="claude-snapshot" else . end)
map(if .name == $name then .version=$version | .source.sha=$sha | .source.ref="claude-snapshot" | .source.path="agent-plugins/spring-tools" else . end)
else
. + [{
"name": $name,
"version": $version,
"source": {
"source": "git-subdir",
"url": "https://github.com/spring-projects/spring-tools.git",
"path": "claude-plugins/spring-tools",
"path": "agent-plugins/spring-tools",
"ref": "claude-snapshot",
"sha": $sha
},
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ hs_err_pid*

servers/
target/
build/
bin/
*.log
*.log.*.gz
Expand Down
31 changes: 23 additions & 8 deletions CLAUDE.md → AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CLAUDE.md
# AGENTS.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This file provides guidance to AI coding agents (Claude Code, Codex, GitHub Copilot, OpenCode and others) when working with code in this repository.

## Repository Overview

Expand Down Expand Up @@ -30,8 +30,10 @@ sts4/
│ ├── commons-vscode/ # Shared TypeScript utilities
│ └── boot-dev-pack/ # Extension pack bundling Spring Boot extensions
├── nodejs-packages/ # Shared Node.js utilities
└── claude-plugins/ # Claude Code plugin (standalone LS + MCP server)
├── spring-tools/ # Plugin source
└── agent-plugins/ # Agent plugin (standalone LS + MCP server) for Claude Code, Codex, GitHub Copilot and OpenCode
├── spring-tools/ # Plugin source (skills, agents, hooks, evals, explanations/<CODE>.md playbooks)
├── tools/ # Node.js checkers + tests keeping playbooks/skills/agents in sync with the LS
├── research-notes/ # TODO_quickfixes.md: problem code → quick fix → playbook coverage table
└── update-local-jars.sh # Rebuilds the standalone LS JAR for the plugin
```

Expand Down Expand Up @@ -83,15 +85,27 @@ npm run vsce-pre-release-package

`preinstall.sh` packs `commons-vscode` locally, then builds the Spring Boot language server fat JAR and extracts it into `language-server/`.

### Claude Code Plugin
### Agent Plugin

The `claude-plugins/spring-tools` plugin uses the **standalone** language server (no JDT LS, uses Jandex for type indexing):
The `agent-plugins/spring-tools` plugin uses the **standalone** language server (no JDT LS, uses Jandex for type indexing):

```bash
cd claude-plugins
cd agent-plugins
./update-local-jars.sh # Rebuild and copy the standalone LS JAR into the plugin
```

The same plugin source serves every host: `plugin.json` and `mcp.json` for Claude Code, `com.github.copilot/` for GitHub Copilot, `opencode/` for OpenCode, and `.agents/plugins/marketplace.json` as the Codex catalog pointing to it (not a second implementation). Plugin JavaScript uses four-space indentation; plugin tests use Node's `node:test` with `*.test.mjs` filenames.

Every diagnostic code needs a playbook `agent-plugins/spring-tools/explanations/<CODE>.md` and a row in `agent-plugins/research-notes/TODO_quickfixes.md`. Every MCP tool (`boot/mcp/*Tools.java`) must be reachable through a skill or hook and have a behavioral eval with a mock. When adding or renaming a problem type or an MCP tool, run from the repository root (details in `agent-plugins/spring-tools/README.md`):

```bash
node agent-plugins/tools/check-explanations.mjs --require-all --todo --links
node agent-plugins/tools/check-plugin-config.mjs
npm --prefix agent-plugins/tools test
```

After changing `launcher.js`, `install.js`, `hooks/hooks.json` or the standalone LS, run the end-to-end MCP smoke test (needs the rebuilt JAR from `update-local-jars.sh` and Java 21+): `node agent-plugins/tools/smoke/mcp-smoke.mjs`.

## Running Tests

```bash
Expand Down Expand Up @@ -138,7 +152,8 @@ IDE Client (VSCode/Eclipse) ←—LSP over stdio/socket—→ Java Language Serv
```

- The full Spring Boot LS (`spring-boot-language-server`) requires JDT Language Server in the same process for Java type resolution
- The standalone variant (`spring-boot-language-server-standalone`) resolves types via Maven/Gradle classpath + Jandex indexing — used by the Claude Code plugin
- The standalone variant (`spring-boot-language-server-standalone`) resolves types via Maven/Gradle classpath + Jandex indexing — used by the agent plugin
- In the plugin the LSP transport is disabled (`-Dlanguageserver.enabled=false`), so no document is ever "open": the `getProjectDiagnostics` MCP tool only sees what the indexers produce — Java diagnostics from `SpringIndexerJava`, config-file diagnostics (`application*`, `bootstrap*`, `*.factories`) from `SpringIndexerConfigFiles`, plus the build-file version validation

### Key Commons Modules

Expand Down
52 changes: 52 additions & 0 deletions README-dev-env.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,57 @@
# Spring Tools - Dev Environment Setup

## Build commands

Run each block from the repository root. Java sources target Java 21; CI uses JDK 25.

### Java language servers

```bash
cd headless-services
./mvnw clean install
# Build a server and its dependencies:
./mvnw clean install -pl spring-boot-language-server -am
# Run one test after dependencies have been installed:
./mvnw test -pl spring-boot-language-server -Dtest=BootLanguageServerTest
```

### Eclipse integrations and distribution

Select a target-platform profile supported by the module's parent POM.

```bash
cd eclipse-language-servers
./mvnw -Pe440 clean install
```

```bash
cd eclipse-distribution
./mvnw -Pe440 -Psnapshot clean package
```

### VS Code extension

```bash
cd vscode-extensions/vscode-spring-boot
./scripts/preinstall.sh
npm install
npm run check-types
npm run lint
npm run compile
npm run watch
```

The preinstall script builds shared dependencies and the language server JAR.
Use `npm run vsce-package` to package the extension.

### Agent plugin

The IDE language server integrates with JDT LS. The agent plugin instead uses
the standalone server with Maven/Gradle classpath resolution and Jandex indexing;
its file watcher keeps MCP diagnostics current without open editor documents.
See the [plugin guide](agent-plugins/spring-tools/README.md) for building a local
JAR, smoke tests, playbook checks, and behavioral evals.

## Running the Spring Tools Java Language Server locally

Import the headless-services modules into your workspace as existing Maven projects.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Depending on your development environment, you can choose between:

- __Visual Studio Code:__ We provide extensions for Visual Studio Code if you prefer this lightweight editor for your development.
- __Cursor:__ We provide extensions for Cursor if you prefer this lightweight editor for agentic development.
- __Claude Code:__ The Spring Tools plugin for Claude Code brings more Spring smartness to Claude.
- __AI coding agents:__ The [Spring Tools Language Server agent plugin](agent-plugins/spring-tools/README.md) provides Spring-aware MCP tools and skills for Claude Code, GitHub Copilot CLI and Codex; OpenCode can connect to the same server over MCP.
- __Spring Tools for Eclipse distribution:__ a ready-to-use distribution of the latest Eclipse Desktop IDE, prepackaged with Spring Tools and a few additional third-party extensions. Download, unpack, and start.
- __Spring Tools for Eclipse extensions:__ you can install the Spring Tools for Eclipse into an existing Eclipse Desktop IDE installation and use it from there.
- __Eclipse Theia:__ You can install the same extensions into Theia-based environments like gitpod.
Expand Down
Loading