Skip to content

Commit 7003bf4

Browse files
issue#:626 Allow dynamic generation of tool definition
1 parent 082444e commit 7003bf4

File tree

6 files changed

+327
-20
lines changed

6 files changed

+327
-20
lines changed

mcp-core/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package io.modelcontextprotocol.server;
66

77
import java.time.Duration;
8+
import java.util.ArrayList;
89
import java.util.HashMap;
910
import java.util.List;
1011
import java.util.Map;
@@ -117,6 +118,10 @@ public class McpAsyncServer {
117118

118119
private final ConcurrentHashMap<McpSchema.CompleteReference, McpServerFeatures.AsyncCompletionSpecification> completions = new ConcurrentHashMap<>();
119120

121+
private final boolean callGetToolCallbacksEverytime;
122+
123+
private final List<ToolCallbackProvider> toolCallbackProviders;
124+
120125
private List<String> protocolVersions;
121126

122127
private McpUriTemplateManagerFactory uriTemplateManagerFactory = new DefaultMcpUriTemplateManagerFactory();
@@ -143,6 +148,18 @@ public class McpAsyncServer {
143148
this.completions.putAll(features.completions());
144149
this.uriTemplateManagerFactory = uriTemplateManagerFactory;
145150
this.jsonSchemaValidator = jsonSchemaValidator;
151+
this.callGetToolCallbacksEverytime = features.callGetToolCallbacksEverytime();
152+
this.toolCallbackProviders = features.toolCallbackProviders();
153+
154+
// If flag is false, call getToolCallbacks during startup
155+
if (!this.callGetToolCallbacksEverytime && !this.toolCallbackProviders.isEmpty()) {
156+
for (ToolCallbackProvider provider : this.toolCallbackProviders) {
157+
List<McpServerFeatures.AsyncToolSpecification> callbackTools = provider.getToolCallbacks();
158+
if (callbackTools != null) {
159+
this.tools.addAll(withStructuredOutputHandling(jsonSchemaValidator, callbackTools));
160+
}
161+
}
162+
}
146163

147164
Map<String, McpRequestHandler<?>> requestHandlers = prepareRequestHandlers();
148165
Map<String, McpNotificationHandler> notificationHandlers = prepareNotificationHandlers(features);
@@ -168,6 +185,18 @@ public class McpAsyncServer {
168185
this.completions.putAll(features.completions());
169186
this.uriTemplateManagerFactory = uriTemplateManagerFactory;
170187
this.jsonSchemaValidator = jsonSchemaValidator;
188+
this.callGetToolCallbacksEverytime = features.callGetToolCallbacksEverytime();
189+
this.toolCallbackProviders = features.toolCallbackProviders();
190+
191+
// If flag is false, call getToolCallbacks during startup
192+
if (!this.callGetToolCallbacksEverytime && !this.toolCallbackProviders.isEmpty()) {
193+
for (ToolCallbackProvider provider : this.toolCallbackProviders) {
194+
List<McpServerFeatures.AsyncToolSpecification> callbackTools = provider.getToolCallbacks();
195+
if (callbackTools != null) {
196+
this.tools.addAll(withStructuredOutputHandling(jsonSchemaValidator, callbackTools));
197+
}
198+
}
199+
}
171200

172201
Map<String, McpRequestHandler<?>> requestHandlers = prepareRequestHandlers();
173202
Map<String, McpNotificationHandler> notificationHandlers = prepareNotificationHandlers(features);
@@ -513,8 +542,23 @@ public Mono<Void> notifyToolsListChanged() {
513542

514543
private McpRequestHandler<McpSchema.ListToolsResult> toolsListRequestHandler() {
515544
return (exchange, params) -> {
516-
List<Tool> tools = this.tools.stream().map(McpServerFeatures.AsyncToolSpecification::tool).toList();
517-
545+
List<Tool> tools = new ArrayList<>();
546+
547+
// Add static tools
548+
tools.addAll(this.tools.stream().map(McpServerFeatures.AsyncToolSpecification::tool).toList());
549+
550+
// If flag is true, call getToolCallbacks on every request
551+
if (this.callGetToolCallbacksEverytime && !this.toolCallbackProviders.isEmpty()) {
552+
for (ToolCallbackProvider provider : this.toolCallbackProviders) {
553+
List<McpServerFeatures.AsyncToolSpecification> callbackTools = provider.getToolCallbacks();
554+
if (callbackTools != null) {
555+
tools.addAll(callbackTools.stream()
556+
.map(McpServerFeatures.AsyncToolSpecification::tool)
557+
.toList());
558+
}
559+
}
560+
}
561+
518562
return Mono.just(new McpSchema.ListToolsResult(tools, null));
519563
};
520564
}

mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java

Lines changed: 170 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ private SingleSessionAsyncSpecification(McpServerTransportProvider transportProv
237237
public McpAsyncServer build() {
238238
var features = new McpServerFeatures.Async(this.serverInfo, this.serverCapabilities, this.tools,
239239
this.resources, this.resourceTemplates, this.prompts, this.completions, this.rootsChangeHandlers,
240-
this.instructions);
240+
this.instructions, this.callGetToolCallbacksEverytime, this.toolCallbackProviders);
241241

242242
var jsonSchemaValidator = (this.jsonSchemaValidator != null) ? this.jsonSchemaValidator
243243
: JsonSchemaValidator.getDefault();
@@ -265,7 +265,7 @@ public StreamableServerAsyncSpecification(McpStreamableServerTransportProvider t
265265
public McpAsyncServer build() {
266266
var features = new McpServerFeatures.Async(this.serverInfo, this.serverCapabilities, this.tools,
267267
this.resources, this.resourceTemplates, this.prompts, this.completions, this.rootsChangeHandlers,
268-
this.instructions);
268+
this.instructions, this.callGetToolCallbacksEverytime, this.toolCallbackProviders);
269269
var jsonSchemaValidator = this.jsonSchemaValidator != null ? this.jsonSchemaValidator
270270
: JsonSchemaValidator.getDefault();
271271
return new McpAsyncServer(transportProvider, jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper,
@@ -331,6 +331,10 @@ abstract class AsyncSpecification<S extends AsyncSpecification<S>> {
331331

332332
final List<BiFunction<McpAsyncServerExchange, List<McpSchema.Root>, Mono<Void>>> rootsChangeHandlers = new ArrayList<>();
333333

334+
boolean callGetToolCallbacksEverytime = false;
335+
336+
final List<ToolCallbackProvider> toolCallbackProviders = new ArrayList<>();
337+
334338
Duration requestTimeout = Duration.ofHours(10); // Default timeout
335339

336340
public abstract McpAsyncServer build();
@@ -407,6 +411,42 @@ public AsyncSpecification<S> instructions(String instructions) {
407411
return this;
408412
}
409413

414+
/**
415+
* Sets whether to call getToolCallbacks on every tools/list request.
416+
* When true, registered ToolCallbackProviders will be called on every tools/list request,
417+
* allowing for dynamic tool generation. When false, getToolCallbacks is only called during startup.
418+
* @param callGetToolCallbacksEverytime true to call on every request, false for startup only
419+
* @return This builder instance for method chaining
420+
*/
421+
public AsyncSpecification<S> callGetToolCallbacksEverytime(boolean callGetToolCallbacksEverytime) {
422+
this.callGetToolCallbacksEverytime = callGetToolCallbacksEverytime;
423+
return this;
424+
}
425+
426+
/**
427+
* Registers a ToolCallbackProvider that can dynamically generate tool definitions.
428+
* @param provider The tool callback provider. Must not be null.
429+
* @return This builder instance for method chaining
430+
* @throws IllegalArgumentException if provider is null
431+
*/
432+
public AsyncSpecification<S> toolCallbackProvider(ToolCallbackProvider provider) {
433+
Assert.notNull(provider, "Tool callback provider must not be null");
434+
this.toolCallbackProviders.add(provider);
435+
return this;
436+
}
437+
438+
/**
439+
* Registers multiple ToolCallbackProviders.
440+
* @param providers The tool callback providers. Must not be null.
441+
* @return This builder instance for method chaining
442+
* @throws IllegalArgumentException if providers is null
443+
*/
444+
public AsyncSpecification<S> toolCallbackProviders(List<ToolCallbackProvider> providers) {
445+
Assert.notNull(providers, "Tool callback providers must not be null");
446+
this.toolCallbackProviders.addAll(providers);
447+
return this;
448+
}
449+
410450
/**
411451
* Sets the server capabilities that will be advertised to clients during
412452
* connection initialization. Capabilities define what features the server
@@ -829,7 +869,8 @@ private SingleSessionSyncSpecification(McpServerTransportProvider transportProvi
829869
public McpSyncServer build() {
830870
McpServerFeatures.Sync syncFeatures = new McpServerFeatures.Sync(this.serverInfo, this.serverCapabilities,
831871
this.tools, this.resources, this.resourceTemplates, this.prompts, this.completions,
832-
this.rootsChangeHandlers, this.instructions);
872+
this.rootsChangeHandlers, this.instructions, this.callGetToolCallbacksEverytime,
873+
this.toolCallbackProviders);
833874
McpServerFeatures.Async asyncFeatures = McpServerFeatures.Async.fromSync(syncFeatures,
834875
this.immediateExecution);
835876

@@ -860,7 +901,8 @@ private StreamableSyncSpecification(McpStreamableServerTransportProvider transpo
860901
public McpSyncServer build() {
861902
McpServerFeatures.Sync syncFeatures = new McpServerFeatures.Sync(this.serverInfo, this.serverCapabilities,
862903
this.tools, this.resources, this.resourceTemplates, this.prompts, this.completions,
863-
this.rootsChangeHandlers, this.instructions);
904+
this.rootsChangeHandlers, this.instructions, this.callGetToolCallbacksEverytime,
905+
this.toolCallbackProviders);
864906
McpServerFeatures.Async asyncFeatures = McpServerFeatures.Async.fromSync(syncFeatures,
865907
this.immediateExecution);
866908
var jsonSchemaValidator = this.jsonSchemaValidator != null ? this.jsonSchemaValidator
@@ -930,6 +972,10 @@ abstract class SyncSpecification<S extends SyncSpecification<S>> {
930972

931973
final List<BiConsumer<McpSyncServerExchange, List<McpSchema.Root>>> rootsChangeHandlers = new ArrayList<>();
932974

975+
boolean callGetToolCallbacksEverytime = false;
976+
977+
final List<ToolCallbackProvider> toolCallbackProviders = new ArrayList<>();
978+
933979
Duration requestTimeout = Duration.ofSeconds(10); // Default timeout
934980

935981
boolean immediateExecution = false;
@@ -1008,6 +1054,42 @@ public SyncSpecification<S> instructions(String instructions) {
10081054
return this;
10091055
}
10101056

1057+
/**
1058+
* Sets whether to call getToolCallbacks on every tools/list request.
1059+
* When true, registered ToolCallbackProviders will be called on every tools/list request,
1060+
* allowing for dynamic tool generation. When false, getToolCallbacks is only called during startup.
1061+
* @param callGetToolCallbacksEverytime true to call on every request, false for startup only
1062+
* @return This builder instance for method chaining
1063+
*/
1064+
public SyncSpecification<S> callGetToolCallbacksEverytime(boolean callGetToolCallbacksEverytime) {
1065+
this.callGetToolCallbacksEverytime = callGetToolCallbacksEverytime;
1066+
return this;
1067+
}
1068+
1069+
/**
1070+
* Registers a ToolCallbackProvider that can dynamically generate tool definitions.
1071+
* @param provider The tool callback provider. Must not be null.
1072+
* @return This builder instance for method chaining
1073+
* @throws IllegalArgumentException if provider is null
1074+
*/
1075+
public SyncSpecification<S> toolCallbackProvider(ToolCallbackProvider provider) {
1076+
Assert.notNull(provider, "Tool callback provider must not be null");
1077+
this.toolCallbackProviders.add(provider);
1078+
return this;
1079+
}
1080+
1081+
/**
1082+
* Registers multiple ToolCallbackProviders.
1083+
* @param providers The tool callback providers. Must not be null.
1084+
* @return This builder instance for method chaining
1085+
* @throws IllegalArgumentException if providers is null
1086+
*/
1087+
public SyncSpecification<S> toolCallbackProviders(List<ToolCallbackProvider> providers) {
1088+
Assert.notNull(providers, "Tool callback providers must not be null");
1089+
this.toolCallbackProviders.addAll(providers);
1090+
return this;
1091+
}
1092+
10111093
/**
10121094
* Sets the server capabilities that will be advertised to clients during
10131095
* connection initialization. Capabilities define what features the server
@@ -1472,6 +1554,10 @@ class StatelessAsyncSpecification {
14721554

14731555
final Map<McpSchema.CompleteReference, McpStatelessServerFeatures.AsyncCompletionSpecification> completions = new HashMap<>();
14741556

1557+
boolean callGetToolCallbacksEverytime = false;
1558+
1559+
final List<ToolCallbackProvider> toolCallbackProviders = new ArrayList<>();
1560+
14751561
Duration requestTimeout = Duration.ofSeconds(10); // Default timeout
14761562

14771563
public StatelessAsyncSpecification(McpStatelessServerTransport transport) {
@@ -1551,6 +1637,42 @@ public StatelessAsyncSpecification instructions(String instructions) {
15511637
return this;
15521638
}
15531639

1640+
/**
1641+
* Sets whether to call getToolCallbacks on every tools/list request.
1642+
* When true, registered ToolCallbackProviders will be called on every tools/list request,
1643+
* allowing for dynamic tool generation. When false, getToolCallbacks is only called during startup.
1644+
* @param callGetToolCallbacksEverytime true to call on every request, false for startup only
1645+
* @return This builder instance for method chaining
1646+
*/
1647+
public StatelessAsyncSpecification callGetToolCallbacksEverytime(boolean callGetToolCallbacksEverytime) {
1648+
this.callGetToolCallbacksEverytime = callGetToolCallbacksEverytime;
1649+
return this;
1650+
}
1651+
1652+
/**
1653+
* Registers a ToolCallbackProvider that can dynamically generate tool definitions.
1654+
* @param provider The tool callback provider. Must not be null.
1655+
* @return This builder instance for method chaining
1656+
* @throws IllegalArgumentException if provider is null
1657+
*/
1658+
public StatelessAsyncSpecification toolCallbackProvider(ToolCallbackProvider provider) {
1659+
Assert.notNull(provider, "Tool callback provider must not be null");
1660+
this.toolCallbackProviders.add(provider);
1661+
return this;
1662+
}
1663+
1664+
/**
1665+
* Registers multiple ToolCallbackProviders.
1666+
* @param providers The tool callback providers. Must not be null.
1667+
* @return This builder instance for method chaining
1668+
* @throws IllegalArgumentException if providers is null
1669+
*/
1670+
public StatelessAsyncSpecification toolCallbackProviders(List<ToolCallbackProvider> providers) {
1671+
Assert.notNull(providers, "Tool callback providers must not be null");
1672+
this.toolCallbackProviders.addAll(providers);
1673+
return this;
1674+
}
1675+
15541676
/**
15551677
* Sets the server capabilities that will be advertised to clients during
15561678
* connection initialization. Capabilities define what features the server
@@ -1870,7 +1992,8 @@ public StatelessAsyncSpecification jsonSchemaValidator(JsonSchemaValidator jsonS
18701992

18711993
public McpStatelessAsyncServer build() {
18721994
var features = new McpStatelessServerFeatures.Async(this.serverInfo, this.serverCapabilities, this.tools,
1873-
this.resources, this.resourceTemplates, this.prompts, this.completions, this.instructions);
1995+
this.resources, this.resourceTemplates, this.prompts, this.completions, this.instructions,
1996+
this.callGetToolCallbacksEverytime, this.toolCallbackProviders);
18741997
return new McpStatelessAsyncServer(transport, jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper,
18751998
features, requestTimeout, uriTemplateManagerFactory,
18761999
jsonSchemaValidator != null ? jsonSchemaValidator : JsonSchemaValidator.getDefault());
@@ -1934,6 +2057,10 @@ class StatelessSyncSpecification {
19342057

19352058
final Map<McpSchema.CompleteReference, McpStatelessServerFeatures.SyncCompletionSpecification> completions = new HashMap<>();
19362059

2060+
boolean callGetToolCallbacksEverytime = false;
2061+
2062+
final List<ToolCallbackProvider> toolCallbackProviders = new ArrayList<>();
2063+
19372064
Duration requestTimeout = Duration.ofSeconds(10); // Default timeout
19382065

19392066
public StatelessSyncSpecification(McpStatelessServerTransport transport) {
@@ -2013,6 +2140,42 @@ public StatelessSyncSpecification instructions(String instructions) {
20132140
return this;
20142141
}
20152142

2143+
/**
2144+
* Sets whether to call getToolCallbacks on every tools/list request.
2145+
* When true, registered ToolCallbackProviders will be called on every tools/list request,
2146+
* allowing for dynamic tool generation. When false, getToolCallbacks is only called during startup.
2147+
* @param callGetToolCallbacksEverytime true to call on every request, false for startup only
2148+
* @return This builder instance for method chaining
2149+
*/
2150+
public StatelessSyncSpecification callGetToolCallbacksEverytime(boolean callGetToolCallbacksEverytime) {
2151+
this.callGetToolCallbacksEverytime = callGetToolCallbacksEverytime;
2152+
return this;
2153+
}
2154+
2155+
/**
2156+
* Registers a ToolCallbackProvider that can dynamically generate tool definitions.
2157+
* @param provider The tool callback provider. Must not be null.
2158+
* @return This builder instance for method chaining
2159+
* @throws IllegalArgumentException if provider is null
2160+
*/
2161+
public StatelessSyncSpecification toolCallbackProvider(ToolCallbackProvider provider) {
2162+
Assert.notNull(provider, "Tool callback provider must not be null");
2163+
this.toolCallbackProviders.add(provider);
2164+
return this;
2165+
}
2166+
2167+
/**
2168+
* Registers multiple ToolCallbackProviders.
2169+
* @param providers The tool callback providers. Must not be null.
2170+
* @return This builder instance for method chaining
2171+
* @throws IllegalArgumentException if providers is null
2172+
*/
2173+
public StatelessSyncSpecification toolCallbackProviders(List<ToolCallbackProvider> providers) {
2174+
Assert.notNull(providers, "Tool callback providers must not be null");
2175+
this.toolCallbackProviders.addAll(providers);
2176+
return this;
2177+
}
2178+
20162179
/**
20172180
* Sets the server capabilities that will be advertised to clients during
20182181
* connection initialization. Capabilities define what features the server
@@ -2348,7 +2511,8 @@ public StatelessSyncSpecification immediateExecution(boolean immediateExecution)
23482511

23492512
public McpStatelessSyncServer build() {
23502513
var syncFeatures = new McpStatelessServerFeatures.Sync(this.serverInfo, this.serverCapabilities, this.tools,
2351-
this.resources, this.resourceTemplates, this.prompts, this.completions, this.instructions);
2514+
this.resources, this.resourceTemplates, this.prompts, this.completions, this.instructions,
2515+
this.callGetToolCallbacksEverytime, this.toolCallbackProviders);
23522516
var asyncFeatures = McpStatelessServerFeatures.Async.fromSync(syncFeatures, this.immediateExecution);
23532517
var asyncServer = new McpStatelessAsyncServer(transport,
23542518
jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, asyncFeatures, requestTimeout,

0 commit comments

Comments
 (0)