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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package io.modelcontextprotocol.server;

import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -117,6 +118,10 @@ public class McpAsyncServer {

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

private final boolean callGetToolCallbacksEverytime;

private final List<ToolCallbackProvider> toolCallbackProviders;

private List<String> protocolVersions;

private McpUriTemplateManagerFactory uriTemplateManagerFactory = new DefaultMcpUriTemplateManagerFactory();
Expand All @@ -143,6 +148,18 @@ public class McpAsyncServer {
this.completions.putAll(features.completions());
this.uriTemplateManagerFactory = uriTemplateManagerFactory;
this.jsonSchemaValidator = jsonSchemaValidator;
this.callGetToolCallbacksEverytime = features.callGetToolCallbacksEverytime();
this.toolCallbackProviders = features.toolCallbackProviders();

// If flag is false, call getToolCallbacks during startup
if (!this.callGetToolCallbacksEverytime && !this.toolCallbackProviders.isEmpty()) {
for (ToolCallbackProvider provider : this.toolCallbackProviders) {
List<McpServerFeatures.AsyncToolSpecification> callbackTools = provider.getToolCallbacks();
if (callbackTools != null) {
this.tools.addAll(withStructuredOutputHandling(jsonSchemaValidator, callbackTools));
}
}
}

Map<String, McpRequestHandler<?>> requestHandlers = prepareRequestHandlers();
Map<String, McpNotificationHandler> notificationHandlers = prepareNotificationHandlers(features);
Expand All @@ -168,6 +185,18 @@ public class McpAsyncServer {
this.completions.putAll(features.completions());
this.uriTemplateManagerFactory = uriTemplateManagerFactory;
this.jsonSchemaValidator = jsonSchemaValidator;
this.callGetToolCallbacksEverytime = features.callGetToolCallbacksEverytime();
this.toolCallbackProviders = features.toolCallbackProviders();

// If flag is false, call getToolCallbacks during startup
if (!this.callGetToolCallbacksEverytime && !this.toolCallbackProviders.isEmpty()) {
for (ToolCallbackProvider provider : this.toolCallbackProviders) {
List<McpServerFeatures.AsyncToolSpecification> callbackTools = provider.getToolCallbacks();
if (callbackTools != null) {
this.tools.addAll(withStructuredOutputHandling(jsonSchemaValidator, callbackTools));
}
}
}

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

private McpRequestHandler<McpSchema.ListToolsResult> toolsListRequestHandler() {
return (exchange, params) -> {
List<Tool> tools = this.tools.stream().map(McpServerFeatures.AsyncToolSpecification::tool).toList();

List<Tool> tools = new ArrayList<>();

// Add static tools
tools.addAll(this.tools.stream().map(McpServerFeatures.AsyncToolSpecification::tool).toList());

// If flag is true, call getToolCallbacks on every request
if (this.callGetToolCallbacksEverytime && !this.toolCallbackProviders.isEmpty()) {
for (ToolCallbackProvider provider : this.toolCallbackProviders) {
List<McpServerFeatures.AsyncToolSpecification> callbackTools = provider.getToolCallbacks();
if (callbackTools != null) {
tools.addAll(callbackTools.stream()
.map(McpServerFeatures.AsyncToolSpecification::tool)
.toList());
}
}
}

return Mono.just(new McpSchema.ListToolsResult(tools, null));
};
}
Expand Down
176 changes: 170 additions & 6 deletions mcp-core/src/main/java/io/modelcontextprotocol/server/McpServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ private SingleSessionAsyncSpecification(McpServerTransportProvider transportProv
public McpAsyncServer build() {
var features = new McpServerFeatures.Async(this.serverInfo, this.serverCapabilities, this.tools,
this.resources, this.resourceTemplates, this.prompts, this.completions, this.rootsChangeHandlers,
this.instructions);
this.instructions, this.callGetToolCallbacksEverytime, this.toolCallbackProviders);

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

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

boolean callGetToolCallbacksEverytime = false;

final List<ToolCallbackProvider> toolCallbackProviders = new ArrayList<>();

Duration requestTimeout = Duration.ofHours(10); // Default timeout

public abstract McpAsyncServer build();
Expand Down Expand Up @@ -407,6 +411,42 @@ public AsyncSpecification<S> instructions(String instructions) {
return this;
}

/**
* Sets whether to call getToolCallbacks on every tools/list request.
* When true, registered ToolCallbackProviders will be called on every tools/list request,
* allowing for dynamic tool generation. When false, getToolCallbacks is only called during startup.
* @param callGetToolCallbacksEverytime true to call on every request, false for startup only
* @return This builder instance for method chaining
*/
public AsyncSpecification<S> callGetToolCallbacksEverytime(boolean callGetToolCallbacksEverytime) {
this.callGetToolCallbacksEverytime = callGetToolCallbacksEverytime;
return this;
}

/**
* Registers a ToolCallbackProvider that can dynamically generate tool definitions.
* @param provider The tool callback provider. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if provider is null
*/
public AsyncSpecification<S> toolCallbackProvider(ToolCallbackProvider provider) {
Assert.notNull(provider, "Tool callback provider must not be null");
this.toolCallbackProviders.add(provider);
return this;
}

/**
* Registers multiple ToolCallbackProviders.
* @param providers The tool callback providers. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if providers is null
*/
public AsyncSpecification<S> toolCallbackProviders(List<ToolCallbackProvider> providers) {
Assert.notNull(providers, "Tool callback providers must not be null");
this.toolCallbackProviders.addAll(providers);
return this;
}

/**
* Sets the server capabilities that will be advertised to clients during
* connection initialization. Capabilities define what features the server
Expand Down Expand Up @@ -829,7 +869,8 @@ private SingleSessionSyncSpecification(McpServerTransportProvider transportProvi
public McpSyncServer build() {
McpServerFeatures.Sync syncFeatures = new McpServerFeatures.Sync(this.serverInfo, this.serverCapabilities,
this.tools, this.resources, this.resourceTemplates, this.prompts, this.completions,
this.rootsChangeHandlers, this.instructions);
this.rootsChangeHandlers, this.instructions, this.callGetToolCallbacksEverytime,
this.toolCallbackProviders);
McpServerFeatures.Async asyncFeatures = McpServerFeatures.Async.fromSync(syncFeatures,
this.immediateExecution);

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

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

boolean callGetToolCallbacksEverytime = false;

final List<ToolCallbackProvider> toolCallbackProviders = new ArrayList<>();

Duration requestTimeout = Duration.ofSeconds(10); // Default timeout

boolean immediateExecution = false;
Expand Down Expand Up @@ -1008,6 +1054,42 @@ public SyncSpecification<S> instructions(String instructions) {
return this;
}

/**
* Sets whether to call getToolCallbacks on every tools/list request.
* When true, registered ToolCallbackProviders will be called on every tools/list request,
* allowing for dynamic tool generation. When false, getToolCallbacks is only called during startup.
* @param callGetToolCallbacksEverytime true to call on every request, false for startup only
* @return This builder instance for method chaining
*/
public SyncSpecification<S> callGetToolCallbacksEverytime(boolean callGetToolCallbacksEverytime) {
this.callGetToolCallbacksEverytime = callGetToolCallbacksEverytime;
return this;
}

/**
* Registers a ToolCallbackProvider that can dynamically generate tool definitions.
* @param provider The tool callback provider. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if provider is null
*/
public SyncSpecification<S> toolCallbackProvider(ToolCallbackProvider provider) {
Assert.notNull(provider, "Tool callback provider must not be null");
this.toolCallbackProviders.add(provider);
return this;
}

/**
* Registers multiple ToolCallbackProviders.
* @param providers The tool callback providers. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if providers is null
*/
public SyncSpecification<S> toolCallbackProviders(List<ToolCallbackProvider> providers) {
Assert.notNull(providers, "Tool callback providers must not be null");
this.toolCallbackProviders.addAll(providers);
return this;
}

/**
* Sets the server capabilities that will be advertised to clients during
* connection initialization. Capabilities define what features the server
Expand Down Expand Up @@ -1472,6 +1554,10 @@ class StatelessAsyncSpecification {

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

boolean callGetToolCallbacksEverytime = false;

final List<ToolCallbackProvider> toolCallbackProviders = new ArrayList<>();

Duration requestTimeout = Duration.ofSeconds(10); // Default timeout

public StatelessAsyncSpecification(McpStatelessServerTransport transport) {
Expand Down Expand Up @@ -1551,6 +1637,42 @@ public StatelessAsyncSpecification instructions(String instructions) {
return this;
}

/**
* Sets whether to call getToolCallbacks on every tools/list request.
* When true, registered ToolCallbackProviders will be called on every tools/list request,
* allowing for dynamic tool generation. When false, getToolCallbacks is only called during startup.
* @param callGetToolCallbacksEverytime true to call on every request, false for startup only
* @return This builder instance for method chaining
*/
public StatelessAsyncSpecification callGetToolCallbacksEverytime(boolean callGetToolCallbacksEverytime) {
this.callGetToolCallbacksEverytime = callGetToolCallbacksEverytime;
return this;
}

/**
* Registers a ToolCallbackProvider that can dynamically generate tool definitions.
* @param provider The tool callback provider. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if provider is null
*/
public StatelessAsyncSpecification toolCallbackProvider(ToolCallbackProvider provider) {
Assert.notNull(provider, "Tool callback provider must not be null");
this.toolCallbackProviders.add(provider);
return this;
}

/**
* Registers multiple ToolCallbackProviders.
* @param providers The tool callback providers. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if providers is null
*/
public StatelessAsyncSpecification toolCallbackProviders(List<ToolCallbackProvider> providers) {
Assert.notNull(providers, "Tool callback providers must not be null");
this.toolCallbackProviders.addAll(providers);
return this;
}

/**
* Sets the server capabilities that will be advertised to clients during
* connection initialization. Capabilities define what features the server
Expand Down Expand Up @@ -1870,7 +1992,8 @@ public StatelessAsyncSpecification jsonSchemaValidator(JsonSchemaValidator jsonS

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

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

boolean callGetToolCallbacksEverytime = false;

final List<ToolCallbackProvider> toolCallbackProviders = new ArrayList<>();

Duration requestTimeout = Duration.ofSeconds(10); // Default timeout

public StatelessSyncSpecification(McpStatelessServerTransport transport) {
Expand Down Expand Up @@ -2013,6 +2140,42 @@ public StatelessSyncSpecification instructions(String instructions) {
return this;
}

/**
* Sets whether to call getToolCallbacks on every tools/list request.
* When true, registered ToolCallbackProviders will be called on every tools/list request,
* allowing for dynamic tool generation. When false, getToolCallbacks is only called during startup.
* @param callGetToolCallbacksEverytime true to call on every request, false for startup only
* @return This builder instance for method chaining
*/
public StatelessSyncSpecification callGetToolCallbacksEverytime(boolean callGetToolCallbacksEverytime) {
this.callGetToolCallbacksEverytime = callGetToolCallbacksEverytime;
return this;
}

/**
* Registers a ToolCallbackProvider that can dynamically generate tool definitions.
* @param provider The tool callback provider. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if provider is null
*/
public StatelessSyncSpecification toolCallbackProvider(ToolCallbackProvider provider) {
Assert.notNull(provider, "Tool callback provider must not be null");
this.toolCallbackProviders.add(provider);
return this;
}

/**
* Registers multiple ToolCallbackProviders.
* @param providers The tool callback providers. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if providers is null
*/
public StatelessSyncSpecification toolCallbackProviders(List<ToolCallbackProvider> providers) {
Assert.notNull(providers, "Tool callback providers must not be null");
this.toolCallbackProviders.addAll(providers);
return this;
}

/**
* Sets the server capabilities that will be advertised to clients during
* connection initialization. Capabilities define what features the server
Expand Down Expand Up @@ -2348,7 +2511,8 @@ public StatelessSyncSpecification immediateExecution(boolean immediateExecution)

public McpStatelessSyncServer build() {
var syncFeatures = new McpStatelessServerFeatures.Sync(this.serverInfo, this.serverCapabilities, this.tools,
this.resources, this.resourceTemplates, this.prompts, this.completions, this.instructions);
this.resources, this.resourceTemplates, this.prompts, this.completions, this.instructions,
this.callGetToolCallbacksEverytime, this.toolCallbackProviders);
var asyncFeatures = McpStatelessServerFeatures.Async.fromSync(syncFeatures, this.immediateExecution);
var asyncServer = new McpStatelessAsyncServer(transport,
jsonMapper == null ? McpJsonMapper.getDefault() : jsonMapper, asyncFeatures, requestTimeout,
Expand Down
Loading