Skip to content

Commit b404596

Browse files
authored
Merge pull request quarkusio#50027 from phillip-kruger/dev-mcp-disable-methods
Dev MCP: Allow enable/disable of methods
2 parents 5324eab + dcd6f3e commit b404596

File tree

43 files changed

+614
-232
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+614
-232
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package io.quarkus.runtime.annotations;
2+
3+
import static java.lang.annotation.ElementType.METHOD;
4+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
5+
6+
import java.lang.annotation.Documented;
7+
import java.lang.annotation.Retention;
8+
import java.lang.annotation.Target;
9+
10+
/**
11+
* Enable a JsonRPC Method for MCP by default
12+
*/
13+
@Retention(RUNTIME)
14+
@Target({ METHOD })
15+
@Documented
16+
public @interface DevMCPEnableByDefault {
17+
18+
}

docs/src/main/asciidoc/dev-mcp.adoc

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,17 @@ footerPageBuildItem.addBuildTimeData(
5252
);
5353
----
5454

55-
The extra `description` argument is new: without it the data only appears in the Dev UI. Once supplied, the `jokes` data becomes an MCP resource.
55+
The extra `description` argument is new: without it the data only appears in the Dev UI. Once supplied, the `jokes` data becomes an MCP resource, however, by default this resource will be disabled. You can change the default by passing in `true` as another parameter after description, that will change this default to be enabled. Users can change this in Dev UI anyway, so this is just a sensible default.
56+
57+
[source,java]
58+
----
59+
footerPageBuildItem.addBuildTimeData(
60+
"jokes",
61+
jokesBuildItem.getJokes(),
62+
"Some funny jokes that the user might enjoy",
63+
true
64+
);
65+
----
5666

5767
==== MCP Resources against a recorded value
5868

