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": "./claude-plugins/spring-tools"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Developer tools"
}
]
}
153 changes: 153 additions & 0 deletions .github/workflows/claude-plugin-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
name: Claude Plugin Check

# Keeps the Claude Code plugin under claude-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:
- 'claude-plugins/**'
- '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/claude-plugin-check.yml'
pull_request:
paths:
- 'claude-plugins/**'
- '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/claude-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: claude-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 claude-plugins/tools/check-explanations.mjs --require-all --todo
- name: Check plugin configuration and cross-host adapters
run: node claude-plugins/tools/check-plugin-config.mjs --cross-host
- name: Run plugin tooling tests
run: node --test --test-reporter=spec 'claude-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 claude-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 ./claude-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: ./claude-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 claude-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: claude-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
claude-plugins/spring-tools/evals/results/**/report.html
if-no-files-found: ignore
12 changes: 8 additions & 4 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
tmp=$(mktemp)
jq --arg v "$version" '.version = $v' claude-plugins/spring-tools/plugin.json > "$tmp" && mv "$tmp" claude-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 claude-plugins/spring-tools/.claude-plugin/plugin.json claude-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 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
13 changes: 12 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ sts4/
│ └── 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
├── 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 @@ -92,6 +94,14 @@ cd claude-plugins
./update-local-jars.sh # Rebuild and copy the standalone LS JAR into the plugin
```

Every diagnostic code needs a playbook `claude-plugins/spring-tools/explanations/<CODE>.md` and a row in `claude-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 `claude-plugins/spring-tools/README.md`):

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

## Running Tests

```bash
Expand Down Expand Up @@ -139,6 +149,7 @@ 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
- 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
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 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
2 changes: 1 addition & 1 deletion RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ publishes atomically in one shot, so verification has to happen locally

| # | Step | Workflow | Key inputs |
|---|------|----------|------------|
| 1 | Test locally | — manual | Run [`claude-plugins/update-local-jars.sh`](claude-plugins/update-local-jars.sh) to build the standalone LS jar from the current `headless-services` snapshot and drop it into `spring-tools/language-server/`, then exercise the plugin through the local `spring-tools-local` marketplace ([`claude-plugins/.claude-plugin/marketplace.json`](claude-plugins/.claude-plugin/marketplace.json)) |
| 1 | Test locally | — manual | Run [`claude-plugins/update-local-jars.sh`](claude-plugins/update-local-jars.sh) to build the standalone LS jar from the current `headless-services` snapshot and drop it into `spring-tools/language-server/`, then run the automated checks from the repository root — `node claude-plugins/tools/check-explanations.mjs --require-all --todo --links`, `node claude-plugins/tools/check-plugin-config.mjs`, `node --test 'claude-plugins/tools/test/*.test.mjs'` and the end-to-end `node claude-plugins/tools/smoke/mcp-smoke.mjs` (the same checks CI runs in [`claude-plugin-check.yml`](.github/workflows/claude-plugin-check.yml)), run the behavioral eval suite with a logged-in Claude Code (`cd claude-plugins/spring-tools && claude plugin eval . --no-publish`, or dispatch the `plugin-evals` job of that workflow) — and exercise the plugin through the local `spring-tools-local` marketplace ([`claude-plugins/.claude-plugin/marketplace.json`](claude-plugins/.claude-plugin/marketplace.json)) |
| 2 | Release (build + publish) | [`release-standalone-ls.yml`](.github/workflows/release-standalone-ls.yml) | `dist: release` — builds the standalone LS jar, uploads it to the CDN, tags the repo `v<version>`, and updates the release marketplace's `marketplace.json` to point at that tag |

(Nightly/on-demand snapshot builds of the plugin are handled separately by
Expand Down
1 change: 1 addition & 0 deletions claude-plugins/research-notes/TODO_quickfixes.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ When a new problem type is added to the language server, add a row to the matchi
| `WEB_SECURITY_CONFIGURER_ADAPTER` | `WebSecurityConfigurerAdapter` is removed | OpenRewrite: `org.openrewrite.java.spring.security5.WebSecurityConfigurerAdapter` | ✅ |
| `DOMAIN_ID_FOR_REPOSITORY` | Invalid Domain ID type for Spring Data Repository | *None* | ✅ |
| `WEB_ANNOTATION_NAMES` | Implicit web annotations names | OpenRewrite: `org.openrewrite.java.spring.ImplicitWebAnnotationNames` | ✅ |
| `MODEL_ATTRIBUTE_NAMES` | Redundant explicit `@ModelAttribute` name matching the type-derived default | *None* | ✅ |
| `VALUE_CLASSPATH_RESOURCE_TYPE` | Invalid type for classpath resource in `@Value` | *None* | ✅ |
| `WEB_CONFIGURER_CONFIGURATION` | Missing `@Configuration` on web configurer | OpenRewrite: `org.openrewrite.java.spring.boot2.AddConfigurationAnnotation` | ✅ |
| `MISSING_VALIDATED_ANNOTATION` | Missing `@Validated` on component | OpenRewrite: `org.openrewrite.java.spring.boot2.AddValidatedAnnotation` | ✅ |
Expand Down
4 changes: 3 additions & 1 deletion claude-plugins/spring-tools/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
language-server/
node_modules/
node_modules/
boot-ls.log
evals/results/
Loading