JDBC ベースの MCP Server を構築する:開発者ガイド
はじめに
Model Context Protocol(MCP)は、Claude のような AI アシスタントが外部のデータソースやツールと連携するための標準化された方法を提供します。このガイドでは、オープンソースの CData JDBC MCP Server をベースに、JDBC ドライバーを通じてさまざまなデータソースに接続する MCP サーバーの構築方法を解説します。
このサーバーは、AI アシスタントとデータソース間のブリッジとして機能し、SQL を直接記述することなく、自然言語のクエリでデータベース、SaaS アプリケーション、API から情報を取得できます。このガイドを読み終える頃には、あらゆる JDBC 互換ドライバーで動作する同様のサーバーを作成する方法を理解できるでしょう。
プロジェクト構成とセットアップ
まず、プロジェクト構成を見ていきましょう。サーバーは Java パッケージのコアセットを中心に構成されています。
com.cdata.mcp ├── config // 設定管理 ├── resources // MCP リソースの実装 ├── tools // MCP ツールの実装 └── util // ユーティリティクラス
プロジェクトのビルド管理には Maven を使用しています。pom.xml の重要な部分は以下のとおりです。
<dependencies>
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp</artifactId>
<version>0.8.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.16</version>
</dependency>
<!-- テスト用依存関係 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
主な依存関係は、プロトコル実装を提供する MCP Java SDK です。プロジェクトではロギングに SLF4J も使用しています。
コアインターフェース
サーバーは2つの主要なインターフェースを定義しています。
- ITool - クライアントから呼び出せる MCP ツールを表します
- IResource - クライアントからアクセスできる MCP リソースを表します
ITool インターフェースは以下のとおりです。
public interface ITool {
public void register(McpServer.SyncSpec mcp) throws Exception;
public McpSchema.CallToolResult run(Map<String, Object> args);
}
IResource インターフェースは以下のとおりです。
public interface IResource {
public void register(McpServer.SyncSpec mcp, Table table);
public McpSchema.ReadResourceResult run(McpSchema.ReadResourceRequest args);
}
これらのインターフェースは、ツールとリソースが MCP サーバーにどのように登録され、クライアントから呼び出されたときにどのように実行されるかを定義しています。
設定管理
Config クラスは、サーバー設定の解析と検証を担当します。サーバー起動時に指定された .prp ファイルからプロパティを読み込みます。
public class Config {
private static final String PREFIX = "Prefix";
private static final String DRIVER = "DriverClass";
private static final String DRIVER_JAR = "DriverPath";
private static final String JDBC_URL = "JdbcUrl";
private static final String TABLES = "Tables";
private static final String LOG_FILE = "LogFile";
// 設定用のプロパティとメソッド
// ...
}
必須の設定プロパティは以下のとおりです。
- Prefix: MCP ツールのプレフィックス(例:"salesforce")
- DriverClass: JDBC ドライバーのクラス名(例:"cdata.jdbc.salesforce.SalesforceDriver")
- DriverPath: JDBC ドライバー JAR ファイルへのパス
- JdbcUrl: データソースに接続するための JDBC URL
- Tables: 公開するテーブルのリスト(オプション、空の場合はすべてのテーブル)
初期化中、Config クラスは JDBC ドライバーを動的に読み込み、接続を検証することで、サーバーを起動する前にすべてが正しく設定されていることを確認します。
JDBC 統合
Config クラスは、JDBC ドライバーの読み込みとデータソースへの接続作成を処理します。URLClassLoader を使用してドライバー JAR を実行時に読み込みます。
private void loadDriver() throws Exception {
URLClassLoader ucl = new URLClassLoader(
new URL[] {
new File(this.getDriverJar()).toURI().toURL(),
},
this.getClass().getClassLoader()
);
Class dc = ucl.loadClass(this.getDriver());
this.driver = (Driver)dc.getDeclaredConstructor().newInstance();
loadSqlInfo();
}
newConnection メソッドは、データソースへの新しい接続を作成します。
public Connection newConnection() throws SQLException {
return this.driver.connect(this.getJdbcUrl(), new Properties());
}
このアプローチにより、サーバーはあらゆる JDBC ドライバーで動作できるため、さまざまなデータソースへの接続に高い汎用性を発揮します。
テーブル表現
Table クラスは、カタログ、スキーマ、名前コンポーネントを持つデータベーステーブルを表します。
public class Table {
private String _catalog;
private String _schema;
private String _name;
// テーブル操作用のメソッド
// ...
}
このクラスには、さまざまな形式からテーブル名を解析し、MCP リソース用の URL パスに変換するメソッドが含まれています。parseList メソッドは、カンマ区切りのテーブル名リストを解析します。
public static List<Table> parseList(String text) {
List<Table> list = new ArrayList<>();
Tokenizer t = new Tokenizer(text);
while (!t.eof()) {
if (list.size() > 0) {
t.skipListDelimiter();
}
Table table = parseInt(t);
if (table != null) {
list.add(table);
}
}
return list;
}
このクラスには、MCP リソース用の URL パスを含む、異なるテーブル名形式間の変換ユーティリティメソッドも含まれています。
CSV ユーティリティ
サーバーは、すべてのツール結果の出力形式として CSV を使用しています。CsvWriter クラスと CsvUtils クラスが、JDBC の ResultSet オブジェクトを CSV 形式に変換する処理を担当します。
public static String resultSetToCsv(ResultSet rs, String[][] columns) throws SQLException {
CsvWriter csv = new CsvWriter();
ResultSetMetaData meta = rs.getMetaData();
writeMeta(csv, meta, columns);
while (rs.next()) {
writeRow(csv, meta, columns, rs);
}
rs.close();
return csv.end();
}
CsvWriter クラスは、CSV コンテンツを構築するためのシンプルな API を提供します。
public class CsvWriter {
private StringBuilder buffer = new StringBuilder();
public Row row() {
return new Row();
}
public String end() {
return this.buffer.toString();
}
// 行を構築するための内部 Row クラス
// ...
}
ツールの実装
サーバーは3つの MCP ツールを実装しています。
- GetTablesTool - データソースで利用可能なテーブルの一覧を取得
- GetColumnsTool - 特定のテーブルのカラム一覧を取得
- RunQueryTool - SQL SELECT クエリを実行
各ツールは ITool インターフェースを実装し、同様のパターンに従います。
- MCP サーバーにツールを登録し、名前、説明、パラメータスキーマを定義
- ツールの呼び出しを処理し、結果を返す run メソッドを実装
GetTablesTool 実装の一部を以下に示します。
public class GetTablesTool implements ITool {
private Config config;
private Logger logger = LoggerFactory.getLogger(GetTablesTool.class);
public GetTablesTool(Config config) {
this.config = config;
}
public void register(McpServer.SyncSpec mcp) throws Exception {
String schema = new JsonSchemaBuilder()
.addString("catalog", "The catalog name")
.addString("schema", "The schema name")
.build();
mcp.tool(
new McpSchema.Tool(
config.getPrefix() + "_get_tables",
"Retrieves a list of objects, entities, collections, etc. (as tables) available in the data source...",
schema
),
this::run
);
}
// run メソッドの実装
// ...
}
RunQueryTool は、クライアントから提供された SQL クエリを実行するため、特に興味深いツールです。
@Override
public McpSchema.CallToolResult run(Map<String, Object> args) {
String sql = (String)args.get("sql");
this.logger.info("RunQueryTool({})", sql);
try {
try (Connection cn = config.newConnection()) {
List<McpSchema.Content> content = new ArrayList<>();
String csv = queryToCsv(cn, sql);
List<McpSchema.Role> roles = new ArrayList<>();
roles.add(McpSchema.Role.USER);
content.add(
new McpSchema.TextContent(roles, 1.0, csv)
);
return new McpSchema.CallToolResult(content, false);
}
} catch ( Exception ex ) {
throw new RuntimeException("ERROR: " + ex.getMessage());
}
}
リソースの実装
ツールに加えて、サーバーは MCP リソースも公開できます。TableMetadataResource クラスは IResource インターフェースを実装し、データベーステーブルのメタデータを提供します。
public class TableMetadataResource implements IResource {
private Config config;
private static final String[][] META_COLS = new String[][] {
new String[] { "TABLE_CAT", "Catalog" },
new String[] { "TABLE_SCHEM", "Schema" },
new String[] { "TABLE_NAME", "Table" },
new String[] { "COLUMN_NAME", "Column" },
new String[] { "TYPE_NAME", "DataType" }
};
// リソース実装用のメソッド
// ...
}
リソースは URL を通じてアクセスされ、データベースメタデータを参照する方法を提供します。
MCP サーバーの設定
Program クラスはエントリーポイントとして機能し、適切なツールとリソースを持つ MCP サーバーをセットアップします。
public class Program {
private ServerMcpTransport transport;
private Config config;
private McpSyncServer mcpServer;
private static final boolean STDIO = true;
public void init(String configPath) throws Exception {
// 設定の初期化
// ...
this.transport = new StdioServerTransport(new ObjectMapper());
}
public void configureMcp() throws Exception {
McpServer.SyncSpec spec =
McpServer.sync(this.transport)
.serverInfo(this.config.getServerName(), this.config.getServerVersion())
.capabilities(
McpSchema.ServerCapabilities.builder()
.tools(true)
.resources(false, true)
.build()
);
registerResources(this.config, spec);
registerTools(this.config, spec);
this.mcpServer = spec.build();
}
// main メソッドとその他の設定
// ...
}
サーバーは StdioServerTransport を使用して、標準入出力を通じてクライアントと通信します。これにより、Claude Desktop との統合が容易になり、サーバーは子プロセスとして起動されます。
JSON Schema の構築
JsonSchemaBuilder クラスは、ツールパラメータスキーマの定義に使用される JSON Schema ドキュメントを構築するための流暢な API を提供します。
public class JsonSchemaBuilder {
public static final String STRING = "string";
public static final String INTEGER = "integer";
public static final String NUMBER = "number";
public static final String BOOLEAN = "boolean";
public static final String OBJECT = "object";
public static final String SCHEMA_REF = "http://json-schema.org/draft-07/schema#";
private List<Property> properties = new ArrayList<>();
private List<String> required = new ArrayList<>();
public JsonSchemaBuilder addString(String name, String description) {
this.properties.add(new Property(name, STRING, description));
return this;
}
// JSON Schema を構築するためのその他のメソッド
// ...
}
このクラスは、ツールパラメータ用の JSON Schema ドキュメントの作成を簡素化し、各ツールの期待される入力を簡単に定義できます。
テスト
プロジェクトには、Table クラスや JsonSchemaBuilder などの主要コンポーネントのユニットテストが含まれています。これらのテストにより、コア機能が期待どおりに動作することを確認します。
public class TableTests {
@Test
public void justName() {
Table table = Table.parse("mytable");
Assert.assertEquals("mytable", table.name());
}
// その他のテストメソッド
// ...
}
テストは開発プロセスの重要な部分であり、コンポーネントが完全なサーバーに統合される前に、個別に正しく動作することを確認します。
すべてをまとめる
アプリケーションのエントリーポイントは、Program クラスの main メソッドです。
public static void main(String[] args) throws Exception {
if (args.length < 1) {
System.err.println("Usage: <properties-file-path>");
System.exit(-1);
}
String path = args[0];
final Program p = new Program();
p.init(args[0]);
p.configureMcp();
if (!STDIO) {
//p.runHttpServer();
} else {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
synchronized (p) {
p.notify();
}
}
});
synchronized (p) {
p.wait();
p.mcpServer.closeGracefully();
}
}
}
このメソッドは、設定を初期化し、MCP サーバーをセットアップしてから、プロセスが終了するのを待機します。Claude Desktop で実行する場合、サーバーはクライアントが切断するかプロセスを終了するまで動作し続けます。
Claude Desktop との統合
サーバーを Claude Desktop で使用するには、Claude Desktop がサーバーに接続するように設定する必要があります。これは、適切な MCP サーバー設定を含む claude_desktop_config.json ファイルを作成することで行います。
{
"mcpServers": {
"salesforce": {
"command": "PATH\\TO\\java.exe",
"args": [
"-jar",
"PATH\\TO\\CDataMCP-jar-with-dependencies.jar",
"PATH\\TO\\Salesforce.prp"
]
}
}
}
この設定は、サーバープロセスの起動方法と使用する設定ファイルを Claude Desktop に指示します。
まとめ
JDBC データソースに接続する MCP サーバーの構築は、AI アシスタントの機能を拡張する強力な方法です。このガイドで説明したパターンとテクニックに従うことで、さまざまなデータソースに接続する独自の MCP サーバーを作成できます。
主要なコンポーネントは以下のとおりです。
- JDBC ドライバーと接続の設定管理
- データをクエリするための MCP ツール実装
- 結果の CSV フォーマット
- MCP サーバーの設定とライフサイクル管理
これらのコンポーネントを組み合わせることで、AI アシスタントと JDBC ドライバーを持つあらゆるデータソースの間にブリッジを作成し、データとの自然言語によるインタラクションを実現できます。
無料トライアルと詳細情報
JDBC ドライバー上に独自の MCP Server を構築する準備ができたら、GitHub リポジトリをチェックして、CData JDBC Driver の30日間無料トライアルをダウンロードしてください。
自己完結型の MCP Server をお探しの場合は、無料のベータサーバーをご確認ください。