@@ -76,6 +86,8 @@ BuildTimeActionBuildItem createBuildTimeActions() {
7686
<3> Set a human‑readable description.
7787
<4> Provide the runtime value returned by your recorder.
7888

89+
By default this resource will be disabled, and the user has to enable it in Dev UI. You can change this default by adding `.enableMcpFuctionByDefault()` in the builder method.
90+
7991
=== MCP tools
8092

8193
A tool corresponds to a method that a client can call. Any JSON‑RPC method can be exposed as a tool by supplying descriptions on the method and its parameters.
@@ -100,6 +112,8 @@ public class MyExtensionRPCService {
100112
<1> Description of the method.
101113
<2> Description of each parameter.
102114

115+
By default this tool will be disabled, and the user has to enable it in Dev UI. You can change this default by adding the `@DevMCPEnableByDefault` annotation on the method.
116+
103117
You must register the JSON‑RPC service in the deployment module:
104118

105119
[source,java]
@@ -145,6 +159,8 @@ BuildTimeActionBuildItem createBuildTimeActions() {
145159

146160
The code in the `function` runs on the deployment classpath. The function can return a plain value, a `CompletionStage` or `CompletableFuture` for asynchronous work.
147161

162+
By default this tool will be disabled, and the user has to enable it in Dev UI. You can change this default by adding `.enableMcpFuctionByDefault()` in the builder method.
163+
148164
=== JSON‑RPC usage
149165

150166
By default all JSON‑RPC methods are visible in the Dev UI. Only methods with descriptions are exposed via Dev MCP.

extensions/agroal/runtime-dev/src/main/java/io/quarkus/agroal/runtime/dev/ui/DatabaseInspector.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import io.quarkus.assistant.runtime.dev.Assistant;
4141
import io.quarkus.datasource.common.runtime.DataSourceUtil;
4242
import io.quarkus.runtime.LaunchMode;
43+
import io.quarkus.runtime.annotations.DevMCPEnableByDefault;
4344
import io.quarkus.runtime.annotations.JsonRpcDescription;
4445

4546
public final class DatabaseInspector {
@@ -96,6 +97,7 @@ protected void init() {
9697
}
9798

9899
@JsonRpcDescription("Get all available datasources for the Database")
100+
@DevMCPEnableByDefault
99101
public List<Datasource> getDataSources() {
100102
if (isDev) {
101103
List<Datasource> datasources = new ArrayList<>();
@@ -110,6 +112,7 @@ public List<Datasource> getDataSources() {
110112
}
111113

112114
@JsonRpcDescription("Get a spesific datasource for the Database by name")
115+
@DevMCPEnableByDefault
113116
private Datasource getDatasource(@JsonRpcDescription("Datasource name") String datasource) {
114117
if (isDev) {
115118
AgroalDataSource ads = checkedDataSources.get(datasource);
@@ -126,6 +129,7 @@ private Datasource getDatasource(@JsonRpcDescription("Datasource name") String d
126129
}
127130

128131
@JsonRpcDescription("Get all the tables for a certain datasource")
132+
@DevMCPEnableByDefault
129133
public List<Table> getTables(@JsonRpcDescription("Datasource name") String datasource) {
130134
if (isDev) {
131135
List<Table> tableList = new ArrayList<>();
@@ -238,6 +242,7 @@ public String generateDot(@JsonRpcDescription("Datasource name") String datasour
238242
}
239243

240244
@JsonRpcDescription("Execute SQL against a certain datasource")
245+
@DevMCPEnableByDefault
241246
public DataSet executeSQL(@JsonRpcDescription("Datasource name") String datasource,
242247
@JsonRpcDescription("Valid SQL to execute") String sql,
243248
@JsonRpcDescription("Page number for pagable rusults, starting at 1") Integer pageNumber,

extensions/devui/deployment-spi/src/main/java/io/quarkus/devui/spi/DevUIContent.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ public class DevUIContent {
1111
private final byte[] template;
1212
private final Map<String, Object> data;
1313
private final Map<String, String> descriptions;
14+
private final Map<String, String> mcpDefaultEnabled;
1415
private final Map<String, String> contentTypes;
1516

1617
private DevUIContent(DevUIContent.Builder builder) {
1718
this.fileName = builder.fileName;
1819
this.template = builder.template;
1920
this.data = builder.data;
2021
this.descriptions = builder.descriptions;
22+
this.mcpDefaultEnabled = builder.mcpDefaultEnabled;
2123
this.contentTypes = builder.contentTypes;
2224
}
2325

@@ -37,6 +39,10 @@ public Map<String, String> getDescriptions() {
3739
return descriptions;
3840
}
3941

42+
public Map<String, String> getMcpDefaultEnables() {
43+
return mcpDefaultEnabled;
44+
}
45+
4046
public Map<String, String> getContentTypes() {
4147
return contentTypes;
4248
}
@@ -51,6 +57,7 @@ public static class Builder {
5157
private Map<String, Object> data;
5258
private Map<String, String> descriptions;
5359
private Map<String, String> contentTypes;
60+
private Map<String, String> mcpDefaultEnabled;
5461

5562
private Builder() {
5663
this.data = new HashMap<>();
@@ -88,6 +95,11 @@ public Builder descriptions(Map<String, String> descriptions) {
8895
return this;
8996
}
9097

98+
public Builder mcpDefaultEnables(Map<String, String> mcpDefaultEnabled) {
99+
this.mcpDefaultEnabled = mcpDefaultEnabled;
100+
return this;
101+
}
102+
91103
public Builder contentTypes(Map<String, String> contentTypes) {
92104
this.contentTypes = contentTypes;
93105
return this;

extensions/devui/deployment-spi/src/main/java/io/quarkus/devui/spi/buildtime/BuildTimeActionBuildItem.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,31 +61,31 @@ public SubscriptionBuilder subscriptionBuilder() {
6161
@Deprecated
6262
public <T> void addAction(String methodName,
6363
Function<Map<String, String>, T> action) {
64-
this.addAction(new DeploymentJsonRpcMethod(methodName, null, Usage.onlyDevUI(), action));
64+
this.addAction(new DeploymentJsonRpcMethod(methodName, null, Usage.onlyDevUI(), true, action));
6565
}
6666

6767
@Deprecated
6868
public <T> void addAssistantAction(String methodName,
6969
BiFunction<Object, Map<String, String>, T> action) {
70-
this.addAction(new DeploymentJsonRpcMethod(methodName, null, Usage.onlyDevUI(), action));
70+
this.addAction(new DeploymentJsonRpcMethod(methodName, null, Usage.onlyDevUI(), true, action));
7171
}
7272

7373
@Deprecated
7474
public <T> void addAction(String methodName,
7575
RuntimeValue runtimeValue) {
76-
this.addAction(new RecordedJsonRpcMethod(methodName, null, Usage.onlyDevUI(), runtimeValue));
76+
this.addAction(new RecordedJsonRpcMethod(methodName, null, Usage.onlyDevUI(), true, runtimeValue));
7777
}
7878

7979
@Deprecated
8080
public <T> void addSubscription(String methodName,
8181
Function<Map<String, String>, T> action) {
82-
this.addSubscription(new DeploymentJsonRpcMethod(methodName, null, Usage.onlyDevUI(), action));
82+
this.addSubscription(new DeploymentJsonRpcMethod(methodName, null, Usage.onlyDevUI(), true, action));
8383
}
8484

8585
@Deprecated
8686
public <T> void addSubscription(String methodName,
8787
RuntimeValue runtimeValue) {
88-
this.addSubscription(new RecordedJsonRpcMethod(methodName, null, Usage.onlyDevUI(), runtimeValue));
88+
this.addSubscription(new RecordedJsonRpcMethod(methodName, null, Usage.onlyDevUI(), true, runtimeValue));
8989
}
9090

9191
private BuildTimeActionBuildItem addAction(DeploymentJsonRpcMethod deploymentJsonRpcMethod) {
@@ -116,6 +116,7 @@ public final class ActionBuilder {
116116
private Function<Map<String, String>, ?> function;
117117
private BiFunction<Object, Map<String, String>, ?> assistantFunction;
118118
private RuntimeValue<?> runtimeValue;
119+
private boolean mcpEnabledByDefault = false;
119120

120121
public ActionBuilder methodName(String methodName) {
121122
this.methodName = methodName;
@@ -141,6 +142,11 @@ public ActionBuilder usage(EnumSet<Usage> usage) {
141142
return this;
142143
}
143144

145+
public ActionBuilder enableMcpFuctionByDefault() {
146+
this.mcpEnabledByDefault = true;
147+
return this;
148+
}
149+
144150
public <T> ActionBuilder function(Function<Map<String, String>, T> function) {
145151
if (this.runtimeValue != null || this.assistantFunction != null)
146152
throw new IllegalStateException("Only one of runtimeValue, function or assistantFunction is allowed");
@@ -170,13 +176,17 @@ public BuildTimeActionBuildItem build() {
170176
if (function != null) {
171177
return addAction(
172178
new DeploymentJsonRpcMethod(methodName, description, parameters, autoUsage(usage, description),
179+
mcpEnabledByDefault,
173180
function));
174181
} else if (runtimeValue != null) {
175182
return addAction(
176-
new RecordedJsonRpcMethod(methodName, description, autoUsage(usage, description), runtimeValue));
183+
new RecordedJsonRpcMethod(methodName, description, autoUsage(usage, description),
184+
mcpEnabledByDefault,
185+
runtimeValue));
177186
} else if (assistantFunction != null) {
178187
return addAction(
179188
new DeploymentJsonRpcMethod(methodName, description, parameters, autoUsage(usage, description),
189+
mcpEnabledByDefault,
180190
assistantFunction));
181191
} else {
182192
throw new IllegalStateException("Either function, assistantFunction or runtimeValue must be provided");
@@ -189,6 +199,7 @@ public final class SubscriptionBuilder {
189199
private String description;
190200
private Map<String, AbstractJsonRpcMethod.Parameter> parameters = new LinkedHashMap<>();;
191201
private EnumSet<Usage> usage;
202+
private boolean mcpEnabledByDefault = false;
192203
private Function<Map<String, String>, ?> function;
193204
private RuntimeValue<?> runtimeValue;
194205

@@ -216,6 +227,11 @@ public SubscriptionBuilder usage(EnumSet<Usage> usage) {
216227
return this;
217228
}
218229

230+
public SubscriptionBuilder enableMcpFuctionByDefault() {
231+
this.mcpEnabledByDefault = true;
232+
return this;
233+
}
234+
219235
public <T> SubscriptionBuilder function(Function<Map<String, String>, T> function) {
220236
this.function = function;
221237
return this;
@@ -233,10 +249,12 @@ public void build() {
233249
parameters = null;
234250
if (function != null) {
235251
addSubscription(new DeploymentJsonRpcMethod(methodName, description, parameters, autoUsage(usage, description),
252+
mcpEnabledByDefault,
236253
function));
237254
} else if (runtimeValue != null) {
238255
addSubscription(
239-
new RecordedJsonRpcMethod(methodName, description, autoUsage(usage, description), runtimeValue));
256+
new RecordedJsonRpcMethod(methodName, description, autoUsage(usage, description), mcpEnabledByDefault,
257+
runtimeValue));
240258
} else {
241259
throw new IllegalStateException("Either function or runtimeValue must be provided");
242260
}

extensions/devui/deployment-spi/src/main/java/io/quarkus/devui/spi/buildtime/BuildTimeData.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public class BuildTimeData {
77
private Object content;
88
private String description;
99
private String contentType = "application/json"; // default
10+
private boolean mcpEnabledByDefault = false;
1011

1112
public BuildTimeData() {
1213

@@ -21,9 +22,16 @@ public BuildTimeData(Object content, String description) {
2122
this.description = description;
2223
}
2324

24-
public BuildTimeData(Object content, String description, String contentType) {
25+
public BuildTimeData(Object content, String description, boolean mcpEnabledByDefault) {
2526
this.content = content;
2627
this.description = description;
28+
this.mcpEnabledByDefault = mcpEnabledByDefault;
29+
}
30+
31+
public BuildTimeData(Object content, String description, boolean mcpEnabledByDefault, String contentType) {
32+
this.content = content;
33+
this.description = description;
34+
this.mcpEnabledByDefault = mcpEnabledByDefault;
2735
this.contentType = contentType;
2836
}
2937

@@ -50,4 +58,12 @@ public String getContentType() {
5058
public void setContentType(String contentType) {
5159
this.contentType = contentType;
5260
}
61+
62+
public boolean isMcpEnabledByDefault() {
63+
return mcpEnabledByDefault;
64+
}
65+
66+
public void setMcpEnabledByDefault(boolean mcpEnabledByDefault) {
67+
this.mcpEnabledByDefault = mcpEnabledByDefault;
68+
}
5369
}

extensions/devui/deployment-spi/src/main/java/io/quarkus/devui/spi/buildtime/QuteTemplateBuildItem.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,27 +33,34 @@ public List<TemplateData> getTemplateDatas() {
3333
}
3434

3535
public void add(String templatename, Map<String, Object> data) {
36-
templateDatas.add(new TemplateData(templatename, templatename, data, Map.of(), Map.of())); // By default the template is used for only one file.
36+
templateDatas.add(new TemplateData(templatename, templatename, data, Map.of(), Map.of(), Map.of())); // By default the template is used for only one file.
3737
}
3838

3939
public void add(String templatename, String fileName, Map<String, Object> data, Map<String, String> descriptions,
40+
Map<String, String> mcpDefaultEnabled,
4041
Map<String, String> contentTypes) {
41-
templateDatas.add(new TemplateData(templatename, fileName, data, descriptions, contentTypes));
42+
templateDatas.add(new TemplateData(templatename, fileName, data, descriptions, mcpDefaultEnabled, contentTypes));
4243
}
4344

4445
public static class TemplateData {
4546
final String templateName;
4647
final String fileName;
4748
final Map<String, Object> data;
4849
final Map<String, String> descriptions;
50+
final Map<String, String> mcpDefaultEnabled;
4951
final Map<String, String> contentTypes;
5052

51-
private TemplateData(String templateName, String fileName, Map<String, Object> data, Map<String, String> descriptions,
53+
private TemplateData(String templateName,
54+
String fileName,
55+
Map<String, Object> data,
56+
Map<String, String> descriptions,
57+
Map<String, String> mcpDefaultEnabled,
5258
Map<String, String> contentTypes) {
5359
this.templateName = templateName;
5460
this.fileName = fileName;
5561
this.data = data;
5662
this.descriptions = descriptions;
63+
this.mcpDefaultEnabled = mcpDefaultEnabled;
5764
this.contentTypes = contentTypes;
5865
}
5966

@@ -73,6 +80,10 @@ public Map<String, String> getDescriptions() {
7380
return descriptions;
7481
}
7582

83+
public Map<String, String> getMcpDefaultEnables() {
84+
return mcpDefaultEnabled;
85+
}
86+
7687
public Map<String, String> getContentTypes() {
7788
return contentTypes;
7889
}

extensions/devui/deployment-spi/src/main/java/io/quarkus/devui/spi/buildtime/jsonrpc/AbstractJsonRpcMethod.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,26 @@ public abstract class AbstractJsonRpcMethod {
1515
private String description;
1616
private Map<String, Parameter> parameters;
1717
private EnumSet<Usage> usage;
18+
private boolean mcpEnabledByDefault = false;
1819

1920
public AbstractJsonRpcMethod() {
2021
}
2122

2223
public AbstractJsonRpcMethod(String methodName, String description,
23-
EnumSet<Usage> usage) {
24+
EnumSet<Usage> usage, boolean mcpEnabledByDefault) {
2425
this.methodName = methodName;
2526
this.description = description;
2627
this.usage = usage;
28+
this.mcpEnabledByDefault = mcpEnabledByDefault;
2729
}
2830

2931
public AbstractJsonRpcMethod(String methodName, String description, Map<String, Parameter> parameters,
30-
EnumSet<Usage> usage) {
32+
EnumSet<Usage> usage, boolean mcpEnabledByDefault) {
3133
this.methodName = methodName;
3234
this.description = description;
3335
this.parameters = parameters;
3436
this.usage = usage;
37+
this.mcpEnabledByDefault = mcpEnabledByDefault;
3538
}
3639

3740
public String getMethodName() {
@@ -82,6 +85,14 @@ public void setUsage(EnumSet<Usage> usage) {
8285
this.usage = usage;
8386
}
8487

88+
public boolean isMcpEnabledByDefault() {
89+
return mcpEnabledByDefault;
90+
}
91+
92+
public void setMcpEnabledByDefault(boolean mcpEnabledByDefault) {
93+
this.mcpEnabledByDefault = mcpEnabledByDefault;
94+
}
95+
8596
public static class Parameter {
8697
private Class<?> type;
8798
private String description;

0 commit comments

Comments
 (0)