Skip to content
Merged
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
@@ -0,0 +1,79 @@
package schemacrawler.tools.command.aichat.mcp;

import schemacrawler.schema.Catalog;

import java.sql.Connection;

import static java.util.Objects.requireNonNull;

/**
* A singleton service that provides access to database connection resources. This class follows an
* initialization-on-demand pattern where the service must be explicitly initialized once before it
* can be used.
*/
public class ConnectionService {

private static final Object lock = new Object();
private static volatile ConnectionService connectionService;
private final Catalog catalog;
private final Connection connection;
private final McpServerCommandOptions options;

/**
* Private constructor to prevent direct instantiation. Use {@link
* #instantiate(McpServerCommandOptions, Catalog, Connection)} to initialize and {@link
* #getInstance()} to access the singleton instance.
*/
private ConnectionService(
final McpServerCommandOptions options, final Catalog catalog, final Connection connection) {
this.catalog = requireNonNull(catalog, "No catalog provided");
this.connection = requireNonNull(connection, "No connection provided");
this.options = requireNonNull(options, "No options provided");
}

/**
* Initializes the ConnectionService singleton. This method should be called exactly once during
* application startup. Subsequent calls will throw an IllegalStateException.
*
* @param options Command options
* @param catalog Database schema catalog
* @param connection SQL connection
* @throws IllegalStateException if the service has already been initialized
*/
public static void instantiate(
final McpServerCommandOptions options, final Catalog catalog, final Connection connection) {
synchronized (lock) {
if (connectionService != null) {
throw new IllegalStateException("ConnectionService has already been initialized");
}
connectionService = new ConnectionService(options, catalog, connection);
}
}

/**
* Gets the singleton instance of ConnectionService. The service must have been initialized with
* {@link #instantiate(McpServerCommandOptions, Catalog, Connection)} before this method is
* called.
*
* @return The singleton ConnectionService instance
* @throws IllegalStateException if the service has not been initialized
*/
public static ConnectionService getInstance() {
if (connectionService == null) {
throw new IllegalStateException("ConnectionService has not been initialized yet");
}
return connectionService;
}

public Catalog catalog() {
return catalog;
}

public Connection connection() {
return connection;
}

public McpServerCommandOptions options() {
return options;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
========================================================================
SchemaCrawler
http://www.schemacrawler.com
Copyright (c) 2000-2025, Sualeh Fatehi <sualeh@hotmail.com>.
All rights reserved.
------------------------------------------------------------------------

SchemaCrawler is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

SchemaCrawler and the accompanying materials are made available under
the terms of the Eclipse Public License v1.0, GNU General Public License
v3 or GNU Lesser General Public License v3.

You may elect to redistribute this code under any of these licenses.

The Eclipse Public License is available at:
http://www.eclipse.org/legal/epl-v10.html

The GNU General Public License v3 and the GNU Lesser General Public
License v3 are available at:
http://www.gnu.org/licenses/

========================================================================
*/

package schemacrawler.tools.command.aichat.mcp;

import schemacrawler.tools.executable.BaseSchemaCrawlerCommand;
import us.fatehi.utility.property.PropertyName;

import java.util.logging.Level;
import java.util.logging.Logger;

/** SchemaCrawler command plug-in. */
public final class McpServerCommand extends BaseSchemaCrawlerCommand<McpServerCommandOptions> {

private static final Logger LOGGER = Logger.getLogger(McpServerCommand.class.getName());

static final PropertyName COMMAND =
new PropertyName("mcpserver", "Allow AI agents access to your schema");

protected McpServerCommand() {
super(COMMAND);
}

@Override
public void checkAvailability() throws RuntimeException {
LOGGER.log(Level.FINE, "No checks required for MCP server");
}

@Override
public void execute() {
LOGGER.log(Level.FINE, "Starting MCP server");
ConnectionService.instantiate(commandOptions, catalog, connection);
SchemaCrawlerMcpServer.start();
}

@Override
public boolean usesConnection() {
// Support commands that use connections
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
========================================================================
SchemaCrawler
http://www.schemacrawler.com
Copyright (c) 2000-2025, Sualeh Fatehi <sualeh@hotmail.com>.
All rights reserved.
------------------------------------------------------------------------

SchemaCrawler is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

SchemaCrawler and the accompanying materials are made available under
the terms of the Eclipse Public License v1.0, GNU General Public License
v3 or GNU Lesser General Public License v3.

You may elect to redistribute this code under any of these licenses.

The Eclipse Public License is available at:
http://www.eclipse.org/legal/epl-v10.html

The GNU General Public License v3 and the GNU Lesser General Public
License v3 are available at:
http://www.gnu.org/licenses/

========================================================================
*/

package schemacrawler.tools.command.aichat.mcp;

import schemacrawler.tools.executable.CommandOptions;


public record McpServerCommandOptions() implements CommandOptions {

public McpServerCommandOptions {
// No options for this command
}

@Override
public String toString() {
// No options for this command
return this.getClass().getName();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
========================================================================
SchemaCrawler
http://www.schemacrawler.com
Copyright (c) 2000-2025, Sualeh Fatehi <sualeh@hotmail.com>.
All rights reserved.
------------------------------------------------------------------------

SchemaCrawler is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

SchemaCrawler and the accompanying materials are made available under
the terms of the Eclipse Public License v1.0, GNU General Public License
v3 or GNU Lesser General Public License v3.

You may elect to redistribute this code under any of these licenses.

The Eclipse Public License is available at:
http://www.eclipse.org/legal/epl-v10.html

The GNU General Public License v3 and the GNU Lesser General Public
License v3 are available at:
http://www.gnu.org/licenses/

========================================================================
*/

package schemacrawler.tools.command.aichat.mcp;

import schemacrawler.schemacrawler.OptionsBuilder;
import schemacrawler.tools.options.Config;
import schemacrawler.tools.options.ConfigOptionsBuilder;


public final class McpServerCommandOptionsBuilder
implements OptionsBuilder<McpServerCommandOptionsBuilder, McpServerCommandOptions>,
ConfigOptionsBuilder<McpServerCommandOptionsBuilder, McpServerCommandOptions> {

public static McpServerCommandOptionsBuilder builder() {
return new McpServerCommandOptionsBuilder();
}

private McpServerCommandOptionsBuilder() {
// No options for this command
}

@Override
public McpServerCommandOptionsBuilder fromConfig(final Config config) {
// No options for this command
return this;
}

@Override
public McpServerCommandOptionsBuilder fromOptions(final McpServerCommandOptions options) {
// No options for this command
return this;
}

@Override
public Config toConfig() {
// No options for this command
return new Config();
}

@Override
public McpServerCommandOptions toOptions() {
return new McpServerCommandOptions();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
========================================================================
SchemaCrawler
http://www.schemacrawler.com
Copyright (c) 2000-2025, Sualeh Fatehi <sualeh@hotmail.com>.
All rights reserved.
------------------------------------------------------------------------

SchemaCrawler is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

SchemaCrawler and the accompanying materials are made available under
the terms of the Eclipse Public License v1.0, GNU General Public License
v3 or GNU Lesser General Public License v3.

You may elect to redistribute this code under any of these licenses.

The Eclipse Public License is available at:
http://www.eclipse.org/legal/epl-v10.html

The GNU General Public License v3 and the GNU Lesser General Public
License v3 are available at:
http://www.gnu.org/licenses/

========================================================================
*/

package schemacrawler.tools.command.aichat.mcp;

import static schemacrawler.tools.executable.commandline.PluginCommand.newPluginCommand;
import schemacrawler.schemacrawler.exceptions.ExecutionRuntimeException;
import schemacrawler.tools.executable.BaseCommandProvider;
import schemacrawler.tools.executable.commandline.PluginCommand;
import schemacrawler.tools.options.Config;
import schemacrawler.tools.options.OutputOptions;

/** SchemaCrawler command plug-in for AI chat. */
public final class McpServerCommandProvider extends BaseCommandProvider {

public McpServerCommandProvider() {
super(McpServerCommand.COMMAND);
}

@Override
public PluginCommand getCommandLineCommand() {
final PluginCommand pluginCommand = newPluginCommand(McpServerCommand.COMMAND);
return pluginCommand;
}

@Override
public McpServerCommand newSchemaCrawlerCommand(final String command, final Config config) {
if (!supportsCommand(command)) {
throw new IllegalArgumentException("Cannot support command, " + command);
}

try {
final McpServerCommandOptions options =
McpServerCommandOptionsBuilder.builder().fromConfig(config).toOptions();

final McpServerCommand scCommand = new McpServerCommand();
scCommand.configure(options);
return scCommand;
} catch (final Exception e) {
throw new ExecutionRuntimeException(e);
}
}

@Override
public boolean supportsOutputFormat(final String command, final OutputOptions outputOptions) {
return true;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package schemacrawler.tools.command.aichat.mcp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;



/**
* Spring Boot application for the SchemaCrawler AI MCP server. This class enables the Spring AI MCP
* server capabilities.
*/
@SpringBootApplication
public class SchemaCrawlerMcpServer {

public static void main(final String[] args) {
SpringAIUtility.isDryRun = true;
start();
}

public static void start() {
SpringApplication.run(SchemaCrawlerMcpServer.class);
}
}
Loading
Loading