Skip to content

Commit a6c65b9

Browse files
authored
Merge pull request #125 from schemacrawler/exclude
Allow excluding of certain tools
2 parents 6ca8f13 + 967d813 commit a6c65b9

16 files changed

+279
-32
lines changed

schemacrawler-ai-mcpserver/src/main/java/schemacrawler/tools/ai/mcpserver/McpServerContext.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import static us.fatehi.utility.Utility.trimToEmpty;
1414

1515
import java.nio.file.Path;
16+
import java.util.Collection;
1617
import java.util.logging.Level;
1718
import schemacrawler.schema.Catalog;
1819
import schemacrawler.schemacrawler.InfoLevel;
@@ -22,6 +23,7 @@
2223
import schemacrawler.schemacrawler.LoadOptionsBuilder;
2324
import schemacrawler.schemacrawler.SchemaCrawlerOptions;
2425
import schemacrawler.schemacrawler.SchemaCrawlerOptionsBuilder;
26+
import schemacrawler.tools.ai.mcpserver.utility.CollectionsUtility;
2527
import schemacrawler.tools.databaseconnector.EnvironmentalDatabaseConnectionSourceBuilder;
2628
import schemacrawler.tools.offline.jdbc.OfflineConnectionUtility;
2729
import schemacrawler.tools.utility.SchemaCrawlerUtility;
@@ -32,6 +34,12 @@
3234
/** Inner class that handles the MCP server setup. */
3335
final class McpServerContext {
3436

37+
private static final String EXCLUDE_TOOLS = "SCHCRWLR_EXCLUDE_TOOLS";
38+
private static final String INFO_LEVEL = "SCHCRWLR_INFO_LEVEL";
39+
private static final String LOG_LEVEL = "SCHCRWLR_LOG_LEVEL";
40+
private static final String MCP_SERVER_TRANSPORT = "SCHCRWLR_MCP_SERVER_TRANSPORT";
41+
private static final String OFFLINE_DATABASE = "SCHCRWLR_OFFLINE_DATABASE";
42+
3543
private final EnvironmentVariableAccessor envAccessor;
3644
private final McpServerTransportType transport;
3745
private final SchemaCrawlerOptions schemaCrawlerOptions;
@@ -57,8 +65,7 @@ protected McpServerContext(final EnvironmentVariableAccessor envAccessor) {
5765
}
5866

5967
protected DatabaseConnectionSource buildCatalogDatabaseConnectionSource() {
60-
final String offlineDatabasePathString =
61-
trimToEmpty(envAccessor.getenv("SCHCRWLR_OFFLINE_DATABASE"));
68+
final String offlineDatabasePathString = trimToEmpty(envAccessor.getenv(OFFLINE_DATABASE));
6269
if (isBlank(offlineDatabasePathString)) {
6370
return buildOperationsDatabaseConnectionSource();
6471
}
@@ -80,11 +87,6 @@ protected DatabaseConnectionSource buildOperationsDatabaseConnectionSource() {
8087
return databaseConnectionSource;
8188
}
8289

83-
/**
84-
* Adds SchemaCrawler specific arguments to the arguments list.
85-
*
86-
* @param arguments The list of arguments to add to
87-
*/
8890
protected SchemaCrawlerOptions buildSchemaCrawlerOptions() {
8991
final InfoLevel infoLevel = readInfoLevel();
9092

@@ -104,6 +106,10 @@ protected SchemaCrawlerOptions buildSchemaCrawlerOptions() {
104106
return schemaCrawlerOptions;
105107
}
106108

109+
protected Collection<String> excludeTools() {
110+
return CollectionsUtility.setOfStrings(envAccessor.getenv(EXCLUDE_TOOLS));
111+
}
112+
107113
protected Catalog getCatalog() {
108114
final DatabaseConnectionSource connectionSource = buildCatalogDatabaseConnectionSource();
109115
final SchemaCrawlerOptions schemaCrawlerOptions = getSchemaCrawlerOptions();
@@ -129,7 +135,7 @@ protected InfoLevel readInfoLevel() {
129135

130136
final InfoLevel defaultValue = InfoLevel.standard;
131137
try {
132-
final String value = envAccessor.getenv("SCHCRWLR_INFO_LEVEL");
138+
final String value = envAccessor.getenv(INFO_LEVEL);
133139
InfoLevel infoLevel = InfoLevel.valueOfFromString(value);
134140
if (infoLevel == InfoLevel.unknown) {
135141
infoLevel = defaultValue;
@@ -150,7 +156,7 @@ protected Level readLogLevel() {
150156

151157
final Level defaultValue = Level.INFO;
152158

153-
final String value = envAccessor.getenv("SCHCRWLR_LOG_LEVEL");
159+
final String value = envAccessor.getenv(LOG_LEVEL);
154160
if (isBlank(value)) {
155161
return defaultValue;
156162
}
@@ -172,7 +178,7 @@ private McpServerTransportType readTransport() {
172178

173179
final McpServerTransportType defaultValue = McpServerTransportType.stdio;
174180

175-
final String value = envAccessor.getenv("SCHCRWLR_MCP_SERVER_TRANSPORT");
181+
final String value = envAccessor.getenv(MCP_SERVER_TRANSPORT);
176182
if (isBlank(value)) {
177183
return defaultValue;
178184
}

schemacrawler-ai-mcpserver/src/main/java/schemacrawler/tools/ai/mcpserver/McpServerInitializer.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88

99
package schemacrawler.tools.ai.mcpserver;
1010

11+
import static java.util.Collections.emptySet;
12+
import static java.util.Collections.unmodifiableCollection;
1113
import static java.util.Objects.requireNonNull;
1214

1315
import java.sql.Connection;
16+
import java.util.Collection;
1417
import org.springframework.context.ApplicationContextInitializer;
1518
import org.springframework.context.support.GenericApplicationContext;
1619
import org.springframework.lang.NonNull;
@@ -28,11 +31,13 @@ public class McpServerInitializer
2831
private final Catalog catalog;
2932
private final DatabaseConnectionSource connectionSource;
3033
private final McpServerTransportType mcpTransport;
34+
private final Collection<String> excludeTools;
3135

3236
public McpServerInitializer(
3337
final Catalog catalog,
3438
final Connection connection,
35-
final McpServerTransportType mcpTransport) {
39+
final McpServerTransportType mcpTransport,
40+
final Collection<String> excludeTools) {
3641

3742
this.mcpTransport = requireNonNull(mcpTransport, "No MCP Server transport provided");
3843
if (mcpTransport == McpServerTransportType.unknown) {
@@ -54,6 +59,12 @@ public McpServerInitializer(
5459
this.isInErrorState = isInErrorState;
5560

5661
this.catalog = requireNonNull(catalog, "No catalog provided");
62+
63+
if (excludeTools == null) {
64+
this.excludeTools = emptySet();
65+
} else {
66+
this.excludeTools = unmodifiableCollection(excludeTools);
67+
}
5768
}
5869

5970
public McpServerInitializer(final McpServerContext context) {
@@ -87,6 +98,8 @@ public McpServerInitializer(final McpServerContext context) {
8798
connectionSource = EmptyFactory.createEmptyDatabaseConnectionSource();
8899
}
89100
this.connectionSource = connectionSource;
101+
102+
excludeTools = context.excludeTools();
90103
}
91104

92105
@Override
@@ -102,5 +115,6 @@ public void initialize(@NonNull final GenericApplicationContext context) {
102115
"functionDefinitionRegistry",
103116
FunctionDefinitionRegistry.class,
104117
() -> FunctionDefinitionRegistry.getFunctionDefinitionRegistry());
118+
context.registerBean("excludeTools", Collection.class, () -> excludeTools);
105119
}
106120
}

schemacrawler-ai-mcpserver/src/main/java/schemacrawler/tools/ai/mcpserver/McpServerMain.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
package schemacrawler.tools.ai.mcpserver;
1010

1111
import java.sql.Connection;
12+
import java.util.Collection;
1213
import java.util.logging.Level;
1314
import java.util.logging.Logger;
1415
import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -51,9 +52,10 @@ public static void startMcpServer() {
5152
public static void startMcpServer(
5253
final Catalog catalog,
5354
final Connection connection,
54-
final McpServerTransportType mcpTransport) {
55+
final McpServerTransportType mcpTransport,
56+
final Collection<String> excludeTools) {
5557
new SpringApplicationBuilder(McpServer.class)
56-
.initializers(new McpServerInitializer(catalog, connection, mcpTransport))
58+
.initializers(new McpServerInitializer(catalog, connection, mcpTransport, excludeTools))
5759
.profiles(mcpTransport.name())
5860
.run();
5961
LOGGER.log(

schemacrawler-ai-mcpserver/src/main/java/schemacrawler/tools/ai/mcpserver/server/ToolProvider.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import io.modelcontextprotocol.server.McpSyncServerExchange;
1616
import io.modelcontextprotocol.spec.McpSchema.Implementation;
1717
import java.util.ArrayList;
18+
import java.util.Collection;
1819
import java.util.List;
1920
import java.util.logging.Level;
2021
import java.util.logging.Logger;
@@ -42,6 +43,7 @@ public class ToolProvider {
4243
@Autowired private ServerHealth serverHealth;
4344
@Autowired private FunctionDefinitionRegistry functionDefinitionRegistry;
4445
@Autowired private ToolHelper toolHelper;
46+
@Autowired private Collection<String> excludeTools;
4547

4648
@McpTool(
4749
name = "mcp-server-health",
@@ -81,9 +83,14 @@ public List<McpServerFeatures.SyncToolSpecification> schemaCrawlerTools() {
8183
final List<McpServerFeatures.SyncToolSpecification> tools = new ArrayList<>();
8284
for (final FunctionDefinition<?> functionDefinition :
8385
functionDefinitionRegistry.getFunctionDefinitions()) {
84-
LOGGER.log(
85-
Level.INFO,
86-
new StringFormat("Adding callback for:%n%s", functionDefinition.getFunctionName()));
86+
final String functionName = functionDefinition.getFunctionName().getName();
87+
if (excludeTools == null || excludeTools.contains(functionName)) {
88+
LOGGER.log(Level.WARNING, new StringFormat("Excluding tool <%s>", functionName));
89+
continue;
90+
}
91+
LOGGER.log(Level.INFO, new StringFormat("Adding callback for <%s>", functionName));
92+
LOGGER.log(Level.FINE, new StringFormat("%s", functionDefinition.getFunctionName()));
93+
8794
final McpServerFeatures.SyncToolSpecification toolSpecification =
8895
toolHelper.toSyncToolSpecification(functionDefinition);
8996
tools.add(toolSpecification);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package schemacrawler.tools.ai.mcpserver.utility;
2+
3+
import static us.fatehi.utility.Utility.trimToEmpty;
4+
5+
import java.util.Arrays;
6+
import java.util.Collection;
7+
import java.util.Objects;
8+
import java.util.Set;
9+
import java.util.stream.Collectors;
10+
import us.fatehi.utility.UtilityMarker;
11+
12+
@UtilityMarker
13+
public class CollectionsUtility {
14+
15+
public static Collection<String> setOfStrings(final String input) {
16+
final Set<String> setOfStrings =
17+
Arrays.stream(trimToEmpty(input).split(","))
18+
.filter(Objects::nonNull)
19+
.map(String::strip)
20+
.filter(s -> !s.isEmpty())
21+
.collect(Collectors.toSet());
22+
23+
return setOfStrings;
24+
}
25+
26+
private CollectionsUtility() {
27+
// Prevent instantiation
28+
}
29+
}

schemacrawler-ai-mcpserver/src/main/java/schemacrawler/tools/ai/mcpserver/utility/EmptyFactory.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
import java.lang.reflect.Proxy;
1313
import java.sql.DriverManager;
1414
import schemacrawler.schema.Catalog;
15+
import us.fatehi.utility.UtilityMarker;
1516
import us.fatehi.utility.datasource.DatabaseConnectionSource;
1617

18+
@UtilityMarker
1719
public class EmptyFactory {
1820

1921
public static Catalog createEmptyCatalog(final Exception e) {

schemacrawler-ai-mcpserver/src/main/java/schemacrawler/tools/command/mcpserver/McpServerCommand.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
package schemacrawler.tools.command.mcpserver;
1010

11+
import java.util.Collection;
1112
import java.util.logging.Level;
1213
import java.util.logging.Logger;
1314
import schemacrawler.tools.ai.mcpserver.McpServerMain;
@@ -35,7 +36,8 @@ public void checkAvailability() throws RuntimeException {
3536
@Override
3637
public void execute() {
3738
final McpServerTransportType mcpTransport = commandOptions.mcpTransport();
38-
McpServerMain.startMcpServer(catalog, connection, mcpTransport);
39+
final Collection<String> excludeTools = commandOptions.excludeTools();
40+
McpServerMain.startMcpServer(catalog, connection, mcpTransport, excludeTools);
3941
}
4042

4143
@Override

schemacrawler-ai-mcpserver/src/main/java/schemacrawler/tools/command/mcpserver/McpServerCommandOptions.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,20 @@
88

99
package schemacrawler.tools.command.mcpserver;
1010

11+
import static java.util.Objects.requireNonNull;
12+
13+
import java.util.Collection;
1114
import schemacrawler.tools.ai.mcpserver.McpServerTransportType;
1215
import schemacrawler.tools.executable.CommandOptions;
1316

14-
public record McpServerCommandOptions(McpServerTransportType mcpTransport)
17+
public record McpServerCommandOptions(
18+
McpServerTransportType mcpTransport, Collection<String> excludeTools)
1519
implements CommandOptions {
1620

1721
public McpServerCommandOptions {
1822
if (mcpTransport == null || mcpTransport == McpServerTransportType.unknown) {
1923
throw new IllegalArgumentException("No MCP Server transport specified");
2024
}
25+
requireNonNull(excludeTools, "No exclude tools list provided");
2126
}
2227
}

schemacrawler-ai-mcpserver/src/main/java/schemacrawler/tools/command/mcpserver/McpServerCommandOptionsBuilder.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@
99
package schemacrawler.tools.command.mcpserver;
1010

1111
import static schemacrawler.tools.ai.mcpserver.McpServerTransportType.unknown;
12+
13+
import java.util.Collection;
14+
import java.util.Collections;
1215
import schemacrawler.schemacrawler.OptionsBuilder;
1316
import schemacrawler.tools.ai.mcpserver.McpServerTransportType;
17+
import schemacrawler.tools.ai.mcpserver.utility.CollectionsUtility;
1418
import schemacrawler.tools.options.Config;
1519
import schemacrawler.tools.options.ConfigOptionsBuilder;
1620

@@ -23,17 +27,20 @@ public static McpServerCommandOptionsBuilder builder() {
2327
}
2428

2529
private McpServerTransportType mcpTransport;
30+
private Collection<String> excludeTools;
2631

2732
private McpServerCommandOptionsBuilder() {
2833
// MCP Server transport needs to be explicitly specified,
2934
// so default to known
3035
mcpTransport = unknown;
36+
excludeTools = Collections.emptySet();
3137
}
3238

3339
@Override
3440
public McpServerCommandOptionsBuilder fromConfig(final Config config) {
3541
if (config != null) {
3642
mcpTransport = config.getEnumValue("transport", unknown);
43+
excludeTools = CollectionsUtility.setOfStrings(config.getStringValue("exclude-tools", ""));
3744
}
3845

3946
return this;
@@ -43,18 +50,19 @@ public McpServerCommandOptionsBuilder fromConfig(final Config config) {
4350
public McpServerCommandOptionsBuilder fromOptions(final McpServerCommandOptions options) {
4451
if (options != null) {
4552
mcpTransport = options.mcpTransport();
53+
excludeTools = options.excludeTools();
4654
}
4755
return this;
4856
}
4957

5058
@Override
5159
public Config toConfig() {
52-
throw new UnsupportedOperationException("Cannot load transport from config file");
60+
throw new UnsupportedOperationException("Cannot load MCP Server options from config file");
5361
}
5462

5563
@Override
5664
public McpServerCommandOptions toOptions() {
57-
return new McpServerCommandOptions(mcpTransport);
65+
return new McpServerCommandOptions(mcpTransport, excludeTools);
5866
}
5967

6068
public McpServerCommandOptionsBuilder withMcpTransport(

schemacrawler-ai-mcpserver/src/main/java/schemacrawler/tools/command/mcpserver/McpServerCommandProvider.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public PluginCommand getCommandLineCommand() {
3030
final PluginCommand pluginCommand = newPluginCommand(COMMAND);
3131
pluginCommand.addOption(
3232
"transport", McpServerTransportType.class, "Type of MCP server to start");
33+
pluginCommand.addOption(
34+
"exclude-tools", String.class, "Comma-separated list of tools to exclude");
3335
return pluginCommand;
3436
}
3537

0 commit comments

Comments
 (0)