Skip to content

Commit 0fad3f3

Browse files
committed
GH-1882: improving the descriptions and adjusting the beans tool endpoint to focus on one project
1 parent c44eae7 commit 0fad3f3

13 files changed

Lines changed: 121 additions & 52 deletions

File tree

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/ComponentAnalysisMcpTools.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2025 Broadcom
2+
* Copyright (c) 2025, 2026 Broadcom
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -90,7 +90,7 @@ public static record InjectionPointInfo(
9090
Returns information for all beans with the given name (there may be multiple beans with the same name in different contexts).
9191
""")
9292
public List<BeanUsageInfo> getBeanUsageInfo(
93-
@ToolParam(description = "the name of the project in the workspace of the user") String projectName,
93+
@ToolParam(description = "IDE project name from getProjectList().projectName (case-insensitive match)") String projectName,
9494
@ToolParam(description = "the name of the bean to analyze") String beanName) throws Exception {
9595

9696
logger.info("get bean usage info for: {} in project: {}", beanName, projectName);
@@ -133,7 +133,7 @@ public List<BeanUsageInfo> getBeanUsageInfo(
133133
This is useful for finding all implementations of an interface or all beans of a certain class.
134134
""")
135135
public List<ComponentInfo> findBeansByType(
136-
@ToolParam(description = "the name of the project in the workspace of the user") String projectName,
136+
@ToolParam(description = "IDE project name from getProjectList().projectName (case-insensitive match)") String projectName,
137137
@ToolParam(description = "the fully qualified type name to search for") String typeName) throws Exception {
138138

139139
logger.info("find beans by type: {} for project: {}", typeName, projectName);

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/McpConfig.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,17 @@
1010
*******************************************************************************/
1111
package org.springframework.ide.vscode.boot.mcp;
1212

13+
import java.util.List;
14+
1315
import org.springframework.ai.support.ToolCallbacks;
1416
import org.springframework.ai.tool.ToolCallbackProvider;
1517
import org.springframework.context.annotation.Bean;
1618
import org.springframework.context.annotation.Configuration;
1719

20+
import org.springframework.ide.vscode.boot.mcp.prompts.Prompts;
21+
22+
import io.modelcontextprotocol.server.McpServerFeatures;
23+
1824
/**
1925
* @author Martin Lippert
2026
*/
@@ -40,10 +46,13 @@ ToolCallbackProvider registerTools(
4046
requestMappingMcpTools,
4147
componentAnalysisMcpTools));
4248
}
43-
44-
// @Bean
45-
// List<McpServerFeatures.SyncPromptSpecification> springToolsPrompts() {
46-
// return Prompts.PROMPTS;
47-
// }
48-
49+
50+
/**
51+
* MCP protocol prompts (templates clients can expand into user/assistant messages), separate from {@link ToolCallbackProvider} tools.
52+
*/
53+
@Bean
54+
List<McpServerFeatures.SyncPromptSpecification> springToolsPrompts() {
55+
return Prompts.PROMPTS;
56+
}
57+
4958
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/ProjectInformation.java

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ public ProjectInformation(JavaProjectFinder projectFinder) {
3939

4040

4141
@Tool(description = """
42-
This function provides a list of all the projects that are in the current workspace
43-
""")
42+
Lists all Java projects in the workspace with Boot flag and JRE level.
43+
Use each Project.projectName when calling other tools; those tools match this name case-insensitively.
44+
""")
4445
public List<Project> getProjectList() throws Exception {
4546
return projectFinder.all()
4647
.stream()
@@ -52,10 +53,10 @@ public static record Project(String projectName, boolean isSpringBootProject, St
5253

5354

5455
@Tool(description = """
55-
This function provides information about which version of Spring Boot the given project from the workspace uses
56-
""")
56+
Returns the Spring Boot version for a workspace Java project (from the resolved classpath / BOM).
57+
""")
5758
public Version getSpringBootVersion(
58-
@ToolParam(description = "the name of the project in the workspace of the user") String projectName) throws Exception {
59+
@ToolParam(description = "IDE project name from getProjectList().projectName (case-insensitive match)") String projectName) throws Exception {
5960

6061
IJavaProject project = getProject(projectName);
6162

@@ -69,10 +70,10 @@ public Version getSpringBootVersion(
6970

7071

7172
@Tool(description = """
72-
This function provides information about which version of Java the given project from the workspace uses
73-
""")
73+
Returns the Java/JRE version configured for the project's classpath.
74+
""")
7475
public String getJavaVersion(
75-
@ToolParam(description = "the name of the project in the workspace of the user") String projectName) throws Exception {
76+
@ToolParam(description = "IDE project name from getProjectList().projectName (case-insensitive match)") String projectName) throws Exception {
7677

7778
IJavaProject project = getProject(projectName);
7879
IClasspath classpath = project.getClasspath();
@@ -82,11 +83,11 @@ public String getJavaVersion(
8283

8384

8485
@Tool(description = """
85-
This function provides detailed information about the libraries that this project uses and the versio of those libraries.
86-
It gets this information from the resolved classpath and is therefore very precise.
87-
""")
86+
Returns non-system binary classpath entries for the project (resolved JARs) with versions from build tooling.
87+
Each Library.name is the classpath entry path (often a local .m2 or Gradle cache path), not necessarily a Maven coordinate; use it to see exact resolved artifacts.
88+
""")
8889
public List<Library> getResolvedProjectClasspath(
89-
@ToolParam(description = "the name of the project in the workspace of the user") String projectName) throws Exception {
90+
@ToolParam(description = "IDE project name from getProjectList().projectName (case-insensitive match)") String projectName) throws Exception {
9091

9192
IJavaProject project = getProject(projectName);
9293

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/RequestMappingMcpTools.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2025 Broadcom
2+
* Copyright (c) 2025, 2026 Broadcom
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -70,7 +70,7 @@ public static record RequestMappingInfo(
7070
as well as HttpExchange interfaces.
7171
""")
7272
public List<RequestMappingInfo> getRequestMappings(
73-
@ToolParam(description = "the name of the project in the workspace of the user") String projectName) throws Exception {
73+
@ToolParam(description = "IDE project name from getProjectList().projectName (case-insensitive match)") String projectName) throws Exception {
7474

7575
logger.info("get request mappings for project: {}", projectName);
7676

@@ -96,7 +96,7 @@ Find request mappings by HTTP method (GET, POST, PUT, DELETE, PATCH, etc.).
9696
Returns all endpoints that handle the specified HTTP method.
9797
""")
9898
public List<RequestMappingInfo> findRequestMappingsByMethod(
99-
@ToolParam(description = "the name of the project in the workspace of the user") String projectName,
99+
@ToolParam(description = "IDE project name from getProjectList().projectName (case-insensitive match)") String projectName,
100100
@ToolParam(description = "the HTTP method to filter by (e.g., 'GET', 'POST', 'PUT', 'DELETE', 'PATCH')") String httpMethod) throws Exception {
101101

102102
logger.info("find request mappings by method: {} for project: {}", httpMethod, projectName);
Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2025 Broadcom
2+
* Copyright (c) 2025, 2026 Broadcom
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -10,10 +10,15 @@
1010
*******************************************************************************/
1111
package org.springframework.ide.vscode.boot.mcp;
1212

13+
import java.util.Optional;
14+
1315
import org.slf4j.Logger;
1416
import org.slf4j.LoggerFactory;
1517
import org.springframework.ai.tool.annotation.Tool;
18+
import org.springframework.ai.tool.annotation.ToolParam;
1619
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
20+
import org.springframework.ide.vscode.commons.java.IJavaProject;
21+
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
1722
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
1823
import org.springframework.stereotype.Component;
1924

@@ -24,17 +29,36 @@
2429
public class SpringIndexAccess {
2530

2631
private static final Logger logger = LoggerFactory.getLogger(SpringIndexAccess.class);
27-
private SpringMetamodelIndex springIndex;
32+
private final JavaProjectFinder projectFinder;
33+
private final SpringMetamodelIndex springIndex;
2834

29-
public SpringIndexAccess(SpringMetamodelIndex springIndex) {
35+
public SpringIndexAccess(JavaProjectFinder projectFinder, SpringMetamodelIndex springIndex) {
36+
this.projectFinder = projectFinder;
3037
this.springIndex = springIndex;
3138
}
3239

33-
@Tool(description = "Get detailed information about the spring beans and their dependencies via injection points of the current projects in the workspace")
34-
public Bean[] getBeanDetails() {
35-
logger.info("get Spring project bean details");
36-
37-
return springIndex.getBeans();
40+
@Tool(description = """
41+
Returns indexed Spring beans for one workspace Java project, including injection points where the index exposes them.
42+
Use getProjectList to obtain valid project names. For a single named bean or type-scoped queries, prefer getBeanUsageInfo or findBeansByType.
43+
""")
44+
public Bean[] getBeanDetails(
45+
@ToolParam(description = "IDE project name from getProjectList().projectName (case-insensitive match)") String projectName)
46+
throws Exception {
47+
logger.info("get Spring project bean details for project: {}", projectName);
48+
49+
IJavaProject project = getProject(projectName);
50+
return springIndex.getBeansOfProject(project.getElementName());
51+
}
52+
53+
private IJavaProject getProject(String projectName) throws Exception {
54+
Optional<? extends IJavaProject> found = projectFinder.all().stream()
55+
.filter(project -> project.getElementName().equalsIgnoreCase(projectName))
56+
.findFirst();
57+
58+
if (found.isEmpty()) {
59+
throw new Exception("project with name " + projectName + " not found");
60+
}
61+
return found.get();
3862
}
3963

4064
}

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/SpringIoApiMcpTools.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2025 the original author or authors.
2+
* Copyright 2025, 2026 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,7 +23,9 @@
2323
import org.slf4j.Logger;
2424
import org.slf4j.LoggerFactory;
2525
import org.springframework.ai.tool.annotation.Tool;
26+
import org.springframework.ai.tool.annotation.ToolParam;
2627
import org.springframework.beans.factory.ObjectProvider;
28+
import org.springframework.beans.factory.annotation.Autowired;
2729
import org.springframework.beans.factory.annotation.Value;
2830
import org.springframework.core.ParameterizedTypeReference;
2931
import org.springframework.http.MediaType;
@@ -56,6 +58,7 @@ public class SpringIoApiMcpTools {
5658
private volatile RestClient apiClient;
5759
private volatile RestClient calClient;
5860

61+
@Autowired
5962
public SpringIoApiMcpTools(BootJavaConfig bootJavaConfig,
6063
@Value("${calendar.window.days:180}") long daysFromToday,
6164
ObjectProvider<Clock> clockProvider) {
@@ -116,8 +119,17 @@ public record Generation(String name, String initialReleaseDate, String ossSuppo
116119
public record UpcomingRelease(boolean allDay, String backgroundColor, LocalDate start, String title, String url) {
117120
}
118121

119-
@Tool(description = "Get information about Spring project releases")
120-
public Release[] getReleases(String project) {
122+
@Tool(description = """
123+
Get the full list of releases for a Spring project from the live Spring IO API (HAL).
124+
For only the current GA release plus OSS/commercial support end dates for that line, prefer getLatestReleaseInformation instead.
125+
""")
126+
public Release[] getReleases(
127+
@ToolParam(description = """
128+
Spring IO API project slug (path segment under /projects), e.g. "spring-boot", "spring-framework", "spring-data-jpa".
129+
This is not the IDE workspace project name from getProjectList; it is the Spring portfolio project id from Spring IO.
130+
Base URL is configured as workspace setting boot-java.io.api (projects list URL; API base is derived from it).
131+
""")
132+
String project) {
121133
logger.info("get Spring project releases for: {}", project);
122134
ReleasesRoot release = apiClient.get()
123135
.uri(uriBuilder -> uriBuilder.path("/projects/" + project + "/releases").build())
@@ -128,8 +140,15 @@ public Release[] getReleases(String project) {
128140
return Objects.requireNonNull(release)._embedded.releases;
129141
}
130142

131-
@Tool(description = "Get information about support ranges and dates for Spring projects")
132-
public Generation[] getGenerations(String project) {
143+
@Tool(description = """
144+
Get support generation windows (OSS and commercial end dates) for a Spring project from the live Spring IO API.
145+
For the current GA line's support dates tied to the latest release only, prefer getLatestReleaseInformation instead.
146+
""")
147+
public Generation[] getGenerations(
148+
@ToolParam(description = """
149+
Spring IO API project slug, e.g. "spring-boot", "spring-framework". Same semantics as getReleases project parameter.
150+
""")
151+
String project) {
133152
logger.info("get Spring project support dates for: {}", project);
134153
GenerationsRoot generations = apiClient.get()
135154
.uri(uriBuilder -> uriBuilder.path("/projects/" + project + "/generations").build())
@@ -140,7 +159,11 @@ public Generation[] getGenerations(String project) {
140159
return Objects.requireNonNull(generations)._embedded.generations;
141160
}
142161

143-
@Tool(description = "Get information about upcoming releases for Spring projects in the near future")
162+
@Tool(description = """
163+
Get upcoming Spring ecosystem releases from the Spring release calendar API (date range from today for calendar.window.days).
164+
Neither getReleases nor getLatestReleaseInformation provide this calendar view; use this for forward-looking dates.
165+
Calendar base URL is workspace setting boot-java.io.calendar-api.
166+
""")
144167
public List<UpcomingRelease> getUpcomingReleases() {
145168
LocalDate start = LocalDate.now(clock);
146169
LocalDate end = start.plusDays(this.daysFromToday);

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/SpringIoMcpUrls.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2026 Broadcom
2+
* Copyright (c) 2025, 2026 Broadcom
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/SpringVersionsAndGenerations.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2025 Broadcom
2+
* Copyright (c) 2025, 2026 Broadcom
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -42,9 +42,16 @@ public SpringVersionsAndGenerations(SpringProjectsProvider provider) {
4242

4343
public record ReleaseInformation(String projectName, String version, String endOfOssSupport, String endOfCommercialSupport) {}
4444

45-
@Tool(description = "This function provides information about the latest release of a specific Spring project, including the version number and release date")
45+
@Tool(description = """
46+
Returns the latest general-availability (current) release for a Spring portfolio project plus OSS and commercial support end dates for that generation.
47+
Data is resolved from the language server's Spring projects metadata (same source used for validations), not a live per-call HAL dump.
48+
Use getReleases when you need every release record; use getGenerations when you need all generation windows from the live Spring IO API; use getUpcomingReleases for scheduled upcoming releases from the calendar API.
49+
""")
4650
public ReleaseInformation getLatestReleaseInformation(
47-
@ToolParam(description = "the technical name of the Spring project, e.g. \"spring-boot\" for Spring Boot or \"spring-data-jpa\" for Spring Data JPA") String projectName) {
51+
@ToolParam(description = """
52+
Spring project technical id, e.g. "spring-boot", "spring-framework", "spring-data-jpa" (not the IDE workspace project name).
53+
""")
54+
String projectName) {
4855

4956
try {
5057
ResolvedSpringProject info = provider.getProject(projectName);

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/StereotypeInformation.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2025 Broadcom
2+
* Copyright (c) 2025, 2026 Broadcom
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -49,7 +49,7 @@ public StereotypeInformation(JavaProjectFinder projectFinder, SpringMetamodelInd
4949
This function provides information about all the stereotype definitions that are defined and available in the given project
5050
""")
5151
public Set<StereotypeDefinition> getStereotypesList(
52-
@ToolParam(description = "the name of the project in the workspace of the user") String projectName)
52+
@ToolParam(description = "IDE project name from getProjectList().projectName (case-insensitive match)") String projectName)
5353
throws Exception {
5454

5555
IJavaProject project = getProject(projectName);
@@ -63,7 +63,7 @@ public Set<StereotypeDefinition> getStereotypesList(
6363
This way you can identify, for example, all components from a specific stereotype (e.g. all data repositories, all services, all entities, and so on)
6464
""")
6565
public List<ComponentWithStereotypes> getListOfComponentsAndTheirStereotypes(
66-
@ToolParam(description = "the name of the project in the workspace of the user") String projectName)
66+
@ToolParam(description = "IDE project name from getProjectList().projectName (case-insensitive match)") String projectName)
6767
throws Exception {
6868

6969
IJavaProject project = getProject(projectName);
@@ -86,7 +86,7 @@ Find all Spring components by stereotype (Controller, Service, Repository, Compo
8686
Returns all components that have the specified stereotype.
8787
""")
8888
public List<ComponentWithStereotypes> findComponentsByStereotype(
89-
@ToolParam(description = "the name of the project in the workspace of the user") String projectName,
89+
@ToolParam(description = "IDE project name from getProjectList().projectName (case-insensitive match)") String projectName,
9090
@ToolParam(description = "the stereotype name to filter by (e.g., 'Controller', 'Service', 'Repository', 'Entity')") String stereotypeName)
9191
throws Exception {
9292

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/mcp/prompts/GenerateNewPrompts.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2025 Broadcom
2+
* Copyright (c) 2025, 2026 Broadcom
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -28,8 +28,13 @@ public class GenerateNewPrompts {
2828
private static List<SyncPromptSpecification> initializePrompts() {
2929
ArrayList<SyncPromptSpecification> prompts = new ArrayList<>();
3030

31-
var generateRepositorySample = new McpSchema.Prompt("create a sample Spring Data JDBC repository", "Creates a sample Spring Data JDBC repository with a sample domain type",
32-
List.of(new McpSchema.PromptArgument("domain-type-name", "The name to the domain type the repository should be for", true)));
31+
var generateRepositorySample = new McpSchema.Prompt(
32+
"create a sample Spring Data JDBC repository",
33+
"Expands into instructions to add a Spring Data JDBC repository interface plus a minimal domain type. Use when bootstrapping persistence code.",
34+
List.of(new McpSchema.PromptArgument(
35+
"domain-type-name",
36+
"Simple Java type name for the domain class (e.g. Customer, OrderLine)",
37+
true)));
3338

3439
var promptSpecification = new McpServerFeatures.SyncPromptSpecification(generateRepositorySample, (exchange, getPromptRequest) -> {
3540
String domainTypeName = (String) getPromptRequest.arguments().get("domain-type-name");

0 commit comments

Comments
 (0)